Settings - update for new UI (no more Drawer)
- follow the UX spec by no more using a Drawer
- the Dashboard is now a Fragment that contains the list of Headers
- the search results are also put into a Fragment that is replacing
the initial one (Dashboard or other) when expanding the SearchView
- use a SearchView for query input
- when tapping on a Header or a Search Result, re-launch Settings as
an Activity so that we are benefiting from the Activity stack for
UP affordance and BACK button
- manage UP affordance to show it only when needed
- move some Actions to the Menu in the ActionBar for allowing space
to the Search action and removing some clutter
- fix an issue with the Index and WiFiEnabler and their cached Context
that was not updated when there was a Configuration change
- simplify the SettingsActivity code by extracting some inner classes
Change-Id: I50b5f77bb44a7fade1886114dbbc820609a5e63d
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 0e6128c..c2b01cf 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -24,11 +24,10 @@
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
+import android.app.SearchManager;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -38,17 +37,13 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
-import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
-import android.graphics.drawable.Drawable;
import android.nfc.NfcAdapter;
import android.os.Bundle;
import android.os.Handler;
import android.os.INetworkManagementService;
import android.os.Message;
-import android.os.Parcel;
-import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -57,28 +52,20 @@
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
-import android.support.v4.app.ActionBarDrawerToggle;
-import android.support.v4.widget.DrawerLayout;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.util.Xml;
-import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.AbsListView;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
import android.widget.Button;
-import android.widget.ImageButton;
-import android.widget.ImageView;
import android.widget.ListView;
-import android.widget.Switch;
-import android.widget.TextView;
+import android.widget.SearchView;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.XmlUtils;
import com.android.settings.accessibility.AccessibilitySettings;
@@ -86,11 +73,15 @@
import com.android.settings.accounts.AccountSyncSettings;
import com.android.settings.accounts.AuthenticatorHelper;
import com.android.settings.accounts.ManageAccountsSettings;
+import com.android.settings.applications.InstalledAppDetails;
import com.android.settings.applications.ManageApplications;
import com.android.settings.applications.ProcessStatsUi;
-import com.android.settings.bluetooth.BluetoothEnabler;
import com.android.settings.bluetooth.BluetoothSettings;
import com.android.settings.dashboard.DashboardSummary;
+import com.android.settings.dashboard.Header;
+import com.android.settings.dashboard.HeaderAdapter;
+import com.android.settings.dashboard.NoHomeDialogFragment;
+import com.android.settings.dashboard.SearchResultsSummary;
import com.android.settings.deviceinfo.Memory;
import com.android.settings.deviceinfo.UsbSettings;
import com.android.settings.fuelgauge.PowerUsageSummary;
@@ -109,7 +100,6 @@
import com.android.settings.vpn2.VpnSettings;
import com.android.settings.wfd.WifiDisplaySettings;
import com.android.settings.wifi.AdvancedWifiSettings;
-import com.android.settings.wifi.WifiEnabler;
import com.android.settings.wifi.WifiSettings;
import com.android.settings.wifi.p2p.WifiP2pSettings;
import org.xmlpull.v1.XmlPullParser;
@@ -122,16 +112,22 @@
import java.util.HashMap;
import java.util.List;
+import static com.android.settings.dashboard.Header.HEADER_ID_UNDEFINED;
+
public class SettingsActivity extends Activity
implements PreferenceManager.OnPreferenceTreeClickListener,
PreferenceFragment.OnPreferenceStartFragmentCallback,
- ButtonBarHandler, OnAccountsUpdateListener, FragmentManager.OnBackStackChangedListener {
+ ButtonBarHandler, OnAccountsUpdateListener, FragmentManager.OnBackStackChangedListener,
+ SearchView.OnQueryTextListener, SearchView.OnCloseListener,
+ MenuItem.OnActionExpandListener {
private static final String LOG_TAG = "Settings";
// Constants for state save/restore
- private static final String SAVE_KEY_HEADERS_TAG = ":settings:headers";
- private static final String SAVE_KEY_CURRENT_HEADER_TAG = ":settings:cur_header";
+ private static final String SAVE_KEY_HEADERS = ":settings:headers";
+ private static final String SAVE_KEY_CURRENT_HEADER = ":settings:cur_header";
+ private static final String SAVE_KEY_SEARCH_MENU_EXPANDED = ":settings:search_menu_expanded";
+ private static final String SAVE_KEY_SEARCH_QUERY = ":settings:search_query";
/**
* When starting this activity, the invoking Intent can contain this extra
@@ -180,7 +176,7 @@
* this extra can also be specify to supply the title to be shown for
* that fragment.
*/
- protected static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
+ public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
private static final String META_DATA_KEY_HEADER_ID =
"com.android.settings.TOP_LEVEL_HEADER_ID";
@@ -193,13 +189,10 @@
private static boolean sShowNoHomeNotice = false;
private String mFragmentClass;
- private int mTopLevelHeaderId;
- private Header mFirstHeader;
private Header mSelectedHeader;
private Header mCurrentHeader;
private CharSequence mInitialTitle;
- private Header mInitialHeader;
// Show only these settings for restricted users
private int[] SETTINGS_FOR_RESTRICTED = {
@@ -279,22 +272,19 @@
TrustedCredentialsSettings.class.getName(),
PaymentSettings.class.getName(),
KeyboardLayoutPickerFragment.class.getName(),
- DashboardSummary.class.getName(),
- ZenModeSettings.class.getName()
+ ZenModeSettings.class.getName(),
+ NotificationSettings.class.getName(),
+ ChooseLockPassword.ChooseLockPasswordFragment.class.getName(),
+ ChooseLockPattern.ChooseLockPatternFragment.class.getName(),
+ InstalledAppDetails.class.getName()
};
private SharedPreferences mDevelopmentPreferences;
private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
- // TODO: Update Call Settings based on airplane mode state.
-
- protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>();
-
private AuthenticatorHelper mAuthenticatorHelper;
private boolean mListeningToAccountUpdates;
- private Button mNextButton;
-
private boolean mBatteryPresent = true;
private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
@@ -312,14 +302,21 @@
}
};
+ private Button mNextButton;
+ private ActionBar mActionBar;
+
+ private SearchView mSearchView;
+ private MenuItem mSearchMenuItem;
+ private boolean mSearchMenuItemExpanded = false;
+ private boolean mIsShowingSearchResults = false;
+ private SearchResultsSummary mSearchResultsFragment;
+ private String mSearchQuery;
+
+ // Headers
+ protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>();
private final ArrayList<Header> mHeaders = new ArrayList<Header>();
private HeaderAdapter mHeaderAdapter;
- private DrawerLayout mDrawerLayout;
- private ListView mDrawer;
- private ActionBarDrawerToggle mDrawerToggle;
- private ActionBar mActionBar;
-
private static final int MSG_BUILD_HEADERS = 1;
private Handler mHandler = new Handler() {
@Override
@@ -329,17 +326,13 @@
mHeaders.clear();
onBuildHeaders(mHeaders);
mHeaderAdapter.notifyDataSetChanged();
- if (mCurrentHeader != null) {
- Header mappedHeader = findBestMatchingHeader(mCurrentHeader, mHeaders);
- if (mappedHeader != null) {
- setSelectedHeader(mappedHeader);
- }
- }
} break;
}
}
};
+ private boolean mNeedToRevertToInitialFragment = false;
+
@Override
public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
// Override the fragment title for Wallpaper settings
@@ -364,87 +357,6 @@
return false;
}
- private class DrawerListener implements DrawerLayout.DrawerListener {
- @Override
- public void onDrawerOpened(View drawerView) {
- mDrawerToggle.onDrawerOpened(drawerView);
- }
-
- @Override
- public void onDrawerClosed(View drawerView) {
- mDrawerToggle.onDrawerClosed(drawerView);
- // Cannot process clicks when the App is finishing
- if (isFinishing() || mSelectedHeader == null) {
- return;
- }
- switchToHeader(mSelectedHeader, false, false);
- mSelectedHeader = null;
- }
-
- @Override
- public void onDrawerSlide(View drawerView, float slideOffset) {
- mDrawerToggle.onDrawerSlide(drawerView, slideOffset);
- }
-
- @Override
- public void onDrawerStateChanged(int newState) {
- mDrawerToggle.onDrawerStateChanged(newState);
- }
- }
-
- private class DrawerItemClickListener implements ListView.OnItemClickListener {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- mDrawerLayout.closeDrawer(mDrawer);
- onListItemClick((ListView)parent, view, position, id);
- }
- }
-
- private Header findBestMatchingHeader(Header current, ArrayList<Header> from) {
- ArrayList<Header> matches = new ArrayList<Header>();
- for (int j=0; j<from.size(); j++) {
- Header oh = from.get(j);
- if (current == oh || (current.id != HEADER_ID_UNDEFINED && current.id == oh.id)) {
- // Must be this one.
- matches.clear();
- matches.add(oh);
- break;
- }
- if (current.fragment != null) {
- if (current.fragment.equals(oh.fragment)) {
- matches.add(oh);
- }
- } else if (current.intent != null) {
- if (current.intent.equals(oh.intent)) {
- matches.add(oh);
- }
- } else if (current.title != null) {
- if (current.title.equals(oh.title)) {
- matches.add(oh);
- }
- }
- }
- final int NM = matches.size();
- if (NM == 1) {
- return matches.get(0);
- } else if (NM > 1) {
- for (int j=0; j<NM; j++) {
- Header oh = matches.get(j);
- if (current.fragmentArguments != null &&
- current.fragmentArguments.equals(oh.fragmentArguments)) {
- return oh;
- }
- if (current.extras != null && current.extras.equals(oh.extras)) {
- return oh;
- }
- if (current.title != null && current.title.equals(oh.title)) {
- return oh;
- }
- }
- }
- return null;
- }
-
private void invalidateHeaders() {
if (!mHandler.hasMessages(MSG_BUILD_HEADERS)) {
mHandler.sendEmptyMessage(MSG_BUILD_HEADERS);
@@ -452,30 +364,48 @@
}
@Override
- protected void onPostCreate(Bundle savedInstanceState) {
- super.onPostCreate(savedInstanceState);
-
- // Sync the toggle state after onRestoreInstanceState has occurred.
- mDrawerToggle.syncState();
- }
-
- @Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- mDrawerToggle.onConfigurationChanged(newConfig);
Index.getInstance(this).update();
}
@Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (mDrawerToggle.onOptionsItemSelected(item)) {
- return true;
+ protected void onStart() {
+ super.onStart();
+
+ if (mNeedToRevertToInitialFragment) {
+ revertToInitialFragment();
}
- return super.onOptionsItemSelected(item);
}
@Override
- protected void onCreate(Bundle savedInstanceState) {
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.options_menu, menu);
+
+ // Cache the search query (can be overriden by the OnQueryTextListener)
+ final String query = mSearchQuery;
+
+ // Associate searchable configuration with the SearchView
+ SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
+ mSearchView = (SearchView) menu.findItem(R.id.search).getActionView();
+
+ mSearchView.setOnQueryTextListener(this);
+ mSearchView.setOnCloseListener(this);
+
+ mSearchMenuItem = menu.findItem(R.id.search);
+ mSearchMenuItem.setOnActionExpandListener(this);
+
+ if (mSearchMenuItemExpanded) {
+ mSearchMenuItem.expandActionView();
+ }
+ mSearchView.setQuery(query, true /* submit */);
+
+ return true;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedState) {
if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
}
@@ -489,78 +419,67 @@
DevicePolicyManager dpm =
(DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
- mHeaderAdapter= new HeaderAdapter(this, mHeaders, mAuthenticatorHelper, dpm);
+ mHeaderAdapter = new HeaderAdapter(this, mHeaders, mAuthenticatorHelper, dpm);
mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
Context.MODE_PRIVATE);
getMetaData();
- super.onCreate(savedInstanceState);
+ super.onCreate(savedState);
setContentView(R.layout.settings_main);
getFragmentManager().addOnBackStackChangedListener(this);
- mActionBar = getActionBar();
- mActionBar.setDisplayHomeAsUpEnabled(true);
- mActionBar.setHomeButtonEnabled(true);
+ boolean displayHomeAsUpEnabled = true;
- mDrawer = (ListView) findViewById(R.id.headers_drawer);
- mDrawer.setAdapter(mHeaderAdapter);
- mDrawer.setOnItemClickListener(new DrawerItemClickListener());
- mDrawer.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
+ String initialFragmentName = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
+ Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
- mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
- mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
- R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close);
+ if (savedState != null) {
+ mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
+ mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
- if (savedInstanceState != null) {
- // We are restarting from a previous saved state; used that to
+ // We are restarting from a previous saved state; used that to
// initialize, instead of starting fresh.
mInitialTitle = getTitle();
- ArrayList<Header> headers =
- savedInstanceState.getParcelableArrayList(SAVE_KEY_HEADERS_TAG);
+ ArrayList<Header> headers = savedState.getParcelableArrayList(SAVE_KEY_HEADERS);
if (headers != null) {
mHeaders.addAll(headers);
- int curHeader = savedInstanceState.getInt(SAVE_KEY_CURRENT_HEADER_TAG,
- (int) HEADER_ID_UNDEFINED);
- if (curHeader >= 0 && curHeader < mHeaders.size()) {
- setSelectedHeader(mHeaders.get(curHeader));
- mInitialHeader = mCurrentHeader;
- }
setTitleFromBackStack();
}
} else {
- String initialFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
- Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
-
// We need to build the Headers in all cases
onBuildHeaders(mHeaders);
- if (initialFragment != null) {
- // If we are just showing a fragment, we want to run in
- // new fragment mode, but don't need to compute and show
- // the headers.
- final int initialTitleResId = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE, 0);
- mInitialTitle = (initialTitleResId > 0) ? getText(initialTitleResId) : getTitle();
+ if (initialFragmentName != null) {
+ final ComponentName cn = getIntent().getComponent();
+ // No UP is we are launched thru a Settings shortcut
+ if (!cn.getClassName().equals(SubSettings.class.getName())) {
+ displayHomeAsUpEnabled = false;
+ }
+ final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
+ mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
setTitle(mInitialTitle);
- switchToHeaderInner(initialFragment, initialArguments, true, false, mInitialTitle);
- setSelectedHeaderById(mTopLevelHeaderId);
- mInitialHeader = mCurrentHeader;
+ switchToFragment( initialFragmentName, initialArguments, true, false,
+ mInitialTitle, false);
} else {
- // 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.
+ // No UP if we are displaying the Headers
+ displayHomeAsUpEnabled = false;
if (mHeaders.size() > 0) {
- mInitialHeader = onGetInitialHeader();
- mInitialTitle = getHeaderTitle(mInitialHeader);
- switchToHeader(mInitialHeader, false, true);
+ mInitialTitle = getText(R.string.dashboard_title);
+ switchToFragment(DashboardSummary.class.getName(), null, false, false,
+ mInitialTitle, false);
}
}
}
+ mActionBar = getActionBar();
+ mActionBar.setHomeButtonEnabled(true);
+ mActionBar.setDisplayHomeAsUpEnabled(displayHomeAsUpEnabled);
+
// see if we should show Back/Next buttons
Intent intent = getIntent();
if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
@@ -615,39 +534,11 @@
}
}
}
-
- if (!onIsHidingHeaders()) {
- highlightHeader(mTopLevelHeaderId);
- }
- }
-
- public Header onGetInitialHeader() {
- String fragmentClass = getStartingFragmentClass(super.getIntent());
- if (fragmentClass != null) {
- Header header = new Header();
- header.fragment = fragmentClass;
- header.title = getTitle();
- header.fragmentArguments = getIntent().getExtras();
- return header;
- }
-
- return mFirstHeader;
- }
-
- @Override
- public void onBackPressed() {
- if (mDrawerLayout.isDrawerOpen(mDrawer)) {
- mDrawerLayout.closeDrawer(mDrawer);
- return;
- }
- super.onBackPressed();
}
@Override
public void onBackStackChanged() {
- if (setTitleFromBackStack() == 0) {
- setSelectedHeaderById(mInitialHeader.id);
- }
+ setTitleFromBackStack();
}
private int setTitleFromBackStack() {
@@ -682,14 +573,16 @@
super.onSaveInstanceState(outState);
if (mHeaders.size() > 0) {
- outState.putParcelableArrayList(SAVE_KEY_HEADERS_TAG, mHeaders);
+ outState.putParcelableArrayList(SAVE_KEY_HEADERS, mHeaders);
if (mCurrentHeader != null) {
int index = mHeaders.indexOf(mCurrentHeader);
if (index >= 0) {
- outState.putInt(SAVE_KEY_CURRENT_HEADER_TAG, index);
+ outState.putInt(SAVE_KEY_CURRENT_HEADER, index);
}
}
}
+ outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, mSearchMenuItem.isActionViewExpanded());
+ outState.putString(SAVE_KEY_SEARCH_QUERY, mSearchView.getQuery().toString());
}
@Override
@@ -709,16 +602,12 @@
invalidateHeaders();
registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
-
- mDrawerLayout.setDrawerListener(new DrawerListener());
}
@Override
public void onPause() {
super.onPause();
- mDrawerLayout.setDrawerListener(null);
-
unregisterReceiver(mBatteryInfoReceiver);
mHeaderAdapter.pause();
@@ -746,133 +635,24 @@
return false;
}
- private CharSequence getHeaderTitle(Header header) {
- if (header == null || header.fragment == null) return getTitle();
- final CharSequence title;
- if (header.fragment.equals(DashboardSummary.class.getName())) {
- title = getResources().getString(R.string.settings_label);
- } else {
- title = header.getTitle(getResources());
- }
- return title;
- }
-
- private void setSelectedHeaderById(long headerId) {
- final int count = mHeaders.size();
- for (int n = 0; n < count; n++) {
- Header h = mHeaders.get(n);
- if (h.id == headerId) {
- setSelectedHeader(h);
- return;
- }
- }
- }
-
- /**
- * As the Headers can be rebuilt, their references can change, so use this method with caution!
- */
- private void setSelectedHeader(Header header) {
- if (header == null) {
- mCurrentHeader = null;
- return;
- }
- // Update selected Header into Drawer only if it is not "Add Account"
- if (header.id == R.id.account_add) {
- mDrawer.clearChoices();
- return;
- }
- mCurrentHeader = header;
- int index = mHeaders.indexOf(header);
- if (index >= 0) {
- mDrawer.setItemChecked(index, true);
- } else {
- mDrawer.clearChoices();
- }
- }
-
- private void highlightHeader(int id) {
- if (id != 0) {
- Integer index = mHeaderIndexMap.get(id);
- if (index != null) {
- mDrawer.setItemChecked(index, true);
- if (mDrawer.getVisibility() == View.VISIBLE) {
- mDrawer.smoothScrollToPosition(index);
- }
- }
- }
- }
-
/**
* When in two-pane mode, switch to the fragment pane to show the given
* preference fragment.
*
* @param header The new header to display.
- * @param validate true means that the fragment's Header needs to be validated.
- * @param initial true means that it is the initial Header.
*/
- private void switchToHeader(Header header, boolean validate, boolean initial) {
+ private void onHeaderClick(Header header) {
if (header == null) {
return;
}
- // For switching to another Header it should be a different one
- if (mCurrentHeader == null || header.id != mCurrentHeader.id) {
- if (header.fragment != null) {
- boolean addToBackStack;
- if (initial) {
- addToBackStack = false;
- } else {
- if (header.id != mInitialHeader.id) {
- addToBackStack = true;
- } else {
- addToBackStack = (mTopLevelHeaderId > 0);
- }
- }
- switchToHeaderInner(header.fragment, header.fragmentArguments, validate,
- addToBackStack, getHeaderTitle(header));
- setSelectedHeader(header);
- } else if (header.intent != null) {
- setSelectedHeader(header);
- startActivity(header.intent);
- } else {
- throw new IllegalStateException(
- "Can't switch to header that has no Fragment nor Intent");
- }
- }
- }
-
- /**
- * Switch to a specific Header with taking care of validation, Title and BackStack
- */
- private void switchToHeaderInner(String fragmentName, Bundle args, boolean validate,
- boolean addToBackStack, CharSequence title) {
- getFragmentManager().popBackStack(BACK_STACK_PREFS,
- FragmentManager.POP_BACK_STACK_INCLUSIVE);
- if (validate && !isValidFragment(fragmentName)) {
- throw new IllegalArgumentException("Invalid fragment for this activity: "
- + fragmentName);
- }
- Fragment f = Fragment.instantiate(this, fragmentName, args);
- FragmentTransaction transaction = getFragmentManager().beginTransaction();
- transaction.replace(R.id.prefs, f);
- transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
- if (addToBackStack) {
- transaction.addToBackStack(BACK_STACK_PREFS);
- }
- if (title != null) {
- transaction.setBreadCrumbTitle(title);
- }
- transaction.commitAllowingStateLoss();
- }
-
- @Override
- public void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
-
- // If it is not launched from history, then reset to top-level
- if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
- if (mDrawer != null) {
- mDrawer.setSelectionFromTop(0, 0);
- }
+ if (header.fragment != null) {
+ startWithFragment(header.fragment, header.fragmentArguments, null, 0,
+ header.getTitle(getResources()));
+ } else if (header.intent != null) {
+ startActivity(header.intent);
+ } else {
+ throw new IllegalStateException(
+ "Can't switch to header that has no Fragment nor Intent");
}
}
@@ -949,9 +729,8 @@
* 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);
+ CharSequence titleText, Fragment resultTo, int resultRequestCode) {
+ switchToFragment(fragmentClass, args, resultTo, resultRequestCode, titleRes, titleText);
}
/**
@@ -987,7 +766,7 @@
}
/**
- * Start a new fragment.
+ * Start a new fragment. Used by #startPreferencePanel.
*
* @param fragmentName The name of the fragment to display.
* @param args Optional arguments to supply to the fragment.
@@ -1000,8 +779,8 @@
* 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) {
+ private void switchToFragment(String fragmentName, Bundle args, Fragment resultTo,
+ int resultRequestCode, int titleRes, CharSequence titleText) {
final CharSequence cs;
if (titleRes != 0) {
cs = getText(titleRes);
@@ -1022,6 +801,75 @@
}
/**
+ * Switch to a specific Fragment with taking care of validation, Title and BackStack
+ */
+ private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
+ boolean addToBackStack, CharSequence title, boolean withTransition) {
+ if (validate && !isValidFragment(fragmentName)) {
+ throw new IllegalArgumentException("Invalid fragment for this activity: "
+ + fragmentName);
+ }
+ Fragment f = Fragment.instantiate(this, fragmentName, args);
+ FragmentTransaction transaction = getFragmentManager().beginTransaction();
+ transaction.replace(R.id.prefs, f);
+ if (withTransition) {
+ transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
+ }
+ if (addToBackStack) {
+ transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
+ }
+ if (title != null) {
+ transaction.setBreadCrumbTitle(title);
+ }
+ transaction.commitAllowingStateLoss();
+ return f;
+ }
+
+ /**
+ * Start a new instance of this activity, showing only the given fragment.
+ * When launched in this mode, the given preference fragment will be instantiated and fill the
+ * entire activity.
+ *
+ * @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 title String to display for the title of this set of preferences.
+ */
+ public void startWithFragment(String fragmentName, Bundle args,
+ Fragment resultTo, int resultRequestCode, CharSequence title) {
+ Intent intent = onBuildStartFragmentIntent(fragmentName, args, title);
+ if (resultTo == null) {
+ startActivity(intent);
+ } else {
+ resultTo.startActivityForResult(intent, resultRequestCode);
+ }
+ }
+
+ /**
+ * Build an Intent to launch a new activity showing the selected fragment.
+ * The implementation constructs an Intent that re-launches the current activity with the
+ * appropriate arguments to display the fragment.
+ *
+ * @param fragmentName The name of the fragment to display.
+ * @param args Optional arguments to supply to the fragment.
+ * @param title Optional title to show for this item.
+ * @return Returns an Intent that can be launched to display the given
+ * fragment.
+ */
+ public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args, CharSequence title) {
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setClass(this, SubSettings.class);
+ intent.putExtra(EXTRA_SHOW_FRAGMENT, fragmentName);
+ intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
+ intent.putExtra(EXTRA_SHOW_FRAGMENT_TITLE, title);
+ intent.putExtra(EXTRA_NO_HEADERS, true);
+ return intent;
+ }
+
+ /**
* Called when the activity needs its list of headers build.
*
* @param headers The list in which to place the headers.
@@ -1226,11 +1074,6 @@
// Increment if the current one wasn't removed by the Utils code.
if (i < target.size() && target.get(i) == header) {
- // Hold on to the first header, when we need to reset to the top-level
- if (mFirstHeader == null &&
- HeaderAdapter.getHeaderType(header) != HeaderAdapter.HEADER_TYPE_CATEGORY) {
- mFirstHeader = header;
- }
mHeaderIndexMap.put(id, i);
i++;
}
@@ -1336,7 +1179,6 @@
ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
PackageManager.GET_META_DATA);
if (ai == null || ai.metaData == null) return;
- mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID);
mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
} catch (NameNotFoundException nnfe) {
// No recovery
@@ -1353,261 +1195,19 @@
return mNextButton;
}
- public static class NoHomeDialogFragment extends DialogFragment {
- public static void show(Activity parent) {
- final NoHomeDialogFragment dialog = new NoHomeDialogFragment();
- dialog.show(parent.getFragmentManager(), null);
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- return new AlertDialog.Builder(getActivity())
- .setMessage(R.string.only_one_home_message)
- .setPositiveButton(android.R.string.ok, null)
- .create();
- }
+ public HeaderAdapter getHeaderAdapter() {
+ return mHeaderAdapter;
}
- private static class HeaderAdapter extends ArrayAdapter<Header> {
- static final int HEADER_TYPE_CATEGORY = 0;
- static final int HEADER_TYPE_NORMAL = 1;
- static final int HEADER_TYPE_SWITCH = 2;
- static final int HEADER_TYPE_BUTTON = 3;
- private static final int HEADER_TYPE_COUNT = HEADER_TYPE_BUTTON + 1;
-
- private final WifiEnabler mWifiEnabler;
- private final BluetoothEnabler mBluetoothEnabler;
- private AuthenticatorHelper mAuthHelper;
- private DevicePolicyManager mDevicePolicyManager;
-
- private static class HeaderViewHolder {
- ImageView mIcon;
- TextView mTitle;
- TextView mSummary;
- Switch mSwitch;
- ImageButton mButton;
- View mDivider;
- }
-
- private LayoutInflater mInflater;
-
- static int getHeaderType(Header header) {
- if (header.fragment == null && header.intent == null) {
- return HEADER_TYPE_CATEGORY;
- } else if (header.id == R.id.security_settings) {
- return HEADER_TYPE_BUTTON;
- } else {
- return HEADER_TYPE_NORMAL;
- }
- }
-
- @Override
- public int getItemViewType(int position) {
- Header header = getItem(position);
- return getHeaderType(header);
- }
-
- @Override
- public boolean areAllItemsEnabled() {
- return false; // because of categories
- }
-
- @Override
- public boolean isEnabled(int position) {
- return getItemViewType(position) != HEADER_TYPE_CATEGORY;
- }
-
- @Override
- public int getViewTypeCount() {
- return HEADER_TYPE_COUNT;
- }
-
- @Override
- public boolean hasStableIds() {
- return true;
- }
-
- public HeaderAdapter(Context context, List<Header> objects,
- AuthenticatorHelper authenticatorHelper, DevicePolicyManager dpm) {
- super(context, 0, objects);
-
- mAuthHelper = authenticatorHelper;
- mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- // Temp Switches provided as placeholder until the adapter replaces these with actual
- // Switches inflated from their layouts. Must be done before adapter is set in super
- mWifiEnabler = new WifiEnabler(context, new Switch(context));
- mBluetoothEnabler = new BluetoothEnabler(context, new Switch(context));
- mDevicePolicyManager = dpm;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- HeaderViewHolder holder;
- Header header = getItem(position);
- int headerType = getHeaderType(header);
- View view = null;
-
- if (convertView == null) {
- holder = new HeaderViewHolder();
- switch (headerType) {
- case HEADER_TYPE_CATEGORY:
- view = new TextView(getContext(), null,
- android.R.attr.listSeparatorTextViewStyle);
- holder.mTitle = (TextView) view;
- break;
-
- case HEADER_TYPE_SWITCH:
- view = mInflater.inflate(R.layout.preference_header_switch_item, parent,
- false);
- holder.mIcon = (ImageView) view.findViewById(R.id.icon);
- holder.mTitle = (TextView)
- view.findViewById(com.android.internal.R.id.title);
- holder.mSummary = (TextView)
- view.findViewById(com.android.internal.R.id.summary);
- holder.mSwitch = (Switch) view.findViewById(R.id.switchWidget);
- break;
-
- case HEADER_TYPE_BUTTON:
- view = mInflater.inflate(R.layout.preference_header_button_item, parent,
- false);
- holder.mIcon = (ImageView) view.findViewById(R.id.icon);
- holder.mTitle = (TextView)
- view.findViewById(com.android.internal.R.id.title);
- holder.mSummary = (TextView)
- view.findViewById(com.android.internal.R.id.summary);
- holder.mButton = (ImageButton) view.findViewById(R.id.buttonWidget);
- holder.mDivider = view.findViewById(R.id.divider);
- break;
-
- case HEADER_TYPE_NORMAL:
- view = mInflater.inflate(
- R.layout.preference_header_item, parent,
- false);
- holder.mIcon = (ImageView) view.findViewById(R.id.icon);
- holder.mTitle = (TextView)
- view.findViewById(com.android.internal.R.id.title);
- holder.mSummary = (TextView)
- view.findViewById(com.android.internal.R.id.summary);
- break;
- }
- view.setTag(holder);
- } else {
- view = convertView;
- holder = (HeaderViewHolder) view.getTag();
- }
-
- // All view fields must be updated every time, because the view may be recycled
- switch (headerType) {
- case HEADER_TYPE_CATEGORY:
- holder.mTitle.setText(header.getTitle(getContext().getResources()));
- break;
-
- case HEADER_TYPE_SWITCH:
- // Would need a different treatment if the main menu had more switches
- if (header.id == R.id.wifi_settings) {
- mWifiEnabler.setSwitch(holder.mSwitch);
- } else {
- mBluetoothEnabler.setSwitch(holder.mSwitch);
- }
- updateCommonHeaderView(header, holder);
- break;
-
- case HEADER_TYPE_BUTTON:
- if (header.id == R.id.security_settings) {
- boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled();
- if (hasCert) {
- holder.mButton.setVisibility(View.VISIBLE);
- holder.mDivider.setVisibility(View.VISIBLE);
- boolean isManaged = mDevicePolicyManager.getDeviceOwner() != null;
- if (isManaged) {
- holder.mButton.setImageResource(R.drawable.ic_settings_about);
- } else {
- holder.mButton.setImageResource(
- android.R.drawable.stat_notify_error);
- }
- holder.mButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- Intent intent = new Intent(
- android.provider.Settings.ACTION_MONITORING_CERT_INFO);
- getContext().startActivity(intent);
- }
- });
- } else {
- holder.mButton.setVisibility(View.GONE);
- holder.mDivider.setVisibility(View.GONE);
- }
- }
- updateCommonHeaderView(header, holder);
- break;
-
- case HEADER_TYPE_NORMAL:
- updateCommonHeaderView(header, holder);
- break;
- }
-
- return view;
- }
-
- private void updateCommonHeaderView(Header header, HeaderViewHolder holder) {
- if (header.extras != null
- && header.extras.containsKey(ManageAccountsSettings.KEY_ACCOUNT_TYPE)) {
- String accType = header.extras.getString(
- ManageAccountsSettings.KEY_ACCOUNT_TYPE);
- Drawable icon = mAuthHelper.getDrawableForType(getContext(), accType);
- setHeaderIcon(holder, icon);
- } else {
- if (header.iconRes > 0) {
- holder.mIcon.setImageResource(header.iconRes);
- } else {
- holder.mIcon.setImageDrawable(null);
- }
- }
- if (holder.mIcon != null) {
- if (header.iconRes > 0) {
- holder.mIcon.setBackgroundResource(R.color.background_drawer_icon);
- } else {
- holder.mIcon.setBackground(null);
- }
- }
- holder.mTitle.setText(header.getTitle(getContext().getResources()));
- CharSequence summary = header.getSummary(getContext().getResources());
- if (!TextUtils.isEmpty(summary)) {
- holder.mSummary.setVisibility(View.VISIBLE);
- holder.mSummary.setText(summary);
- } else {
- holder.mSummary.setVisibility(View.GONE);
- }
- }
-
- private void setHeaderIcon(HeaderViewHolder holder, Drawable icon) {
- ViewGroup.LayoutParams lp = holder.mIcon.getLayoutParams();
- lp.width = getContext().getResources().getDimensionPixelSize(
- R.dimen.header_icon_width);
- lp.height = lp.width;
- holder.mIcon.setLayoutParams(lp);
- holder.mIcon.setImageDrawable(icon);
- }
-
- public void resume(Context context) {
- mWifiEnabler.resume();
- mBluetoothEnabler.resume(context);
- }
-
- public void pause() {
- mWifiEnabler.pause();
- mBluetoothEnabler.pause();
- }
- }
-
- private void onListItemClick(ListView l, View v, int position, long id) {
+ public void onListItemClick(ListView l, View v, int position, long id) {
if (!isResumed()) {
return;
}
Object item = mHeaderAdapter.getItem(position);
if (item instanceof Header) {
mSelectedHeader = (Header) item;
+ onHeaderClick(mSelectedHeader);
+ revertToInitialFragment();
}
}
@@ -1628,156 +1228,68 @@
sShowNoHomeNotice = true;
}
- /**
- * Default value for {@link Header#id Header.id} indicating that no
- * identifier value is set. All other values (including those below -1)
- * are valid.
- */
- private static final long HEADER_ID_UNDEFINED = -1;
+ @Override
+ public boolean onQueryTextSubmit(String query) {
+ switchToSearchResultsFragmentIfNeeded();
+ mSearchQuery = query;
+ return mSearchResultsFragment.onQueryTextSubmit(query);
+ }
- /**
- * Description of a single Header item that the user can select.
- */
- static final class Header implements Parcelable {
- /**
- * Identifier for this header, to correlate with a new list when
- * it is updated. The default value is
- * {@link SettingsActivity#HEADER_ID_UNDEFINED}, meaning no id.
- * @attr ref android.R.styleable#PreferenceHeader_id
- */
- public long id = HEADER_ID_UNDEFINED;
+ @Override
+ public boolean onQueryTextChange(String newText) {
+ mSearchQuery = newText;
+ return false;
+ }
- /**
- * Resource ID of title of the header that is shown to the user.
- * @attr ref android.R.styleable#PreferenceHeader_title
- */
- public int titleRes;
+ @Override
+ public boolean onClose() {
+ return false;
+ }
- /**
- * Title of the header that is shown to the user.
- * @attr ref android.R.styleable#PreferenceHeader_title
- */
- public CharSequence title;
-
- /**
- * Resource ID of optional summary describing what this header controls.
- * @attr ref android.R.styleable#PreferenceHeader_summary
- */
- public int summaryRes;
-
- /**
- * Optional summary describing what this header controls.
- * @attr ref android.R.styleable#PreferenceHeader_summary
- */
- public CharSequence summary;
-
- /**
- * Optional icon resource to show for this header.
- * @attr ref android.R.styleable#PreferenceHeader_icon
- */
- public int iconRes;
-
- /**
- * Full class name of the fragment to display when this header is
- * selected.
- * @attr ref android.R.styleable#PreferenceHeader_fragment
- */
- public String fragment;
-
- /**
- * Optional arguments to supply to the fragment when it is
- * instantiated.
- */
- public Bundle fragmentArguments;
-
- /**
- * Intent to launch when the preference is selected.
- */
- public Intent intent;
-
- /**
- * Optional additional data for use by subclasses of the activity
- */
- public Bundle extras;
-
- public Header() {
- // Empty
- }
-
- /**
- * Return the currently set title. If {@link #titleRes} is set,
- * this resource is loaded from <var>res</var> and returned. Otherwise
- * {@link #title} is returned.
- */
- public CharSequence getTitle(Resources res) {
- if (titleRes != 0) {
- return res.getText(titleRes);
+ @Override
+ public boolean onMenuItemActionExpand(MenuItem item) {
+ if (item.getItemId() == mSearchMenuItem.getItemId()) {
+ if (mSearchResultsFragment == null) {
+ switchToSearchResultsFragmentIfNeeded();
}
- return title;
}
+ return true;
+ }
- /**
- * Return the currently set summary. If {@link #summaryRes} is set,
- * this resource is loaded from <var>res</var> and returned. Otherwise
- * {@link #summary} is returned.
- */
- public CharSequence getSummary(Resources res) {
- if (summaryRes != 0) {
- return res.getText(summaryRes);
+ @Override
+ public boolean onMenuItemActionCollapse(MenuItem item) {
+ if (item.getItemId() == mSearchMenuItem.getItemId()) {
+ if (mIsShowingSearchResults) {
+ revertToInitialFragment();
}
- return summary;
}
+ return true;
+ }
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeLong(id);
- dest.writeInt(titleRes);
- TextUtils.writeToParcel(title, dest, flags);
- dest.writeInt(summaryRes);
- TextUtils.writeToParcel(summary, dest, flags);
- dest.writeInt(iconRes);
- dest.writeString(fragment);
- dest.writeBundle(fragmentArguments);
- if (intent != null) {
- dest.writeInt(1);
- intent.writeToParcel(dest, flags);
+ private void switchToSearchResultsFragmentIfNeeded() {
+ if (!mIsShowingSearchResults) {
+ Fragment current = getFragmentManager().findFragmentById(R.id.prefs);
+ if (current != null && current instanceof SearchResultsSummary) {
+ mSearchResultsFragment = (SearchResultsSummary) current;
} else {
- dest.writeInt(0);
+ String title = getString(R.string.search_results_title);
+ mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
+ SearchResultsSummary.class.getName(), null, false, true, title, true);
}
- dest.writeBundle(extras);
+ mIsShowingSearchResults = true;
}
+ }
- public void readFromParcel(Parcel in) {
- id = in.readLong();
- titleRes = in.readInt();
- title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
- summaryRes = in.readInt();
- summary = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
- iconRes = in.readInt();
- fragment = in.readString();
- fragmentArguments = in.readBundle();
- if (in.readInt() != 0) {
- intent = Intent.CREATOR.createFromParcel(in);
- }
- extras = in.readBundle();
- }
+ public void needToRevertToInitialFragment() {
+ mNeedToRevertToInitialFragment = true;
+ }
- Header(Parcel in) {
- readFromParcel(in);
- }
-
- public static final Creator<Header> CREATOR = new Creator<Header>() {
- public Header createFromParcel(Parcel source) {
- return new Header(source);
- }
- public Header[] newArray(int size) {
- return new Header[size];
- }
- };
+ private void revertToInitialFragment() {
+ mNeedToRevertToInitialFragment = false;
+ getFragmentManager().popBackStack(SettingsActivity.BACK_STACK_PREFS,
+ FragmentManager.POP_BACK_STACK_INCLUSIVE);
+ mSearchResultsFragment = null;
+ mIsShowingSearchResults = false;
+ mSearchMenuItem.collapseActionView();
}
}