TabActivity with ActivityGroup and PreferenceActivity child - android

I have a TabActivity where each tab has ActivityGroup.
On the home ActivityChild of the first group I have an menu option, which gives to the user the option to open preferences.
When I click "Preferences" on menu, I start PreferenceActivity inside ActivityGroup, which shows PreferenceActivity on the first tab.
The problem is when I click on any specific preference which has to show a Dialog (for EditTextPreference).
I have the following exception:
android.view.WindowManager$BadTokenException: Unable to add window -- token android.app.LocalActivityManager$LocalActivityRecord#405d3a20
I understand that the problem is because Dialog to be shown by PreferenceActivity uses wrong context, BUT i don't now how change the context of created dialog.
Belows is the PreferenceActivity I've created.
public class PreferencesActivity extends PreferenceActivity implements OnSharedPreferenceChangeListener{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.preferences);
addPreferencesFromResource(R.xml.preferences);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
prefs.registerOnSharedPreferenceChangeListener(this);
}
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,String key) {
}
}
I don't want to create custom dialogs. I want to use the mechanism of PreferenceActivity for that.
Below is the code that I'm using to add to group:
i = new Intent(MyActivity.this, PreferencesActivity.class);
TabGroupActivity parentActivity = (TabGroupActivity) getParent();
parentActivity.startChildActivity("PreferencesActivity", i);
Any ideas?

This is very common problem with dialog's in Tab Host.
Actually the Activity context is not sufficient to show a Dialog in Tab.
You have to use the context of your GroupActivity for the dialog to be enabled without exception

Related

Save App Preferences from PreferenceScreen when pressing back button

I'm trying to learn how to develop Android apps. I followed a video tutorial on YouTube, and it ended by adding a simple App Settings screen to the application.
However, there's one point that bothers me: when I press the back button on my phone's navigation bar, the changed settings aren't applied.
I have tried searching on Google, but none of the solutions I found have worked. The fact that I don't yet understand 100% of what's happening on the proposed solutions may also contribute to my difficulty on solving this one problem.
The behavior I expect from the app is that when I press the back button on the navigation bar, the changed settings should be applied.
For instance, I have a setting for dark background, which is controlled by a checkbox. The current behavior is: I check the setting for dark background. When I press the back button on the navigation bar, the setting isn't applied (I do have a method that loads the preferences on my MainActivity). What I want to happen is when I press the back button, the dark background is applied in this case.
From what I understand, I believe that overriding onBackPressed should do the trick, but I don't know what should be executed in order to properly apply the settings.
Here are the class and layout of my PreferenceScreen. Regarding the strings on the XML, they aren't actually hard-coded. I just copied the English values here to show the text that should appear on the interface.
public class AppPreferences extends AppCompatActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_note_detail);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
SettingsFragment settingsFragment = new SettingsFragment();
fragmentTransaction.add(android.R.id.content, settingsFragment, "SETTINGS_FRAGMENT");
fragmentTransaction.commit();
}
public static class SettingsFragment extends PreferenceFragment
{
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.app_preferences);
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="General">
<EditTextPreference
android:title="Notebook"
android:summary="The title that will be used on the main action bar."
android:key="title"
android:defaultValue="Notebook" />
</PreferenceCategory>
<PreferenceCategory
android:title="Color">
<CheckBoxPreference
android:title="Dark Background"
android:summary="Is the main background color dark?"
android:key="background_color"
android:defaultValue="false" />
</PreferenceCategory>
</PreferenceScreen>
You will need to use
public class AppPreferences extends AppCompatActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_note_detail);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
SettingsFragment settingsFragment = new SettingsFragment();
fragmentTransaction.add(android.R.id.content, settingsFragment, "SETTINGS_FRAGMENT");
fragmentTransaction.commit();
}
public static class SettingsFragment extends PreferenceFragment
{
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.app_preferences);
Preference preference = findPreference("background_color");
preference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
//do your action here
return false;
}
});
}
}
}
Or from other activity:
PreferenceManager.setDefaultValues(this, R.xml.your_setting_xml, false);
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
if (settings.getBoolean("background_color", true)) {
//do your action here
.........
Refer to this question also, (it has similar use case):
Checkbox Preference and Checking if its enabled or disable
Like #Chisko said, there isn't enough code in your question for us to be able to figure out your end goal - although I'm guessing you are wanting some form of persistent storage for your app to be able to save your app preferences. For this, you will want to use something in Android called SharedPreferences. This allows you to save simple data types to be accessed later.
Give that link a read, and then try saving/loading one simple piece of data. You'll want to load it from SharedPreferences on starting the activity (you can specify a default value if it hasn't been saved yet) and then you'll want to save the data in onBackPressed() as you said.
Best of luck and if you run into any issues, just comment here.
#Abdulhamid Dhaiban correctly points it out.
I'll add to your suggestion about overriding the onBackPressed() method.
If your "Up" button (top left <-) provides the correct result, then you can set the Back button to behave like the Up button by just adding the following code:
#Override
public void onBackPressed() {
super.onBackPressed();
NavUtils.navigateUpFromSameTask(this);
}
Hope it helps!

