Merge "Update task affinity and parent activity for all exported activities."
diff --git a/res/layout/data_usage_summary_preference.xml b/res/layout/data_usage_summary_preference.xml
new file mode 100644
index 0000000..445e7cd
--- /dev/null
+++ b/res/layout/data_usage_summary_preference.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 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="wrap_content"
+ android:paddingStart="@dimen/preference_no_icon_padding_start"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:orientation="vertical"
+ android:background="@drawable/selectable_card_grey"
+ android:selectable="false"
+ style="@style/EntityHeader">
+
+ <TextView
+ android:id="@+id/usage_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"
+ android:paddingBottom="5dp"
+ android:text="@string/data_usage_title" />
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="5dp"
+ android:fontFamily="@*android:string/config_headlineFontFamily"
+ android:textColor="?android:attr/colorAccent"
+ android:textAppearance="@android:style/TextAppearance.Material.Large" />
+
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"
+ android:paddingBottom="5dp" />
+
+ <com.android.settings.widget.LinearColorBar
+ android:id="@+id/color_bar"
+ android:layout_width="match_parent"
+ android:layout_height="28dp" />
+
+ <LinearLayout
+ android:id="@+id/label_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="2dp"
+ android:orientation="horizontal">
+
+ <TextView android:id="@android:id/text1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary" />
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" />
+
+ <TextView android:id="@android:id/text2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary" />
+
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/cycle_left_time"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="5dp" />
+
+ <TextView
+ android:id="@+id/carrier_and_update"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="5dp" />
+
+ <Button
+ android:id="@+id/launch_mdp_app_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="left"
+ android:text="@string/launch_mdp_app_text"
+ style="@style/ActionPrimaryButton" />
+
+ <TextView
+ android:id="@+id/data_limits"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="5dp" />
+
+</LinearLayout>
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 2540640..72db253 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -5844,6 +5844,8 @@
<string name="data_usage_enable_3g">2G-3G data</string>
<!-- Toggle switch title for enabling 4G data network connection. [CHAR LIMIT=32] -->
<string name="data_usage_enable_4g">4G data</string>
+ <!-- Toggle switch title for enabling roaming on the primary data SIM card. [CHAR LIMIT=32] -->
+ <string name="data_roaming_enable_mobile">Roaming</string>
<!-- Data Usage Foreground label. [CHAR LIMIT=40] -->
<string name="data_usage_forground_label">Foreground:</string>
@@ -8655,6 +8657,27 @@
<item quantity="other"><xliff:g id="count" example="10">%1$d</xliff:g> apps allowed to use unrestricted data when Data Saver is on</item>
</plurals>
+ <!-- Data usage title text [CHAR LIMIT=30] -->
+ <string name="data_usage_title">Primary data</string>
+
+ <!-- Data usage string [CHAR LIMIT=30] -->
+ <string name="data_used"><xliff:g name="bytes" example="2 GB">^1</xliff:g> used</string>
+
+ <!-- Optional part of data usage showing the remaining amount [CHAR LIMIT=30] -->
+ <string name="data_remaining"><xliff:g name="bytes" example="2 GB">, ^1</xliff:g> left</string>
+
+ <!-- Informational text about time left in billing cycle [CHAR LIMIT=30] -->
+ <string name="cycle_left_time_text"><xliff:g name="time" example="2d">%1$s</xliff:g> left in this cycle</string>
+
+ <!-- Informational text about carrier and update time [CHAR LIMIT=30] -->
+ <string name="carrier_and_update_text">Updated by <xliff:g name="carrier" example="T-mobile">%1$s</xliff:g> <xliff:g name="time" example="3m">%2$s</xliff:g></string>
+
+ <!-- Informational text about update time only, without carrier [CHAR LIMIT=30] -->
+ <string name="no_carrier_update_text">Updated <xliff:g name="time" example="3m">%1$s</xliff:g></string>
+
+ <!-- Button to launch external data plan app [CHAR LIMIT=30] -->
+ <string name="launch_mdp_app_text">VIEW PLAN</string>
+
<!-- Name of Data Saver screens [CHAR LIMIT=30] -->
<string name="data_saver_title">Data saver</string>
diff --git a/res/xml/battery_saver_settings.xml b/res/xml/battery_saver_settings.xml
index 47199c2..9e32440 100644
--- a/res/xml/battery_saver_settings.xml
+++ b/res/xml/battery_saver_settings.xml
@@ -16,6 +16,7 @@
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/battery_saver"
android:key="battery_saver">
@@ -23,12 +24,15 @@
<SwitchPreference
android:key="auto_battery_saver"
android:title="@string/battery_saver_auto_title"
- android:summary="@string/battery_saver_auto_summary"/>
+ android:summary="@string/battery_saver_auto_summary"
+ settings:controller="com.android.settings.fuelgauge.batterysaver.AutoBatterySaverPreferenceController"/>
+
<com.android.settings.widget.SeekBarPreference
android:key="battery_saver_seek_bar"
android:title="@string/battery_saver_seekbar_title"
android:max="75"
android:min="5"/>
+
<com.android.settings.applications.LayoutPreference
android:key="battery_saver_button_container"
android:selectable="false"
diff --git a/res/xml/connected_devices_advanced.xml b/res/xml/connected_devices_advanced.xml
index 0b75abf..83a63e4 100644
--- a/res/xml/connected_devices_advanced.xml
+++ b/res/xml/connected_devices_advanced.xml
@@ -25,8 +25,8 @@
android:title="@string/bluetooth_settings_title"
android:icon="@drawable/ic_settings_bluetooth"
android:summary="@string/bluetooth_pref_summary"
- settings:controller="com.android.settings.bluetooth.BluetoothSwitchPreferenceController"
- android:order="-7"/>
+ android:order="-7"
+ settings:controller="com.android.settings.bluetooth.BluetoothSwitchPreferenceController"/>
<SwitchPreference
android:key="toggle_nfc"
@@ -54,6 +54,7 @@
android:title="@string/bluetooth_on_while_driving_pref"
android:icon="@drawable/ic_settings_bluetooth"
android:summary="@string/bluetooth_on_while_driving_summary"
+ settings:controller="com.android.settings.connecteddevice.BluetoothOnWhileDrivingPreferenceController"
android:order="-2"/>
<Preference
diff --git a/res/xml/data_usage.xml b/res/xml/data_usage.xml
index 958459c..88d7f32 100644
--- a/res/xml/data_usage.xml
+++ b/res/xml/data_usage.xml
@@ -20,24 +20,8 @@
android:key="data_usage_screen"
android:title="@string/data_usage_summary_title">
- <PreferenceCategory
- android:key="data_usage_category"
- android:title="@string/usage">
-
- <com.android.settings.SummaryPreference
+ <com.android.settings.datausage.DataUsageSummaryPreference
android:key="status_header"
android:selectable="false" />
- <Preference
- android:key="limit_summary"
- android:selectable="false"
- settings:allowDividerBelow="true" />
-
- <com.android.settings.datausage.DataSaverPreference
- android:key="restrict_background"
- android:title="@string/data_saver_title"
- android:fragment="com.android.settings.datausage.DataSaverSummary" />
-
- </PreferenceCategory>
-
</PreferenceScreen>
diff --git a/res/xml/display_settings.xml b/res/xml/display_settings.xml
index 4d0c992..3cafc3f 100644
--- a/res/xml/display_settings.xml
+++ b/res/xml/display_settings.xml
@@ -43,8 +43,9 @@
<com.android.settingslib.RestrictedSwitchPreference
android:key="auto_brightness"
android:title="@string/auto_brightness_title"
- settings:keywords="@string/keywords_display_auto_brightness"
android:summary="@string/auto_brightness_summary"
+ settings:keywords="@string/keywords_display_auto_brightness"
+ settings:controller="com.android.settings.display.AutoBrightnessPreferenceController"
settings:useAdminDisabledSummary="true"
settings:userRestriction="no_config_brightness" />
diff --git a/res/xml/security_dashboard_settings.xml b/res/xml/security_dashboard_settings.xml
index dafd36c..00eaa7e 100644
--- a/res/xml/security_dashboard_settings.xml
+++ b/res/xml/security_dashboard_settings.xml
@@ -79,7 +79,8 @@
<SwitchPreference
android:key="visiblepattern_profile"
android:summary="@string/summary_placeholder"
- android:title="@string/lockpattern_settings_enable_visible_pattern_title_profile" />
+ android:title="@string/lockpattern_settings_enable_visible_pattern_title_profile"
+ settings:controller="com.android.settings.security.VisiblePatternProfilePreferenceController"/>
<Preference
android:key="fingerprint_settings_profile"
@@ -103,7 +104,8 @@
<SwitchPreference
android:key="show_password"
android:title="@string/show_password"
- android:summary="@string/show_password_summary" />
+ android:summary="@string/show_password_summary"
+ settings:controller="com.android.settings.security.ShowPasswordPreferenceController"/>
</PreferenceCategory>
diff --git a/res/xml/security_lockscreen_settings.xml b/res/xml/security_lockscreen_settings.xml
index 1da1de5..3d3bfd0 100644
--- a/res/xml/security_lockscreen_settings.xml
+++ b/res/xml/security_lockscreen_settings.xml
@@ -16,6 +16,7 @@
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="security_lockscreen_settings_screen"
android:title="@string/lockscreen_settings_title">
@@ -36,7 +37,8 @@
<SwitchPreference
android:key="security_setting_lockdown_enabled"
android:title="@string/lockdown_settings_title"
- android:summary="@string/lockdown_settings_summary" />
+ android:summary="@string/lockdown_settings_summary"
+ settings:controller="com.android.settings.security.LockdownButtonPreferenceController"/>
<PreferenceCategory
android:key="security_setting_lock_screen_notif_work_header"
diff --git a/src/com/android/settings/core/BasePreferenceController.java b/src/com/android/settings/core/BasePreferenceController.java
index 01d98b8..4bd7ba4 100644
--- a/src/com/android/settings/core/BasePreferenceController.java
+++ b/src/com/android/settings/core/BasePreferenceController.java
@@ -30,6 +30,8 @@
* Abstract class to consolidate utility between preference controllers and act as an interface
* for Slices. The abstract classes that inherit from this class will act as the direct interfaces
* for each type when plugging into Slices.
+ *
+ * TODO (b/73074893) Add Lifecycle Setting method.
*/
public abstract class BasePreferenceController extends AbstractPreferenceController {
diff --git a/src/com/android/settings/datausage/DataUsageSummary.java b/src/com/android/settings/datausage/DataUsageSummary.java
index b63cee3..339de7c 100644
--- a/src/com/android/settings/datausage/DataUsageSummary.java
+++ b/src/com/android/settings/datausage/DataUsageSummary.java
@@ -18,7 +18,6 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.net.NetworkPolicyManager;
import android.net.NetworkTemplate;
import android.os.Bundle;
import android.os.UserManager;
@@ -28,6 +27,7 @@
import android.support.v7.preference.PreferenceScreen;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionPlan;
import android.text.BidiFormatter;
import android.text.Spannable;
import android.text.SpannableString;
@@ -40,7 +40,6 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
-import com.android.settings.SummaryPreference;
import com.android.settings.Utils;
import com.android.settings.dashboard.SummaryLoader;
import com.android.settings.search.BaseSearchIndexProvider;
@@ -65,7 +64,6 @@
public static final String KEY_RESTRICT_BACKGROUND = "restrict_background";
private static final String KEY_STATUS_HEADER = "status_header";
- private static final String KEY_LIMIT_SUMMARY = "limit_summary";
// Mobile data keys
public static final String KEY_MOBILE_USAGE_TITLE = "mobile_category";
@@ -77,13 +75,9 @@
public static final String KEY_WIFI_USAGE_TITLE = "wifi_category";
public static final String KEY_WIFI_DATA_USAGE = "wifi_data_usage";
- private DataUsageController mDataUsageController;
- private DataUsageInfoController mDataInfoController;
- private SummaryPreference mSummaryPreference;
- private Preference mLimitPreference;
+ private DataUsageSummaryPreference mSummaryPreference;
+ private DataUsageSummaryPreferenceController mSummaryController;
private NetworkTemplate mDefaultTemplate;
- private int mDataUsageTemplate;
- private NetworkPolicyEditor mPolicyEditor;
@Override
public int getHelpResource() {
@@ -95,25 +89,20 @@
super.onCreate(icicle);
final Context context = getContext();
- NetworkPolicyManager policyManager = NetworkPolicyManager.from(context);
- mPolicyEditor = new NetworkPolicyEditor(policyManager);
boolean hasMobileData = DataUsageUtils.hasMobileData(context);
- mDataUsageController = new DataUsageController(context);
- mDataInfoController = new DataUsageInfoController();
int defaultSubId = DataUsageUtils.getDefaultSubscriptionId(context);
if (defaultSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
hasMobileData = false;
}
mDefaultTemplate = DataUsageUtils.getDefaultTemplate(context, defaultSubId);
- mSummaryPreference = (SummaryPreference) findPreference(KEY_STATUS_HEADER);
+ mSummaryPreference = (DataUsageSummaryPreference) findPreference(KEY_STATUS_HEADER);
if (!hasMobileData || !isAdmin()) {
removePreference(KEY_RESTRICT_BACKGROUND);
}
if (hasMobileData) {
- mLimitPreference = findPreference(KEY_LIMIT_SUMMARY);
List<SubscriptionInfo> subscriptions =
services.mSubscriptionManager.getActiveSubscriptionInfoList();
if (subscriptions == null || subscriptions.size() == 0) {
@@ -127,10 +116,6 @@
addMobileSection(subInfo.getSubscriptionId());
}
}
- mSummaryPreference.setSelectable(true);
- } else {
- removePreference(KEY_LIMIT_SUMMARY);
- mSummaryPreference.setSelectable(false);
}
boolean hasWifiRadio = DataUsageUtils.hasWifiRadio(context);
if (hasWifiRadio) {
@@ -139,10 +124,6 @@
if (hasEthernet(context)) {
addEthernetSection();
}
- mDataUsageTemplate = hasMobileData ? R.string.cell_data_template
- : hasWifiRadio ? R.string.wifi_data_template
- : R.string.ethernet_data_template;
-
setHasOptionsMenu(true);
}
@@ -189,7 +170,11 @@
@Override
protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
- return null;
+ final ArrayList<AbstractPreferenceController> controllers = new ArrayList<>();
+ mSummaryController =
+ new DataUsageSummaryPreferenceController(context);
+ controllers.add(mSummaryController);
+ return controllers;
}
private void addMobileSection(int subId) {
@@ -269,36 +254,6 @@
}
private void updateState() {
- DataUsageController.DataUsageInfo info = mDataUsageController.getDataUsageInfo(
- mDefaultTemplate);
- Context context = getContext();
- mDataInfoController.updateDataLimit(info,
- services.mPolicyEditor.getPolicy(mDefaultTemplate));
-
- if (mSummaryPreference != null) {
- mSummaryPreference.setTitle(
- formatUsage(context, getString(mDataUsageTemplate), info.usageLevel));
- final long limit = mDataInfoController.getSummaryLimit(info);
- mSummaryPreference.setSummary(info.period);
- if (limit <= 0) {
- mSummaryPreference.setChartEnabled(false);
- } else {
- mSummaryPreference.setChartEnabled(true);
- mSummaryPreference.setLabels(Formatter.formatFileSize(context, 0),
- Formatter.formatFileSize(context, limit));
- mSummaryPreference.setRatios(info.usageLevel / (float) limit, 0,
- (limit - info.usageLevel) / (float) limit);
- }
- }
- if (mLimitPreference != null && (info.warningLevel > 0 || info.limitLevel > 0)) {
- String warning = Formatter.formatFileSize(context, info.warningLevel);
- String limit = Formatter.formatFileSize(context, info.limitLevel);
- mLimitPreference.setSummary(getString(info.limitLevel <= 0 ? R.string.cell_warning_only
- : R.string.cell_warning_and_limit, warning, limit));
- } else if (mLimitPreference != null) {
- mLimitPreference.setSummary(null);
- }
-
PreferenceScreen screen = getPreferenceScreen();
for (int i = 1; i < screen.getPreferenceCount(); i++) {
((TemplatePreferenceCategory) screen.getPreference(i)).pushTemplates(services);
@@ -323,6 +278,7 @@
@Override
public void updateDataUsage() {
updateState();
+ mSummaryController.updateState(mSummaryPreference);
}
private static class SummaryProvider
@@ -341,17 +297,39 @@
@Override
public void setListening(boolean listening) {
if (listening) {
- DataUsageController.DataUsageInfo info = mDataController.getDataUsageInfo();
- String used;
- if (info == null) {
- used = Formatter.formatFileSize(mActivity, 0);
- } else if (info.limitLevel <= 0) {
- used = Formatter.formatFileSize(mActivity, info.usageLevel);
- } else {
- used = Utils.formatPercentage(info.usageLevel, info.limitLevel);
- }
mSummaryLoader.setSummary(this,
- mActivity.getString(R.string.data_usage_summary_format, used));
+ mActivity.getString(R.string.data_usage_summary_format, formatUsedData()));
+ }
+ }
+
+ private String formatUsedData() {
+ SubscriptionManager subscriptionManager = (SubscriptionManager) mActivity
+ .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+ int defaultSubId = subscriptionManager.getDefaultSubscriptionId();
+ if (defaultSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ return formatFallbackData();
+ }
+ SubscriptionPlan dfltPlan = DataUsageSummaryPreferenceController
+ .getPrimaryPlan(subscriptionManager, defaultSubId);
+ if (dfltPlan == null) {
+ return formatFallbackData();
+ }
+ if (DataUsageSummaryPreferenceController.unlimited(dfltPlan.getDataLimitBytes())) {
+ return Formatter.formatFileSize(mActivity, dfltPlan.getDataUsageBytes());
+ } else {
+ return Utils.formatPercentage(dfltPlan.getDataUsageBytes(),
+ dfltPlan.getDataLimitBytes());
+ }
+ }
+
+ private String formatFallbackData() {
+ DataUsageController.DataUsageInfo info = mDataController.getDataUsageInfo();
+ if (info == null) {
+ return Formatter.formatFileSize(mActivity, 0);
+ } else if (info.limitLevel <= 0) {
+ return Formatter.formatFileSize(mActivity, info.usageLevel);
+ } else {
+ return Utils.formatPercentage(info.usageLevel, info.limitLevel);
}
}
}
diff --git a/src/com/android/settings/datausage/DataUsageSummaryPreference.java b/src/com/android/settings/datausage/DataUsageSummaryPreference.java
new file mode 100644
index 0000000..984df02
--- /dev/null
+++ b/src/com/android/settings/datausage/DataUsageSummaryPreference.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2018 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.datausage;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.android.settings.R;
+import com.android.settings.SummaryPreference;
+import com.android.settingslib.utils.StringUtil;
+
+import libcore.util.Objects;
+
+/**
+ * Provides a summary of data usage.
+ */
+public class DataUsageSummaryPreference extends SummaryPreference {
+
+ private int mNumPlans;
+ /** The ending time of the billing cycle in milliseconds since epoch. */
+ private long mCycleEndTimeMs;
+ /** The time of the last update in standard milliseconds since the epoch */
+ private long mSnapshotTimeMs;
+ /** Name of carrier, or null if not available */
+ private CharSequence mCarrierName;
+ private String mLimitInfoText;
+ private Intent mLaunchIntent;
+
+ public DataUsageSummaryPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setLayoutResource(R.layout.data_usage_summary_preference);
+ }
+
+ public void setLimitInfo(String text) {
+ if (!Objects.equal(text, mLimitInfoText)) {
+ mLimitInfoText = text;
+ notifyChanged();
+ }
+ }
+
+ public void setUsageInfo(long cycleEnd, long snapshotTime, CharSequence carrierName,
+ int numPlans, Intent launchIntent) {
+ mCycleEndTimeMs = cycleEnd;
+ mSnapshotTimeMs = snapshotTime;
+ mCarrierName = carrierName;
+ mNumPlans = numPlans;
+ mLaunchIntent = launchIntent;
+ notifyChanged();
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+
+ TextView usageTitle = (TextView) holder.findViewById(R.id.usage_title);
+ usageTitle.setVisibility(mNumPlans > 1 ? View.VISIBLE : View.GONE);
+
+ TextView cycleTime = (TextView) holder.findViewById(R.id.cycle_left_time);
+ cycleTime.setText(getContext().getString(R.string.cycle_left_time_text,
+ StringUtil.formatElapsedTime(getContext(),
+ mCycleEndTimeMs - System.currentTimeMillis(),false /* withSeconds */)));
+
+ TextView carrierInfo = (TextView) holder.findViewById(R.id.carrier_and_update);
+ setCarrierInfo(carrierInfo, mCarrierName, mSnapshotTimeMs);
+
+ Button launchButton = (Button) holder.findViewById(R.id.launch_mdp_app_button);
+ launchButton.setOnClickListener((view) -> {
+ getContext().sendBroadcast(mLaunchIntent);
+ });
+ if (mLaunchIntent != null) {
+ launchButton.setVisibility(View.VISIBLE);
+ } else {
+ launchButton.setVisibility(View.GONE);
+ }
+
+ TextView limitInfo = (TextView) holder.findViewById(R.id.data_limits);
+ limitInfo.setVisibility(
+ mLimitInfoText == null || mLimitInfoText.isEmpty() ? View.GONE : View.VISIBLE);
+ limitInfo.setText(mLimitInfoText);
+ }
+
+ private void setCarrierInfo(TextView carrierInfo, CharSequence carrierName, long updateAge) {
+ if (mNumPlans > 0 && updateAge >= 0L) {
+ carrierInfo.setVisibility(View.VISIBLE);
+ if (carrierName != null) {
+ carrierInfo.setText(getContext().getString(R.string.carrier_and_update_text,
+ carrierName, StringUtil.formatRelativeTime(
+ getContext(), updateAge, false /* withSeconds */)));
+ } else {
+ carrierInfo.setText(getContext().getString(R.string.no_carrier_update_text,
+ StringUtil.formatRelativeTime(
+ getContext(), updateAge, false /* withSeconds */)));
+ }
+ } else {
+ carrierInfo.setVisibility(View.GONE);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java b/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java
new file mode 100644
index 0000000..11da829
--- /dev/null
+++ b/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2018 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.datausage;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.NetworkPolicyManager;
+import android.net.NetworkTemplate;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionPlan;
+import android.text.BidiFormatter;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.TextUtils;
+import android.text.format.Formatter;
+import android.text.style.RelativeSizeSpan;
+import android.util.Log;
+import android.util.RecurrenceRule;
+
+import com.android.internal.util.CollectionUtils;
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.NetworkPolicyEditor;
+import com.android.settingslib.net.DataUsageController;
+
+import java.util.List;
+
+/**
+ * This is the controller for the top of the data usage screen that retrieves carrier data from the
+ * new subscriptions framework API if available. The controller reads subscription information from
+ * the framework and falls back to legacy usage data if none are available.
+ */
+public class DataUsageSummaryPreferenceController extends BasePreferenceController {
+
+ private static final String TAG = "DataUsageController";
+ private static final String KEY = "status_header";
+ private static final long PETA = 1000000000000000L;
+ private static final float RELATIVE_SIZE_LARGE = 1.25f * 1.25f; // (1/0.8)^2
+ private static final float RELATIVE_SIZE_SMALL = 1.0f / RELATIVE_SIZE_LARGE; // 0.8^2
+
+ private final DataUsageController mDataUsageController;
+ private final DataUsageInfoController mDataInfoController;
+ private final NetworkTemplate mDefaultTemplate;
+ private final NetworkPolicyEditor mPolicyEditor;
+ private final int mDataUsageTemplate;
+ private final boolean mHasMobileData;
+ private final SubscriptionManager mSubscriptionManager;
+
+ /** Name of the carrier, or null if not available */
+ private CharSequence mCarrierName;
+
+ /** The number of registered plans, [0,N] */
+ private int mDataplanCount;
+
+ /** The time of the last update in milliseconds since the epoch, or -1 if unknown */
+ private long mSnapshotTime;
+
+ /**
+ * The size of the first registered plan if one exists or the size of the warning if it is set.
+ * -1 if no information is available.
+ */
+ private long mDataplanSize;
+ /** The number of bytes used since the start of the cycle. */
+ private long mDataplanUse;
+ /** The starting time of the billing cycle in ms since the epoch */
+ private long mCycleStart;
+ /** The ending time of the billing cycle in ms since the epoch */
+ private long mCycleEnd;
+
+ private Intent mManageSubscriptionIntent;
+
+ public DataUsageSummaryPreferenceController(Context context) {
+ super(context, KEY);
+
+ final int defaultSubId = DataUsageUtils.getDefaultSubscriptionId(context);
+ mDefaultTemplate = DataUsageUtils.getDefaultTemplate(context, defaultSubId);
+ NetworkPolicyManager policyManager = NetworkPolicyManager.from(context);
+ mPolicyEditor = new NetworkPolicyEditor(policyManager);
+
+ mHasMobileData = DataUsageUtils.hasMobileData(context)
+ && defaultSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+ mDataUsageController = new DataUsageController(context);
+ mDataInfoController = new DataUsageInfoController();
+
+ if (mHasMobileData) {
+ mDataUsageTemplate = R.string.cell_data_template;
+ } else if (DataUsageUtils.hasWifiRadio(context)) {
+ mDataUsageTemplate = R.string.wifi_data_template;
+ } else {
+ mDataUsageTemplate = R.string.ethernet_data_template;
+ }
+
+ mSubscriptionManager = (SubscriptionManager)
+ mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+ }
+
+ @VisibleForTesting
+ DataUsageSummaryPreferenceController(
+ Context context,
+ DataUsageController dataUsageController,
+ DataUsageInfoController dataInfoController,
+ NetworkTemplate defaultTemplate,
+ NetworkPolicyEditor policyEditor,
+ int dataUsageTemplate,
+ boolean hasMobileData,
+ SubscriptionManager subscriptionManager) {
+ super(context, KEY);
+ mDataUsageController = dataUsageController;
+ mDataInfoController = dataInfoController;
+ mDefaultTemplate = defaultTemplate;
+ mPolicyEditor = policyEditor;
+ mDataUsageTemplate = dataUsageTemplate;
+ mHasMobileData = hasMobileData;
+ mSubscriptionManager = subscriptionManager;
+ }
+
+ @VisibleForTesting
+ void setPlanValues(int dataPlanCount, long dataPlanSize, long dataPlanUse) {
+ mDataplanCount = dataPlanCount;
+ mDataplanSize = dataPlanSize;
+ mDataplanUse = dataPlanUse;
+ }
+
+ @VisibleForTesting
+ void setCarrierValues(String carrierName, long snapshotTime, long cycleEnd, Intent intent) {
+ mCarrierName = carrierName;
+ mSnapshotTime = snapshotTime;
+ mCycleEnd = cycleEnd;
+ mManageSubscriptionIntent = intent;
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ DataUsageSummaryPreference summaryPreference = (DataUsageSummaryPreference) preference;
+ DataUsageController.DataUsageInfo info = mDataUsageController.getDataUsageInfo(
+ mDefaultTemplate);
+
+ mDataInfoController.updateDataLimit(info, mPolicyEditor.getPolicy(mDefaultTemplate));
+
+ if (mSubscriptionManager != null) {
+ refreshDataplanInfo(info);
+ }
+
+ if (mDataplanCount == 0 && (info.warningLevel > 0 || info.limitLevel > 0)) {
+ final String warning = Formatter.formatFileSize(mContext, info.warningLevel);
+ final String limit = Formatter.formatFileSize(mContext, info.limitLevel);
+ summaryPreference.setLimitInfo(mContext.getString(info.limitLevel <= 0
+ ? R.string.cell_warning_only
+ : R.string.cell_warning_and_limit, warning, limit));
+ } else {
+ summaryPreference.setLimitInfo(null);
+ }
+
+ final StringBuilder title = new StringBuilder();
+ if (mHasMobileData) {
+ title.append(formatUsage(mContext, mContext.getString(R.string.data_used),
+ mDataplanUse));
+ if (mDataplanCount >= 0 && mDataplanSize > 0L) {
+ title.append(formatUsage(mContext, mContext.getString(R.string.data_remaining),
+ mDataplanSize - mDataplanUse));
+ }
+ } else {
+ title.append(formatUsage(mContext, mContext.getString(mDataUsageTemplate),
+ mDataplanUse));
+ }
+ summaryPreference.setTitle(title.toString());
+
+ if (mDataplanSize <= 0) {
+ summaryPreference.setChartEnabled(false);
+ } else {
+ summaryPreference.setChartEnabled(true);
+ summaryPreference.setLabels(Formatter.formatFileSize(mContext, 0 /* sizeBytes */),
+ Formatter.formatFileSize(mContext, mDataplanSize));
+ summaryPreference.setRatios(mDataplanUse / (float) mDataplanSize, 0 /* middle */,
+ (mDataplanSize - mDataplanUse) / (float) mDataplanSize);
+ }
+ summaryPreference.setUsageInfo(mCycleEnd, mSnapshotTime, mCarrierName,
+ mDataplanCount, mManageSubscriptionIntent);
+ }
+
+ // TODO(b/70950124) add test for this method once the robolectric shadow run script is
+ // completed (b/3526807)
+ private void refreshDataplanInfo(DataUsageController.DataUsageInfo info) {
+ // reset data before overwriting
+ mCarrierName = null;
+ mDataplanCount = 0;
+ mDataplanSize = mDataInfoController.getSummaryLimit(info);
+ mDataplanUse = info.usageLevel;
+ mCycleStart = info.cycleStart;
+ mCycleEnd = info.cycleEnd;
+ mSnapshotTime = -1L;
+
+ final int defaultSubId = SubscriptionManager.getDefaultSubscriptionId();
+ final SubscriptionInfo subInfo = mSubscriptionManager.getDefaultDataSubscriptionInfo();
+ if (subInfo != null && mHasMobileData) {
+ mCarrierName = subInfo.getCarrierName();
+ List<SubscriptionPlan> plans = mSubscriptionManager.getSubscriptionPlans(defaultSubId);
+ final SubscriptionPlan primaryPlan = getPrimaryPlan(mSubscriptionManager, defaultSubId);
+ if (primaryPlan != null) {
+ mDataplanCount = plans.size();
+ mDataplanSize = primaryPlan.getDataLimitBytes();
+ if (unlimited(mDataplanSize)) {
+ mDataplanSize = 0L;
+ }
+ mDataplanUse = primaryPlan.getDataUsageBytes();
+
+ RecurrenceRule rule = primaryPlan.getCycleRule();
+ if (rule != null && rule.start != null && rule.end != null) {
+ mCycleStart = rule.start.toEpochSecond() * 1000L;
+ mCycleEnd = rule.end.toEpochSecond() * 1000L;
+ }
+ mSnapshotTime = System.currentTimeMillis() - primaryPlan.getDataUsageTime();
+ }
+ }
+ mManageSubscriptionIntent =
+ mSubscriptionManager.createManageSubscriptionIntent(defaultSubId);
+ Log.i(TAG, "Have " + mDataplanCount + " plans, dflt sub-id " + defaultSubId
+ + ", intent " + mManageSubscriptionIntent);
+ }
+
+ public static SubscriptionPlan getPrimaryPlan(SubscriptionManager subManager, int primaryId) {
+ List<SubscriptionPlan> plans = subManager.getSubscriptionPlans(primaryId);
+ if (CollectionUtils.isEmpty(plans)) {
+ return null;
+ }
+ // First plan in the list is the primary plan
+ SubscriptionPlan plan = plans.get(0);
+ return plan.getDataLimitBytes() > 0
+ && saneSize(plan.getDataUsageBytes())
+ && plan.getCycleRule() != null ? plan : null;
+ }
+
+ private static boolean saneSize(long value) {
+ return value >= 0L && value < PETA;
+ }
+
+ public static boolean unlimited(long size) {
+ return size == SubscriptionPlan.BYTES_UNLIMITED;
+ }
+
+ @VisibleForTesting
+ private static CharSequence formatUsage(Context context, String template, long usageLevel) {
+ final int FLAGS = Spannable.SPAN_INCLUSIVE_INCLUSIVE;
+
+ final Formatter.BytesResult usedResult = Formatter.formatBytes(context.getResources(),
+ usageLevel, Formatter.FLAG_CALCULATE_ROUNDED);
+ final SpannableString enlargedValue = new SpannableString(usedResult.value);
+ enlargedValue.setSpan(
+ new RelativeSizeSpan(RELATIVE_SIZE_LARGE), 0, enlargedValue.length(), FLAGS);
+
+ final SpannableString amountTemplate = new SpannableString(
+ context.getString(com.android.internal.R.string.fileSizeSuffix)
+ .replace("%1$s", "^1").replace("%2$s", "^2"));
+ final CharSequence formattedUsage = TextUtils.expandTemplate(amountTemplate,
+ enlargedValue, usedResult.units);
+
+ final SpannableString fullTemplate = new SpannableString(template);
+ fullTemplate.setSpan(
+ new RelativeSizeSpan(RELATIVE_SIZE_SMALL), 0, fullTemplate.length(), FLAGS);
+ return TextUtils.expandTemplate(fullTemplate,
+ BidiFormatter.getInstance().unicodeWrap(formattedUsage.toString()));
+ }
+}
diff --git a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
index 2095f25..2f93d0a 100644
--- a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
@@ -62,7 +62,8 @@
*/
public class BatteryAppListPreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin, LifecycleObserver, OnPause, OnDestroy {
- private static final boolean USE_FAKE_DATA = true;
+ @VisibleForTesting
+ static final boolean USE_FAKE_DATA = false;
private static final int MAX_ITEMS_TO_LIST = USE_FAKE_DATA ? 30 : 10;
private static final int MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP = 10;
private static final int STATS_TYPE = BatteryStats.STATS_SINCE_CHARGED;
diff --git a/src/com/android/settings/security/VisiblePatternProfilePreferenceController.java b/src/com/android/settings/security/VisiblePatternProfilePreferenceController.java
index a9e56f4..55448e2 100644
--- a/src/com/android/settings/security/VisiblePatternProfilePreferenceController.java
+++ b/src/com/android/settings/security/VisiblePatternProfilePreferenceController.java
@@ -44,6 +44,11 @@
private Preference mPreference;
+ public VisiblePatternProfilePreferenceController(Context context) {
+ this(context, null /* lifecycle */);
+ }
+
+ // TODO (b/73074893) Replace this constructor without Lifecycle using setter method instead.
public VisiblePatternProfilePreferenceController(Context context, Lifecycle lifecycle) {
super(context, KEY_VISIBLE_PATTERN_PROFILE);
mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
diff --git a/tests/robotests/assets/grandfather_slice_controller_not_in_xml b/tests/robotests/assets/grandfather_slice_controller_not_in_xml
new file mode 100644
index 0000000..f11027e
--- /dev/null
+++ b/tests/robotests/assets/grandfather_slice_controller_not_in_xml
@@ -0,0 +1 @@
+com.android.settings.testutils.FakeToggleController
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java
new file mode 100644
index 0000000..ea1d29b
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2018 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.datausage;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.NetworkTemplate;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.NetworkPolicyEditor;
+import com.android.settingslib.net.DataUsageController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.concurrent.TimeUnit;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class DataUsageSummaryPreferenceControllerTest {
+ private static final long UPDATE_BACKOFF_MS = TimeUnit.MINUTES.toMillis(13);
+ private static final long CYCLE_BACKOFF_MS = TimeUnit.DAYS.toMillis(6);
+ private static final long CYCLE_LENGTH_MS = TimeUnit.DAYS.toMillis(30);
+ private static final long USAGE1 = 373000000L;
+ private static final long LIMIT1 = 1000000000L;
+ private static final String CARRIER_NAME = "z-mobile";
+ private static final String PERIOD = "Feb";
+
+ @Mock
+ private DataUsageController mDataUsageController;
+ @Mock
+ private DataUsageInfoController mDataInfoController;
+ @Mock
+ private DataUsageSummaryPreference mSummaryPreference;
+ @Mock
+ private NetworkPolicyEditor mPolicyEditor;
+ @Mock
+ private NetworkTemplate mNetworkTemplate;
+
+ private Context mContext;
+ private DataUsageSummaryPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+
+ mController = new DataUsageSummaryPreferenceController(
+ mContext,
+ mDataUsageController,
+ mDataInfoController,
+ mNetworkTemplate,
+ mPolicyEditor,
+ R.string.cell_data_template,
+ true,
+ null);
+ }
+
+ @Test
+ public void testSummaryUpdate_onePlan_basic() {
+ final long now = System.currentTimeMillis();
+ final DataUsageController.DataUsageInfo info = createTestDataUsageInfo(now);
+
+ final Intent intent = new Intent();
+
+ when(mDataUsageController.getDataUsageInfo()).thenReturn(info);
+ mController.setPlanValues(1 /* dataPlanCount */, LIMIT1, USAGE1);
+ mController.setCarrierValues(CARRIER_NAME, now - UPDATE_BACKOFF_MS, info.cycleEnd, intent);
+
+ mController.updateState(mSummaryPreference);
+ verify(mSummaryPreference).setLimitInfo(null);
+ verify(mSummaryPreference).setUsageInfo(info.cycleEnd, now - UPDATE_BACKOFF_MS,
+ CARRIER_NAME, 1 /* numPlans */, intent);
+ verify(mSummaryPreference).setChartEnabled(true);
+ }
+
+ @Test
+ public void testSummaryUpdate_noPlan_basic() {
+ final long now = System.currentTimeMillis();
+ final DataUsageController.DataUsageInfo info = createTestDataUsageInfo(now);
+
+ final Intent intent = new Intent();
+
+ when(mDataUsageController.getDataUsageInfo(any())).thenReturn(info);
+ mController.setPlanValues(0 /* dataPlanCount */, LIMIT1, USAGE1);
+ mController.setCarrierValues(CARRIER_NAME, now - UPDATE_BACKOFF_MS, info.cycleEnd, intent);
+
+ mController.updateState(mSummaryPreference);
+ verify(mSummaryPreference).setLimitInfo("500 MB Data warning / 1.00 GB Data limit");
+ verify(mSummaryPreference).setUsageInfo(info.cycleEnd, now - UPDATE_BACKOFF_MS,
+ CARRIER_NAME, 0 /* numPlans */, intent);
+ verify(mSummaryPreference).setChartEnabled(true);
+ }
+
+ @Test
+ public void testSummaryUpdate_noCarrier_basic() {
+ final long now = System.currentTimeMillis();
+ final DataUsageController.DataUsageInfo info = createTestDataUsageInfo(now);
+
+ when(mDataUsageController.getDataUsageInfo(any())).thenReturn(info);
+ mController.setPlanValues(0 /* dataPlanCount */, LIMIT1, USAGE1);
+ mController.setCarrierValues(null /* carrierName */, -1L /* snapshotTime */,
+ info.cycleEnd, null /* intent */);
+ mController.updateState(mSummaryPreference);
+
+ verify(mSummaryPreference).setLimitInfo("500 MB Data warning / 1.00 GB Data limit");
+ verify(mSummaryPreference).setUsageInfo(
+ info.cycleEnd,
+ -1L /* snapshotTime */,
+ null /* carrierName */,
+ 0 /* numPlans */,
+ null /* launchIntent */);
+ verify(mSummaryPreference).setChartEnabled(true);
+ }
+
+ @Test
+ public void testSummaryUpdate_noPlanData_basic() {
+ final long now = System.currentTimeMillis();
+
+ final DataUsageController.DataUsageInfo info = createTestDataUsageInfo(now);
+
+ when(mDataUsageController.getDataUsageInfo(any())).thenReturn(info);
+ mController.setPlanValues(0 /* dataPlanCount */, -1L /* dataPlanSize */, USAGE1);
+ mController.setCarrierValues(null /* carrierName */, -1L /* snapshotTime */,
+ info.cycleEnd, null /* intent */);
+ mController.updateState(mSummaryPreference);
+
+ verify(mSummaryPreference).setLimitInfo("500 MB Data warning / 1.00 GB Data limit");
+ verify(mSummaryPreference).setUsageInfo(
+ info.cycleEnd,
+ -1L /* snapshotTime */,
+ null /* carrierName */,
+ 0 /* numPlans */,
+ null /* launchIntent */);
+ verify(mSummaryPreference).setChartEnabled(false);
+ }
+
+ private DataUsageController.DataUsageInfo createTestDataUsageInfo(long now) {
+ DataUsageController.DataUsageInfo info = new DataUsageController.DataUsageInfo();
+ info.carrier = CARRIER_NAME;
+ info.period = PERIOD;
+ info.startDate = now;
+ info.limitLevel = LIMIT1;
+ info.warningLevel = LIMIT1 >> 1;
+ info.usageLevel = USAGE1;
+ info.cycleStart = now - CYCLE_BACKOFF_MS;
+ info.cycleEnd = info.cycleStart + CYCLE_LENGTH_MS;
+ return info;
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceTest.java b/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceTest.java
new file mode 100644
index 0000000..769d9e7
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceTest.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2018 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.datausage;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.android.settings.R;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.shadow.SettingsShadowResourcesImpl;
+import com.android.settingslib.utils.StringUtil;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.spy;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION, shadows =
+ SettingsShadowResourcesImpl.class)
+public class DataUsageSummaryPreferenceTest {
+ private static final long CYCLE_DURATION_MILLIS = 1000000000L;
+ private static final long UPDATE_LAG_MILLIS = 10000000L;
+ private static final String DUMMY_CARRIER = "z-mobile";
+
+ private Context mContext;
+ private PreferenceViewHolder mHolder;
+ private DataUsageSummaryPreference mSummaryPreference;
+ private TextView mUsageTitle;
+ private TextView mCycleTime;
+ private TextView mCarrierInfo;
+ private TextView mDataLimits;
+ private Button mLaunchButton;
+
+ private long mCycleEnd;
+ private long mUpdateTime;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+ mSummaryPreference = new DataUsageSummaryPreference(mContext, null /* attrs */);
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ View view = inflater.inflate(mSummaryPreference.getLayoutResource(), null /* root */,
+ false /* attachToRoot */);
+
+ mHolder = PreferenceViewHolder.createInstanceForTests(view);
+
+ final long now = System.currentTimeMillis();
+ mCycleEnd = now + CYCLE_DURATION_MILLIS;
+ mUpdateTime = now - UPDATE_LAG_MILLIS;
+ }
+
+ @Test
+ public void testSetUsageInfo_withLaunchIntent_launchButtonShown() {
+ mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, DUMMY_CARRIER, 0 /* numPlans */,
+ new Intent());
+
+ bindViewHolder();
+ assertThat(mLaunchButton.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void testSetUsageInfo_withoutLaunchIntent_launchButtonNotShown() {
+ mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, DUMMY_CARRIER, 0 /* numPlans */,
+ null /* launchIntent */);
+
+ bindViewHolder();
+ assertThat(mLaunchButton.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void testSetUsageInfo_withDataPlans_carrierInfoShown() {
+ mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, DUMMY_CARRIER, 1 /* numPlans */,
+ new Intent());
+
+ bindViewHolder();
+ assertThat(mCarrierInfo.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void testSetUsageInfo_withNoDataPlans_carrierInfoNotShown() {
+ mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, DUMMY_CARRIER, 0 /* numPlans */,
+ new Intent());
+
+ bindViewHolder();
+ assertThat(mCarrierInfo.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void testSetUsageInfo_withNoDataPlans_usageTitleNotShown() {
+ mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, DUMMY_CARRIER, 0 /* numPlans */,
+ new Intent());
+
+ bindViewHolder();
+ assertThat(mUsageTitle.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void testSetUsageInfo_withMultipleDataPlans_usageTitleShown() {
+ mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, DUMMY_CARRIER, 2 /* numPlans */,
+ new Intent());
+
+ bindViewHolder();
+ assertThat(mUsageTitle.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void testSetUsageInfo_cycleRemainingTimeShown() {
+ mSummaryPreference.setUsageInfo(mCycleEnd, mUpdateTime, DUMMY_CARRIER, 0 /* numPlans */,
+ new Intent());
+ String cyclePrefix = StringUtil.formatElapsedTime(mContext, CYCLE_DURATION_MILLIS,
+ false /* withSeconds */).toString();
+ String text = mContext.getString(R.string.cycle_left_time_text, cyclePrefix);
+
+ bindViewHolder();
+ assertThat(mCycleTime.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mCycleTime.getText()).isEqualTo(text);
+ }
+
+ @Test
+ public void testSetLimitInfo_withLimitInfo_dataLimitsShown() {
+ final String limitText = "test limit text";
+ mSummaryPreference.setLimitInfo(limitText);
+
+ bindViewHolder();
+ assertThat(mDataLimits.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mDataLimits.getText()).isEqualTo(limitText);
+ }
+
+ @Test
+ public void testSetLimitInfo_withNullLimitInfo_dataLimitsNotShown() {
+ mSummaryPreference.setLimitInfo(null);
+
+ bindViewHolder();
+ assertThat(mDataLimits.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ private void bindViewHolder() {
+ mSummaryPreference.onBindViewHolder(mHolder);
+ mUsageTitle = (TextView) mHolder.findViewById(R.id.usage_title);
+ mCycleTime = (TextView) mHolder.findViewById(R.id.cycle_left_time);
+ mCarrierInfo = (TextView) mHolder.findViewById(R.id.carrier_and_update);
+ mDataLimits = (TextView) mHolder.findViewById(R.id.data_limits);
+ mLaunchButton = (Button) mHolder.findViewById(R.id.launch_mdp_app_button);
+ }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryAppListPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryAppListPreferenceControllerTest.java
index a814989..cee84de 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryAppListPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryAppListPreferenceControllerTest.java
@@ -220,4 +220,9 @@
assertThat(mPreferenceController.isAvailable()).isFalse();
}
+
+ @Test
+ public void testNeverUseFakeData() {
+ assertThat(BatteryAppListPreferenceController.USE_FAKE_DATA).isFalse();
+ }
}
diff --git a/tests/robotests/src/com/android/settings/search/BaseSearchIndexProviderTest.java b/tests/robotests/src/com/android/settings/search/BaseSearchIndexProviderTest.java
index 6c6d7ab..260e3ae 100644
--- a/tests/robotests/src/com/android/settings/search/BaseSearchIndexProviderTest.java
+++ b/tests/robotests/src/com/android/settings/search/BaseSearchIndexProviderTest.java
@@ -137,7 +137,6 @@
final List<String> nonIndexableKeys = provider
.getNonIndexableKeys(RuntimeEnvironment.application);
- assertThat(nonIndexableKeys).containsAllOf("status_header", "limit_summary",
- "restrict_background");
+ assertThat(nonIndexableKeys).contains("status_header");
}
}
diff --git a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
index 340d04b..3512ded 100644
--- a/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SettingsSliceProviderTest.java
@@ -31,6 +31,7 @@
import com.android.settings.TestConfig;
import com.android.settings.testutils.DatabaseTestUtils;
+import com.android.settings.testutils.FakeToggleController;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.After;
diff --git a/tests/robotests/src/com/android/settings/slices/SliceBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/slices/SliceBroadcastReceiverTest.java
index f5d5ff0..1c5899d 100644
--- a/tests/robotests/src/com/android/settings/slices/SliceBroadcastReceiverTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SliceBroadcastReceiverTest.java
@@ -31,6 +31,7 @@
import com.android.settings.search.SearchFeatureProvider;
import com.android.settings.search.SearchFeatureProviderImpl;
import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.FakeToggleController;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.After;
diff --git a/tests/robotests/src/com/android/settings/slices/SliceBuilderUtilsTest.java b/tests/robotests/src/com/android/settings/slices/SliceBuilderUtilsTest.java
index 0923571..88e4695 100644
--- a/tests/robotests/src/com/android/settings/slices/SliceBuilderUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SliceBuilderUtilsTest.java
@@ -29,6 +29,7 @@
import com.android.settings.R;
import com.android.settings.TestConfig;
import com.android.settings.core.BasePreferenceController;
+import com.android.settings.testutils.FakeToggleController;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
diff --git a/tests/robotests/src/com/android/settings/slices/SliceControllerInXmlTest.java b/tests/robotests/src/com/android/settings/slices/SliceControllerInXmlTest.java
new file mode 100644
index 0000000..66ed459
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/slices/SliceControllerInXmlTest.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2018 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.slices;
+
+import static com.android.settings.TestConfig.SDK_VERSION;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.content.res.XmlResourceParser;
+import android.provider.SearchIndexableResource;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Xml;
+
+import com.android.settings.TestConfig;
+import com.android.settings.core.TogglePreferenceController;
+import com.android.settings.core.codeinspection.ClassScanner;
+import com.android.settings.core.codeinspection.CodeInspector;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.search.DatabaseIndexingUtils;
+import com.android.settings.search.Indexable;
+import com.android.settings.search.SearchFeatureProvider;
+import com.android.settings.search.SearchFeatureProviderImpl;
+import com.android.settings.search.XmlParserUtils;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = SDK_VERSION)
+public class SliceControllerInXmlTest {
+
+ private static final List<Class> mSliceControllerClasses = new ArrayList<>(Arrays.asList(
+ TogglePreferenceController.class
+ ));
+
+ private final List<String> mXmlDeclaredControllers = new ArrayList<>();
+ private final List<String> mGrandfatheredClasses = new ArrayList<>();
+
+ private final String ERROR_MISSING_CONTROLLER =
+ "The following controllers were expected to be declared by "
+ + "'settings:controller=Controller_Class_Name' in their corresponding Xml. "
+ + "If it should not appear in XML, add the controller's classname to "
+ + "grandfather_slice_controller_not_in_xml. Controllers:\n";
+
+ private Context mContext;
+
+ SearchFeatureProvider mSearchProvider;
+ private FakeFeatureFactory mFakeFeatureFactory;
+
+ @Before
+ public void setUp() {
+ mContext = spy(RuntimeEnvironment.application);
+
+ mSearchProvider = new SearchFeatureProviderImpl();
+ mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
+ mFakeFeatureFactory.searchFeatureProvider = mSearchProvider;
+
+ CodeInspector.initializeGrandfatherList(mGrandfatheredClasses,
+ "grandfather_slice_controller_not_in_xml");
+ initDeclaredControllers();
+ }
+
+ private void initDeclaredControllers() {
+ final List<Integer> xmlResources = getIndexableXml();
+ XmlResourceParser parser;
+
+ for (int xmlResId : xmlResources) {
+ try {
+ parser = mContext.getResources().getXml(xmlResId);
+
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG) {
+ // Parse next until start tag is found
+ }
+
+ final int outerDepth = parser.getDepth();
+ final AttributeSet attrs = Xml.asAttributeSet(parser);
+ String controllerClassName;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ controllerClassName = XmlParserUtils.getController(mContext, attrs);
+
+ if (!TextUtils.isEmpty(controllerClassName)) {
+ mXmlDeclaredControllers.add(controllerClassName);
+ }
+ }
+ } catch (Exception e) {
+ // Assume an issue with robolectric resources
+ }
+ }
+ }
+
+ @Test
+ public void testAllControllersDeclaredInXml() throws Exception {
+ final List<Class<?>> classes = new ClassScanner().getClassesForPackage(
+ mContext.getPackageName());
+ final List<String> missingControllersInXml = new ArrayList<>();
+
+ for (Class<?> clazz : classes) {
+ if (!isInlineSliceClass(clazz)) {
+ // Only care about inline-slice controller classes.
+ continue;
+ }
+
+ if (!mXmlDeclaredControllers.contains(clazz.getName())) {
+ // Class clazz should have been declared in XML (unless whitelisted).
+ missingControllersInXml.add(clazz.getName());
+ }
+ }
+
+ // Removed whitelisted classes
+ missingControllersInXml.removeAll(mGrandfatheredClasses);
+
+ final String missingControllerError = buildErrorMessage(ERROR_MISSING_CONTROLLER,
+ missingControllersInXml);
+
+ assertWithMessage(missingControllerError).that(missingControllersInXml).isEmpty();
+ }
+
+ private boolean isInlineSliceClass(Class clazz) {
+ while (clazz != null) {
+ clazz = clazz.getSuperclass();
+ if (mSliceControllerClasses.contains(clazz)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private String buildErrorMessage(String errorSummary, List<String> errorClasses) {
+ final StringBuilder error = new StringBuilder(errorSummary);
+ for (String c : errorClasses) {
+ error.append(c).append("\n");
+ }
+ return error.toString();
+ }
+
+ private List<Integer> getIndexableXml() {
+ final List<Integer> xmlResSet = new ArrayList<>();
+
+ final Collection<Class> indexableClasses = FeatureFactory.getFactory(
+ mContext).getSearchFeatureProvider().getSearchIndexableResources()
+ .getProviderValues();
+
+ for (Class clazz : indexableClasses) {
+
+ Indexable.SearchIndexProvider provider = DatabaseIndexingUtils.getSearchIndexProvider(
+ clazz);
+
+ if (provider == null) {
+ continue;
+ }
+
+ List<SearchIndexableResource> resources = provider.getXmlResourcesToIndex(mContext,
+ true);
+
+ if (resources == null) {
+ continue;
+ }
+
+ for (SearchIndexableResource resource : resources) {
+ // Add '0's anyway. It won't break the test.
+ xmlResSet.add(resource.xmlResId);
+ }
+ }
+ return xmlResSet;
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/slices/FakeToggleController.java b/tests/robotests/src/com/android/settings/testutils/FakeToggleController.java
similarity index 97%
rename from tests/robotests/src/com/android/settings/slices/FakeToggleController.java
rename to tests/robotests/src/com/android/settings/testutils/FakeToggleController.java
index 1b08e35..c984c6c 100644
--- a/tests/robotests/src/com/android/settings/slices/FakeToggleController.java
+++ b/tests/robotests/src/com/android/settings/testutils/FakeToggleController.java
@@ -15,7 +15,7 @@
*
*/
-package com.android.settings.slices;
+package com.android.settings.testutils;
import android.content.Context;
import android.provider.Settings;