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
This commit is contained in:
Fabrice Di Meglio
2014-04-24 14:48:48 -07:00
parent 1102f76e58
commit 769630c895
20 changed files with 661 additions and 924 deletions

View File

@@ -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">
android:layout_height="match_parent"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:scrollbarStyle="outsideOverlay"
android:background="@color/dashboard_background_color">
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:orientation="vertical">
<LinearLayout
android:id="@+id/dashboard_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
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>
</ScrollView>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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"

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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);
}
public void setNeedToRebuildCategories(boolean need) {
mNeedToRebuildCategories = need;
}
/**
* 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;
}
/**
* 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() {

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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();
ListView listView = (ListView) view.findViewById(android.R.id.list);
mLayoutInflater = inflater;
mContainer = container;
ListAdapter adapter = ((SettingsActivity) getActivity()).getHeaderAdapter();
listView.setAdapter(adapter);
final View rootView = inflater.inflate(R.layout.dashboard, container, false);
mDashboard = (ViewGroup) rootView.findViewById(R.id.dashboard_container);
return view;
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);
final int tilesCount = category.getTilesCount();
for (int i = 0; i < tilesCount; i++) {
DashboardTile tile = category.getTile(i);
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 onListItemClick(ListView l, View v, int position, long id) {
((SettingsActivity) getActivity()).onListItemClick(l, v, position, id);
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);
}
}
private void rebuildCategories() {
if (!mHandler.hasMessages(MSG_BUILD_CATEGORIES)) {
mHandler.sendEmptyMessage(MSG_BUILD_CATEGORIES);
}
}
@Override
public void onAccountsUpdated(Account[] accounts) {
final SettingsActivity sa = (SettingsActivity) getActivity();
sa.setNeedToRebuildCategories(true);
rebuildCategories();
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);