I've got this source xml:
<source>
<category id="1" />
<item1 />
<item2 />
<category id="2"/>
<item1 />
<item2 />
</source>
As you can see all items have the same hierarchy.
And I need to "translate"/serialize it to another XML like this:
<source>
<category id="1">
<item1 />
<item2 />
</category>
<category id="2">
<item1 />
<item2 />
</category>
</source>
Where "items" are children of "category".
I'm using XmlPullParser and XmlSerializer from Android tools but I don't mind to use another if they are compatible with Android environment
Tx
I've found another way using XSLT:
This way we use XML-only specific tools for transform without handling any data with objects.
create a transform.xsl file to handle transformation:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" encoding="UTF-8" />
<xsl:strip-space elements="*" />
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="source">
<source>
<xsl:apply-templates select="category" />
</source>
</xsl:template>
<xsl:template match="category">
<xsl:variable name="place" select="count(preceding-sibling::category)" />
<category>
<xsl:attribute name="id">
<xsl:value-of select="#id" />
</xsl:attribute>
<xsl:apply-templates select="following-sibling::*[not(self::category)]">
<xsl:with-param name="slot" select="$place" />
</xsl:apply-templates>
</category>
</xsl:template>
<xsl:template match="item1">
<xsl:param name="slot" />
<xsl:choose>
<xsl:when test="count(preceding-sibling::category) = $slot + 1">
<xsl:copy-of select="." />
</xsl:when>
<xsl:otherwise />
</xsl:choose>
</xsl:template>
<xsl:template match="item2">
<xsl:param name="slot" />
<xsl:choose>
<xsl:when test="count(preceding-sibling::category) = $slot + 1">
<xsl:copy-of select="." />
</xsl:when>
<xsl:otherwise />
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
And then write the code to handle transformation to a file with desired output data.xml
AssetManager am = getAssets();
xml = am.open("source.xml");
xsl = am.open("transform.xsl");
Source xmlSource = new StreamSource(xml);
Source xsltSource = new StreamSource(xsl);
TransformerFactory transFact = TransformerFactory.newInstance();
Transformer trans = transFact.newTransformer(xsltSource);
File f = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/data.xml");
StreamResult result = new StreamResult(f);
trans.transform(xmlSource, result);
And its done.
more info here:
http://www.dpawson.co.uk/xsl/sect2/flatfile.html
For an easy xml format this shouldn't be that hard.
A possible way to read the first xml file with a sax parser would be:
public class MySaxHandler extends DefaultHandler {
private List<Category> items = new LinkedList<Category>();
private Category currentCategory;
public void startElement(String uri, String localName, String qName, Attributes attributes) {
if (localName.equals("category")) {
currentCategory = new Category(attributes.getValue("id"));
items.add(currentCategory);
}
if (localName.equals("item1") {
currentCategory.setItem1(new Item1(...));
}
if (localName.equals("item2") {
currentCategory.setItem2(new Item2(...));
}
}
}
For each <category> tag you create a new Category object. The following items will be added to the last category object. While reading the contents you create the hierarchy you need later (items are added to the appropriate category).
It should be easy to transform this code to use XmlPullParser instead of sax parser. I just used sax because I am more familiar with it.
When you finished reading the first file you need to write your hierarchy to a new file.
You can do this in a way like this:
StringBuilder b = new StringBuilder();
for (int i = 0; i < categories.size(); i++) {
b.append(categories.get(i).getXml());
}
// write content of b into file
getXml() for each category could look like:
public String getXml() {
StringBuilder b = new StringBuilder();
b.append("<category id=\"" + this.id + "\">");
for (int i = 0; i < items.size(); i++) {
b.append(items.get(i).getXml());
}
b.append("</category>");
return b.toString();
}
Each item creates its own xml in its getXml() method which could be
public String getXml() {
return "<item1 />";
}
in the easiest case.
Please note that building xml by hand is only suitable if you xml structure stays that simple. If the structure is becoming more complicated you should make use of some lightweight xml libraries that work on Android (like xstream).
Related
I'm developing a NavigationRail and I wanted to display the text of each item in two lines, as show in this official example, but it keeps getting ellipsized in the end. I've tried to redefine the applying style but nothing seems to work.
Any help? Thanks in advance!
As per the Navigation rail - Material Design documentation it seems that there is no an official way to set multiple lines for an item text. But you can achieve this with a simple workaround like in the below example:
1.Define the NavigationRailView in xml like the below:
<com.google.android.material.navigationrail.NavigationRailView
android:id="#+id/navigation_rail"
android:layout_width="wrap_content"
android:layout_height="match_parent"
app:labelVisibilityMode="labeled"
app:menu="#menu/navigation_rail_menu"/>
where #menu/navigation_rail_menu contains the below sample menu items:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/menu_item_all_files"
android:enabled="true"
android:icon="#android:drawable/ic_menu_gallery"
android:title="All\nFiles" />
<item
android:id="#+id/menu_item_recent"
android:enabled="true"
android:icon="#android:drawable/ic_menu_slideshow"
android:title="Recent\nFiles" />
<item
android:id="#+id/menu_item_all_pictures"
android:enabled="true"
android:icon="#android:drawable/ic_menu_gallery"
android:title="All\nPictures" />
</menu>
2.And for each item change the Max number of lines in a programmatically way like in the below example:
//get the NavigationRailView
NavigationRailView navigationRailView = findViewById(R.id.navigation_rail);
//menu item 1
NavigationBarItemView item1 = navigationRailView.findViewById(R.id.menu_item_all_files);
BaselineLayout item1BaselineLayout = item1.findViewById(R.id.navigation_bar_item_labels_group);
setItemMaxLines(item1BaselineLayout, 2);
//menu item 2
NavigationBarItemView item2 = navigationRailView.findViewById(R.id.menu_item_recent);
BaselineLayout item2BaselineLayout = item2.findViewById(R.id.navigation_bar_item_labels_group);
setItemMaxLines(item2BaselineLayout, 2);
//menu item 3
NavigationBarItemView item3 = navigationRailView.findViewById(R.id.menu_item_all_pictures);
BaselineLayout item3BaselineLayout = item3.findViewById(R.id.navigation_bar_item_labels_group);
setItemMaxLines(item3BaselineLayout, 2);
where setItemMaxLines() is a helper function to change for each Item the MaxLines:
private void setItemMaxLines(BaselineLayout baselineLayout, int maxLines){
if(baselineLayout!=null){
for(int i=0; i<baselineLayout.getChildCount(); i++){
View child = baselineLayout.getChildAt(i);
if(child instanceof MaterialTextView){
((MaterialTextView)child).setMaxLines(maxLines);
((MaterialTextView)child).setGravity(Gravity.CENTER);
}
}
}
}
Result:
The complete reference of the BottomNavigationView used is:
com.google.android.material.bottomnavigation.BottomNavigationView
The scenario is as follows, imagine you have 1 activity with a navigation menu at the bottom, the menu has 5 items (A, B, C, D, E), and there are also 2 hidden fragments (F, G) , all fragments extend androidx.fragment.app.Fragment
bottom_nav_menu.xml:
<?xml version="1.0" encoding="utf-8"?>
<menu android:id="#+id/menu_menu" xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#+id/navigation_a" android:title="" />
<item android:id="#+id/navigation_b" android:title="" />
<item android:id="#+id/navigation_c" android:title="" />
<item android:id="#+id/navigation_d" android:title="" />
<item android:id="#+id/navigation_e" android:title="" />
</menu>
navigation_default.xml:
<?xml version="1.0" encoding="utf-8"?>
<navigation android:id="#+id/mobile_navigation" app:startDestination="#+id/navigation_a">
<fragment android:id="#+id/navigation_a" tools:layout="#layout/fragment_a" />
<fragment android:id="#+id/navigation_b" tools:layout="#layout/fragment_b" />
<fragment android:id="#+id/navigation_c" tools:layout="#layout/fragment_c" />
<fragment android:id="#+id/navigation_d" tools:layout="#layout/fragment_d" />
<fragment android:id="#+id/navigation_e" tools:layout="#layout/fragment_e" />
<fragment android:id="#+id/navigation_f" tools:layout="#layout/fragment_f" /><!-- hidden -->
<fragment android:id="#+id/navigation_g" tools:layout="#layout/fragment_g" /><!-- hidden -->
</navigation>
Activity.java:
AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
R.id.navigation_a,
R.id.navigation_b,
R.id.navigation_c,
R.id.navigation_d,
R.id.navigation_e,
R.id.navigation_f,
R.id.navigation_g).build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_activity_main);
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
NavigationUI.setupWithNavController(activityMainBinding.navView, navController);
I use navController.navigate(destination) to navigate to these hidden fragments, in general the method will handle any automatic transitions made by the program, to reproduce the problem let's say you are in a visible fragment, for example C, now if we use the navigate function to go from C to F navController.navigate(R.id.navigation_f), then again from F to A navController.navigate(R.id.navigation_a), if the user manually clicks on C it will redirect him to A (which means the content of A will be displayed rather than the content of C), and what I noticed that to avoid this weird behavior I need to close the automatic circle, which means the cycle should be: [C->F->C->A] rather than: [C->F->A], any better options?
I end up using actions instead of navigating directly to a specific fragment, an action must have a source and a destination, so rather than using R.id.navigation_f I used R.id.action_c_to_f, to keep things simple, I created a class to handle all possible transitions in the app:
Navigation.java:
public class Navigation {
/**/
public static void navigate(#IdRes int resId){
/**/
int[] destinations = new int[]{
/**/
R.id.navigation_a,
R.id.navigation_b,
R.id.navigation_c,
R.id.navigation_d,
R.id.navigation_e,
R.id.navigation_f,
R.id.navigation_g
};
/**/
int[] actions = new int[]{
/**/
R.id.action_c_to_f,
R.id.action_f_to_a,
/**/
};
/**/
int[][] transitions = new int[][]{
/**/
new int[]{ 2, 5, actions[0] }, // R.id.navigation_c -> R.id.navigation_f
new int[]{ 5, 0, actions[1] }, // R.id.navigation_f -> R.id.navigation_a
/**/
};
/**/
int i = indexOf(destinations, navController.getCurrentDestination().getId());
/**/
int j = indexOf(destinations, resId);
/**/
for (int k = 0; k < transitions.length; k++){
/**/
if (transitions[k][0] == i && transitions[k][1] == j){
/**/
int x = k;
/**/
new Handler(Looper.getMainLooper()).post(() -> navController.navigate(transitions[x][2]));
/**/
return;
/**/
}
}
}
/**/
public static int indexOf(int[] array, int value){
/**/
for (int i = 0; i < array.length; i++){
/**/
if (array[i] == value){
/**/
return i;
/**/
}
}
/**/
return -1;
/**/
}
}
navigation_default.xml:
<?xml version="1.0" encoding="utf-8"?>
<navigation android:id="#+id/mobile_navigation" app:startDestination="#+id/navigation_a">
<fragment android:id="#+id/navigation_a" tools:layout="#layout/fragment_a" />
<fragment android:id="#+id/navigation_b" tools:layout="#layout/fragment_b" />
<fragment android:id="#+id/navigation_d" tools:layout="#layout/fragment_d" />
<fragment android:id="#+id/navigation_e" tools:layout="#layout/fragment_e" />
<fragment android:id="#+id/navigation_g" tools:layout="#layout/fragment_g" />
<fragment android:id="#+id/navigation_c" tools:layout="#layout/fragment_c">
<action android:id="#+id/action_c_to_f"
app:destination="#id/navigation_f"
app:enterAnim="#anim/nav_default_enter_anim"
app:exitAnim="#anim/nav_default_exit_anim"
app:popEnterAnim="#anim/nav_default_pop_enter_anim"
app:popExitAnim="#anim/nav_default_pop_exit_anim"
app:popUpTo="#+id/navigation_c"
app:popUpToInclusive="true" />
</fragment>
<fragment android:id="#+id/navigation_f" tools:layout="#layout/fragment_f">
<action android:id="#+id/action_f_to_a"
app:destination="#id/navigation_a"
app:enterAnim="#anim/nav_default_enter_anim"
app:exitAnim="#anim/nav_default_exit_anim"
app:popEnterAnim="#anim/nav_default_pop_enter_anim"
app:popExitAnim="#anim/nav_default_pop_exit_anim"
app:popUpTo="#+id/navigation_a"
app:popUpToInclusive="true" />
</fragment>
</navigation>
Note: I believe this issue is related to an internal bug in Android, this is just a workaround until this issue is resolved, normally navigating directly to a specific fragment should work without interfering with manual navigation of the user, this should work as if the user manually jumped to the visible|hidden fragment.
I have this problem with telerik RadButton, im trying to use the platform specific UseDefaultShadow of android, which works fine on a regular Button but not a RadButton.
The way im doing it is by setting a global style in App.xaml
<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:telerikInput="clr-namespace:Telerik.XamarinForms.Input;assembly=Telerik.XamarinForms.Input"
xmlns:android="clr-namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core"
x:Class="App3.App">
<Application.Resources>
<ResourceDictionary>
<Style TargetType="telerikInput:RadButton">
<Setter Property="android:Button.UseDefaultPadding" Value="false" />
<Setter Property="android:Button.UseDefaultShadow " Value="true" />
<Setter Property="FontFamily" Value="Quicksand-Regular.otf#Quicksand" />
<Setter Property="MinimumHeightRequest" Value="50" />
<Setter Property="MinimumWidthRequest" Value="50" />
<Setter Property="BackgroundColor" Value="DarkTurquoise"/>
<Setter Property="TextColor" Value="White" />
<Setter Property="CornerRadius" Value="60" />
</Style>
<Style TargetType="Button">
<Setter Property="android:Button.UseDefaultPadding" Value="false" />
<Setter Property="android:Button.UseDefaultShadow" Value="true" />
<Setter Property="FontFamily" Value="Quicksand-Regular.otf#Quicksand" />
<Setter Property="MinimumHeightRequest" Value="50" />
<Setter Property="MinimumWidthRequest" Value="50" />
<Setter Property="BackgroundColor" Value="DarkTurquoise"/>
<Setter Property="TextColor" Value="White" />
<Setter Property="CornerRadius" Value="60" />
</Style>
</ResourceDictionary>
</Application.Resources>
</Application>
And im creating my buttons by code like this..
public RadButton CustomButtonCreator(FoodModel food, int row, int col)
{
var data = Xamarin.Forms.Application.Current.Properties;
RadButton custombutton = new RadButton
{
Text =food.Name,
ClassId = food.ClassId.ToString(),
VerticalOptions = LayoutOptions.Center,
HorizontalOptions = LayoutOptions.Center,
MinimumHeightRequest = 50,
WidthRequest = 300,
BackgroundColor = Color.DarkTurquoise,
TextColor = Color.Black,
BorderColor = Color.White,
CornerRadius = 60
};
buttonlist.Add(custombutton);
custombutton.Clicked += async (sender, args) => OnButtonClicked(sender, args);
var stackLayout = new StackLayout
{
Children = { custombutton }
};
custombutton.Behaviors.Add(new LongPressBehavior()); //LONG PRESS BEHAVIORS
var yo = custombutton.Behaviors[0] as LongPressBehavior; //LONG PRESS BEHAVIORS
yo.LongPressed += OnButtonLongPressed; //LONG PRESS BEHAVIORS
custombutton.Behaviors[0] = yo; //LONG PRESS BEHAVIORS
Grid.SetRow(stackLayout, row);
Grid.SetColumn(stackLayout, col);
ButtonGrid.Children.Add(stackLayout);
return custombutton;
}
As i said it works fine for the regular button.
Now for the RadButton , the weird thing is that while the shadow doesn't get loaded on init, if i change the colors of the buttons back and forth the shadows appear!
I also tried setting the shadow by code
using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.AndroidSpecific;
....
custombutton.On <Android>().SetUseDefaultPadding(true).SetUseDefaultShadow(true);
But the result is exactly the same, which suggests that the shadow value is set but not taken into account at first. Are the RadButton's properties overridding the default ones? And is there a way to overcome this problem without having to use regular buttons?
I have PreferenceFragment and a PreferenceActivity from which I add headers from a XML file on this way:
PreferenceActivity
#Override
public void onBuildHeaders(List<Header> target) {
if(DEBUG) Log.i("PreferenceActivity", "onBuildHeaders() -> LogicAnalizerPrefs");
if(android.os.Build.VERSION.SDK_INT >= 12) {
loadHeadersFromResource(R.xml.preference_header_logicanalizer, target);
}
}
PreferenceFragment:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(DEBUG) Log.i("PreferenceFragment", "onCreate() -> LogicAnalizerPrefsFragment");
int res = getActivity().getResources().getIdentifier(getArguments().getString("logicprefsheaders"), "xml", getActivity().getPackageName());
addPreferencesFromResource(res);
}
And the XML file where I have the headers is:
R.xml.preference_header_logicanalizer:
<header
android:fragment="com.protocolanalyzer.andres.LogicAnalizerPrefsFragment"
android:icon="#drawable/settings"
android:title="General" >
<extra
android:name="logicprefsheaders"
android:value="logicgeneral" />
</header>
<header
android:fragment="com.protocolanalyzer.andres.LogicAnalizerPrefsFragment"
android:icon="#drawable/settings"
android:title="Canal 1" >
<extra
android:name="logicprefsheaders"
android:value="c1analizerprefs" />
</header>
<header
android:fragment="com.protocolanalyzer.andres.LogicAnalizerPrefsFragment"
android:icon="#drawable/settings"
android:title="Canal 2" >
<extra
android:name="logicprefsheaders"
android:value="c2analizerprefs" />
</header>
<header
android:fragment="com.protocolanalyzer.andres.LogicAnalizerPrefsFragment"
android:icon="#drawable/settings"
android:title="Canal 3" >
<extra
android:name="logicprefsheaders"
android:value="c3analizerprefs" />
</header>
<header
android:fragment="com.protocolanalyzer.andres.LogicAnalizerPrefsFragment"
android:icon="#drawable/settings"
android:title="Canal 4" >
<extra
android:name="logicprefsheaders"
android:value="c4analizerprefs" />
</header>
And this is one of my xml files which is used to display one of the preferences when a Header is clicked:
c1analizerprefs.xml:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<PreferenceCategory android:title="#string/AnalyzerProtocolTitle1" >
<ListPreference
android:defaultValue="0"
android:entries="#array/protocolList"
android:entryValues="#array/protocolValues"
android:key="protocol1"
android:summary="#string/AnalyzerProtocolSummary"
android:title="#string/AnalyzerProtocolTitle1" />
<ListPreference
android:defaultValue="1"
android:entries="#array/channelNames"
android:entryValues="#array/protocolValues"
android:key="SCL1"
android:summary="#string/AnalyzerSCLSummary"
android:title="#string/AnalyzerSCLTitle" />
<EditTextPreference
android:defaultValue="9600"
android:title="#string/AnalyzerBaudTitle"
android:key="BaudRate1"
android:summary="#string/AnalyzerBaudSummary"
android:inputType="number" />
</PreferenceCategory>
</PreferenceScreen>
So in a large screen I have this result as expected and I use only one PreferenceFragment to add my 4 headers.
But my four Preferences defined in the XML like c1analizerprefs.xml are almost the same the only change is a number (For example: Pref. 1, Pref. 2, ...) so I want to add them in Java so I can use a for() to add 4 or more Preferences easily changing the number of repetitions because the text is always the same I only change a number so on this way I don't need to create one XML file for each preference, I create them dynamically in Java.
How can I add a Preference to a Header in Java instead of using a XML file? In PreferenceFragment I only have addPreferencesFromResource() or addPreferencesFromIntent(). Is any way to add a Preference Object?
Just in case someone is actually looking for some code...
The following example loads some headers from XML and then appends to them programatically.
1st override onBuildHeaders inside your PreferenceActivity:
#Override
public void onBuildHeaders(List<Header> target) {
loadHeadersFromResource(R.xml.preference_headers, target);
// Add layer as a new settings header
Header header = new Header();
header.title = "More Settings";
header.summary = "Change even more settings";
header.fragment = SettingsFragment.class.getName();
Bundle b = new Bundle();
b.putString("category", "MoreSettings");
header.fragmentArguments = b;
target.add(header);
}
Then override onHeaderClick inside your PreferenceActivity. Note that you pass the header's fragmentArguments to the fragment so you can detect which header was clicked (category):
#Override
public void onHeaderClick(Header header, int position) {
this.startPreferencePanel(SettingsFragment.class.getName(), header.fragmentArguments, header.titleRes, header.title, null, 0);
}
Then inside your PreferenceFragment override onCreate:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
mCategory = getArguments().getString("category");
}
else {
// Orientation Change
mCategory = savedInstanceState.getString("category");
}
if (mCategory.equals("Map")) {
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.preferences_map);
}
if (mCategory.equals("MoreSettings")) {
// Load the preferences from an XML resource
PreferenceScreen preferenceScreen = getPreferenceManager().createPreferenceScreen(getActivity());
// add prefrences using preferenceScreen.addPreference()
this.setPreferenceScreen(preferenceScreen);
}
}
In case you want to see the xml for predefined headers.
res/xml/preference_headers.xml
<?xml version="1.0" encoding="utf-8"?>
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
<header
android:fragment="com.appName.fragments.SettingsFragment"
android:title="#string/pref_category_map_title"
android:summary="#string/pref_category_map_sum" >
<!-- key/value pairs can be included as arguments for the fragment. -->
<extra android:name="category" android:value="Map" />
</header>
</preference-headers>
res/xml/preferences_map.xml
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<CheckBoxPreference
android:key="pref_zoomToCurrentLocationOnStart"
android:title="#string/pref_zoomToCurrentLocationOnStart_title"
android:summary="#string/pref_zoomToCurrentLocationOnStart_sum"
android:defaultValue="true" />
<CheckBoxPreference
android:key="pref_myLocation"
android:title="#string/pref_myLocation_title"
android:summary="#string/pref_myLocation_sum"
android:defaultValue="true" />
</PreferenceScreen>
Adding headers dynamically will not be easy. As android documentation states:
Blockquote Typical implementations will use loadHeadersFromResource(int, List) to fill in the list from a resource.
If you still want to go with a dynamic solution, you might want to take a look at the source code of loadHeadersFromResource (for example, here http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.1_r1/android/preference/PreferenceActivity.java#PreferenceActivity.loadHeadersFromResource(int%2Cjava.util.List) )
To sum up,
you need to create a PreferenceActivity.Header object,
prepare it for usage by setting its various properties that can be found in documentation: http://developer.android.com/reference/android/preference/PreferenceActivity.Header.html,
and finally add that created header to the list: target.add(header); (target is the argument that is passed to onBuildHeaders)
Hopefully I haven't misunderstood your question:
getPreferenceScreen().addPreference( preference );
Great answer by #goodies4uall
I will merely add (since i cannot yet edit posts) that you need not do anything fancy in onHeaderClick().
If you wish to add a header outside of onBuildHeaders() the best way to do it is to invalidate them and allow onBuildHeaders() to add the new one when it next fires (invalidateHeaders will cause onBuildHeaders to fire again), like so:
private boolean flagToAddMyNewHeader = false;
public void someEventFiredThatRequiresANewHeader() {
flagToAddMyNewHeader = true;
invalidateHeaders();
}
And then your onBuildHeaders:
#Override
public void onBuildHeaders(List<Header> target) {
loadHeadersFromResource(R.xml.preference_headers, target);
if (flagToAddMyNewHeader) {
flagToAddMyNewHeader = false;
Header header = new Header();
header.titleRes = R.string.my_new_header_title;
header.iconRes = R.drawable.my_new_header_icon;
header.fragment = MyNewHeaderFragment.class.getName();
Bundle b = new Bundle();
b.putString("category", "MyNewHeader");
header.fragmentArguments = b;
// inserts my new header as the 2nd entry
target.add(1, header);
}
}
I am using WCF webservice in android... i've to authenticate the user with windows authentication
Here's my java code
public void test3()
{
try
{
DefaultHttpClient httpClient = new DefaultHttpClient();
URI uri = new URI("http://10.0.2.2/MicrosoftDynamicsAXAif60/testgroup/xppservice.svc");
// Send GET request to <service>/GetText
HttpPost httpget = new HttpPost(uri);
StringEntity se = null;
// declare it as XML
se = new StringEntity(this.returnXML(), HTTP.UTF_8);
se.setContentType("text/xml");
httpget.setHeader("Content-Type", "application/xml;charset=UTF-8");
httpget.setEntity(se);
//NTCredentials nt = new NTCredentials("username", "pass", "", "domain");
httpClient.getCredentialsProvider().setCredentials(AuthScope.ANY, nt);
HttpResponse response = httpClient.execute(httpget);
HttpEntity responseEntity = response.getEntity();
// Read response data into buffer
long intCount = responseEntity.getContentLength();
char[] buffer = new char[(int)intCount];
InputStream stream = responseEntity.getContent();
InputStreamReader reader = new InputStreamReader(stream, "UTF-8");
try
{
reader.read(buffer);
String str = new String(buffer);
Toast.makeText(this, str, 10);
}
catch (IOException e)
{e.printStackTrace();}
stream.close();
}
catch (Exception e) {e.printStackTrace();}
}
Now am getting HTTP Error 401.2 - Unauthorized
Here's xml config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="wsHttpBindingWithWindowsAuth">
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="Windows" >
<extendedProtectionPolicy protectionScenario="TransportSelected" policyEnforcement="Always" />
</transport>
<message clientCredentialType="Windows" />
</security>
</binding>
</wsHttpBinding>
<basicHttpBinding>
<binding name="basicHttpBindingWithWindowsAuth">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows" />
</security>
</binding>
</basicHttpBinding>
<netTcpBinding>
<binding name="DefaultServiceGroupBinding" closeTimeout="00:05:00" openTimeout="00:05:00" receiveTimeout="00:10:00" sendTimeout="00:05:00" transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions" hostNameComparisonMode="StrongWildcard" listenBacklog="10" maxBufferPoolSize="524288" maxBufferSize="81920000" maxConnections="10" maxReceivedMessageSize="81920000">
<readerQuotas maxDepth="32000000" maxStringContentLength="81920000" maxArrayLength="163840000" maxBytesPerRead="4096000" maxNameTableCharCount="16384000" />
<reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />
<security mode="Transport">
<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
<message clientCredentialType="Windows" />
</security>
</binding>
</netTcpBinding>
</bindings>
<services>
<!--ROUTING SERVICE -->
<!--Define a routing service. The Behavior Configuration field references the name of the Routing Behavior that this service will use-->
<service name="System.ServiceModel.Routing.RoutingService" behaviorConfiguration="routingData">
<!--
Define and configure the endpoint we want the Router to listen on and the Contract we want it to use
Router provided contracts are: ISimplexDatagramRouter, ISimplexSessionRouter, IRequestReplyRouter, and IDuplexSessionRouter.
-->
<endpoint address=""
binding="basicHttpBinding"
bindingConfiguration="basicHttpBindingWithWindowsAuth"
name="reqReplyEndpoint"
contract="System.ServiceModel.Routing.IRequestReplyRouter" >
<identity>
<servicePrincipalName />
</identity>
</endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="routingData">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="False" />
<!--
Define the Routing Behavior. The routingTableName attribute contains the name of the Routing Table that this behavior will use.
<routing filterTableName="AosRoutingTable" />
-->
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="clientEndpointBehavior">
<clientEndpointBehavior />
</behavior>
</endpointBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add
name="clientEndpointBehavior"
type="Microsoft.Dynamics.Ax.Services.Host.ClientEndpointBehaviorExtension, Microsoft.Dynamics.IntegrationFramework.WebService.Process, Version=6.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</behaviorExtensions>
</extensions>
<!--
Define the client endpoints that we want the Router to communicate with. These are the destinations that the Router will send messages to.
Note that we always define the client endpoint contract as *. The router will mirror the selected contract defied on the endpoint
on which the message was originally recieved.
-->
<client>
<endpoint name="ServiceCustom" address="net.tcp://AOS_SERVICE_HOST/DynamicsAx/Services/testgroup" binding="netTcpBinding" bindingConfiguration="DefaultServiceGroupBinding" contract="*" behaviorConfiguration="clientEndpointBehavior">
<identity><servicePrincipalName /></identity>
</endpoint>
</client>
<routing>
<!--
Define the filters that we want the router to use. In this example we define a MatchAll message filter, which will match all messages it sees.
-->
<filters>
<filter name="MatchServiceCustom" filterType="Custom" customType="Microsoft.Dynamics.Ax.Services.Host.MatchAxServiceFilter, Microsoft.Dynamics.IntegrationFramework.WebService.Process" filterData="http://tempuri.org/ServiceCustom" />
</filters>
<!-- Define the routing table that contains the matchAll filter defined in the filters section. -->
<filterTables>
<filterTable name="AosRoutingTable">
<!-- Map the filter to a client endpoint that was previously defined. Messages that match this filter will be sent to this destination. -->
<add filterName="MatchServiceCustom" endpointName="ServiceCustom" />
</filterTable>
</filterTables>
</routing>
</system.serviceModel>
</configuration>
The xml that i am sending
<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
<s:Header>
<a:Action>http://tempuri.org/ServiceCustom/CreateMyDataObject</a:Action>
<h:CallContext i:nil="true" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:h="http://schemas.microsoft.com/dynamics/2010/01/datacontracts" />
<a:MessageID>urn:uuid:c70fb5f6-1345-469d-84fd-bb24500d329a</a:MessageID>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
</s:Header>
<s:Body>
<ServiceCustomCreateMyDataObjectRequest xmlns="http://tempuri.org">
<_s>23</_s>
</ServiceCustomCreateMyDataObjectRequest>
</s:Body>
</s:Envelope>
Solved!
http://mrrask.wordpress.com/2009/08/21/android-authenticating-via-ntlm/
(The OP answered by posting a web link. Converted to a Community Wiki Answer; Question with no answers, but issue solved in the comments (or extended in chat) )
The link was: http://mrrask.wordpress.com/2009/08/21/android-authenticating-via-ntlm/
A summary of which is:
It uses an HttpClient which needs an AuthScemeFactory that will return the AuthScheme descendant NTLMScheme. NTLMScheme requires an implementation of the NTLMEngine interface to operate on. The JCIFS library has functionality for creating type 1 2 & 3 NTLM messages making it a perfect candidate for implementing the NTLMEngine.