Fix bug #12939786 BACK should go back into the Fragments BackStack and finally to Overview

...and then exit the Settings App

- fix the way we manage the Fragment BackStack
- revert back ChooseLockGeneric to be a PreferenceActivity

Change-Id: I3c366b4be606e2e211facd0299b9a2de5cc6ea79
This commit is contained in:
Fabrice Di Meglio
2014-02-07 18:53:14 -08:00
parent c94122a199
commit 4cc95a53c2
3 changed files with 126 additions and 153 deletions

View File

@@ -27,6 +27,7 @@ import android.os.Process;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
import android.preference.Preference; import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen; import android.preference.PreferenceScreen;
import android.security.KeyStore; import android.security.KeyStore;
import android.util.EventLog; import android.util.EventLog;
@@ -41,7 +42,7 @@ import java.util.List;
import libcore.util.MutableBoolean; import libcore.util.MutableBoolean;
public class ChooseLockGeneric extends SettingsActivity { public class ChooseLockGeneric extends PreferenceActivity {
@Override @Override
public Intent getIntent() { public Intent getIntent() {

View File

@@ -70,7 +70,6 @@ import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.Button; import android.widget.Button;
@@ -400,7 +399,7 @@ public class SettingsActivity extends Activity
@Override @Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
mDrawerLayout.closeDrawer(mDrawer); mDrawerLayout.closeDrawer(mDrawer);
onListItemClick((ListView)parent, view, position, id); onListItemClick((ListView) parent, view, position, id);
} }
} }
@@ -517,7 +516,6 @@ public class SettingsActivity extends Activity
mDrawer = (ListView) findViewById(R.id.headers_drawer); mDrawer = (ListView) findViewById(R.id.headers_drawer);
mDrawer.setAdapter(mHeaderAdapter); mDrawer.setAdapter(mHeaderAdapter);
mDrawer.setOnItemClickListener(new DrawerItemClickListener()); mDrawer.setOnItemClickListener(new DrawerItemClickListener());
mDrawer.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close); R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close);
@@ -540,31 +538,23 @@ public class SettingsActivity extends Activity
} }
} else { } else {
// We need to first to build the headers.
onBuildHeaders(mHeaders);
final CharSequence initialTitle = getTitle();
// If there are headers, get the initial fragment and push it first so that pressing
// BACK will always go to it before exiting the app
if (mHeaders.size() > 0) {
switchToHeader(mFirstHeader, false, true);
}
// Got to the initial fragment if one is specified
if (initialFragment != null) { if (initialFragment != null) {
// If we are just showing a fragment, we want to run in final int initialTitleRes = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE, 0);
// new fragment mode, but don't need to compute and show final Header h =
// the headers. onGetInitialHeader(initialTitle, initialTitleRes, initialArguments);
switchToHeader(initialFragment, initialArguments, true); switchToHeader(h, false, false);
final int initialTitle = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE, 0);
if (initialTitle != 0) {
setTitle(getText(initialTitle));
}
} else {
// We need to try to build the headers.
onBuildHeaders(mHeaders);
// If there are headers, then at this point we need to show
// them and, depending on the screen, we may also show in-line
// the currently selected preference fragment.
if (mHeaders.size() > 0) {
if (initialFragment == null) {
Header h = onGetInitialHeader();
switchToHeader(h, false);
} else {
switchToHeader(initialFragment, initialArguments, false);
}
}
} }
} }
@@ -635,7 +625,7 @@ public class SettingsActivity extends Activity
@Override @Override
public void onBackStackChanged() { public void onBackStackChanged() {
final int count = getFragmentManager().getBackStackEntryCount() + 1; final int count = getFragmentManager().getBackStackEntryCount();
TitlePair pair = null; TitlePair pair = null;
int last; int last;
while (mTitleStack.size() > count) { while (mTitleStack.size() > count) {
@@ -657,6 +647,9 @@ public class SettingsActivity extends Activity
} }
setTitle(title); setTitle(title);
} }
} else {
final String title = getResources().getString(R.string.settings_label);
setTitle(title);
} }
} }
} }
@@ -746,20 +739,18 @@ public class SettingsActivity extends Activity
* preference fragment. * preference fragment.
* *
* @param header The new header to display. * @param header The new header to display.
* @param validate true means that the fragment's Header needs to be validated * @param validate true means that the fragment's Header needs to be validated.
* @param initial true means that the fragment is the initial one.
*/ */
private void switchToHeader(Header header, boolean validate) { private void switchToHeader(Header header, boolean validate, boolean initial) {
if (mCurHeader == header) { if (mCurHeader != header) {
// This is the header we are currently displaying. Just make sure
// to pop the stack up to its root state.
getFragmentManager().popBackStack(BACK_STACK_PREFS,
FragmentManager.POP_BACK_STACK_INCLUSIVE);
} else {
mTitleStack.clear();
if (header.fragment == null) { if (header.fragment == null) {
throw new IllegalStateException("can't switch to header that has no fragment"); throw new IllegalStateException("can't switch to header that has no fragment");
} }
switchToHeaderInner(header.fragment, header.fragmentArguments, validate); getFragmentManager().popBackStackImmediate(BACK_STACK_PREFS,
FragmentManager.POP_BACK_STACK_INCLUSIVE);
mTitleStack.clear();
switchToHeaderInner(header.fragment, header.fragmentArguments, validate, !initial);
setSelectedHeader(header); setSelectedHeader(header);
final CharSequence title; final CharSequence title;
if (header.fragment.equals("com.android.settings.dashboard.DashboardSummary")) { if (header.fragment.equals("com.android.settings.dashboard.DashboardSummary")) {
@@ -785,44 +776,116 @@ public class SettingsActivity extends Activity
} }
} }
public Header onGetInitialHeader() { public Header onGetInitialHeader(CharSequence title, int titleRes, Bundle args) {
String fragmentClass = getStartingFragmentClass(super.getIntent()); String fragmentClass = getStartingFragmentClass(super.getIntent());
if (fragmentClass != null) { if (fragmentClass != null) {
Header header = new Header(); Header header = new Header();
header.fragment = fragmentClass; header.fragment = fragmentClass;
header.title = getTitle(); header.title = title;
header.fragmentArguments = getIntent().getExtras(); header.titleRes = titleRes;
header.fragmentArguments = args;
mCurrentHeader = header; mCurrentHeader = header;
return header; return header;
} }
return mFirstHeader; return mFirstHeader;
} }
/** private void switchToHeaderInner(String fragmentName, Bundle args, boolean validate,
* When in two-pane mode, switch the fragment pane to show the given boolean addToBackStack) {
* preference fragment.
*
* @param fragmentName The name of the fragment to display.
* @param args Optional arguments to supply to the fragment.
* @param validate true means that the fragment's Header needs to be validated
*/
private void switchToHeader(String fragmentName, Bundle args, boolean validate) {
setSelectedHeader(null);
switchToHeaderInner(fragmentName, args, validate);
}
private void switchToHeaderInner(String fragmentName, Bundle args, boolean validate) {
getFragmentManager().popBackStack(BACK_STACK_PREFS,
FragmentManager.POP_BACK_STACK_INCLUSIVE);
if (validate && !isValidFragment(fragmentName)) { if (validate && !isValidFragment(fragmentName)) {
throw new IllegalArgumentException("Invalid fragment for this activity: " throw new IllegalArgumentException("Invalid fragment for this activity: "
+ fragmentName); + fragmentName);
} }
Fragment f = Fragment.instantiate(this, fragmentName, args); Fragment f = Fragment.instantiate(this, fragmentName, args);
FragmentTransaction transaction = getFragmentManager().beginTransaction(); FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
transaction.replace(R.id.prefs, f); transaction.replace(R.id.prefs, f);
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
if (addToBackStack) {
transaction.addToBackStack(BACK_STACK_PREFS);
}
transaction.commitAllowingStateLoss();
}
/**
* Start a new fragment.
*
* @param fragmentName The name of the fragment to display.
* @param args Optional arguments to supply to the fragment.
* @param resultTo Option fragment that should receive the result of
* the activity launch.
* @param resultRequestCode If resultTo is non-null, this is the request code in which to
* report the result.
* @param titleRes Resource ID of string to display for the title of. If the Resource ID is a
* valid one then it will be used to get the title. Otherwise the titleText
* argument will be used as the title.
* @param titleText string to display for the title of.
*/
private void startWithFragment(String fragmentName, Bundle args, Fragment resultTo,
int resultRequestCode, int titleRes, CharSequence titleText) {
Fragment f = Fragment.instantiate(this, fragmentName, args);
if (resultTo != null) {
f.setTargetFragment(resultTo, resultRequestCode);
}
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.prefs, f);
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
transaction.addToBackStack(BACK_STACK_PREFS);
transaction.commitAllowingStateLoss();
final TitlePair pair;
final CharSequence cs;
if (titleRes != 0) {
pair = new TitlePair(titleRes, null);
cs = getText(titleRes);
} else {
pair = new TitlePair(0, titleText);
cs = titleText;
}
setTitle(cs);
mTitleStack.add(pair);
}
/**
* Start a new fragment containing a preference panel. If the preferences
* are being displayed in multi-pane mode, the given fragment class will
* be instantiated and placed in the appropriate pane. If running in
* single-pane mode, a new activity will be launched in which to show the
* fragment.
*
* @param fragmentClass Full name of the class implementing the fragment.
* @param args Any desired arguments to supply to the fragment.
* @param titleRes Optional resource identifier of the title of this
* fragment.
* @param titleText Optional text of the title of this fragment.
* @param resultTo Optional fragment that result data should be sent to.
* If non-null, resultTo.onActivityResult() will be called when this
* preference panel is done. The launched panel must use
* {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
* @param resultRequestCode If resultTo is non-null, this is the caller's
* request code to be received with the resut.
*/
public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
CharSequence titleText, Fragment resultTo,
int resultRequestCode) {
startWithFragment(fragmentClass, args, resultTo, resultRequestCode, titleRes, titleText);
}
/**
* Start a new fragment.
*
* @param fragment The fragment to start
* @param push If true, the current fragment will be pushed onto the back stack. If false,
* the current fragment will be replaced.
*/
public void startPreferenceFragment(Fragment fragment, boolean push) {
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.prefs, fragment);
if (push) {
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
transaction.addToBackStack(BACK_STACK_PREFS);
} else {
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
}
transaction.commitAllowingStateLoss(); transaction.commitAllowingStateLoss();
} }
@@ -903,31 +966,6 @@ public class SettingsActivity extends Activity
return intentClass; return intentClass;
} }
/**
* Start a new fragment containing a preference panel. If the preferences
* are being displayed in multi-pane mode, the given fragment class will
* be instantiated and placed in the appropriate pane. If running in
* single-pane mode, a new activity will be launched in which to show the
* fragment.
*
* @param fragmentClass Full name of the class implementing the fragment.
* @param args Any desired arguments to supply to the fragment.
* @param titleRes Optional resource identifier of the title of this
* fragment.
* @param titleText Optional text of the title of this fragment.
* @param resultTo Optional fragment that result data should be sent to.
* If non-null, resultTo.onActivityResult() will be called when this
* preference panel is done. The launched panel must use
* {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
* @param resultRequestCode If resultTo is non-null, this is the caller's
* request code to be received with the resut.
*/
public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
CharSequence titleText, Fragment resultTo,
int resultRequestCode) {
startWithFragment(fragmentClass, args, resultTo, resultRequestCode, titleRes, titleText);
}
/** /**
* Called by a preference panel fragment to finish itself. * Called by a preference panel fragment to finish itself.
* *
@@ -941,64 +979,6 @@ public class SettingsActivity extends Activity
setResult(resultCode, resultData); setResult(resultCode, resultData);
} }
/**
* Start a new fragment.
*
* @param fragment The fragment to start
* @param push If true, the current fragment will be pushed onto the back stack. If false,
* the current fragment will be replaced.
*/
public void startPreferenceFragment(Fragment fragment, boolean push) {
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.prefs, fragment);
if (push) {
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
transaction.addToBackStack(BACK_STACK_PREFS);
} else {
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
}
transaction.commitAllowingStateLoss();
}
/**
* Start a new fragment.
*
* @param fragmentName The name of the fragment to display.
* @param args Optional arguments to supply to the fragment.
* @param resultTo Option fragment that should receive the result of
* the activity launch.
* @param resultRequestCode If resultTo is non-null, this is the request code in which to
* report the result.
* @param titleRes Resource ID of string to display for the title of. If the Resource ID is a
* valid one then it will be used to get the title. Otherwise the titleText
* argument will be used as the title.
* @param titleText string to display for the title of.
*/
private void startWithFragment(String fragmentName, Bundle args, Fragment resultTo,
int resultRequestCode, int titleRes, CharSequence titleText) {
Fragment f = Fragment.instantiate(this, fragmentName, args);
if (resultTo != null) {
f.setTargetFragment(resultTo, resultRequestCode);
}
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.prefs, f);
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
transaction.addToBackStack(BACK_STACK_PREFS);
transaction.commitAllowingStateLoss();
final TitlePair pair;
final CharSequence cs;
if (titleRes != 0) {
pair = new TitlePair(titleRes, null);
cs = getText(titleRes);
} else {
pair = new TitlePair(0, titleText);
cs = titleText;
}
setTitle(cs);
mTitleStack.add(pair);
}
/** /**
* Called when the activity needs its list of headers build. By * Called when the activity needs its list of headers build. By
* implementing this and adding at least one item to the list, you * implementing this and adding at least one item to the list, you
@@ -1592,7 +1572,7 @@ public class SettingsActivity extends Activity
* Called when the user selects an item in the header list. The default * Called when the user selects an item in the header list. The default
* implementation will call either * implementation will call either
* {@link #startWithFragment(String, android.os.Bundle, android.app.Fragment, int, int, CharSequence)} * {@link #startWithFragment(String, android.os.Bundle, android.app.Fragment, int, int, CharSequence)}
* or {@link #switchToHeader(com.android.settings.SettingsActivity.Header, boolean)} * or {@link #switchToHeader(com.android.settings.SettingsActivity.Header, boolean, boolean)}
* as appropriate. * as appropriate.
* *
* @param header The header that was selected. * @param header The header that was selected.
@@ -1600,7 +1580,7 @@ public class SettingsActivity extends Activity
private void onHeaderClick(Header header) { private void onHeaderClick(Header header) {
if (header == null) return; if (header == null) return;
if (header.fragment != null) { if (header.fragment != null) {
switchToHeader(header, false); switchToHeader(header, false, false);
} else if (header.intent != null) { } else if (header.intent != null) {
startActivity(header.intent); startActivity(header.intent);
} }

View File

@@ -328,14 +328,6 @@ public class PrintServiceSettingsFragment extends SettingsPreferenceFragment
final boolean enabled = arguments.getBoolean(PrintSettingsFragment.EXTRA_CHECKED); final boolean enabled = arguments.getBoolean(PrintSettingsFragment.EXTRA_CHECKED);
mToggleSwitch.setCheckedInternal(enabled); mToggleSwitch.setCheckedInternal(enabled);
// Title.
SettingsActivity activity = (SettingsActivity) getActivity();
if (!activity.onIsHidingHeaders()) {
mOldActivityTitle = getActivity().getTitle();
String title = arguments.getString(PrintSettingsFragment.EXTRA_TITLE);
getActivity().getActionBar().setTitle(title);
}
// Settings title and intent. // Settings title and intent.
String settingsTitle = arguments.getString(PrintSettingsFragment.EXTRA_SETTINGS_TITLE); String settingsTitle = arguments.getString(PrintSettingsFragment.EXTRA_SETTINGS_TITLE);
String settingsComponentName = arguments.getString( String settingsComponentName = arguments.getString(