summaryrefslogtreecommitdiffstats
path: root/src/com/android/settings/dashboard
diff options
context:
space:
mode:
authorFabrice Di Meglio <fdimeglio@google.com>2014-03-21 19:24:43 -0700
committerFabrice Di Meglio <fdimeglio@google.com>2014-03-28 15:51:29 -0700
commitd25314d3305ed1a07b53991a978cd71219ef2a10 (patch)
tree797a75d9ff94055ee025294848e0154fae26f365 /src/com/android/settings/dashboard
parent5f3442af6f1d1141f8f4dff033d4176214281b81 (diff)
downloadpackages_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/settings/dashboard')
-rw-r--r--src/com/android/settings/dashboard/DashboardSummary.java322
-rw-r--r--src/com/android/settings/dashboard/Header.java177
-rw-r--r--src/com/android/settings/dashboard/HeaderAdapter.java275
-rw-r--r--src/com/android/settings/dashboard/NoHomeDialogFragment.java39
-rw-r--r--src/com/android/settings/dashboard/SearchResultsSummary.java331
5 files changed, 831 insertions, 313 deletions
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;
+ }
+ }
+}