diff options
author | Fabrice Di Meglio <fdimeglio@google.com> | 2014-03-21 19:24:43 -0700 |
---|---|---|
committer | Fabrice Di Meglio <fdimeglio@google.com> | 2014-03-28 15:51:29 -0700 |
commit | d25314d3305ed1a07b53991a978cd71219ef2a10 (patch) | |
tree | 797a75d9ff94055ee025294848e0154fae26f365 /src/com/android | |
parent | 5f3442af6f1d1141f8f4dff033d4176214281b81 (diff) | |
download | packages_apps_Settings-d25314d3305ed1a07b53991a978cd71219ef2a10.zip packages_apps_Settings-d25314d3305ed1a07b53991a978cd71219ef2a10.tar.gz packages_apps_Settings-d25314d3305ed1a07b53991a978cd71219ef2a10.tar.bz2 |
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
Diffstat (limited to 'src/com/android')
-rw-r--r-- | src/com/android/settings/SettingsActivity.java | 974 | ||||
-rw-r--r-- | src/com/android/settings/Utils.java | 3 | ||||
-rwxr-xr-x | src/com/android/settings/bluetooth/BluetoothSettings.java | 4 | ||||
-rw-r--r-- | src/com/android/settings/dashboard/DashboardSummary.java | 322 | ||||
-rw-r--r-- | src/com/android/settings/dashboard/Header.java | 177 | ||||
-rw-r--r-- | src/com/android/settings/dashboard/HeaderAdapter.java | 275 | ||||
-rw-r--r-- | src/com/android/settings/dashboard/NoHomeDialogFragment.java | 39 | ||||
-rw-r--r-- | src/com/android/settings/dashboard/SearchResultsSummary.java | 331 | ||||
-rw-r--r-- | src/com/android/settings/search/Index.java | 19 | ||||
-rw-r--r-- | src/com/android/settings/wifi/WifiEnabler.java | 5 | ||||
-rw-r--r-- | src/com/android/settings/wifi/WifiSettings.java | 11 |
11 files changed, 1105 insertions, 1055 deletions
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.Activity; 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; 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.Preference; 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.accessibility.CaptionPropertiesFragment; 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.users.UserSettings; 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.Comparator; 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 @@ public class SettingsActivity extends Activity * 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 @@ public class SettingsActivity extends Activity 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 @@ public class SettingsActivity extends Activity 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 @@ public class SettingsActivity extends Activity } }; + 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 @@ public class SettingsActivity extends Activity 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 @@ public class SettingsActivity extends Activity 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 @@ public class SettingsActivity extends Activity } @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 @@ public class SettingsActivity extends Activity 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 @@ public class SettingsActivity extends Activity } } } - - 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 @@ public class SettingsActivity extends Activity 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 @@ public class SettingsActivity extends Activity 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 @@ public class SettingsActivity extends Activity 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 @@ public class SettingsActivity extends Activity * 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 @@ public class SettingsActivity extends Activity } /** - * 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 @@ public class SettingsActivity extends Activity * 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 @@ public class SettingsActivity extends Activity } /** + * 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 @@ public class SettingsActivity extends Activity // 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 @@ public class SettingsActivity extends Activity 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 @@ public class SettingsActivity extends Activity 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(); - } - } - - 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(); - } + public HeaderAdapter getHeaderAdapter() { + return mHeaderAdapter; } - 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 @@ public class SettingsActivity extends Activity 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; - - /** - * Resource ID of title of the header that is shown to the user. - * @attr ref android.R.styleable#PreferenceHeader_title - */ - public int titleRes; - - /** - * 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 - } + @Override + public boolean onQueryTextChange(String newText) { + mSearchQuery = newText; + return false; + } - /** - * 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); - } - return title; - } + @Override + public boolean onClose() { + return false; + } - /** - * 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 onMenuItemActionExpand(MenuItem item) { + if (item.getItemId() == mSearchMenuItem.getItemId()) { + if (mSearchResultsFragment == null) { + switchToSearchResultsFragmentIfNeeded(); } - return summary; - } - - @Override - public int describeContents() { - return 0; } + return true; + } - @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); - } else { - dest.writeInt(0); + @Override + public boolean onMenuItemActionCollapse(MenuItem item) { + if (item.getItemId() == mSearchMenuItem.getItemId()) { + if (mIsShowingSearchResults) { + revertToInitialFragment(); } - dest.writeBundle(extras); } + return 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); + private void switchToSearchResultsFragmentIfNeeded() { + if (!mIsShowingSearchResults) { + Fragment current = getFragmentManager().findFragmentById(R.id.prefs); + if (current != null && current instanceof SearchResultsSummary) { + mSearchResultsFragment = (SearchResultsSummary) current; + } else { + String title = getString(R.string.search_results_title); + mSearchResultsFragment = (SearchResultsSummary) switchToFragment( + SearchResultsSummary.class.getName(), null, false, true, title, true); } - extras = in.readBundle(); + mIsShowingSearchResults = true; } + } - Header(Parcel in) { - readFromParcel(in); - } + public void needToRevertToInitialFragment() { + mNeedToRevertToInitialFragment = true; + } - 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(); } } diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index bf44835..1ae0829 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -55,6 +55,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ListView; import android.widget.TabWidget; +import com.android.settings.dashboard.Header; import java.io.IOException; import java.io.InputStream; @@ -237,7 +238,7 @@ public class Utils { } public static boolean updateHeaderToSpecificActivityFromMetaDataOrRemove(Context context, - List<SettingsActivity.Header> target, SettingsActivity.Header header) { + List<Header> target, Header header) { Intent intent = header.intent; if (intent != null) { diff --git a/src/com/android/settings/bluetooth/BluetoothSettings.java b/src/com/android/settings/bluetooth/BluetoothSettings.java index cc0371f..80f06b4 100755 --- a/src/com/android/settings/bluetooth/BluetoothSettings.java +++ b/src/com/android/settings/bluetooth/BluetoothSettings.java @@ -129,7 +129,7 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment implem final SettingsActivity activity = (SettingsActivity) getActivity(); - if (!activity.onIsHidingHeaders()) { + if (activity.onIsHidingHeaders()) { activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, ActionBar.DISPLAY_SHOW_CUSTOM); activity.getActionBar().setCustomView(mSwitch, new ActionBar.LayoutParams( @@ -198,7 +198,7 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment implem R.string.bluetooth_search_for_devices; menu.add(Menu.NONE, MENU_ID_SCAN, 0, textId) .setEnabled(bluetoothIsEnabled && !isDiscovering) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); menu.add(Menu.NONE, MENU_ID_RENAME_DEVICE, 0, R.string.bluetooth_rename_device) .setEnabled(bluetoothIsEnabled) .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); diff --git a/src/com/android/settings/dashboard/DashboardSummary.java b/src/com/android/settings/dashboard/DashboardSummary.java index b48e07e..f023dec 100644 --- a/src/com/android/settings/dashboard/DashboardSummary.java +++ b/src/com/android/settings/dashboard/DashboardSummary.java @@ -16,339 +16,35 @@ package com.android.settings.dashboard; -import android.app.Fragment; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.res.Resources; -import android.database.Cursor; -import android.graphics.drawable.Drawable; -import android.os.AsyncTask; +import android.app.ListFragment; import android.os.Bundle; -import android.text.Editable; -import android.text.TextUtils; -import android.text.TextWatcher; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.inputmethod.InputMethodManager; -import android.widget.AdapterView; -import android.widget.BaseAdapter; -import android.widget.EditText; -import android.widget.ImageView; +import android.widget.ListAdapter; import android.widget.ListView; -import android.widget.TextView; import com.android.settings.R; import com.android.settings.SettingsActivity; -import com.android.settings.search.Index; - -import java.util.HashMap; - -public class DashboardSummary extends Fragment { +public class DashboardSummary extends ListFragment { private static final String LOG_TAG = "DashboardSummary"; - private EditText mEditText; - private ListView mListView; - - private SearchResultsAdapter mAdapter; - private Index mIndex; - private UpdateSearchResultsTask mUpdateSearchResultsTask; - - /** - * A basic AsyncTask for updating the query results cursor - */ - private class UpdateSearchResultsTask extends AsyncTask<String, Void, Cursor> { - @Override - protected Cursor doInBackground(String... params) { - return mIndex.search(params[0]); - } - - @Override - protected void onPostExecute(Cursor cursor) { - if (!isCancelled()) { - setCursor(cursor); - } else if (cursor != null) { - cursor.close(); - } - } - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mIndex = Index.getInstance(getActivity()); - mAdapter = new SearchResultsAdapter(getActivity()); - } - - @Override - public void onStop() { - super.onStop(); - - clearResults(); - } - - @Override - public void onStart() { - super.onStart(); - - updateSearchResults(); - } - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View view = inflater.inflate(R.layout.dashboard, container, false); - mEditText = (EditText)view.findViewById(R.id.edittext_query); - mEditText.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - updateSearchResults(); - } - - @Override - public void afterTextChanged(Editable s) { - } - }); - mEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() { - @Override - public void onFocusChange(View v, boolean hasFocus) { - if (!hasFocus) { - closeSoftKeyboard(); - } - } - }); - - mListView = (ListView) view.findViewById(R.id.list_results); - mListView.setAdapter(mAdapter); - mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - closeSoftKeyboard(); - - final Cursor cursor = mAdapter.mCursor; - cursor.moveToPosition(position); - - final String className = cursor.getString(Index.COLUMN_INDEX_CLASS_NAME); - final String screenTitle = cursor.getString(Index.COLUMN_INDEX_SCREEN_TITLE); - - final String action = cursor.getString(Index.COLUMN_INDEX_INTENT_ACTION); - - if (TextUtils.isEmpty(action)) { - ((SettingsActivity) getActivity()).startPreferencePanel(className, null, 0, - screenTitle, null, 0); - } else { - final Intent intent = new Intent(action); - - final String targetPackage = cursor.getString( - Index.COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE); - final String targetClass = cursor.getString( - Index.COLUMN_INDEX_INTENT_ACTION_TARGET_CLASS); - if (!TextUtils.isEmpty(targetPackage) && !TextUtils.isEmpty(targetClass)) { - final ComponentName component = - new ComponentName(targetPackage, targetClass); - intent.setComponent(component); - } + ListView listView = (ListView) view.findViewById(android.R.id.list); - getActivity().startActivity(intent); - } - } - }); + ListAdapter adapter = ((SettingsActivity) getActivity()).getHeaderAdapter(); + listView.setAdapter(adapter); return view; } - private void closeSoftKeyboard() { - InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null && imm.isActive(mEditText)) { - imm.hideSoftInputFromWindow(mEditText.getWindowToken(), 0); - } - } - - private void clearResults() { - if (mUpdateSearchResultsTask != null) { - mUpdateSearchResultsTask.cancel(false); - mUpdateSearchResultsTask = null; - } - setCursor(null); - } - - private void setCursor(Cursor cursor) { - Cursor oldCursor = mAdapter.swapCursor(cursor); - if (oldCursor != null) { - oldCursor.close(); - } - } - - private String getFilteredQueryString() { - final CharSequence query = mEditText.getText().toString(); - final StringBuilder filtered = new StringBuilder(); - for (int n = 0; n < query.length(); n++) { - char c = query.charAt(n); - if (!Character.isLetterOrDigit(c) && !Character.isSpaceChar(c)) { - continue; - } - filtered.append(c); - } - return filtered.toString(); - } - - private void updateSearchResults() { - if (mUpdateSearchResultsTask != null) { - mUpdateSearchResultsTask.cancel(false); - mUpdateSearchResultsTask = null; - } - final String query = getFilteredQueryString(); - if (TextUtils.isEmpty(query)) { - setCursor(null); - } else { - mUpdateSearchResultsTask = new UpdateSearchResultsTask(); - mUpdateSearchResultsTask.execute(query); - } - } - - private static class SearchResult { - public String title; - public String summary; - public int iconResId; - public Context context; - - public SearchResult(Context context, String title, String summary, int iconResId) { - this.context = context; - this.title = title; - this.summary = summary; - this.iconResId = iconResId; - } - } - - private static class SearchResultsAdapter extends BaseAdapter { - - private Cursor mCursor; - private LayoutInflater mInflater; - private boolean mDataValid; - private Context mContext; - private HashMap<String, Context> mContextMap = new HashMap<String, Context>(); - - public SearchResultsAdapter(Context context) { - mContext = context; - mInflater = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - mDataValid = false; - } - - public Cursor swapCursor(Cursor newCursor) { - if (newCursor == mCursor) { - return null; - } - Cursor oldCursor = mCursor; - mCursor = newCursor; - if (newCursor != null) { - mDataValid = true; - notifyDataSetChanged(); - } else { - mDataValid = false; - notifyDataSetInvalidated(); - } - return oldCursor; - } - - @Override - public int getCount() { - if (!mDataValid || mCursor == null || mCursor.isClosed()) return 0; - return mCursor.getCount(); - } - - @Override - public Object getItem(int position) { - if (mDataValid && mCursor.moveToPosition(position)) { - final String title = mCursor.getString(Index.COLUMN_INDEX_TITLE); - final String summary = mCursor.getString(Index.COLUMN_INDEX_SUMMARY); - final String iconResStr = mCursor.getString(Index.COLUMN_INDEX_ICON); - final String className = mCursor.getString( - Index.COLUMN_INDEX_CLASS_NAME); - final String packageName = mCursor.getString( - Index.COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE); - - Context packageContext; - if (TextUtils.isEmpty(className) && !TextUtils.isEmpty(packageName)) { - packageContext = mContextMap.get(packageName); - if (packageContext == null) { - try { - packageContext = mContext.createPackageContext(packageName, 0); - } catch (PackageManager.NameNotFoundException e) { - Log.e(LOG_TAG, "Cannot create Context for package: " + packageName); - return null; - } - mContextMap.put(packageName, packageContext); - } - } else { - packageContext = mContext; - } - final int iconResId = TextUtils.isEmpty(iconResStr) ? - R.drawable.empty_icon : Integer.parseInt(iconResStr); - return new SearchResult(packageContext, title, summary, iconResId); - } - return null; - } - - @Override - public long getItemId(int position) { - return 0; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - if (!mDataValid && convertView == null) { - throw new IllegalStateException( - "this should only be called when the cursor is valid"); - } - if (!mCursor.moveToPosition(position)) { - throw new IllegalStateException("couldn't move cursor to position " + position); - } - - View view; - TextView textTitle; - TextView textSummary; - ImageView imageView; - - if (convertView == null) { - view = mInflater.inflate(R.layout.search_result, parent, false); - } else { - view = convertView; - } - textTitle = (TextView) view.findViewById(R.id.title); - textSummary = (TextView) view.findViewById(R.id.summary); - imageView = (ImageView) view.findViewById(R.id.icon); - - SearchResult result = (SearchResult) getItem(position); - - textTitle.setText(result.title); - textSummary.setText(result.summary); - if (result.iconResId != R.drawable.empty_icon) { - final Context packageContext = result.context; - final Drawable drawable; - try { - drawable = packageContext.getDrawable(result.iconResId); - imageView.setImageDrawable(drawable); - } catch (Resources.NotFoundException nfe) { - // Not much we can do except logging - Log.e(LOG_TAG, "Cannot load Drawable for " + result.title); - } - imageView.setBackgroundResource(R.color.background_search_result_icon); - } else { - imageView.setImageDrawable(null); - imageView.setBackgroundResource(R.drawable.empty_icon); - } - - return view; - } + @Override + public void onListItemClick(ListView l, View v, int position, long id) { + ((SettingsActivity) getActivity()).onListItemClick(l, v, position, id); } } diff --git a/src/com/android/settings/dashboard/Header.java b/src/com/android/settings/dashboard/Header.java new file mode 100644 index 0000000..86e55ac --- /dev/null +++ b/src/com/android/settings/dashboard/Header.java @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.dashboard; + +import android.content.Intent; +import android.content.res.Resources; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; + +/** + * Description of a single Header item that the user can select. + */ +public class Header implements Parcelable { + /** + * Default value for {@link Header#id Header.id} indicating that no + * identifier value is set. All other values (including those below -1) + * are valid. + */ + public static final long HEADER_ID_UNDEFINED = -1; + + /** + * Identifier for this header, to correlate with a new list when + * it is updated. The default value is + * {@link Header#HEADER_ID_UNDEFINED}, meaning no id. + * @attr ref android.R.styleable#PreferenceHeader_id + */ + public long id = HEADER_ID_UNDEFINED; + + /** + * Resource ID of title of the header that is shown to the user. + * @attr ref android.R.styleable#PreferenceHeader_title + */ + public int titleRes; + + /** + * 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); + } + return title; + } + + /** + * 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); + } + return summary; + } + + @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); + } else { + dest.writeInt(0); + } + dest.writeBundle(extras); + } + + 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(); + } + + 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]; + } + }; +} diff --git a/src/com/android/settings/dashboard/HeaderAdapter.java b/src/com/android/settings/dashboard/HeaderAdapter.java new file mode 100644 index 0000000..720f83b --- /dev/null +++ b/src/com/android/settings/dashboard/HeaderAdapter.java @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.dashboard; + +import android.app.admin.DevicePolicyManager; +import android.content.Context; +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.Switch; +import android.widget.TextView; +import com.android.settings.R; +import com.android.settings.accounts.AuthenticatorHelper; +import com.android.settings.accounts.ManageAccountsSettings; +import com.android.settings.bluetooth.BluetoothEnabler; +import com.android.settings.wifi.WifiEnabler; + +import java.util.List; + +/** + * A basic ArrayAdapter for dealing with the Headers + */ +public class HeaderAdapter extends ArrayAdapter<Header> { + public static final int HEADER_TYPE_CATEGORY = 0; + public static final int HEADER_TYPE_NORMAL = 1; + public static final int HEADER_TYPE_SWITCH = 2; + public 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; + + public 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 View.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(context); + mBluetoothEnabler.resume(context); + } + + public void pause() { + mWifiEnabler.pause(); + mBluetoothEnabler.pause(); + } +}
\ No newline at end of file diff --git a/src/com/android/settings/dashboard/NoHomeDialogFragment.java b/src/com/android/settings/dashboard/NoHomeDialogFragment.java new file mode 100644 index 0000000..a795cc9 --- /dev/null +++ b/src/com/android/settings/dashboard/NoHomeDialogFragment.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.dashboard; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.os.Bundle; +import com.android.settings.R; + +public 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(); + } +} diff --git a/src/com/android/settings/dashboard/SearchResultsSummary.java b/src/com/android/settings/dashboard/SearchResultsSummary.java new file mode 100644 index 0000000..992bb59 --- /dev/null +++ b/src/com/android/settings/dashboard/SearchResultsSummary.java @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.dashboard; + +import android.app.Fragment; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.database.Cursor; +import android.graphics.drawable.Drawable; +import android.os.AsyncTask; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.TextView; +import com.android.settings.R; +import com.android.settings.SettingsActivity; +import com.android.settings.search.Index; + +import java.util.HashMap; + +public class SearchResultsSummary extends Fragment { + + private static final String LOG_TAG = "SearchResultsSummary"; + + private ListView mListView; + + private SearchResultsAdapter mAdapter; + private UpdateSearchResultsTask mUpdateSearchResultsTask; + + /** + * A basic AsyncTask for updating the query results cursor + */ + private class UpdateSearchResultsTask extends AsyncTask<String, Void, Cursor> { + @Override + protected Cursor doInBackground(String... params) { + return Index.getInstance(getActivity()).search(params[0]); + } + + @Override + protected void onPostExecute(Cursor cursor) { + if (!isCancelled()) { + setCursor(cursor); + } else if (cursor != null) { + cursor.close(); + } + } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mAdapter = new SearchResultsAdapter(getActivity()); + } + + @Override + public void onStop() { + super.onStop(); + + clearResults(); + } + + @Override + public void onDestroy() { + mListView = null; + mAdapter = null; + mUpdateSearchResultsTask = null; + + super.onDestroy(); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + final View view = inflater.inflate(R.layout.search_results, container, false); + + mListView = (ListView) view.findViewById(R.id.list_results); + mListView.setAdapter(mAdapter); + mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + final Cursor cursor = mAdapter.mCursor; + cursor.moveToPosition(position); + + final String className = cursor.getString(Index.COLUMN_INDEX_CLASS_NAME); + final String screenTitle = cursor.getString(Index.COLUMN_INDEX_SCREEN_TITLE); + + final String action = cursor.getString(Index.COLUMN_INDEX_INTENT_ACTION); + + final SettingsActivity sa = (SettingsActivity) getActivity(); + + sa.needToRevertToInitialFragment(); + + if (TextUtils.isEmpty(action)) { + sa.startWithFragment(className, null, null, 0, screenTitle); + } else { + final Intent intent = new Intent(action); + + final String targetPackage = cursor.getString( + Index.COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE); + final String targetClass = cursor.getString( + Index.COLUMN_INDEX_INTENT_ACTION_TARGET_CLASS); + if (!TextUtils.isEmpty(targetPackage) && !TextUtils.isEmpty(targetClass)) { + final ComponentName component = + new ComponentName(targetPackage, targetClass); + intent.setComponent(component); + } + + sa.startActivity(intent); + } + } + }); + + return view; + } + + public boolean onQueryTextSubmit(String query) { + updateSearchResults(query); + return true; + } + + public boolean onClose() { + clearResults(); + return false; + } + + private void clearResults() { + if (mUpdateSearchResultsTask != null) { + mUpdateSearchResultsTask.cancel(false); + mUpdateSearchResultsTask = null; + } + setCursor(null); + } + + private void setCursor(Cursor cursor) { + if (mAdapter == null) { + return; + } + Cursor oldCursor = mAdapter.swapCursor(cursor); + if (oldCursor != null) { + oldCursor.close(); + } + } + + private String getFilteredQueryString(CharSequence query) { + final StringBuilder filtered = new StringBuilder(); + for (int n = 0; n < query.length(); n++) { + char c = query.charAt(n); + if (!Character.isLetterOrDigit(c) && !Character.isSpaceChar(c)) { + continue; + } + filtered.append(c); + } + return filtered.toString(); + } + + private void updateSearchResults(CharSequence cs) { + if (mUpdateSearchResultsTask != null) { + mUpdateSearchResultsTask.cancel(false); + mUpdateSearchResultsTask = null; + } + final String query = getFilteredQueryString(cs); + if (TextUtils.isEmpty(query)) { + setCursor(null); + } else { + mUpdateSearchResultsTask = new UpdateSearchResultsTask(); + mUpdateSearchResultsTask.execute(query); + } + } + + private static class SearchResult { + public String title; + public String summary; + public int iconResId; + public Context context; + + public SearchResult(Context context, String title, String summary, int iconResId) { + this.context = context; + this.title = title; + this.summary = summary; + this.iconResId = iconResId; + } + } + + private static class SearchResultsAdapter extends BaseAdapter { + + private Cursor mCursor; + private LayoutInflater mInflater; + private boolean mDataValid; + private Context mContext; + private HashMap<String, Context> mContextMap = new HashMap<String, Context>(); + + public SearchResultsAdapter(Context context) { + mContext = context; + mInflater = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mDataValid = false; + } + + public Cursor swapCursor(Cursor newCursor) { + if (newCursor == mCursor) { + return null; + } + Cursor oldCursor = mCursor; + mCursor = newCursor; + if (newCursor != null) { + mDataValid = true; + notifyDataSetChanged(); + } else { + mDataValid = false; + notifyDataSetInvalidated(); + } + return oldCursor; + } + + @Override + public int getCount() { + if (!mDataValid || mCursor == null || mCursor.isClosed()) return 0; + return mCursor.getCount(); + } + + @Override + public Object getItem(int position) { + if (mDataValid && mCursor.moveToPosition(position)) { + final String title = mCursor.getString(Index.COLUMN_INDEX_TITLE); + final String summary = mCursor.getString(Index.COLUMN_INDEX_SUMMARY); + final String iconResStr = mCursor.getString(Index.COLUMN_INDEX_ICON); + final String className = mCursor.getString( + Index.COLUMN_INDEX_CLASS_NAME); + final String packageName = mCursor.getString( + Index.COLUMN_INDEX_INTENT_ACTION_TARGET_PACKAGE); + + Context packageContext; + if (TextUtils.isEmpty(className) && !TextUtils.isEmpty(packageName)) { + packageContext = mContextMap.get(packageName); + if (packageContext == null) { + try { + packageContext = mContext.createPackageContext(packageName, 0); + } catch (PackageManager.NameNotFoundException e) { + Log.e(LOG_TAG, "Cannot create Context for package: " + packageName); + return null; + } + mContextMap.put(packageName, packageContext); + } + } else { + packageContext = mContext; + } + final int iconResId = TextUtils.isEmpty(iconResStr) ? + R.drawable.empty_icon : Integer.parseInt(iconResStr); + return new SearchResult(packageContext, title, summary, iconResId); + } + return null; + } + + @Override + public long getItemId(int position) { + return 0; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if (!mDataValid && convertView == null) { + throw new IllegalStateException( + "this should only be called when the cursor is valid"); + } + if (!mCursor.moveToPosition(position)) { + throw new IllegalStateException("couldn't move cursor to position " + position); + } + + View view; + TextView textTitle; + TextView textSummary; + ImageView imageView; + + if (convertView == null) { + view = mInflater.inflate(R.layout.search_result_item, parent, false); + } else { + view = convertView; + } + textTitle = (TextView) view.findViewById(R.id.title); + textSummary = (TextView) view.findViewById(R.id.summary); + imageView = (ImageView) view.findViewById(R.id.icon); + + SearchResult result = (SearchResult) getItem(position); + + textTitle.setText(result.title); + textSummary.setText(result.summary); + if (result.iconResId != R.drawable.empty_icon) { + final Context packageContext = result.context; + final Drawable drawable; + try { + drawable = packageContext.getDrawable(result.iconResId); + imageView.setImageDrawable(drawable); + } catch (Resources.NotFoundException nfe) { + // Not much we can do except logging + Log.e(LOG_TAG, "Cannot load Drawable for " + result.title); + } + imageView.setBackgroundResource(R.color.background_search_result_icon); + } else { + imageView.setImageDrawable(null); + imageView.setBackgroundResource(R.drawable.empty_icon); + } + + return view; + } + } +} diff --git a/src/com/android/settings/search/Index.java b/src/com/android/settings/search/Index.java index 1a6bfb9..6ac5196 100644 --- a/src/com/android/settings/search/Index.java +++ b/src/com/android/settings/search/Index.java @@ -96,7 +96,7 @@ public class Index { private static Index sInstance; private final AtomicBoolean mIsAvailable = new AtomicBoolean(false); private final UpdateData mDataToProcess = new UpdateData(); - private final Context mContext; + private Context mContext; /** * A private class to describe the update data for the Index database @@ -124,6 +124,8 @@ public class Index { public static Index getInstance(Context context) { if (sInstance == null) { sInstance = new Index(context); + } else { + sInstance.setContext(context); } return sInstance; } @@ -132,6 +134,10 @@ public class Index { mContext = context; } + public void setContext(Context context) { + mContext = context; + } + public boolean isAvailable() { return mIsAvailable.get(); } @@ -675,6 +681,17 @@ public class Index { return (data != null) ? data.toString() : null; } + private int getResId(Context context, AttributeSet set, int[] attrs, int resId) { + final TypedArray sa = context.obtainStyledAttributes(set, attrs); + final TypedValue tv = sa.peekValue(resId); + + if (tv != null && tv.type == TypedValue.TYPE_STRING) { + return tv.resourceId; + } else { + return 0; + } + } + /** * A private class for updating the Index database */ diff --git a/src/com/android/settings/wifi/WifiEnabler.java b/src/com/android/settings/wifi/WifiEnabler.java index ad0ba56..e41429b 100644 --- a/src/com/android/settings/wifi/WifiEnabler.java +++ b/src/com/android/settings/wifi/WifiEnabler.java @@ -38,7 +38,7 @@ import com.android.settings.search.Index; import java.util.concurrent.atomic.AtomicBoolean; public class WifiEnabler implements CompoundButton.OnCheckedChangeListener { - private final Context mContext; + private Context mContext; private Switch mSwitch; private AtomicBoolean mConnected = new AtomicBoolean(false); @@ -77,7 +77,8 @@ public class WifiEnabler implements CompoundButton.OnCheckedChangeListener { mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); } - public void resume() { + public void resume(Context context) { + mContext = context; // Wi-Fi state is sticky, so just let the receiver update UI mContext.registerReceiver(mReceiver, mIntentFilter); mSwitch.setOnCheckedChangeListener(this); diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java index 667feb3..21fecd3 100644 --- a/src/com/android/settings/wifi/WifiSettings.java +++ b/src/com/android/settings/wifi/WifiSettings.java @@ -416,7 +416,7 @@ public class WifiSettings extends RestrictedSettingsFragment if (activity instanceof SettingsActivity) { SettingsActivity sa = (SettingsActivity) activity; - addSwitch = !sa.onIsHidingHeaders(); + addSwitch = sa.onIsHidingHeaders(); } else if (activity instanceof WifiPickerActivity) { PreferenceActivity pa = (PreferenceActivity) activity; addSwitch = pa.onIsHidingHeaders(); @@ -458,12 +458,13 @@ public class WifiSettings extends RestrictedSettingsFragment @Override public void onResume() { + final Activity activity = getActivity(); super.onResume(); if (mWifiEnabler != null) { - mWifiEnabler.resume(); + mWifiEnabler.resume(activity); } - getActivity().registerReceiver(mReceiver, mFilter); + activity.registerReceiver(mReceiver, mFilter); updateAccessPoints(); } @@ -497,11 +498,11 @@ public class WifiSettings extends RestrictedSettingsFragment menu.add(Menu.NONE, MENU_ID_WPS_PBC, 0, R.string.wifi_menu_wps_pbc) .setIcon(ta.getDrawable(1)) .setEnabled(wifiIsEnabled) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); menu.add(Menu.NONE, MENU_ID_ADD_NETWORK, 0, R.string.wifi_add_network) .setIcon(ta.getDrawable(0)) .setEnabled(wifiIsEnabled) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.wifi_menu_scan) //.setIcon(R.drawable.ic_menu_scan_network) .setEnabled(wifiIsEnabled) |