SharedPreferences sub PreferenceScreen with custom fragment class

I have a PreferenceScreen with a sub PreferenceScreen:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<PreferenceCategory android:title="SOUND">
<PreferenceScreen android:title="Sound Options"
android:fragment="com.test.SoundPreferenceFragment">
</PreferenceScreen>
</PreferenceCategory>
...
The sub preference is a custom implementation of a PreferenceFragment
public class SoundPreferencesFragment extends PreferenceFragment implements
OnSharedPreferenceChangeListener {
...
I am wondering how I am supposed to start the custom fragment. In my main activity I do this:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getFragmentManager().beginTransaction().replace(android.R.id.content, settingsPreference).commit();
}
Which works great to start my main settings preference screen.
I then override:
#Override
public boolean onPreferenceStartFragment(PreferenceFragment fragment, Preference preference) {
getFragmentManager().beginTransaction().replace(android.R.id.content, new SoundPreferenceFragment()).commit();
return true;
}
That seems to work but I have a few questions.
Is that the right way?
When I go into the Sounds preference and hit the back button it takes me all the way out of settings. Is that how it should work?
I think I can also tell the sub preference to start a new intent, i.e. create a new SoundPreferencesActivity. Is that a more 'correct' way.
EDIT:
I also tried not to extend 'onPreferenceStartFragment' in my main activity hoping that android would take care of starting the new preference correctly' It does seem to start the new PreferenceFragment but it overlays the UI right on top of the main preferences UI.
You have to create a new activity and attach the SoundPreferencesFragment .
when you select the item in main setting activity , start the new activity

Clicking tab not showing Home activity

I have 3 tabs in my sample application with activity group. First tab contains search activity i.e.Home/Root activity and am displaying the results of search in another activity but under same tab i.e Tab1. When I press back button in result activity, it is going to search activity. Everything works fine till here. Now I want to go search activity by pressing tab1 instead of pressing back button. How can achieve this? I tried something like this
public class TabSample extends TabActivity {
public TabHost tabHost;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.tabHost = getTabHost();
tabHost.addTab(tabHost.newTabSpec("tab1").setIndicator("OPT")
.setContent(new Intent(this, TabGroup1Activity.class)));
tabHost.addTab(tabHost.newTabSpec("tab2").setIndicator("EDIT")
.setContent(new Intent(this, TabGroup2Activity.class)));
tabHost.setCurrentTab(1);
tabHost.setOnTabChangedListener(new OnTabChangeListener() {
public void onTabChanged(String arg0) {
if (tabHost.getCurrentTabTag().equals("tab1")) {
//What should I do to display search activity here
} else {
tabHost.setCurrentTab(1);
}
}
});
tabHost.setFocusable(true);
tabHost.requestFocus();
}
}
Can anyone please help let me know how to invoke search activity when tab is pressed? What will go into if part? Because if I use tabHost.setCurrentTab(index), it will display result activity but not search activity.
NOTE: I followed the tutorial given in this link.
I think what you want to do is this: when the 'tab1' tag is selected, go back to TabGroup1Activity if (and only if) the current activity is not that activity (basically you want to simulate a 'back' press).
If so, what you want is this:
if (getCurrentActivity().getClass() != TabGroup1Activity.class)
getCurrentActivity().finish()
I'm not 100% sure I understand you fully, but let's see :)
In your onTabChanged listener you can switch on which tab have been tabed, and then open the activity as normal inside an activitygroup:
public void onTabChanged(String tabId) {
if (tabId.contentEquals("tab1")) {
Intent intent = new Intent(tabHost.getContext(), TabGroup1Activity.class);
View view = StartGroup.group.getLocalActivityManager().startActivity("tab1", intent).getDecorView();
StartGroup.group.setContentView(view);
}
}
I just reviewed my code and think there's a bit more to explain here. The problem is that you don't stack activities as normal. Instead the workaround is to make a content stack and change these instead. So what I have done is to create a class StartGroup which extends
ButtonHandlerActivityGroup:
public class StartGroup extends ButtonHandlerActivityGroup {
// Keep this in a static variable to make it accessible for all the nested activities, lets them manipulate the view
public static StartGroup group;
// Need to keep track of the history if you want the back-button to work properly,
// don't use this if your activities requires a lot of memory.
private ArrayList<View> history;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.history = new ArrayList<View>();
group = this;
// Start the root activity within the group and get its view
View view = getLocalActivityManager().startActivity("UserList", new Intent(this, UserList.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)).getDecorView();
replaceView(view);
}
public void back() {
if (history.size() > 0) {
// pop the last view
history.remove(history.size()-1);
setContentView(history.get(history.size()-1));
} else {
finish();
}
}
}
Then from the TabMaster class or what you call it you can use the StartGroup class to change the content view of an activity group.
This is something I wrote to work on devices from 2.2, so there might be an easier and more androidish way to accomplished it, but this works on almost all devices :)
Here is another thread where the use a similar approach:
Launching activities within a tab in Android
Let me know if I can help more.
There is an ArrayList in your ActivityGroup so override onPause() method in ActivityGroup and remove all the ids from ArrayList except the first one which must be your SearchActivity.
So when you go to other tab then comes back to SearchActivity( or on Tab1 ) Home will be displayed.

