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/settings/dashboard | |
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/settings/dashboard')
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; + } + } +} |