diff options
author | Fabrice Di Meglio <fdimeglio@google.com> | 2014-04-24 14:48:48 -0700 |
---|---|---|
committer | Fabrice Di Meglio <fdimeglio@google.com> | 2014-04-25 17:24:18 -0700 |
commit | 769630c8956fa844545d964166da90cc802fabac (patch) | |
tree | ecb9c4f835c026f373f76ca85bf13868399cbf2b | |
parent | 1102f76e5852d4cc482264e67c52cabf85d7a51d (diff) | |
download | packages_apps_Settings-769630c8956fa844545d964166da90cc802fabac.tar.gz packages_apps_Settings-769630c8956fa844545d964166da90cc802fabac.tar.bz2 packages_apps_Settings-769630c8956fa844545d964166da90cc802fabac.zip |
Settings new dashboard - part 2
Introduce the new Dashboard (a grid like presentation of
Settings top categories) per UX specification.
- the Dashboard is composed of "categories" and in each of them
you have "tiles"
- implement a new layout for showing top categories
(DashboardContainerView). This layout basically acts like a
grid
- depending on the device configuration make the grid with 1
column in portrait / 2 colums in landscape (phones) OR 2 columns
in portrait and 3 in landscape (tablets)
- take care of Accounts adding and removing (as it changes the
number of tiles to show)
Also remove all the old code related to Headers
Change-Id: Ie29944132c1b4c3f7b073d5a7d4453b8f5ec19a7
-rw-r--r-- | res/layout/dashboard.xml | 32 | ||||
-rw-r--r-- | res/layout/dashboard_category.xml | 38 | ||||
-rw-r--r-- | res/layout/dashboard_tile.xml | 58 | ||||
-rw-r--r-- | res/layout/settings_main.xml | 3 | ||||
-rw-r--r-- | res/values-land/config.xml | 22 | ||||
-rw-r--r-- | res/values-sw600dp-land/config.xml | 22 | ||||
-rw-r--r-- | res/values-sw600dp/config.xml | 22 | ||||
-rwxr-xr-x | res/values-sw600dp/dimens.xml | 4 | ||||
-rw-r--r-- | res/values/colors.xml | 7 | ||||
-rwxr-xr-x | res/values/config.xml | 3 | ||||
-rwxr-xr-x | res/values/dimens.xml | 4 | ||||
-rw-r--r-- | res/xml/settings_headers.xml | 217 | ||||
-rw-r--r-- | src/com/android/settings/SettingsActivity.java | 443 | ||||
-rw-r--r-- | src/com/android/settings/SoundSettings.java | 10 | ||||
-rw-r--r-- | src/com/android/settings/Utils.java | 48 | ||||
-rw-r--r-- | src/com/android/settings/dashboard/DashboardContainerView.java | 134 | ||||
-rw-r--r-- | src/com/android/settings/dashboard/DashboardSummary.java | 153 | ||||
-rw-r--r-- | src/com/android/settings/dashboard/DashboardTileView.java | 91 | ||||
-rw-r--r-- | src/com/android/settings/dashboard/HeaderAdapter.java | 265 | ||||
-rw-r--r-- | src/com/android/settings/dashboard/SearchResultsSummary.java | 13 |
20 files changed, 663 insertions, 926 deletions
diff --git a/res/layout/dashboard.xml b/res/layout/dashboard.xml index 8a2928906..116580ada 100644 --- a/res/layout/dashboard.xml +++ b/res/layout/dashboard.xml @@ -14,21 +14,21 @@ limitations under the License. --> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/dashboard" android:layout_width="match_parent" - android:layout_height="match_parent"> - - <LinearLayout android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_gravity="center" - android:orientation="vertical"> - - <ListView android:id="@id/android:list" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:background="@color/background_drawer" /> - - </LinearLayout> - -</FrameLayout> + android:layout_height="match_parent" + android:paddingStart="12dp" + android:paddingEnd="12dp" + android:scrollbarStyle="outsideOverlay" + android:background="@color/dashboard_background_color"> + + <LinearLayout + android:id="@+id/dashboard_container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="center_horizontal" + android:orientation="vertical" + /> + +</ScrollView> diff --git a/res/layout/dashboard_category.xml b/res/layout/dashboard_category.xml new file mode 100644 index 000000000..8abbee290 --- /dev/null +++ b/res/layout/dashboard_category.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/category" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView android:id="@+id/category_title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:singleLine="true" + android:ellipsize="marquee" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textAlignment="viewStart" + /> + + <com.android.settings.dashboard.DashboardContainerView + android:id="@+id/category_content" + android:layout_width="match_parent" + android:layout_height="wrap_content" + /> + +</LinearLayout> diff --git a/res/layout/dashboard_tile.xml b/res/layout/dashboard_tile.xml new file mode 100644 index 000000000..03b4b0970 --- /dev/null +++ b/res/layout/dashboard_tile.xml @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center_vertical" + android:minHeight="72dp" + android:background="@android:color/white"> + + <ImageView + android:id="@+id/icon" + android:layout_width="40dp" + android:layout_height="40dp" + android:scaleType="centerInside" + android:layout_marginStart="12dp" + android:layout_marginEnd="16dp" + /> + + <RelativeLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" > + + <TextView android:id="@+id/title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:singleLine="true" + android:textAppearance="?android:attr/textAppearanceMedium" + android:ellipsize="marquee" + android:fadingEdge="horizontal" /> + + <TextView android:id="@+id/status" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@android:id/title" + android:layout_alignStart="@android:id/title" + android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="?android:attr/textColorSecondary" + /> + + </RelativeLayout> + +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/settings_main.xml b/res/layout/settings_main.xml index db732dce0..47ed02b14 100644 --- a/res/layout/settings_main.xml +++ b/res/layout/settings_main.xml @@ -27,12 +27,13 @@ android:layout_height="match_parent" android:layout_width="match_parent"> - <android.preference.PreferenceFrameLayout + <FrameLayout android:id="@+id/prefs" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1" /> + </LinearLayout> <RelativeLayout android:id="@+id/button_bar" diff --git a/res/values-land/config.xml b/res/values-land/config.xml new file mode 100644 index 000000000..543dfeeab --- /dev/null +++ b/res/values-land/config.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + + <!-- Dashboard number of columns --> + <integer name="dashboard_num_columns">2</integer> + +</resources> diff --git a/res/values-sw600dp-land/config.xml b/res/values-sw600dp-land/config.xml new file mode 100644 index 000000000..eb1a7c33e --- /dev/null +++ b/res/values-sw600dp-land/config.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + + <!-- Dashboard number of columns --> + <integer name="dashboard_num_columns">3</integer> + +</resources> diff --git a/res/values-sw600dp/config.xml b/res/values-sw600dp/config.xml new file mode 100644 index 000000000..543dfeeab --- /dev/null +++ b/res/values-sw600dp/config.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + + <!-- Dashboard number of columns --> + <integer name="dashboard_num_columns">2</integer> + +</resources> diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml index 4f4a22187..154b34f0f 100755 --- a/res/values-sw600dp/dimens.xml +++ b/res/values-sw600dp/dimens.xml @@ -38,4 +38,8 @@ <dimen name="keyguard_appwidget_picker_margin_left">2dip</dimen> <dimen name="keyguard_appwidget_picker_margin_right">2dip</dimen> <integer name="keyguard_appwidget_picker_cols">2</integer> + + <!-- Dashboard padding between each tiles within the layout --> + <dimen name="dashboard_cell_gap">8dp</dimen> + </resources> diff --git a/res/values/colors.xml b/res/values/colors.xml index acbccaacb..ce60cf3f3 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -15,9 +15,8 @@ --> <resources> - <color name="background_drawer">@android:color/white</color> - <color name="background_drawer_icon">#ffcccccc</color> - <color name="background_search_result_icon">#ffcccccc</color> + <!-- TODO: remove this as this is temporary. Waiting for the new Assets. --> + <color name="temporary_background_icon">#ffcccccc</color> <color name="black">#000</color> <color name="red">#F00</color> @@ -60,4 +59,6 @@ <color name="quantum_orange_700">#fff57c00</color> <color name="quantum_orange_A200">#ffffab40</color> <color name="quantum_orange_A400">#ffff9100</color> + + <color name="dashboard_background_color">#ffe1e1e0</color> </resources> diff --git a/res/values/config.xml b/res/values/config.xml index 557dadb97..86d47fede 100755 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -29,4 +29,7 @@ very long strings too. --> <integer name="maximum_user_dictionary_word_length" translatable="false">48</integer> + <!-- Dashboard number of columns --> + <integer name="dashboard_num_columns">1</integer> + </resources> diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 818816449..562dd80ec 100755 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -84,4 +84,8 @@ <dimen name="notification_app_icon_badge_margin">4dp</dimen> <dimen name="notification_app_settings_divider_height">48dp</dimen> <dimen name="zen_mode_dropdown_width">160dp</dimen> + + <!-- Dashboard padding between each tiles within the layout --> + <dimen name="dashboard_cell_gap">4dp</dimen> + </resources> diff --git a/res/xml/settings_headers.xml b/res/xml/settings_headers.xml deleted file mode 100644 index bf44854f7..000000000 --- a/res/xml/settings_headers.xml +++ /dev/null @@ -1,217 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2010 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. ---> - -<preference-headers - xmlns:android="http://schemas.android.com/apk/res/android"> - - <!-- WIRELESS and NETWORKS --> - <header android:id="@+id/wireless_section" - android:title="@string/header_category_wireless_networks" /> - - <!-- Wifi --> - <header - android:id="@+id/wifi_settings" - android:fragment="com.android.settings.wifi.WifiSettings" - android:title="@string/wifi_settings_title" - android:icon="@drawable/ic_settings_wireless" /> - - <!-- Bluetooth --> - <header - android:id="@+id/bluetooth_settings" - android:fragment="com.android.settings.bluetooth.BluetoothSettings" - android:title="@string/bluetooth_settings_title" - android:icon="@drawable/ic_settings_bluetooth2" /> - - <!-- Data Usage --> - <header - android:id="@+id/data_usage_settings" - android:fragment="com.android.settings.DataUsageSummary" - android:title="@string/data_usage_summary_title" - android:icon="@drawable/ic_settings_data_usage" /> - - <!-- Operator hook --> - <header - android:fragment="com.android.settings.WirelessSettings" - android:id="@+id/operator_settings"> - <intent android:action="com.android.settings.OPERATOR_APPLICATION_SETTING" /> - </header> - - <!-- Other wireless and network controls --> - <header - android:id="@+id/wireless_settings" - android:title="@string/radio_controls_title" - android:breadCrumbTitle="@string/wireless_networks_settings_title" - android:fragment="com.android.settings.WirelessSettings" /> - - <!-- DEVICE --> - <header android:id="@+id/device_section" - android:title="@string/header_category_device" /> - - <!-- Home --> - <header - android:id="@+id/home_settings" - android:icon="@drawable/ic_settings_home" - android:fragment="com.android.settings.HomeSettings" - android:title="@string/home_settings" /> - - <!-- Sound --> - <header - android:id="@+id/sound_settings" - android:icon="@drawable/ic_settings_sound" - android:fragment="com.android.settings.SoundSettings" - android:title="@string/sound_settings" /> - - <!-- Display --> - <header - android:id="@+id/display_settings" - android:icon="@drawable/ic_settings_display" - android:fragment="com.android.settings.DisplaySettings" - android:title="@string/display_settings" /> - - <!-- Notifications --> - <header - android:id="@+id/notification_settings" - android:icon="@drawable/ic_settings_notifications" - android:fragment="com.android.settings.notification.NotificationSettings" - android:title="@string/notification_settings" /> - - <!-- Storage --> - <header - android:id="@+id/storage_settings" - android:fragment="com.android.settings.deviceinfo.Memory" - android:icon="@drawable/ic_settings_storage" - android:title="@string/storage_settings" /> - - <!-- Battery --> - <header - android:id="@+id/battery_settings" - android:fragment="com.android.settings.fuelgauge.PowerUsageSummary" - android:icon="@drawable/ic_settings_battery" - android:title="@string/power_usage_summary_title" /> - - <!-- Application Settings --> - <header - android:fragment="com.android.settings.applications.ManageApplications" - android:icon="@drawable/ic_settings_applications" - android:title="@string/applications_settings" - android:id="@+id/application_settings" /> - - <!-- Manage users --> - <header - android:fragment="com.android.settings.users.UserSettings" - android:icon="@drawable/ic_settings_multiuser" - android:title="@string/user_settings_title" - android:id="@+id/user_settings" /> - - <!-- Manage NFC payment apps --> - <header - android:fragment="com.android.settings.nfc.PaymentSettings" - android:icon="@drawable/ic_settings_nfc_payment" - android:title="@string/nfc_payment_settings_title" - android:id="@+id/nfc_payment_settings" /> - - <!-- Manufacturer hook --> - <header - android:fragment="com.android.settings.WirelessSettings" - android:id="@+id/manufacturer_settings"> - <intent android:action="com.android.settings.MANUFACTURER_APPLICATION_SETTING" /> - </header> - - <!-- PERSONAL --> - <header android:id="@+id/personal_section" - android:title="@string/header_category_personal" /> - - <!-- Location --> - <header - android:fragment="com.android.settings.location.LocationSettings" - android:icon="@drawable/ic_settings_location" - android:title="@string/location_settings_title" - android:id="@+id/location_settings" /> - - <!-- Security --> - <header - android:fragment="com.android.settings.SecuritySettings" - android:icon="@drawable/ic_settings_security" - android:title="@string/security_settings_title" - android:id="@+id/security_settings" /> - - <!-- Language --> - <header - android:id="@+id/language_settings" - android:fragment="com.android.settings.inputmethod.InputMethodAndLanguageSettings" - android:icon="@drawable/ic_settings_language" - android:title="@string/language_settings" /> - - <!-- Backup and reset --> - <header - android:fragment="com.android.settings.PrivacySettings" - android:icon="@drawable/ic_settings_backup" - android:title="@string/privacy_settings" - android:id="@+id/privacy_settings" /> - - <!-- ACCOUNTS section --> - <header - android:id="@+id/account_settings" - android:title="@string/account_settings" /> - - <header - android:id="@+id/account_add" - android:title="@string/add_account_label" - android:icon="@drawable/ic_menu_add_dark"> - <intent - android:action="android.settings.ADD_ACCOUNT_SETTINGS"/> - </header> - - <!-- SYSTEM --> - <header android:id="@+id/system_section" - android:title="@string/header_category_system" /> - - <!-- Date & Time --> - <header - android:id="@+id/date_time_settings" - android:fragment="com.android.settings.DateTimeSettings" - android:icon="@drawable/ic_settings_date_time" - android:title="@string/date_and_time_settings_title" /> - - <!-- Accessibility feedback --> - <header - android:id="@+id/accessibility_settings" - android:fragment="com.android.settings.accessibility.AccessibilitySettings" - android:icon="@drawable/ic_settings_accessibility" - android:title="@string/accessibility_settings" /> - - <!-- Print --> - <header - android:id="@+id/print_settings" - android:fragment="com.android.settings.print.PrintSettingsFragment" - android:icon="@*android:drawable/ic_print" - android:title="@string/print_settings" /> - - <!-- Development --> - <header - android:id="@+id/development_settings" - android:fragment="com.android.settings.DevelopmentSettings" - android:icon="@drawable/ic_settings_development" - android:title="@string/development_settings_title" /> - - <!-- About Device --> - <header - android:id="@+id/about_settings" - android:fragment="com.android.settings.DeviceInfoSettings" - android:icon="@drawable/ic_settings_about" - android:title="@string/about_settings" /> - -</preference-headers> diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java index 699955a9d..1bc0501ab 100644 --- a/src/com/android/settings/SettingsActivity.java +++ b/src/com/android/settings/SettingsActivity.java @@ -24,7 +24,6 @@ import android.app.Activity; import android.app.Fragment; import android.app.FragmentManager; import android.app.FragmentTransaction; -import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -62,7 +61,6 @@ import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; -import android.widget.ListView; import android.widget.SearchView; import com.android.internal.util.ArrayUtils; @@ -80,7 +78,6 @@ import com.android.settings.dashboard.DashboardCategory; import com.android.settings.dashboard.DashboardSummary; import com.android.settings.dashboard.DashboardTile; 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; @@ -115,7 +112,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; -import java.util.HashMap; import java.util.List; import static com.android.settings.dashboard.Header.HEADER_ID_UNDEFINED; @@ -130,7 +126,7 @@ public class SettingsActivity extends Activity private static final String LOG_TAG = "Settings"; // Constants for state save/restore - private static final String SAVE_KEY_HEADERS = ":settings:headers"; + private static final String SAVE_KEY_CATEGORIES = ":settings:categories"; private static final String SAVE_KEY_SEARCH_MENU_EXPANDED = ":settings:search_menu_expanded"; private static final String SAVE_KEY_SEARCH_QUERY = ":settings:search_query"; private static final String SAVE_KEY_SHOW_HOME_AS_UP = ":settings:show_home_as_up"; @@ -199,7 +195,6 @@ public class SettingsActivity extends Activity private static boolean sShowNoHomeNotice = false; private String mFragmentClass; - private Header mSelectedHeader; private CharSequence mInitialTitle; @@ -305,7 +300,7 @@ public class SettingsActivity extends Activity if (mBatteryPresent != batteryPresent) { mBatteryPresent = batteryPresent; - invalidateHeaders(); + invalidateCategories(); } } } @@ -324,21 +319,17 @@ public class SettingsActivity extends Activity private SearchResultsSummary mSearchResultsFragment; private String mSearchQuery; - // Headers - private final ArrayList<Header> mHeaders = new ArrayList<Header>(); - private HeaderAdapter mHeaderAdapter; + // Categories + private ArrayList<DashboardCategory> mCategories = new ArrayList<DashboardCategory>(); + private boolean mNeedToRebuildCategories; - private List<DashboardCategory> mCategories = new ArrayList<DashboardCategory>(); - - private static final int MSG_BUILD_HEADERS = 1; + private static final int MSG_BUILD_CATEGORIES = 1; private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { - case MSG_BUILD_HEADERS: { - mHeaders.clear(); - onBuildHeaders(mHeaders); - mHeaderAdapter.notifyDataSetChanged(); + case MSG_BUILD_CATEGORIES: { + buildDashboardCategories(mCategories); } break; } } @@ -346,6 +337,18 @@ public class SettingsActivity extends Activity private boolean mNeedToRevertToInitialFragment = false; + public AuthenticatorHelper getAuthenticatorHelper() { + return mAuthenticatorHelper; + } + + public List<DashboardCategory> getDashboardCategories() { + if (mNeedToRebuildCategories) { + buildDashboardCategories(mCategories); + mNeedToRebuildCategories = false; + } + return mCategories; + } + @Override public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) { // Override the fragment title for Wallpaper settings @@ -370,9 +373,9 @@ public class SettingsActivity extends Activity return false; } - private void invalidateHeaders() { - if (!mHandler.hasMessages(MSG_BUILD_HEADERS)) { - mHandler.sendEmptyMessage(MSG_BUILD_HEADERS); + private void invalidateCategories() { + if (!mHandler.hasMessages(MSG_BUILD_CATEGORIES)) { + mHandler.sendEmptyMessage(MSG_BUILD_CATEGORIES); } } @@ -428,11 +431,6 @@ public class SettingsActivity extends Activity mAuthenticatorHelper.updateAuthDescriptions(this); mAuthenticatorHelper.onAccountsUpdated(this, null); - DevicePolicyManager dpm = - (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); - - mHeaderAdapter = new HeaderAdapter(this, mHeaders, mAuthenticatorHelper, dpm); - mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE, Context.MODE_PRIVATE); @@ -463,16 +461,17 @@ public class SettingsActivity extends Activity mInitialTitle = (initialTitle != null) ? initialTitle : getTitle(); setTitle(mInitialTitle); - ArrayList<Header> headers = savedState.getParcelableArrayList(SAVE_KEY_HEADERS); - if (headers != null) { - mHeaders.addAll(headers); + ArrayList<DashboardCategory> categories = + savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES); + if (categories != null) { + mCategories.addAll(categories); setTitleFromBackStack(); } mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP); } else { - // We need to build the Headers in all cases - onBuildHeaders(mHeaders); + // We need to build the Categories in all cases + buildDashboardCategories(mCategories); if (initialFragmentName != null) { final ComponentName cn = getIntent().getComponent(); @@ -490,7 +489,7 @@ public class SettingsActivity extends Activity } else { // No UP if we are displaying the Headers mDisplayHomeAsUpEnabled = false; - if (mHeaders.size() > 0) { + if (mCategories.size() > 0) { mInitialTitle = getText(R.string.dashboard_title); switchToFragment(DashboardSummary.class.getName(), null, false, false, mInitialTitle, false); @@ -594,8 +593,8 @@ public class SettingsActivity extends Activity protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - if (mHeaders.size() > 0) { - outState.putParcelableArrayList(SAVE_KEY_HEADERS, mHeaders); + if (mCategories.size() > 0) { + outState.putParcelableArrayList(SAVE_KEY_CATEGORIES, mCategories); } outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled); @@ -619,13 +618,13 @@ public class SettingsActivity extends Activity mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() { @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - invalidateHeaders(); + invalidateCategories(); } }; mDevelopmentPreferences.registerOnSharedPreferenceChangeListener( mDevelopmentPreferencesListener); - invalidateHeaders(); + invalidateCategories(); registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); @@ -679,7 +678,7 @@ public class SettingsActivity extends Activity return; } if (header.fragment != null) { - startWithFragment(header.fragment, header.fragmentArguments, null, 0, + Utils.startWithFragment(this, header.fragment, header.fragmentArguments, null, 0, header.getTitle(getResources())); } else if (header.intent != null) { startActivity(header.intent); @@ -769,7 +768,7 @@ public class SettingsActivity extends Activity // There not much we can do in that case title = ""; } - startWithFragment(fragmentClass, args, resultTo, resultRequestCode, title); + Utils.startWithFragment(this, fragmentClass, args, resultTo, resultRequestCode, title); } /** @@ -829,359 +828,17 @@ public class SettingsActivity extends Activity 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. - */ - private void onBuildHeaders(List<Header> headers) { - loadHeadersFromResource(R.xml.settings_headers, headers); - updateHeaderList(headers); - } - - /** - * Parse the given XML file as a header description, adding each - * parsed Header into the target list. - * - * @param resid The XML resource to load and parse. - * @param target The list in which the parsed headers should be placed. - */ - private void loadHeadersFromResource(int resid, List<Header> target) { - XmlResourceParser parser = null; - try { - parser = getResources().getXml(resid); - AttributeSet attrs = Xml.asAttributeSet(parser); - - int type; - while ((type=parser.next()) != XmlPullParser.END_DOCUMENT - && type != XmlPullParser.START_TAG) { - // Parse next until start tag is found - } - - String nodeName = parser.getName(); - if (!"preference-headers".equals(nodeName)) { - throw new RuntimeException( - "XML document must start with <preference-headers> tag; found" - + nodeName + " at " + parser.getPositionDescription()); - } - - Bundle curBundle = null; - - final int outerDepth = parser.getDepth(); - while ((type=parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { - if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { - continue; - } - - nodeName = parser.getName(); - if ("header".equals(nodeName)) { - Header header = new Header(); - - TypedArray sa = obtainStyledAttributes( - attrs, com.android.internal.R.styleable.PreferenceHeader); - header.id = sa.getResourceId( - com.android.internal.R.styleable.PreferenceHeader_id, - (int)HEADER_ID_UNDEFINED); - TypedValue tv = sa.peekValue( - com.android.internal.R.styleable.PreferenceHeader_title); - if (tv != null && tv.type == TypedValue.TYPE_STRING) { - if (tv.resourceId != 0) { - header.titleRes = tv.resourceId; - } else { - header.title = tv.string; - } - } - tv = sa.peekValue( - com.android.internal.R.styleable.PreferenceHeader_summary); - if (tv != null && tv.type == TypedValue.TYPE_STRING) { - if (tv.resourceId != 0) { - header.summaryRes = tv.resourceId; - } else { - header.summary = tv.string; - } - } - header.iconRes = sa.getResourceId( - com.android.internal.R.styleable.PreferenceHeader_icon, 0); - header.fragment = sa.getString( - com.android.internal.R.styleable.PreferenceHeader_fragment); - sa.recycle(); - - if (curBundle == null) { - curBundle = new Bundle(); - } - - final int innerDepth = parser.getDepth(); - while ((type=parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) { - if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { - continue; - } - - String innerNodeName = parser.getName(); - if (innerNodeName.equals("extra")) { - getResources().parseBundleExtra("extra", attrs, curBundle); - XmlUtils.skipCurrentTag(parser); - - } else if (innerNodeName.equals("intent")) { - header.intent = Intent.parseIntent(getResources(), parser, attrs); - - } else { - XmlUtils.skipCurrentTag(parser); - } - } - - if (curBundle.size() > 0) { - header.fragmentArguments = curBundle; - curBundle = null; - } - - target.add(header); - } else { - XmlUtils.skipCurrentTag(parser); - } - } - - } catch (XmlPullParserException e) { - throw new RuntimeException("Error parsing headers", e); - } catch (IOException e) { - throw new RuntimeException("Error parsing headers", e); - } finally { - if (parser != null) parser.close(); - } - } - - private void updateHeaderList(List<Header> target) { - final boolean showDev = mDevelopmentPreferences.getBoolean( - DevelopmentSettings.PREF_SHOW, - android.os.Build.TYPE.equals("eng")); - int i = 0; - - final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE); - while (i < target.size()) { - Header header = target.get(i); - // Ids are integers, so downcasting - int id = (int) header.id; - if (id == R.id.operator_settings || id == R.id.manufacturer_settings) { - Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header); - } else if (id == R.id.wifi_settings) { - // Remove WiFi Settings if WiFi service is not available. - if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) { - target.remove(i); - } - } else if (id == R.id.bluetooth_settings) { - // Remove Bluetooth Settings if Bluetooth service is not available. - if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) { - target.remove(i); - } - } else if (id == R.id.data_usage_settings) { - // Remove data usage when kernel module not enabled - final INetworkManagementService netManager = INetworkManagementService.Stub - .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)); - try { - if (!netManager.isBandwidthControlEnabled()) { - target.remove(i); - } - } catch (RemoteException e) { - // ignored - } - } else if (id == R.id.battery_settings) { - // Remove battery settings when battery is not available. (e.g. TV) - - if (!mBatteryPresent) { - target.remove(i); - } - } else if (id == R.id.account_settings) { - int headerIndex = i + 1; - i = insertAccountsHeaders(target, headerIndex); - } else if (id == R.id.home_settings) { - if (!updateHomeSettingHeaders(header)) { - target.remove(i); - } - } else if (id == R.id.user_settings) { - if (!UserHandle.MU_ENABLED - || !UserManager.supportsMultipleUsers() - || Utils.isMonkeyRunning()) { - target.remove(i); - } - } else if (id == R.id.nfc_payment_settings) { - if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) { - target.remove(i); - } else { - // Only show if NFC is on and we have the HCE feature - NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this); - if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature( - PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) { - target.remove(i); - } - } - } else if (id == R.id.development_settings) { - if (!showDev) { - target.remove(i); - } - } else if (id == R.id.account_add) { - if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) { - target.remove(i); - } - } - - if (i < target.size() && target.get(i) == header - && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0 - && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) { - target.remove(i); - } - - // Increment if the current one wasn't removed by the Utils code. - if (i < target.size() && target.get(i) == header) { - i++; - } - } - } - - private int insertAccountsHeaders(List<Header> target, int headerIndex) { - String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes(); - List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length); - for (String accountType : accountTypes) { - CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType); - if (label == null) { - continue; - } - - Account[] accounts = AccountManager.get(this).getAccountsByType(accountType); - boolean skipToAccount = accounts.length == 1 - && !mAuthenticatorHelper.hasAccountPreferences(accountType); - Header accHeader = new Header(); - accHeader.title = label; - if (accHeader.extras == null) { - accHeader.extras = new Bundle(); - } - if (skipToAccount) { - accHeader.fragment = AccountSyncSettings.class.getName(); - accHeader.fragmentArguments = new Bundle(); - // Need this for the icon - accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType); - accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]); - accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY, - accounts[0]); - } else { - accHeader.fragment = ManageAccountsSettings.class.getName(); - accHeader.fragmentArguments = new Bundle(); - accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType); - accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, - accountType); - accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL, - label.toString()); - } - accountHeaders.add(accHeader); - mAuthenticatorHelper.preloadDrawableForType(this, accountType); - } - - // Sort by label - Collections.sort(accountHeaders, new Comparator<Header>() { - @Override - public int compare(Header h1, Header h2) { - return h1.title.toString().compareTo(h2.title.toString()); - } - }); - - for (Header header : accountHeaders) { - target.add(headerIndex++, header); - } - if (!mListeningToAccountUpdates) { - AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true); - mListeningToAccountUpdates = true; - } - return headerIndex; - } - - private boolean updateHomeSettingHeaders(Header header) { - // Once we decide to show Home settings, keep showing it forever - SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE); - if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) { - return true; - } - - try { - final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>(); - getPackageManager().getHomeActivities(homeApps); - if (homeApps.size() < 2) { - // When there's only one available home app, omit this settings - // category entirely at the top level UI. If the user just - // uninstalled the penultimate home app candidiate, we also - // now tell them about why they aren't seeing 'Home' in the list. - if (sShowNoHomeNotice) { - sShowNoHomeNotice = false; - NoHomeDialogFragment.show(this); - } - return false; - } else { - // Okay, we're allowing the Home settings category. Tell it, when - // invoked via this front door, that we'll need to be told about the - // case when the user uninstalls all but one home app. - if (header.fragmentArguments == null) { - header.fragmentArguments = new Bundle(); - } - header.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true); - } - } catch (Exception e) { - // Can't look up the home activity; bail on configuring the icon - Log.w(LOG_TAG, "Problem looking up home activity!", e); - } - - sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply(); - return true; + public void setNeedToRebuildCategories(boolean need) { + mNeedToRebuildCategories = need; } /** - * Called when the activity needs its list of categories/tiles build. + * Called when the activity needs its list of categories/tiles built. * * @param categories The list in which to place the tiles categories. */ - private void onBuildDashboardCategories(List<DashboardCategory> categories) { + private void buildDashboardCategories(List<DashboardCategory> categories) { + mCategories.clear(); loadCategoriesFromResource(R.xml.dashboard_categories, categories); updateTilesList(categories); } @@ -1542,22 +1199,6 @@ public class SettingsActivity extends Activity return mNextButton; } - public HeaderAdapter getHeaderAdapter() { - return mHeaderAdapter; - } - - 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, position); - revertToInitialFragment(); - } - } - @Override public boolean shouldUpRecreateTask(Intent targetIntent) { return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class)); @@ -1568,7 +1209,7 @@ public class SettingsActivity extends Activity // TODO: watch for package upgrades to invalidate cache; see 7206643 mAuthenticatorHelper.updateAuthDescriptions(this); mAuthenticatorHelper.onAccountsUpdated(this, accounts); - invalidateHeaders(); + invalidateCategories(); } public static void requestHomeNotice() { diff --git a/src/com/android/settings/SoundSettings.java b/src/com/android/settings/SoundSettings.java index 49928f53e..eadd482d1 100644 --- a/src/com/android/settings/SoundSettings.java +++ b/src/com/android/settings/SoundSettings.java @@ -141,7 +141,10 @@ public class SoundSettings extends SettingsPreferenceFragment implements if (TelephonyManager.PHONE_TYPE_CDMA != activePhoneType) { // device is not CDMA, do not display CDMA emergency_tone - getPreferenceScreen().removePreference(findPreference(KEY_EMERGENCY_TONE)); + Preference pref = findPreference(KEY_EMERGENCY_TONE); + if (pref != null) { + getPreferenceScreen().removePreference(pref); + } } if (!getResources().getBoolean(R.bool.has_silent_mode)) { @@ -150,7 +153,10 @@ public class SoundSettings extends SettingsPreferenceFragment implements if (getResources().getBoolean(com.android.internal.R.bool.config_useFixedVolume)) { // device with fixed volume policy, do not display volumes submenu - getPreferenceScreen().removePreference(findPreference(KEY_RING_VOLUME)); + Preference pref = findPreference(KEY_RING_VOLUME); + if (pref != null) { + getPreferenceScreen().removePreference(pref); + } } mVibrateWhenRinging = (CheckBoxPreference) findPreference(KEY_VIBRATE); diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index d743b6f0a..8abe30db0 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -19,6 +19,7 @@ package com.android.settings; import android.app.ActivityManager; import android.app.AlertDialog; import android.app.Dialog; +import android.app.Fragment; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; @@ -655,4 +656,51 @@ public class Utils { return ((UserManager) context.getSystemService(Context.USER_SERVICE)) .getUsers().size() > 1; } + + /** + * Start a new instance of the activity, showing only the given fragment. + * When launched in this mode, the given preference fragment will be instantiated and fill the + * entire activity. + * + * @param context The context. + * @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 static void startWithFragment(Context context, String fragmentName, Bundle args, + Fragment resultTo, int resultRequestCode, CharSequence title) { + Intent intent = onBuildStartFragmentIntent(context, fragmentName, args, title); + if (resultTo == null) { + context.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 context The Context. + * @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 static Intent onBuildStartFragmentIntent(Context context, String fragmentName, + Bundle args, CharSequence title) { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setClass(context, SubSettings.class); + intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, fragmentName); + intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, args); + intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE, title); + intent.putExtra(SettingsActivity.EXTRA_NO_HEADERS, true); + return intent; + } } diff --git a/src/com/android/settings/dashboard/DashboardContainerView.java b/src/com/android/settings/dashboard/DashboardContainerView.java new file mode 100644 index 000000000..d4512e615 --- /dev/null +++ b/src/com/android/settings/dashboard/DashboardContainerView.java @@ -0,0 +1,134 @@ +/* + * 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.Context; +import android.content.res.Resources; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import com.android.settings.R; + +public class DashboardContainerView extends ViewGroup { + + private int mNumColumns; + private float mCellGap; + + public DashboardContainerView(Context context, AttributeSet attrs) { + super(context, attrs); + + final Resources res = context.getResources(); + mCellGap = res.getDimension(R.dimen.dashboard_cell_gap); + mNumColumns = res.getInteger(R.integer.dashboard_num_columns); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int width = MeasureSpec.getSize(widthMeasureSpec); + final int availableWidth = (int) (width - getPaddingLeft() - getPaddingRight() - + (mNumColumns - 1) * mCellGap); + float cellWidth = (float) Math.ceil(((float) availableWidth) / mNumColumns); + final int N = getChildCount(); + + int cellHeight = 0; + int cursor = 0; + + for (int i = 0; i < N; ++i) { + DashboardTileView v = (DashboardTileView) getChildAt(i); + if (v.getVisibility() == View.GONE) { + continue; + } + + ViewGroup.LayoutParams lp = v.getLayoutParams(); + int colSpan = v.getColumnSpan(); + lp.width = (int) ((colSpan * cellWidth) + (colSpan - 1) * mCellGap); + + // Measure the child + int newWidthSpec = getChildMeasureSpec(widthMeasureSpec, 0, lp.width); + int newHeightSpec = getChildMeasureSpec(heightMeasureSpec, 0, lp.height); + v.measure(newWidthSpec, newHeightSpec); + + // Save the cell height + if (cellHeight <= 0) { + cellHeight = v.getMeasuredHeight(); + } + + lp.height = cellHeight; + + cursor += colSpan; + } + + final int numRows = (int) Math.ceil((float) cursor / mNumColumns); + final int newHeight = (int) ((numRows * cellHeight) + ((numRows - 1) * mCellGap)) + + getPaddingTop() + getPaddingBottom(); + + setMeasuredDimension(width, newHeight); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + final int N = getChildCount(); + final boolean isLayoutRtl = isLayoutRtl(); + final int width = getWidth(); + + int x = getPaddingStart(); + int y = getPaddingTop(); + int cursor = 0; + + for (int i = 0; i < N; ++i) { + final DashboardTileView child = (DashboardTileView) getChildAt(i); + final ViewGroup.LayoutParams lp = child.getLayoutParams(); + if (child.getVisibility() == GONE) { + continue; + } + + final int col = cursor % mNumColumns; + final int colSpan = child.getColumnSpan(); + + final int childWidth = lp.width; + final int childHeight = lp.height; + + int row = cursor / mNumColumns; + + // Push the item to the next row if it can't fit on this one + if ((col + colSpan) > mNumColumns) { + x = getPaddingStart(); + y += childHeight + mCellGap; + row++; + } + + final int childLeft = (isLayoutRtl) ? width - x - childWidth : x; + final int childRight = childLeft + childWidth; + + final int childTop = y; + final int childBottom = childTop + childHeight; + + // Layout the container + child.layout(childLeft, childTop, childRight, childBottom); + + // Offset the position by the cell gap or reset the position and cursor when we + // reach the end of the row + cursor += child.getColumnSpan(); + if (cursor < (((row + 1) * mNumColumns))) { + x += childWidth + mCellGap; + } else { + x = getPaddingStart(); + y += childHeight + mCellGap; + } + } + } +} diff --git a/src/com/android/settings/dashboard/DashboardSummary.java b/src/com/android/settings/dashboard/DashboardSummary.java index f023dec82..54a56ab36 100644 --- a/src/com/android/settings/dashboard/DashboardSummary.java +++ b/src/com/android/settings/dashboard/DashboardSummary.java @@ -16,35 +16,166 @@ package com.android.settings.dashboard; -import android.app.ListFragment; +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.OnAccountsUpdateListener; +import android.app.Fragment; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.ListAdapter; -import android.widget.ListView; +import android.widget.ImageView; +import android.widget.TextView; import com.android.settings.R; import com.android.settings.SettingsActivity; +import com.android.settings.accounts.AuthenticatorHelper; +import com.android.settings.accounts.ManageAccountsSettings; -public class DashboardSummary extends ListFragment { +import java.util.List; + +public class DashboardSummary extends Fragment implements OnAccountsUpdateListener { private static final String LOG_TAG = "DashboardSummary"; + private LayoutInflater mLayoutInflater; + private ViewGroup mContainer; + private ViewGroup mDashboard; + private AuthenticatorHelper mAuthHelper; + + private static final int MSG_BUILD_CATEGORIES = 1; + private Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_BUILD_CATEGORIES: { + final Context context = getActivity(); + rebuildUI(context); + } break; + } + } + }; + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - final View view = inflater.inflate(R.layout.dashboard, container, false); + final Context context = getActivity(); + + mLayoutInflater = inflater; + mContainer = container; + + final View rootView = inflater.inflate(R.layout.dashboard, container, false); + mDashboard = (ViewGroup) rootView.findViewById(R.id.dashboard_container); + + mAuthHelper = ((SettingsActivity) context).getAuthenticatorHelper(); + + rebuildUI(getActivity()); + + return rootView; + } + + private void rebuildUI(Context context) { + final Resources res = getResources(); + + mDashboard.removeAllViews(); + + List<DashboardCategory> categories = + ((SettingsActivity) context).getDashboardCategories(); + + final int count = categories.size(); + + for (int n = 0; n < count; n++) { + DashboardCategory category = categories.get(n); + + View categoryView = mLayoutInflater.inflate(R.layout.dashboard_category, mContainer, + false); + + TextView categoryLabel = (TextView) categoryView.findViewById(R.id.category_title); + categoryLabel.setText(category.getTitle(res)); + + ViewGroup categoryContent = + (ViewGroup) categoryView.findViewById(R.id.category_content); - ListView listView = (ListView) view.findViewById(android.R.id.list); + final int tilesCount = category.getTilesCount(); + for (int i = 0; i < tilesCount; i++) { + DashboardTile tile = category.getTile(i); - ListAdapter adapter = ((SettingsActivity) getActivity()).getHeaderAdapter(); - listView.setAdapter(adapter); + DashboardTileView tileView = new DashboardTileView(context); + updateTileView(context, res, tile, tileView.getImageView(), + tileView.getTitleTextView(), tileView.getStatusTextView()); + + tileView.setTile(tile); + + categoryContent.addView(tileView); + } + + // Add the category + mDashboard.addView(categoryView); + } + } + + @Override + public void onStart() { + super.onStart(); + + AccountManager.get(getActivity()).addOnAccountsUpdatedListener(this, null, false); + } + + @Override + public void onPause() { + super.onPause(); + + AccountManager.get(getActivity()).removeOnAccountsUpdatedListener(this); + } + + private void updateTileView(Context context, Resources res, DashboardTile tile, + ImageView tileIcon, TextView tileTextView, TextView statusTextView) { + + if (tile.extras != null + && tile.extras.containsKey(ManageAccountsSettings.KEY_ACCOUNT_TYPE)) { + String accType = tile.extras.getString(ManageAccountsSettings.KEY_ACCOUNT_TYPE); + Drawable drawable = mAuthHelper.getDrawableForType(context, accType); + tileIcon.setImageDrawable(drawable); + } else { + if (tile.iconRes > 0) { + tileIcon.setImageResource(tile.iconRes); + } else { + tileIcon.setImageDrawable(null); + } + } + if (tileIcon != null) { + if (tile.iconRes > 0) { + tileIcon.setBackgroundResource(R.color.temporary_background_icon); + } else { + tileIcon.setBackground(null); + } + } + tileTextView.setText(tile.getTitle(res)); + + CharSequence summary = tile.getSummary(res); + if (!TextUtils.isEmpty(summary)) { + statusTextView.setVisibility(View.VISIBLE); + statusTextView.setText(summary); + } else { + statusTextView.setVisibility(View.GONE); + } + } - return view; + private void rebuildCategories() { + if (!mHandler.hasMessages(MSG_BUILD_CATEGORIES)) { + mHandler.sendEmptyMessage(MSG_BUILD_CATEGORIES); + } } @Override - public void onListItemClick(ListView l, View v, int position, long id) { - ((SettingsActivity) getActivity()).onListItemClick(l, v, position, id); + public void onAccountsUpdated(Account[] accounts) { + final SettingsActivity sa = (SettingsActivity) getActivity(); + sa.setNeedToRebuildCategories(true); + rebuildCategories(); } } diff --git a/src/com/android/settings/dashboard/DashboardTileView.java b/src/com/android/settings/dashboard/DashboardTileView.java new file mode 100644 index 000000000..250689ec3 --- /dev/null +++ b/src/com/android/settings/dashboard/DashboardTileView.java @@ -0,0 +1,91 @@ +/* + * 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.Context; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.FrameLayout; + +import android.widget.ImageView; +import android.widget.TextView; +import com.android.settings.R; +import com.android.settings.Utils; + +public class DashboardTileView extends FrameLayout implements View.OnClickListener { + + private static final int DEFAULT_COL_SPAN = 1; + + private ImageView mImageView; + private TextView mTitleTextView; + private TextView mStatusTextView; + + private int mColSpan = DEFAULT_COL_SPAN; + + private DashboardTile mTile; + + public DashboardTileView(Context context) { + this(context, null); + } + + public DashboardTileView(Context context, AttributeSet attrs) { + super(context, attrs); + + final View view = LayoutInflater.from(context).inflate(R.layout.dashboard_tile, this); + + mImageView = (ImageView) view.findViewById(R.id.icon); + mTitleTextView = (TextView) view.findViewById(R.id.title); + mStatusTextView = (TextView) view.findViewById(R.id.status); + + setOnClickListener(this); + } + + public TextView getTitleTextView() { + return mTitleTextView; + } + + public TextView getStatusTextView() { + return mStatusTextView; + } + + public ImageView getImageView() { + return mImageView; + } + + public void setTile(DashboardTile tile) { + mTile = tile; + } + + void setColumnSpan(int span) { + mColSpan = span; + } + + int getColumnSpan() { + return mColSpan; + } + + @Override + public void onClick(View v) { + if (mTile.fragment != null) { + Utils.startWithFragment(getContext(), mTile.fragment, mTile.fragmentArguments, null, 0, + mTile.getTitle(getResources())); + } else if (mTile.intent != null) { + getContext().startActivity(mTile.intent); + } + } +} diff --git a/src/com/android/settings/dashboard/HeaderAdapter.java b/src/com/android/settings/dashboard/HeaderAdapter.java deleted file mode 100644 index 2f79c18f8..000000000 --- a/src/com/android/settings/dashboard/HeaderAdapter.java +++ /dev/null @@ -1,265 +0,0 @@ -/* - * 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); - } -}
\ No newline at end of file diff --git a/src/com/android/settings/dashboard/SearchResultsSummary.java b/src/com/android/settings/dashboard/SearchResultsSummary.java index 89e33f67b..e8544aeb3 100644 --- a/src/com/android/settings/dashboard/SearchResultsSummary.java +++ b/src/com/android/settings/dashboard/SearchResultsSummary.java @@ -18,13 +18,11 @@ package com.android.settings.dashboard; import android.app.Fragment; import android.content.ComponentName; -import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Resources; import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Bundle; @@ -41,15 +39,11 @@ import android.widget.SearchView; import android.widget.TextView; import com.android.settings.R; import com.android.settings.SettingsActivity; +import com.android.settings.Utils; import com.android.settings.search.Index; -import com.android.settings.search.IndexDatabaseHelper; -import java.util.Date; import java.util.HashMap; -import static com.android.settings.search.IndexDatabaseHelper.SavedQueriesColums; -import static com.android.settings.search.IndexDatabaseHelper.Tables; - public class SearchResultsSummary extends Fragment { private static final String LOG_TAG = "SearchResultsSummary"; @@ -164,14 +158,13 @@ public class SearchResultsSummary extends Fragment { final String key = cursor.getString(Index.COLUMN_INDEX_KEY); final SettingsActivity sa = (SettingsActivity) getActivity(); - sa.needToRevertToInitialFragment(); if (TextUtils.isEmpty(action)) { Bundle args = new Bundle(); args.putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY, key); - sa.startWithFragment(className, args, null, 0, screenTitle); + Utils.startWithFragment(sa, className, args, null, 0, screenTitle); } else { final Intent intent = new Intent(action); @@ -588,7 +581,7 @@ public class SearchResultsSummary extends Fragment { // 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); + imageView.setBackgroundResource(R.color.temporary_background_icon); } else { imageView.setImageDrawable(null); imageView.setBackgroundResource(R.drawable.empty_icon); |