Android ActivityGroup's child activity setTitle not working

I have TabActivityGroup:
MainActivity class contain some tab, that name loading from db. Sales, Admin, Inquiry like wise I have tab name
For Sales I created SalesActivityGroup.That class is :
public class SalesActivityGroup extends ActivityGroup {
public static SalesActivityGroup group;
private ArrayList<View> history;
private LocalActivityManager mActivityManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.history = new ArrayList<View>();
group = this;
mActivityManager = getLocalActivityManager();
Intent i = new Intent(getBaseContext(), SalesRouteActivity.class);
Bundle bundle = new Bundle();
bundle.putInt("positions", -1);
i.putExtras(bundle);
View view = mActivityManager.startActivity("Sales",i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP )).getDecorView();
replaceView(view);
}
public void replaceView(View v) {
history.add(v);
setContentView(v);
}
public void back(){
if ( history.size() > 1 ){
history.remove(history.size() - 1);
View v = history.get(history.size() - 1);
setContentView(v);
}
else {
this.finish();
}
}
#Override
public void onBackPressed() {
SalesActivityGroup.group.back();
}
}
SalesRouteActivity is first Activity .In there i want to set up the title name.I did using this ways.But not working
public class SalesRouteActivity extends Activity{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.sales_routes);
//getWindow().setTitle("Route");
View viewToLoad = LayoutInflater.from(SalesActivityGroup.group).inflate(R.layout.sales_routes, null);
this.setContentView(viewToLoad);
//this.setTitle("Route");
//getWindow().setTitle("Route");
SalesActivityGroup.group.setTitle("Route");
}
}
Please advice me How can i set the Title name.
Thanks in advance
You can access the parent tab activity like
getParent().getParent().setTitle("New Tilte");
EXPLANATION:
Based on my understanding,
When you call the getParent the first time, you get the activity group that started the child activity.
When you call getParent the second time, you will get the tab activity that started the activity group.
setTitle should work for the activity window which is held by the tabactivity. The sub activities are rendered in the framelayout of the tab activity. So in the child activities access the parent tab activity to set the title.
The best way to do this is to implement the method
protected void onChildTitleChanged(Activity childActivity,CharSequence title) {
super.onChildTitleChanged(childActivity, title);
setTitle(title);
}
Implement this method in the parent activity. For example, In my case I have three activities.
Home Activity
Artists Activity
Album Activity
My Home activity contains a TabHost with Artists activity and Album activity.
I implemented the above method in the Home Activity. The title for Artists activity and Album activity is set in the OnResume methods of those respective activities.
It is not advisable to use ActivityGroup, it has been deprecated.
Refer to this link
Please use Fragment and FragmentManager using Compatibility Library

Updating views of sub Activities of TabActivity from background process

I am using a TabActivity (Main) with 3 TabSpecs
I am using Intents for the content of the 3 Tabs
TabA, TabB, TabC for example.
All these tab activities use common data that is stored in SharedPreferences
In the Main TabActivity I have an options menu which has a refresh option.
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.refresh_menu:
new updateCommonDataFromWeb().execute();
...
}
}
this refresh uses an AsyncTask (updateCommonDataFromWeb) to reload the common data from the web.
I need a way to tell the 3 tab activities to refresh their views and rebuild their content from the newly downloaded data.
When the tab activities are first created they load the data from SharedPreferences like so:
public class TabA extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.taba);
SharedPreferences prefs = getSharedPreferences(this.getString(R.string.prefs_name), 0);
String usage_data = prefs.getString("common_data", "");
}
}
I thought about making a common method on each of the tab activities
like.... reloadViewData()
I thought maybe I could use the activity manager from the Main TabActivity to get the activity of the current tab like so:
String tabTag = tabHost.getCurrentTabTag();
Activity activity = getLocalActivityManager().getActivity(tabTag);
# then call the reloadData() method of the tab activity
activity.reloadViewData();
Unfortunately i cant get this approach to work, whilst activity is the correct instance its an Activity instead of a TabA,TabB or TabC
Maybe i've completely taken the wrong approach to the whole thing.
I have also read alot about not using Activities for tab content instead using views.
However I dont know what view to use to replace my <RelativeLayout /> as i cant use my R.layout.* as views.
If you change the common_data in the sharedPreferences you can also implement OnSharedPreferenceChangeListener in the Activites and check for key.equals("common_data").
You can extend Activity and make all your child activities to inherit from your extended activity, as this way you will implement the same code (service handling) in 1 activity(the parent).
Activity -> MyParentActivity -> MyChildActivity1
This way you will be able to cast all your child activities to MyParentActivity and run your custom method (you want to be implemented)

Categories

Resources