Update data usage UX

Update the UX and dig the data usage screen out of a huge whole of
technical debt.  Switch every to use Preferences rather than standard
layouts and ListViews.

Split data usage into several fragments, all separated.

DataUsageSummary:
 - Shows a summary of the 'default' usage at the top, this will be
   the default sim on phones, or wifi if it has it, or ethernet
   as last attempt to show something.
 - Also has individual categories for each network type that has
   data, cell, wifi, and ethernet. Maybe should look into bt though?

DataUsageList:
- Takes a NetworkTemplate as an input, and can only be reached from
  the network specific categories in DataUsageSummary
- Shows a graph of current usage for that network and links to
  app detail page for any app.
- Has gear link to quick get to billing cycle screen if available

BillingCycleSettings:
 - Just a screen with the cycle day and warning/limits separated
   out from the data usage.

AppDataUsage:
 - App specific data usage details
 - May need some UX iteration given lack of clarity in the spec

Bug: 22459566
Change-Id: I0222d8d7ea7b75a9775207a6026ebbdcce8f5e46
This commit is contained in:
Jason Monk
2016-01-11 14:27:20 -05:00
parent 703dae96e1
commit b37e2887d3
58 changed files with 3553 additions and 3309 deletions

View File

@@ -2226,7 +2226,7 @@
<meta-data android:name="com.android.settings.category"
android:value="com.android.settings.category.wireless" />
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.DataUsageSummary" />
android:value="com.android.settings.datausage.DataUsageSummary" />
</activity>
<activity android:name="Settings$DreamSettingsActivity"

View File

@@ -14,6 +14,12 @@
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/data_sweep_limit_activated" />
</selector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="2dp"
android:height="14.5dp"
android:viewportWidth="2"
android:viewportHeight="56">
<path
android:fillColor="#ffffffff"
android:pathData="M0,48l2,0l0,8l-2,0l0,-8z" />
</vector>

View File

@@ -14,6 +14,12 @@
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/data_sweep_warning_activated" />
</selector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="2dp"
android:height="14.5dp"
android:viewportWidth="2"
android:viewportHeight="56">
<path
android:fillColor="#ffffffff"
android:pathData="M0,48l2,0l0,8l-2,0l0,-8z" />
</vector>

View File

@@ -53,11 +53,5 @@
android:src="@drawable/ic_info"
style="?android:attr/borderlessButtonStyle" />
<View
android:id="@+id/row_divider"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/listDivider" />
</RelativeLayout>

View File

@@ -20,22 +20,34 @@
android:layout_height="?android:attr/actionBarSize"
android:background="@drawable/switchbar_background"
android:gravity="center_vertical"
android:paddingEnd="@dimen/switchbar_subsettings_margin_end"
android:theme="?attr/switchBarTheme" >
<Spinner
android:id="@+id/filter_spinner"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_alignParentStart="true"
android:layout_marginStart="64dp"
android:layout_marginEnd="70dp"
android:layout_alignWithParentIfMissing="true"
android:layout_centerVertical="true"
android:textAlignment="viewStart" />
<View
android:id="@+id/row_divider"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/listDivider" />
<ImageView
android:id="@+id/filter_settings"
android:layout_width="56dp"
android:layout_height="56dp"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:minHeight="0dp"
android:minWidth="0dp"
android:contentDescription="@string/configure"
android:scaleType="center"
android:src="@drawable/ic_settings_24dp"
style="?android:attr/borderlessButtonStyle"
android:visibility="gone" />
</RelativeLayout>

View File

@@ -0,0 +1,31 @@
<!--
Copyright (C) 2016 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:orientation="vertical">
<include layout="@layout/app_header" />
<View
android:layout_width="match_parent"
android:layout_height=".5dp"
android:background="@android:color/white" />
<include layout="@layout/apps_filter_spinner" />
</LinearLayout>

View File

@@ -22,8 +22,8 @@
android:id="@+id/chart"
android:layout_width="match_parent"
android:layout_height="@dimen/data_usage_chart_height"
android:paddingLeft="?android:attr/listPreferredItemPaddingStart"
android:paddingRight="40dp"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:paddingTop="16dp"
android:paddingBottom="24dp">

View File

@@ -31,14 +31,4 @@
android:textAppearance="@android:style/TextAppearance.Material.Subhead"
android:textColor="?android:attr/textColorPrimary" />
<TextView
android:id="@+id/cycle_summary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:textAppearance="@android:style/TextAppearance.Material.Subhead"
android:textColor="?android:attr/textColorPrimary"
android:textAlignment="viewEnd" />
</LinearLayout>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2015 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:gravity="center_vertical|end"
android:layout_marginBottom="5dp" >
<ImageView
android:layout_width="16dp"
android:layout_height="16dp"
android:scaleType="centerInside"
android:src="@color/memory_avg_use"
android:contentDescription="@null" />
<TextView
android:id="@+id/memory_avg"
android:text="@string/memory_avg_use"
android:textAppearance="@android:style/TextAppearance.Material.Body1"
android:textColor="?android:attr/textColorSecondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="16dp" />
<ImageView
android:layout_width="16dp"
android:layout_height="16dp"
android:scaleType="centerInside"
android:src="@color/memory_max_use"
android:contentDescription="@null" />
<TextView
android:id="@+id/memory_max"
android:text="@string/memory_max_use"
android:textAppearance="@android:style/TextAppearance.Material.Body1"
android:textColor="?android:attr/textColorSecondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="16dp" />
</LinearLayout>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2016 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.
-->
<Space xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="0dp" />

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 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.
-->
<!-- Layout used for PreferenceCategory in a PreferenceActivity. -->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="0dp"
android:textAppearance="@android:style/TextAppearance.Material.Body2"
android:textColor="?android:attr/colorAccent"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:paddingTop="16dip" />

View File

@@ -1,45 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2015 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/all_details"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:orientation="vertical">
<TextView
android:id="@+id/memory_state"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:textColor="?android:attr/colorAccent"
android:textAppearance="@android:style/TextAppearance.Material.Display1"
/>
<com.android.settings.applications.LinearColorBar
android:id="@+id/color_bar"
android:layout_width="match_parent"
android:layout_height="28dp"
android:layout_marginBottom="15dp"
/>
</LinearLayout>

View File

@@ -1,47 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2013 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/all_details"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
<com.android.settings.applications.LinearColorBar
android:id="@+id/color_bar"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginTop="12dp"
android:layout_marginBottom="10dp"
/>
<include layout="@layout/memory_key" />
<!-- Force stop and report buttons -->
<LinearLayout
android:id="@+id/two_buttons_panel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="6dip"
android:orientation="vertical">
<include layout="@layout/two_buttons_panel"/>
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2016 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="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:orientation="vertical">
<TextView
android:id="@android:id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="0dp"
android:layout_marginTop="0dp"
android:layout_marginBottom="5dp"
android:textColor="?android:attr/colorAccent"
android:textAppearance="@android:style/TextAppearance.Material.Display1"
/>
<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"
android:maxLines="10" />
<com.android.settings.applications.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>
</LinearLayout>

View File

@@ -15,25 +15,7 @@
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/data_usage_menu_restrict_background"
android:title="@string/data_usage_menu_restrict_background" />
<item
android:id="@+id/data_usage_menu_show_wifi"
android:title="@string/data_usage_menu_show_wifi" />
<item
android:id="@+id/data_usage_menu_show_ethernet"
android:title="@string/data_usage_menu_show_ethernet" />
<item
android:id="@+id/data_usage_menu_metered"
android:title="@string/data_usage_menu_metered" />
<item
android:id="@+id/data_usage_menu_sim_cards"
android:title="@string/data_usage_menu_sim_cards" />
<item
android:id="@+id/data_usage_menu_cellular_networks"
android:title="@string/data_usage_menu_cellular_networks" />
<item
android:id="@+id/data_usage_menu_help"
android:title="@string/help_label" />
</menu>

View File

@@ -94,12 +94,13 @@
<color name="memory_critical">#ffff5621</color>
<color name="memory_avg_use">#ff384248</color>
<color name="memory_max_use">#ff009587</color>
<color name="memory_remaining">#ffced7db</color>
<color name="zen_rule_name_warning">@color/system_warning_color</color>
<!-- Accent color that matches the settings launcher icon -->
<color name="icon_accent">#ffabffec</color>
<color name="summary_default_start">#ff009587</color>
<color name="summary_default_end">#ffced7db</color>
</resources>

View File

@@ -586,7 +586,7 @@
<!-- mobile network settings screen, button on dialog box that appears when you are roaming and clear the "Data roaming" check box -->
<string name="roaming_turn_it_on_button">Turn it on</string>
<!-- mobile network settings screen, message in dialog box that appears when you select the "Data roaming" check box -->
<string name="roaming_warning">When you allow data roaming, you may incur significant roaming charges!</string>
<string name="roaming_warning">You may incur significant charges.</string>
<!-- mobile network settings screen, message in dialog box that appears when you select the "Data roaming" check box. This is for multiuser tablets [CHAR LIMIT=none] -->
<string name="roaming_warning_multiuser" product="tablet">When you allow data roaming, you may incur significant roaming charges!\n\nThis setting affects all users on this tablet.</string>
<!-- mobile network settings screen, message in dialog box that appears when you select the "Data roaming" check box. This is for multiuser phones [CHAR LIMIT=none] -->
@@ -6809,4 +6809,75 @@
<!-- Description of the setting to change the display's color temperature -->
<string name="color_temperature_desc">Enable cool temperature</string>
<!-- Label for category for data usage [CHAR LIMIT=30] -->
<string name="usage">Usage</string>
<!-- Label for cellular data usage in data usage screen [CHAR LIMIT=60] -->
<string name="cellular_data_usage">Cellular data usage</string>
<!-- Label for wifi data usage in data usage screen [CHAR LIMIT=60] -->
<string name="wifi_data_usage">Wi-Fi data usage</string>
<!-- Label for ethernet data usage in data usage screen [CHAR LIMIT=60] -->
<string name="ethernet_data_usage">Ethernet data usage</string>
<!-- Label for section about wifi in data usage screen [CHAR LIMIT=60] -->
<string name="wifi">Wi-Fi</string>
<!-- Label for section about ethernet in data usage screen [CHAR LIMIT=60] -->
<string name="ethernet">Ethernet</string>
<!-- Format string for amount of cellular data used [CHAR LIMIT=30] -->
<string name="cell_data_template"><xliff:g name="units" example="GB">%1$s</xliff:g> cellular data</string>
<!-- Format string for amount of wifi data used [CHAR LIMIT=30] -->
<string name="wifi_data_template"><xliff:g name="units" example="GB">%1$s</xliff:g> Wi-Fi data</string>
<!-- Format string for amount of ethernet data used [CHAR LIMIT=30] -->
<string name="ethernet_data_template"><xliff:g name="units" example="GB">%1$s</xliff:g> ethernet data</string>
<!-- Format for a summary describing the amount of data before the user is warned [CHAR LIMIT=NONE] -->
<string name="cell_warning_only"><xliff:g name="amount" example="1 GB">%1$s</xliff:g> Data warning</string>
<!-- Format for a summary describing the amount of data before the user is warned or limited [CHAR LIMIT=NONE] -->
<string name="cell_warning_and_limit"><xliff:g name="amount" example="1 GB">%1$s</xliff:g> Data warning / <xliff:g name="amount" example="2 GB">%2$s</xliff:g> Data limit</string>
<!-- Title of button and screen for billing cycle preferences [CHAR LIMIT=30 -->
<string name="billing_cycle">Billing cycle</string>
<!-- Summary describing when the billing cycle for their phone carrier starts [CHAR LIMIT=NONE] -->
<string name="billing_cycle_summary">Monthly cycle starts on the <xliff:g name="day" example="1st">%1$s</xliff:g> of every month</string>
<!-- Summary describing when the billing cycle for their phone carrier starts [CHAR LIMIT=NONE] -->
<string name="billing_cycle_fragment_summary">Monthly starting <xliff:g name="day_of_month" example="1st">%1$s</xliff:g></string>
<!-- Title of button and screen for which wifi networks have data restrictions [CHAR LIMIT=30 -->
<string name="network_restrictions">Network restrictions</string>
<!-- A summary shown on data usage screens to indicate inaccuracy of data tracking [CHAR LIMIT=NONE] -->
<string name="operator_warning">Operator data accounting may differ from your device.</string>
<!-- Format string describing how much data has been used [CHAR LIMIT=20] -->
<string name="data_used_template"><xliff:g name="amount" example="1 GB">%1$s</xliff:g> used</string>
<!-- Label for button to set the amount of data before user is warned about usage [CHAR LIMIT=30] -->
<string name="data_warning">Data warning</string>
<!-- Label for switch about whether to limit how much data can be used [CHAR LIMIT=30] -->
<string name="set_data_limit">Set data limit</string>
<!-- Label for button to set the amount of data before user is limited [CHAR LIMIT=30] -->
<string name="data_limit">Data limit</string>
<!-- Summary about how much data has been used in a date range [CHAR LIMIT=NONE] -->
<string name="data_usage_template"><xliff:g name="amount" example="200 MB">%1$s</xliff:g> used between <xliff:g name="date_range" example="Jan 1 -- Feb 2">%2$s</xliff:g></string>
<!-- Accessibility label for button that leads to screen with more configuration options [CHAR LIMIT=NONE] -->
<string name="configure">Configure</string>
<!-- TODO: Actually figure out what to do with the extra apps, and update
the code to do that -->
<string name="data_usage_other_apps" translatable="false">Other apps included in usage</string>
</resources>

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 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.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/data_usage_summary_title">
<com.android.settings.applications.SpacePreference
android:layout_height="8dp" />
<Preference
android:key="total_usage"
android:title="@string/total_size_label"
android:selectable="false"
android:layout="@layout/horizontal_preference" />
<Preference
android:key="foreground_usage"
android:title="@string/data_usage_label_foreground"
android:selectable="false"
android:layout="@layout/horizontal_preference" />
<Preference
android:key="background_usage"
android:title="@string/data_usage_label_background"
android:selectable="false"
android:layout="@layout/horizontal_preference" />
<com.android.settings.applications.SpacePreference
android:layout_height="8dp" />
<Preference
android:key="app_settings"
android:title="@string/data_usage_app_settings" />
<SwitchPreference
android:key="restrict_background"
android:title="@string/data_usage_app_restrict_background"
android:summary="@string/data_usage_app_restrict_background_summary" />
<PreferenceCategory
android:key="app_list"
android:title="@string/data_usage_other_apps" />
</PreferenceScreen>

View File

@@ -18,11 +18,15 @@
android:title="@string/memory_usage">
<PreferenceCategory
android:title="@string/average_memory_use" />
android:title="@string/average_memory_use"
android:layout="@layout/preference_category_short" />
<com.android.settings.applications.LayoutPreference
<com.android.settings.SummaryPreference
android:key="status_header"
android:layout="@layout/proc_stats_ui" />
android:selectable="false" />
<com.android.settings.applications.SpacePreference
android:layout_height="5dp" />
<Preference
android:key="frequency"

36
res/xml/billing_cycle.xml Normal file
View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 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.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/data_usage_summary_title">
<Preference
android:key="billing_cycle"
android:title="@string/billing_cycle" />
<Preference
android:key="data_warning"
android:title="@string/data_warning" />
<SwitchPreference
android:key="set_data_limit"
android:title="@string/set_data_limit" />
<Preference
android:key="data_limit"
android:title="@string/data_limit" />
</PreferenceScreen>

38
res/xml/data_usage.xml Normal file
View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 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.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/data_usage_summary_title">
<PreferenceCategory
android:title="@string/usage"
android:layout="@layout/preference_category_short">
<com.android.settings.SummaryPreference
android:key="status_header"
android:selectable="false" />
<Preference
android:key="limit_summary"
android:selectable="false" />
<com.android.settings.datausage.RestrictBackgroundDataPreference
android:key="restrict_background"
android:title="@string/data_usage_menu_restrict_background" />
</PreferenceCategory>
</PreferenceScreen>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<com.android.settings.datausage.TemplatePreferenceCategory
android:key="mobile_category"
android:title="@string/data_usage_tab_mobile">
<com.android.settings.datausage.CellDataPreference
android:key="data_usage_enable"
android:title="@string/data_usage_enable_mobile" />
<com.android.settings.datausage.DataUsagePreference
android:key="cellular_data_usage"
android:title="@string/cellular_data_usage" />
<com.android.settings.datausage.BillingCyclePreference
android:key="billing_preference"
android:title="@string/billing_cycle" />
</com.android.settings.datausage.TemplatePreferenceCategory>
</PreferenceScreen>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<com.android.settings.datausage.TemplatePreferenceCategory
android:key="ethernet_category"
android:title="@string/ethernet">
<com.android.settings.datausage.DataUsagePreference
android:key="ethernet_data_usage"
android:title="@string/ethernet_data_usage" />
</com.android.settings.datausage.TemplatePreferenceCategory>
</PreferenceScreen>

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 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.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/data_usage_summary_title">
<PreferenceCategory
android:key="usage_amount"
android:layout="@layout/preference_category_short">
<com.android.settings.datausage.ChartDataUsagePreference
android:key="chart_data" />
<Preference
android:summary="@string/operator_warning"
android:selectable="false" />
</PreferenceCategory>
<PreferenceCategory
android:key="apps_group"
android:layout="@layout/preference_category_no_label" />
</PreferenceScreen>

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<com.android.settings.datausage.TemplatePreferenceCategory
android:key="wifi_category"
android:title="@string/wifi">
<com.android.settings.datausage.DataUsagePreference
android:key="wifi_data_usage"
android:title="@string/wifi_data_usage" />
<com.android.settings.datausage.NetworkRestrictionsPreference
android:key="network_restrictions"
android:title="@string/network_restrictions"
android:fragment="com.android.settings.datausage.DataUsageMeteredSettings" />
</com.android.settings.datausage.TemplatePreferenceCategory>
</PreferenceScreen>

View File

@@ -18,13 +18,17 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/app_memory_use"
android:key="app_list">
<PreferenceCategory
android:title="@string/average_memory_use" />
<com.android.settings.applications.LayoutPreference
<PreferenceCategory
android:title="@string/average_memory_use"
android:layout="@layout/preference_category_short" />
<com.android.settings.SummaryPreference
android:key="status_header"
android:selectable="false"
android:layout="@layout/proc_stats_ui" />
android:selectable="false" />
<com.android.settings.applications.SpacePreference
android:layout_height="5dp" />
<Preference
android:key="performance"

View File

@@ -57,7 +57,7 @@ public class AppHeader {
tintColorRes, bar);
}
private static View setupHeaderView(final Activity activity, Drawable icon, CharSequence label,
public static View setupHeaderView(final Activity activity, Drawable icon, CharSequence label,
final String pkgName, final int uid, boolean includeAppInfo, int tintColorRes,
View bar) {
final ImageView appIcon = (ImageView) bar.findViewById(R.id.app_icon);
@@ -86,7 +86,7 @@ public class AppHeader {
return bar;
}
private static boolean includeAppInfo(final Fragment fragment) {
public static boolean includeAppInfo(final Fragment fragment) {
Bundle args = fragment.getArguments();
boolean showInfo = true;
if (args != null && args.getBoolean(EXTRA_HIDE_INFO_BUTTON, false)) {

File diff suppressed because it is too large Load Diff

View File

@@ -1,125 +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;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
public abstract class HighlightingFragment extends InstrumentedFragment {
private static final String TAG = "HighlightSettingsFragment";
private static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 400;
private static final String SAVE_HIGHLIGHTED_KEY = "android:view_highlighted";
private String mViewKey;
private boolean mViewHighlighted = false;
private Drawable mHighlightDrawable;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
if (icicle != null) {
mViewHighlighted = icicle.getBoolean(SAVE_HIGHLIGHTED_KEY);
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mViewHighlighted);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
final Bundle args = getArguments();
if (args != null) {
mViewKey = args.getString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY);
highlightViewIfNeeded();
}
}
public void highlightViewIfNeeded() {
if (!mViewHighlighted &&!TextUtils.isEmpty(mViewKey)) {
highlightView(mViewKey);
}
}
private Drawable getHighlightDrawable() {
if (mHighlightDrawable == null) {
mHighlightDrawable = getActivity().getDrawable(R.drawable.preference_highlight);
}
return mHighlightDrawable;
}
private void highlightView(String key) {
final Drawable highlight = getHighlightDrawable();
// Try locating the View thru its Tag / Key
final View view = findViewForKey(getView(), key);
if (view != null ) {
view.setBackground(highlight);
getView().postDelayed(new Runnable() {
@Override
public void run() {
final int centerX = view.getWidth() / 2;
final int centerY = view.getHeight() / 2;
highlight.setHotspot(centerX, centerY);
view.setPressed(true);
view.setPressed(false);
}
}, DELAY_HIGHLIGHT_DURATION_MILLIS);
mViewHighlighted = true;
}
}
private View findViewForKey(View root, String key) {
if (checkTag(root, key)) {
return root;
}
if (root instanceof ViewGroup) {
final ViewGroup group = (ViewGroup) root;
final int count = group.getChildCount();
for (int n = 0; n < count; n++) {
final View child = group.getChildAt(n);
final View view = findViewForKey(child, key);
if (view != null) {
return view;
}
}
}
return null;
}
private boolean checkTag(View view, String key) {
final Object tag = view.getTag(R.id.preference_highlight_key);
if (tag == null || !(tag instanceof String)) {
return false;
}
final String viewKey = (String) tag;
return (!TextUtils.isEmpty(viewKey) && viewKey.equals(key));
}
}

View File

@@ -34,6 +34,9 @@ public abstract class InstrumentedFragment extends PreferenceFragment {
public static final int CONFIGURE_WIFI = UNDECLARED + 4;
public static final int DISPLAY_SCREEN_ZOOM = UNDECLARED + 5;
public static final int ACCESSIBILITY_FONT_SIZE = UNDECLARED + 6;
public static final int DATA_USAGE_LIST = UNDECLARED + 7;
public static final int BILLING_CYCLE = UNDECLARED + 8;
public static final int APP_DATA_USAGE = UNDECLARED + 9;
/**
* Declare the view of this category.

View File

@@ -68,6 +68,7 @@ import com.android.settings.applications.WriteSettingsDetails;
import com.android.settings.bluetooth.BluetoothSettings;
import com.android.settings.dashboard.DashboardSummary;
import com.android.settings.dashboard.SearchResultsSummary;
import com.android.settings.datausage.DataUsageSummary;
import com.android.settings.deviceinfo.PrivateVolumeForget;
import com.android.settings.deviceinfo.PrivateVolumeSettings;
import com.android.settings.deviceinfo.PublicVolumeSettings;
@@ -85,9 +86,9 @@ import com.android.settings.nfc.PaymentSettings;
import com.android.settings.notification.AppNotificationSettings;
import com.android.settings.notification.ConfigureNotificationSettings;
import com.android.settings.notification.NotificationAccessSettings;
import com.android.settings.notification.SoundSettings;
import com.android.settings.notification.NotificationStation;
import com.android.settings.notification.OtherSoundSettings;
import com.android.settings.notification.SoundSettings;
import com.android.settings.notification.ZenAccessSettings;
import com.android.settings.notification.ZenModeAutomationSettings;
import com.android.settings.notification.ZenModeEventRuleSettings;

View File

@@ -24,7 +24,6 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceGroup;
@@ -187,6 +186,11 @@ public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceF
setEmptyView(loading);
}
public void setLoading(boolean loading, boolean animate) {
View loading_container = getView().findViewById(R.id.loading_container);
Utils.handleLoadingContainer(loading_container, getListView(), !loading, animate);
}
public void registerObserverIfNeeded() {
if (!mIsDataSetObserverRegistered) {
if (mCurrentRootAdapter != null) {

View File

@@ -0,0 +1,99 @@
/*
* Copyright (C) 2016 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;
import android.content.Context;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceViewHolder;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;
import com.android.settings.applications.LinearColorBar;
/**
* Provides a summary of a setting page in a preference. Such as memory or data usage.
*/
public class SummaryPreference extends Preference {
private static final String TAG = "SummaryPreference";
private String mAmount;
private String mUnits;
private int mLeft, mMiddle, mRight;
private float mLeftRatio, mMiddleRatio, mRightRatio;
private String mStartLabel;
private String mEndLabel;
public SummaryPreference(Context context, AttributeSet attrs) {
super(context, attrs);
setLayoutResource(R.layout.settings_summary_preference);
mLeft = context.getColor(R.color.summary_default_start);
mRight = context.getColor(R.color.summary_default_end);
}
public void setAmount(String amount) {
mAmount = amount;
if (mAmount != null && mUnits != null) {
setTitle(TextUtils.expandTemplate(getContext().getText(R.string.storage_size_large),
mAmount, mUnits));
}
}
public void setUnits(String units) {
mUnits = units;
if (mAmount != null && mUnits != null) {
setTitle(TextUtils.expandTemplate(getContext().getText(R.string.storage_size_large),
mAmount, mUnits));
}
}
public void setLabels(String start, String end) {
mStartLabel = start;
mEndLabel = end;
notifyChanged();
}
public void setRatios(float left, float middle, float right) {
mLeftRatio = left;
mMiddleRatio = middle;
mRightRatio = right;
notifyChanged();
}
public void setColors(int left, int middle, int right) {
mLeft = left;
mMiddle = middle;
mRight = right;
notifyChanged();
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
LinearColorBar colorBar = (LinearColorBar) holder.itemView.findViewById(R.id.color_bar);
colorBar.setRatios(mLeftRatio, mMiddleRatio, mRightRatio);
colorBar.setColors(mLeft, mMiddle, mRight);
if (!TextUtils.isEmpty(mStartLabel) || !TextUtils.isEmpty(mEndLabel)) {
holder.findViewById(R.id.label_bar).setVisibility(View.VISIBLE);
((TextView) holder.findViewById(android.R.id.text1)).setText(mStartLabel);
((TextView) holder.findViewById(android.R.id.text2)).setText(mEndLabel);
} else {
holder.findViewById(R.id.label_bar).setVisibility(View.GONE);
}
}
}

View File

@@ -67,6 +67,7 @@ import android.telephony.TelephonyManager;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.style.TtsSpan;
import android.util.ArraySet;
import android.util.Log;
@@ -81,6 +82,7 @@ import android.view.animation.AnimationUtils;
import android.widget.ListView;
import android.widget.TabWidget;
import com.android.internal.util.UserIcons;
import com.android.settings.datausage.DataUsageList;
import java.io.IOException;
import java.io.InputStream;
@@ -91,6 +93,8 @@ import java.util.List;
import java.util.Locale;
import static android.content.Intent.EXTRA_USER;
import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
public final class Utils extends com.android.settingslib.Utils {
@@ -978,5 +982,19 @@ public final class Utils extends com.android.settingslib.Utils {
context.getTheme().resolveAttribute(attr, value, true);
return value.resourceId;
}
private static final StringBuilder sBuilder = new StringBuilder(50);
private static final java.util.Formatter sFormatter = new java.util.Formatter(
sBuilder, Locale.getDefault());
public static String formatDateRange(Context context, long start, long end) {
final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
synchronized (sBuilder) {
sBuilder.setLength(0);
return DateUtils.formatDateRange(context, sFormatter, start, end, flags, null)
.toString();
}
}
}

View File

@@ -21,7 +21,6 @@ import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.LoaderManager.LoaderCallbacks;
import android.app.admin.DevicePolicyManager;
import android.icu.text.ListFormatter;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -37,6 +36,7 @@ import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.icu.text.ListFormatter;
import android.net.INetworkStatsService;
import android.net.INetworkStatsSession;
import android.net.NetworkTemplate;
@@ -63,16 +63,17 @@ import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.AppHeader;
import com.android.settings.DataUsageSummary;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.Utils;
import com.android.settings.applications.PermissionsSummaryHelper.PermissionsResultCallback;
import com.android.settings.datausage.AppDataUsage;
import com.android.settings.datausage.DataUsageList;
import com.android.settings.datausage.DataUsageSummary;
import com.android.settings.fuelgauge.BatteryEntry;
import com.android.settings.fuelgauge.PowerUsageDetail;
import com.android.settings.notification.AppNotificationSettings;
@@ -753,13 +754,7 @@ public class InstalledAppDetails extends AppInfoBase
ProcessStatsBase.launchMemoryDetail((SettingsActivity) getActivity(),
mStatsManager.getMemInfo(), mStats, false);
} else if (preference == mDataPreference) {
Bundle args = new Bundle();
args.putString(DataUsageSummary.EXTRA_SHOW_APP_IMMEDIATE_PKG,
mAppEntry.info.packageName);
SettingsActivity sa = (SettingsActivity) getActivity();
sa.startPreferencePanel(DataUsageSummary.class.getName(), args, -1,
getString(R.string.app_data_usage), this, SUB_INFO_FRAGMENT);
startAppInfoFragment(AppDataUsage.class, getString(R.string.app_data_usage));
} else if (preference == mBatteryPreference) {
BatteryEntry entry = new BatteryEntry(getActivity(), null, mUserManager, mSipper);
PowerUsageDetail.startBatteryDetailPage((SettingsActivity) getActivity(),
@@ -794,7 +789,7 @@ public class InstalledAppDetails extends AppInfoBase
}
public static NetworkTemplate getTemplate(Context context) {
if (DataUsageSummary.hasReadyMobileRadio(context)) {
if (DataUsageList.hasReadyMobileRadio(context)) {
return NetworkTemplate.buildTemplateMobileWildcard();
}
if (DataUsageSummary.hasWifiRadio(context)) {

View File

@@ -40,14 +40,13 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import com.android.internal.logging.MetricsLogger;
import com.android.settings.AppHeader;
import com.android.settings.CancellablePreference;
import com.android.settings.CancellablePreference.OnCancelListener;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.SummaryPreference;
import com.android.settings.applications.ProcStatsEntry.Service;
import java.util.ArrayList;
@@ -87,8 +86,6 @@ public class ProcessStatsDetail extends SettingsPreferenceFragment {
private long mTotalTime;
private long mOnePercentTime;
private LinearColorBar mColorBar;
private double mMaxMemoryUsage;
private double mTotalScale;
@@ -177,20 +174,19 @@ public class ProcessStatsDetail extends SettingsPreferenceFragment {
mProcGroup = (PreferenceCategory) findPreference(KEY_PROCS);
fillProcessesSection();
LayoutPreference headerLayout = (LayoutPreference) findPreference(KEY_DETAILS_HEADER);
SummaryPreference summaryPreference = (SummaryPreference) findPreference(KEY_DETAILS_HEADER);
// TODO: Find way to share this code with ProcessStatsPreference.
boolean statsForeground = mApp.mRunWeight > mApp.mBgWeight;
double avgRam = (statsForeground ? mApp.mRunWeight : mApp.mBgWeight) * mWeightToRam;
float avgRatio = (float) (avgRam / mMaxMemoryUsage);
float remainingRatio = 1 - avgRatio;
mColorBar = (LinearColorBar) headerLayout.findViewById(R.id.color_bar);
Context context = getActivity();
mColorBar.setColors( context.getColor(R.color.memory_max_use), 0,
context.getColor(R.color.memory_remaining));
mColorBar.setRatios(avgRatio, 0, remainingRatio);
((TextView) headerLayout.findViewById(R.id.memory_state)).setText(
Formatter.formatShortFileSize(getContext(), (long) avgRam));
summaryPreference.setRatios(avgRatio, 0, remainingRatio);
Formatter.BytesResult usedResult = Formatter.formatBytes(context.getResources(),
(long) avgRam, Formatter.FLAG_SHORTER);
summaryPreference.setAmount(usedResult.value);
summaryPreference.setUnits(usedResult.units);
long duration = Math.max(mApp.mRunDuration, mApp.mBgDuration);
CharSequence frequency = ProcStatsPackageEntry.getFrequency(duration

View File

@@ -16,18 +16,15 @@
package com.android.settings.applications;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.os.Bundle;
import android.support.v7.preference.Preference;
import android.support.v7.preference.Preference.OnPreferenceClickListener;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.text.format.Formatter.BytesResult;
import android.widget.TextView;
import com.android.internal.logging.MetricsLogger;
import com.android.settings.R;
import com.android.settings.SummaryPreference;
import com.android.settings.Utils;
import com.android.settings.applications.ProcStatsData.MemInfo;
import com.android.settings.dashboard.SummaryLoader;
@@ -42,9 +39,7 @@ public class ProcessStatsSummary extends ProcessStatsBase implements OnPreferenc
private static final String KEY_FREE = "free";
private static final String KEY_APP_LIST = "apps_list";
private LinearColorBar mColors;
private LayoutPreference mHeader;
private TextView mMemStatus;
private SummaryPreference mSummaryPref;
private Preference mPerformance;
private Preference mTotalMemory;
@@ -57,9 +52,10 @@ public class ProcessStatsSummary extends ProcessStatsBase implements OnPreferenc
super.onCreate(icicle);
addPreferencesFromResource(R.xml.process_stats_summary);
mHeader = (LayoutPreference) findPreference(KEY_STATUS_HEADER);
mMemStatus = (TextView) mHeader.findViewById(R.id.memory_state);
mColors = (LinearColorBar) mHeader.findViewById(R.id.color_bar);
mSummaryPref = (SummaryPreference) findPreference(KEY_STATUS_HEADER);
int memColor = getContext().getColor(R.color.running_processes_apps_ram);
mSummaryPref.setColors(memColor, memColor,
getContext().getColor(R.color.running_processes_free_ram));
mPerformance = findPreference(KEY_PERFORMANCE);
mTotalMemory = findPreference(KEY_TOTAL_MEMORY);
@@ -72,8 +68,6 @@ public class ProcessStatsSummary extends ProcessStatsBase implements OnPreferenc
@Override
public void refreshUi() {
Context context = getContext();
int memColor = context.getColor(R.color.running_processes_apps_ram);
mColors.setColors(memColor, memColor, context.getColor(R.color.running_processes_free_ram));
MemInfo memInfo = mStatsManager.getMemInfo();
@@ -92,10 +86,10 @@ public class ProcessStatsSummary extends ProcessStatsBase implements OnPreferenc
} else {
memString = memStatesStr[memStatesStr.length - 1];
}
mMemStatus.setText(TextUtils.expandTemplate(getText(R.string.storage_size_large),
usedResult.value, usedResult.units));
mSummaryPref.setAmount(usedResult.value);
mSummaryPref.setUnits(usedResult.units);
float usedRatio = (float)(usedRam / (freeRam + usedRam));
mColors.setRatios(usedRatio, 0, 1 - usedRatio);
mSummaryPref.setRatios(usedRatio, 0, 1 - usedRatio);
mPerformance.setSummary(memString);
mTotalMemory.setSummary(totalString);

View File

@@ -0,0 +1,343 @@
/*
* Copyright (C) 2016 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 com.android.settings.AppHeader;
import com.android.settings.InstrumentedFragment;
import com.android.settings.R;
import com.android.settings.applications.AppInfoBase;
import com.android.settingslib.AppItem;
import com.android.settingslib.net.ChartData;
import com.android.settingslib.net.ChartDataLoader;
import android.app.LoaderManager;
import android.content.Context;
import android.content.Intent;
import android.content.Loader;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.net.INetworkStatsSession;
import android.net.NetworkPolicy;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.TrafficStats;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.support.v14.preference.SwitchPreference;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceCategory;
import android.text.format.Formatter;
import android.util.ArraySet;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Spinner;
import static android.net.NetworkPolicyManager.POLICY_NONE;
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
public class AppDataUsage extends DataUsageBase implements Preference.OnPreferenceChangeListener {
public static final String ARG_APP_ITEM = "app_item";
public static final String ARG_NETWORK_TEMPLATE = "network_template";
private static final String KEY_TOTAL_USAGE = "total_usage";
private static final String KEY_FOREGROUND_USAGE = "foreground_usage";
private static final String KEY_BACKGROUND_USAGE = "background_usage";
private static final String KEY_APP_SETTINGS = "app_settings";
private static final String KEY_RESTRICT_BACKGROUND = "restrict_background";
private static final String KEY_APP_LIST = "app_list";
private static final int LOADER_CHART_DATA = 2;
private final ArraySet<String> mPackages = new ArraySet<>();
private Preference mTotalUsage;
private Preference mForegroundUsage;
private Preference mBackgroundUsage;
private Preference mAppSettings;
private SwitchPreference mRestrictBackground;
private PreferenceCategory mAppList;
private Drawable mIcon;
private CharSequence mLabel;
private INetworkStatsSession mStatsSession;
private Spinner mCycleSpinner;
private CycleAdapter mCycleAdapter;
private long mStart;
private long mEnd;
private ChartData mChartData;
private NetworkTemplate mTemplate;
private NetworkPolicy mPolicy;
private AppItem mAppItem;
private Intent mAppSettingsIntent;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
final Bundle args = getArguments();
try {
mStatsSession = services.mStatsService.openSession();
} catch (RemoteException e) {
throw new RuntimeException(e);
}
mAppItem = (args != null) ? (AppItem) args.getParcelable(ARG_APP_ITEM) : null;
mTemplate = (args != null) ? (NetworkTemplate) args.getParcelable(ARG_NETWORK_TEMPLATE)
: null;
if (mTemplate == null) {
Context context = getContext();
mTemplate = DataUsageSummary.getDefaultTemplate(context,
DataUsageSummary.getDefaultSubscriptionId(context));
}
if (mAppItem == null) {
int uid = (args != null) ? args.getInt(AppInfoBase.ARG_PACKAGE_UID, -1)
: getActivity().getIntent().getIntExtra(AppInfoBase.ARG_PACKAGE_UID, -1);
if (uid == -1) {
// TODO: Log error.
getActivity().finish();
} else {
addUid(uid);
mAppItem = new AppItem(uid);
mAppItem.addUid(uid);
}
} else {
for (int i = 0; i < mAppItem.uids.size(); i++) {
addUid(mAppItem.uids.keyAt(i));
}
}
if (mPackages.size() != 0) {
PackageManager pm = getPackageManager();
try {
ApplicationInfo info = pm.getApplicationInfo(mPackages.valueAt(0), 0);
mIcon = info.loadIcon(pm);
mLabel = info.loadLabel(pm);
} catch (PackageManager.NameNotFoundException e) {
}
}
addPreferencesFromResource(R.xml.app_data_usage);
mTotalUsage = findPreference(KEY_TOTAL_USAGE);
mForegroundUsage = findPreference(KEY_FOREGROUND_USAGE);
mBackgroundUsage = findPreference(KEY_BACKGROUND_USAGE);
if (UserHandle.isApp(mAppItem.key)) {
mRestrictBackground = (SwitchPreference) findPreference(KEY_RESTRICT_BACKGROUND);
mRestrictBackground.setOnPreferenceChangeListener(this);
mAppSettings = findPreference(KEY_APP_SETTINGS);
mAppSettingsIntent = new Intent(Intent.ACTION_MANAGE_NETWORK_USAGE);
mAppSettingsIntent.addCategory(Intent.CATEGORY_DEFAULT);
PackageManager pm = getPackageManager();
boolean matchFound = false;
for (String packageName : mPackages) {
mAppSettingsIntent.setPackage(packageName);
if (pm.resolveActivity(mAppSettingsIntent, 0) != null) {
matchFound = true;
break;
}
}
if (!matchFound) {
removePreference(KEY_APP_SETTINGS);
mAppSettings = null;
}
if (mPackages.size() > 1) {
mAppList = (PreferenceCategory) findPreference(KEY_APP_LIST);
for (int i = 1 ; i < mPackages.size(); i++) {
new AppPrefLoader().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
mPackages.valueAt(i));
}
} else {
removePreference(KEY_APP_LIST);
}
} else {
removePreference(KEY_APP_SETTINGS);
removePreference(KEY_RESTRICT_BACKGROUND);
removePreference(KEY_APP_LIST);
}
}
@Override
public void onDestroy() {
TrafficStats.closeQuietly(mStatsSession);
super.onDestroy();
}
@Override
public void onResume() {
super.onResume();
mPolicy = services.mPolicyEditor.getPolicy(mTemplate);
getLoaderManager().restartLoader(LOADER_CHART_DATA,
ChartDataLoader.buildArgs(mTemplate, mAppItem), mChartDataCallbacks);
updatePrefs();
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (preference == mRestrictBackground) {
setAppRestrictBackground((Boolean) newValue);
return true;
}
return false;
}
@Override
public boolean onPreferenceTreeClick(Preference preference) {
if (preference == mAppSettings) {
// TODO: target towards entire UID instead of just first package
getActivity().startActivityAsUser(mAppSettingsIntent, new UserHandle(
UserHandle.getUserId(mAppItem.key)));
return true;
}
return super.onPreferenceTreeClick(preference);
}
private void updatePrefs() {
if (mRestrictBackground != null) {
mRestrictBackground.setChecked(getAppRestrictBackground());
}
}
private void addUid(int uid) {
String[] packages = getPackageManager().getPackagesForUid(uid);
if (packages != null) {
for (int i = 0; i < packages.length; i++) {
mPackages.add(packages[i]);
}
}
}
private void bindData() {
if (mChartData == null || mStart == 0) {
return;
}
final Context context = getContext();
final long now = System.currentTimeMillis();
NetworkStatsHistory.Entry entry = null;
entry = mChartData.detailDefault.getValues(mStart, mEnd, now, entry);
final long backgroundBytes = entry.rxBytes + entry.txBytes;
entry = mChartData.detailForeground.getValues(mStart, mEnd, now, entry);
final long foregroundBytes = entry.rxBytes + entry.txBytes;
final long totalBytes = backgroundBytes + foregroundBytes;
mTotalUsage.setSummary(Formatter.formatFileSize(context, totalBytes));
mForegroundUsage.setSummary(Formatter.formatFileSize(context, foregroundBytes));
mBackgroundUsage.setSummary(Formatter.formatFileSize(context, backgroundBytes));
}
private boolean getAppRestrictBackground() {
final int uid = mAppItem.key;
final int uidPolicy = services.mPolicyManager.getUidPolicy(uid);
return (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
}
private void setAppRestrictBackground(boolean restrictBackground) {
final int uid = mAppItem.key;
services.mPolicyManager.setUidPolicy(
uid, restrictBackground ? POLICY_REJECT_METERED_BACKGROUND : POLICY_NONE);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
View header = setPinnedHeaderView(R.layout.data_usage_app_header);
String pkg = mPackages.size() != 0 ? mPackages.valueAt(0) : null;
int uid = 0;
try {
uid = pkg != null ? getPackageManager().getPackageUid(pkg, 0) : 0;
} catch (PackageManager.NameNotFoundException e) {
}
AppHeader.setupHeaderView(getActivity(), mIcon, mLabel,
pkg, uid, AppHeader.includeAppInfo(this), 0, header);
mCycleSpinner = (Spinner) header.findViewById(R.id.filter_spinner);
mCycleAdapter = new CycleAdapter(getContext(), mCycleSpinner, mCycleListener);
}
@Override
protected int getMetricsCategory() {
return InstrumentedFragment.APP_DATA_USAGE;
}
private AdapterView.OnItemSelectedListener mCycleListener =
new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
final CycleAdapter.CycleItem cycle =
(CycleAdapter.CycleItem) parent.getItemAtPosition(position);
mStart = cycle.start;
mEnd = cycle.end;
bindData();
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
// ignored
}
};
private final LoaderManager.LoaderCallbacks<ChartData> mChartDataCallbacks =
new LoaderManager.LoaderCallbacks<ChartData>() {
@Override
public Loader<ChartData> onCreateLoader(int id, Bundle args) {
return new ChartDataLoader(getActivity(), mStatsSession, args);
}
@Override
public void onLoadFinished(Loader<ChartData> loader, ChartData data) {
mChartData = data;
mCycleAdapter.updateCycleList(mPolicy, mChartData);
bindData();
}
@Override
public void onLoaderReset(Loader<ChartData> loader) {
}
};
private class AppPrefLoader extends AsyncTask<String, Void, Preference> {
@Override
protected Preference doInBackground(String... params) {
PackageManager pm = getPackageManager();
String pkg = params[0];
try {
ApplicationInfo info = pm.getApplicationInfo(pkg, 0);
Preference preference = new Preference(getPrefContext());
preference.setIcon(info.loadIcon(pm));
preference.setTitle(info.loadLabel(pm));
preference.setSelectable(false);
return preference;
} catch (PackageManager.NameNotFoundException e) {
}
return null;
}
@Override
protected void onPostExecute(Preference pref) {
if (pref != null && mAppList != null) {
mAppList.addPreference(pref);
}
}
}
}

View File

@@ -0,0 +1,123 @@
/*
* Copyright (C) 2016 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.os.AsyncTask;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceViewHolder;
import android.text.format.Formatter;
import android.view.View;
import android.widget.ProgressBar;
import com.android.settingslib.AppItem;
import com.android.settingslib.net.UidDetail;
import com.android.settingslib.net.UidDetailProvider;
import static com.android.internal.util.Preconditions.checkNotNull;
public class AppDataUsagePreference extends Preference {
private final AppItem mItem;
private final int mPercent;
public AppDataUsagePreference(Context context, AppItem item, int percent,
UidDetailProvider provider) {
super(context);
mItem = item;
mPercent = percent;
setLayoutResource(com.android.settings.R.layout.data_usage_item);
setWidgetLayoutResource(com.android.settings.R.layout.widget_progress_bar);
if (item.restricted && item.total <= 0) {
setSummary(com.android.settings.R.string.data_usage_app_restricted);
} else {
setSummary(Formatter.formatFileSize(context, item.total));
}
// kick off async load of app details
UidDetailTask.bindView(provider, item, this);
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
final ProgressBar progress = (ProgressBar) holder.findViewById(
android.R.id.progress);
if (mItem.restricted && mItem.total <= 0) {
progress.setVisibility(View.GONE);
} else {
progress.setVisibility(View.VISIBLE);
}
progress.setProgress(mPercent);
}
public AppItem getItem() {
return mItem;
}
/**
* Background task that loads {@link UidDetail}, binding to
* {@link DataUsageAdapter} row item when finished.
*/
private static class UidDetailTask extends AsyncTask<Void, Void, UidDetail> {
private final UidDetailProvider mProvider;
private final AppItem mItem;
private final AppDataUsagePreference mTarget;
private UidDetailTask(UidDetailProvider provider, AppItem item,
AppDataUsagePreference target) {
mProvider = checkNotNull(provider);
mItem = checkNotNull(item);
mTarget = checkNotNull(target);
}
public static void bindView(UidDetailProvider provider, AppItem item,
AppDataUsagePreference target) {
final UidDetail cachedDetail = provider.getUidDetail(item.key, false);
if (cachedDetail != null) {
bindView(cachedDetail, target);
} else {
new UidDetailTask(provider, item, target).executeOnExecutor(
AsyncTask.THREAD_POOL_EXECUTOR);
}
}
private static void bindView(UidDetail detail, Preference target) {
if (detail != null) {
target.setIcon(detail.icon);
target.setTitle(detail.label);
} else {
target.setIcon(null);
target.setTitle(null);
}
}
@Override
protected void onPreExecute() {
bindView(null, mTarget);
}
@Override
protected UidDetail doInBackground(Void... params) {
return mProvider.getUidDetail(mItem.key, true);
}
@Override
protected void onPostExecute(UidDetail result) {
bindView(result, mTarget);
}
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright (C) 2016 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.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.NetworkPolicy;
import android.net.NetworkTemplate;
import android.os.Bundle;
import android.os.RemoteException;
import android.support.v7.preference.Preference;
import android.util.AttributeSet;
import com.android.settings.R;
import com.android.settings.Utils;
public class BillingCyclePreference extends Preference implements TemplatePreference {
private NetworkTemplate mTemplate;
private NetworkServices mServices;
private NetworkPolicy mPolicy;
private int mSubId;
public BillingCyclePreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void onAttached() {
super.onAttached();
getContext().registerReceiver(mReceiver, new IntentFilter(
CellDataPreference.ACTION_DATA_ENABLED_CHANGED));
}
@Override
public void onDetached() {
getContext().unregisterReceiver(mReceiver);
super.onDetached();
}
@Override
public void setTemplate(NetworkTemplate template, int subId,
NetworkServices services) {
mTemplate = template;
mSubId = subId;
mServices = services;
mPolicy = services.mPolicyEditor.getPolicy(mTemplate);
setSummary(getContext().getString(R.string.billing_cycle_fragment_summary,
mPolicy != null ? mPolicy.cycleDay : 1));
setIntent(getIntent());
}
private void updateEnabled() {
try {
setEnabled(mPolicy != null && mServices.mNetworkService.isBandwidthControlEnabled()
&& mServices.mTelephonyManager.getDataEnabled(mSubId)
&& mServices.mUserManager.isAdminUser());
} catch (RemoteException e) {
setEnabled(false);
}
}
@Override
public Intent getIntent() {
Bundle args = new Bundle();
args.putParcelable(DataUsageList.EXTRA_NETWORK_TEMPLATE, mTemplate);
return Utils.onBuildStartFragmentIntent(getContext(), BillingCycleSettings.class.getName(),
args, null, 0, getTitle(), false);
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updateEnabled();
}
};
}

View File

@@ -0,0 +1,357 @@
/*
* Copyright (C) 2016 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.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.net.NetworkPolicy;
import android.net.NetworkTemplate;
import android.os.Bundle;
import android.support.v14.preference.SwitchPreference;
import android.support.v7.preference.Preference;
import android.text.format.Formatter;
import android.text.format.Time;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.NumberPicker;
import com.android.settings.InstrumentedFragment;
import com.android.settings.R;
import com.android.settingslib.NetworkPolicyEditor;
import com.android.settingslib.net.DataUsageController;
import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.NetworkPolicy.WARNING_DISABLED;
import static android.net.TrafficStats.GB_IN_BYTES;
import static android.net.TrafficStats.MB_IN_BYTES;
public class BillingCycleSettings extends DataUsageBase implements
Preference.OnPreferenceChangeListener {
private static final String TAG = "BillingCycleSettings";
private static final boolean LOGD = false;
private static final String TAG_CONFIRM_LIMIT = "confirmLimit";
private static final String TAG_CYCLE_EDITOR = "cycleEditor";
private static final String TAG_WARNING_EDITOR = "warningEditor";
private static final String KEY_BILLING_CYCLE = "billing_cycle";
private static final String KEY_DATA_WARNING = "data_warning";
private static final String KEY_SET_DATA_LIMIT = "set_data_limit";
private static final String KEY_DATA_LIMIT = "data_limit";
private NetworkTemplate mNetworkTemplate;
private Preference mBillingCycle;
private Preference mDataWarning;
private SwitchPreference mEnableDataLimit;
private Preference mDataLimit;
private DataUsageController mDataUsageController;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
mDataUsageController = new DataUsageController(getContext());
Bundle args = getArguments();
mNetworkTemplate = args.getParcelable(DataUsageList.EXTRA_NETWORK_TEMPLATE);
addPreferencesFromResource(R.xml.billing_cycle);
mBillingCycle = findPreference(KEY_BILLING_CYCLE);
mDataWarning = findPreference(KEY_DATA_WARNING);
mEnableDataLimit = (SwitchPreference) findPreference(KEY_SET_DATA_LIMIT);
mEnableDataLimit.setOnPreferenceChangeListener(this);
mDataLimit = findPreference(KEY_DATA_LIMIT);
}
@Override
public void onResume() {
super.onResume();
updatePrefs();
}
private void updatePrefs() {
NetworkPolicy policy = services.mPolicyEditor.getPolicy(mNetworkTemplate);
mBillingCycle.setSummary(getString(R.string.billing_cycle_summary, policy != null ?
policy.cycleDay : 1));
mDataWarning.setSummary(Formatter.formatFileSize(getContext(), policy != null
? policy.warningBytes : DataUsageController.DEFAULT_WARNING_LEVEL));
if (policy != null && policy.limitBytes != LIMIT_DISABLED) {
mDataLimit.setSummary(Formatter.formatFileSize(getContext(), policy.limitBytes));
mDataLimit.setEnabled(true);
mEnableDataLimit.setChecked(true);
} else {
mDataLimit.setSummary(null);
mDataLimit.setEnabled(false);
mEnableDataLimit.setChecked(false);
}
}
@Override
public boolean onPreferenceTreeClick(Preference preference) {
if (preference == mBillingCycle) {
CycleEditorFragment.show(this);
return true;
} else if (preference == mDataWarning) {
BytesEditorFragment.show(this, false);
return true;
} else if (preference == mDataLimit) {
BytesEditorFragment.show(this, true);
return true;
}
return super.onPreferenceTreeClick(preference);
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (mEnableDataLimit == preference) {
boolean enabled = (Boolean) newValue;
if (enabled) {
ConfirmLimitFragment.show(this);
} else {
setPolicyLimitBytes(LIMIT_DISABLED);
}
return true;
}
return false;
}
@Override
protected int getMetricsCategory() {
return InstrumentedFragment.BILLING_CYCLE;
}
private void setPolicyLimitBytes(long limitBytes) {
if (LOGD) Log.d(TAG, "setPolicyLimitBytes()");
services.mPolicyEditor.setPolicyLimitBytes(mNetworkTemplate, limitBytes);
updatePrefs();
}
/**
* Dialog to edit {@link NetworkPolicy#warningBytes}.
*/
public static class BytesEditorFragment extends DialogFragment
implements DialogInterface.OnClickListener{
private static final String EXTRA_TEMPLATE = "template";
private static final String EXTRA_LIMIT = "limit";
private View mView;
public static void show(BillingCycleSettings parent, boolean isLimit) {
if (!parent.isAdded()) return;
final Bundle args = new Bundle();
args.putParcelable(EXTRA_TEMPLATE, parent.mNetworkTemplate);
args.putBoolean(EXTRA_LIMIT, isLimit);
final BytesEditorFragment dialog = new BytesEditorFragment();
dialog.setArguments(args);
dialog.setTargetFragment(parent, 0);
dialog.show(parent.getFragmentManager(), TAG_WARNING_EDITOR);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Context context = getActivity();
final LayoutInflater dialogInflater = LayoutInflater.from(context);
mView = dialogInflater.inflate(R.layout.data_usage_bytes_editor, null, false);
setupPicker((NumberPicker) mView.findViewById(R.id.bytes));
return new AlertDialog.Builder(context)
.setTitle(R.string.data_usage_warning_editor_title)
.setView(mView)
.setPositiveButton(R.string.data_usage_cycle_editor_positive, this)
.create();
}
private void setupPicker(NumberPicker bytesPicker) {
final BillingCycleSettings target = (BillingCycleSettings) getTargetFragment();
final NetworkPolicyEditor editor = target.services.mPolicyEditor;
final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
final boolean isLimit = getArguments().getBoolean(EXTRA_LIMIT);
final long warningBytes = editor.getPolicyWarningBytes(template);
final long limitBytes = editor.getPolicyLimitBytes(template);
if (isLimit) {
bytesPicker.setMaxValue(Integer.MAX_VALUE);
if (warningBytes != WARNING_DISABLED) {
bytesPicker.setMinValue((int) (warningBytes / MB_IN_BYTES) + 1);
} else {
bytesPicker.setMinValue(0);
}
} else {
bytesPicker.setMinValue(0);
if (limitBytes != LIMIT_DISABLED) {
bytesPicker.setMaxValue((int) (limitBytes / MB_IN_BYTES) - 1);
} else {
bytesPicker.setMaxValue(Integer.MAX_VALUE);
}
}
bytesPicker.setValue((int) ((isLimit ? limitBytes : warningBytes) / MB_IN_BYTES));
bytesPicker.setWrapSelectorWheel(false);
}
@Override
public void onClick(DialogInterface dialog, int which) {
if (which != DialogInterface.BUTTON_POSITIVE) {
return;
}
final BillingCycleSettings target = (BillingCycleSettings) getTargetFragment();
final NetworkPolicyEditor editor = target.services.mPolicyEditor;
final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
final boolean isLimit = getArguments().getBoolean(EXTRA_LIMIT);
NumberPicker bytesPicker = (NumberPicker) mView.findViewById(R.id.bytes);
// clear focus to finish pending text edits
bytesPicker.clearFocus();
final long bytes = bytesPicker.getValue() * MB_IN_BYTES;
if (isLimit) {
editor.setPolicyLimitBytes(template, bytes);
} else {
editor.setPolicyWarningBytes(template, bytes);
}
target.updatePrefs();
}
}
/**
* Dialog to edit {@link NetworkPolicy#cycleDay}.
*/
public static class CycleEditorFragment extends DialogFragment implements
DialogInterface.OnClickListener{
private static final String EXTRA_TEMPLATE = "template";
private NumberPicker mCycleDayPicker;
public static void show(BillingCycleSettings parent) {
if (!parent.isAdded()) return;
final Bundle args = new Bundle();
args.putParcelable(EXTRA_TEMPLATE, parent.mNetworkTemplate);
final CycleEditorFragment dialog = new CycleEditorFragment();
dialog.setArguments(args);
dialog.setTargetFragment(parent, 0);
dialog.show(parent.getFragmentManager(), TAG_CYCLE_EDITOR);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Context context = getActivity();
final BillingCycleSettings target = (BillingCycleSettings) getTargetFragment();
final NetworkPolicyEditor editor = target.services.mPolicyEditor;
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
final View view = dialogInflater.inflate(R.layout.data_usage_cycle_editor, null, false);
mCycleDayPicker = (NumberPicker) view.findViewById(R.id.cycle_day);
final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
final int cycleDay = editor.getPolicyCycleDay(template);
mCycleDayPicker.setMinValue(1);
mCycleDayPicker.setMaxValue(31);
mCycleDayPicker.setValue(cycleDay);
mCycleDayPicker.setWrapSelectorWheel(true);
return builder.setTitle(R.string.data_usage_cycle_editor_title)
.setView(view)
.setPositiveButton(R.string.data_usage_cycle_editor_positive, this)
.create();
}
@Override
public void onClick(DialogInterface dialog, int which) {
final NetworkTemplate template = getArguments().getParcelable(EXTRA_TEMPLATE);
final BillingCycleSettings target = (BillingCycleSettings) getTargetFragment();
final NetworkPolicyEditor editor = target.services.mPolicyEditor;
// clear focus to finish pending text edits
mCycleDayPicker.clearFocus();
final int cycleDay = mCycleDayPicker.getValue();
final String cycleTimezone = new Time().timezone;
editor.setPolicyCycleDay(template, cycleDay, cycleTimezone);
target.updatePrefs();
}
}
/**
* Dialog to request user confirmation before setting
* {@link NetworkPolicy#limitBytes}.
*/
public static class ConfirmLimitFragment extends DialogFragment implements
DialogInterface.OnClickListener{
private static final String EXTRA_MESSAGE = "message";
private static final String EXTRA_LIMIT_BYTES = "limitBytes";
public static final float FLOAT = 1.2f;
public static void show(BillingCycleSettings parent) {
if (!parent.isAdded()) return;
final NetworkPolicy policy = parent.services.mPolicyEditor
.getPolicy(parent.mNetworkTemplate);
if (policy == null) return;
final Resources res = parent.getResources();
final CharSequence message;
final long minLimitBytes = (long) (policy.warningBytes * FLOAT);
final long limitBytes;
// TODO: customize default limits based on network template
message = res.getString(R.string.data_usage_limit_dialog_mobile);
limitBytes = Math.max(5 * GB_IN_BYTES, minLimitBytes);
final Bundle args = new Bundle();
args.putCharSequence(EXTRA_MESSAGE, message);
args.putLong(EXTRA_LIMIT_BYTES, limitBytes);
final ConfirmLimitFragment dialog = new ConfirmLimitFragment();
dialog.setArguments(args);
dialog.setTargetFragment(parent, 0);
dialog.show(parent.getFragmentManager(), TAG_CONFIRM_LIMIT);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Context context = getActivity();
final CharSequence message = getArguments().getCharSequence(EXTRA_MESSAGE);
return new AlertDialog.Builder(context)
.setTitle(R.string.data_usage_limit_dialog_title)
.setMessage(message)
.setPositiveButton(android.R.string.ok, this)
.setNegativeButton(android.R.string.cancel, null)
.create();
}
@Override
public void onClick(DialogInterface dialog, int which) {
if (which != DialogInterface.BUTTON_POSITIVE) return;
final long limitBytes = getArguments().getLong(EXTRA_LIMIT_BYTES);
final BillingCycleSettings target = (BillingCycleSettings) getTargetFragment();
if (target != null) {
target.setPolicyLimitBytes(limitBytes);
}
}
}
}

View File

@@ -0,0 +1,276 @@
/*
* Copyright (C) 2016 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.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.NetworkTemplate;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v7.preference.PreferenceViewHolder;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.Checkable;
import com.android.internal.logging.MetricsLogger;
import com.android.settings.CustomDialogPreference;
import com.android.settings.R;
import com.android.settings.Utils;
import java.util.List;
public class CellDataPreference extends CustomDialogPreference implements TemplatePreference {
// TODO: Get Telephony to broadcast when this changes, and remove this.
static final String ACTION_DATA_ENABLED_CHANGED =
"com.android.settings.action.DATA_ENABLED_CHANGED";
private static final String TAG = "CellDataPreference";
public int mSubId;
public boolean mChecked;
public boolean mMultiSimDialog;
private TelephonyManager mTelephonyManager;
private SubscriptionManager mSubscriptionManager;
public CellDataPreference(Context context, AttributeSet attrs) {
super(context, attrs, android.R.attr.switchPreferenceStyle);
}
@Override
protected void onRestoreInstanceState(Parcelable s) {
CellDataState state = (CellDataState) s;
super.onRestoreInstanceState(state.getSuperState());
mTelephonyManager = TelephonyManager.from(getContext());
mChecked = state.mChecked;
mMultiSimDialog = state.mMultiSimDialog;
mSubId = state.mSubId;
setKey(getKey() + mSubId);
notifyChanged();
}
@Override
protected Parcelable onSaveInstanceState() {
CellDataState state = new CellDataState(super.onSaveInstanceState());
state.mChecked = mChecked;
state.mMultiSimDialog = mMultiSimDialog;
state.mSubId = mSubId;
return state;
}
@Override
public void onAttached() {
super.onAttached();
getContext().registerReceiver(mReceiver, new IntentFilter(ACTION_DATA_ENABLED_CHANGED));
}
@Override
public void onDetached() {
getContext().unregisterReceiver(mReceiver);
super.onDetached();
}
@Override
public void setTemplate(NetworkTemplate template, int subId, NetworkServices services) {
if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
throw new IllegalArgumentException("CellDataPreference needs a SubscriptionInfo");
}
mTelephonyManager = TelephonyManager.from(getContext());
if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
mSubId = subId;
setKey(getKey() + subId);
}
updateChecked();
}
private void updateChecked() {
setChecked(mTelephonyManager.getDataEnabled(mSubId));
}
@Override
protected void performClick(View view) {
super.performClick(view);
MetricsLogger.action(getContext(), MetricsLogger.ACTION_CELL_DATA_TOGGLE, !mChecked);
if (mChecked) {
// disabling data; show confirmation dialog which eventually
// calls setMobileDataEnabled() once user confirms.
mMultiSimDialog = false;
super.performClick(view);
} else {
// If we are showing the Sim Card tile then we are a Multi-Sim device.
if (Utils.showSimCardTile(getContext())) {
mMultiSimDialog = true;
super.performClick(view);
} else {
setMobileDataEnabled(true);
}
}
}
private void setMobileDataEnabled(boolean enabled) {
if (DataUsageSummary.LOGD) Log.d(TAG, "setMobileDataEnabled()");
mTelephonyManager.setDataEnabled(mSubId, enabled);
setChecked(enabled);
getContext().sendBroadcast(new Intent(ACTION_DATA_ENABLED_CHANGED));
}
private void setChecked(boolean checked) {
if (mChecked == checked) return;
mChecked = checked;
notifyChanged();
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
View switchView = holder.findViewById(android.R.id.switch_widget);
switchView.setClickable(false);
((Checkable) switchView).setChecked(mChecked);
}
@Override
protected void onPrepareDialogBuilder(AlertDialog.Builder builder,
DialogInterface.OnClickListener listener) {
if (mMultiSimDialog) {
showMultiSimDialog(builder, listener);
} else {
showDisableDialog(builder, listener);
}
}
private void showDisableDialog(AlertDialog.Builder builder,
DialogInterface.OnClickListener listener) {
builder.setTitle(null)
.setMessage(R.string.data_usage_disable_mobile)
.setPositiveButton(android.R.string.ok, listener)
.setNegativeButton(android.R.string.cancel, null);
}
private void showMultiSimDialog(AlertDialog.Builder builder,
DialogInterface.OnClickListener listener) {
mSubscriptionManager = SubscriptionManager.from(getContext());
final SubscriptionInfo currentSir = mSubscriptionManager.getActiveSubscriptionInfo(mSubId);
final SubscriptionInfo nextSir = mSubscriptionManager.getDefaultDataSubscriptionInfo();
// If the device is single SIM or is enabling data on the active data SIM then forgo
// the pop-up.
if (!Utils.showSimCardTile(getContext()) ||
(nextSir != null && currentSir != null &&
currentSir.getSubscriptionId() == nextSir.getSubscriptionId())) {
setMobileDataEnabled(true);
if (nextSir != null && currentSir != null &&
currentSir.getSubscriptionId() == nextSir.getSubscriptionId()) {
disableDataForOtherSubscriptions(currentSir);
}
return;
}
final String previousName = (nextSir == null)
? getContext().getResources().getString(R.string.sim_selection_required_pref)
: nextSir.getDisplayName().toString();
builder.setTitle(R.string.sim_change_data_title);
builder.setMessage(getContext().getString(R.string.sim_change_data_message,
currentSir.getDisplayName(), previousName));
builder.setPositiveButton(R.string.okay, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
mSubscriptionManager.setDefaultDataSubId(currentSir.getSubscriptionId());
setMobileDataEnabled(true);
disableDataForOtherSubscriptions(currentSir);
}
});
builder.setNegativeButton(R.string.cancel, null);
builder.create().show();
}
private void disableDataForOtherSubscriptions(SubscriptionInfo currentSir) {
List<SubscriptionInfo> subInfoList = mSubscriptionManager.getActiveSubscriptionInfoList();
if (subInfoList != null) {
for (SubscriptionInfo subInfo : subInfoList) {
if (subInfo.getSubscriptionId() != currentSir.getSubscriptionId()) {
mTelephonyManager.setDataEnabled(subInfo.getSubscriptionId(), false);
}
}
}
getContext().sendBroadcast(new Intent(ACTION_DATA_ENABLED_CHANGED));
}
@Override
protected void onClick(DialogInterface dialog, int which) {
if (which != DialogInterface.BUTTON_POSITIVE) {
return;
}
if (mMultiSimDialog) {
} else {
// TODO: extend to modify policy enabled flag.
setMobileDataEnabled(false);
}
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updateChecked();
}
};
public static class CellDataState extends BaseSavedState {
public int mSubId;
public boolean mChecked;
public boolean mMultiSimDialog;
public CellDataState(Parcelable base) {
super(base);
}
public CellDataState(Parcel source) {
super(source);
mChecked = source.readByte() != 0;
mMultiSimDialog = source.readByte() != 0;
mSubId = source.readInt();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeByte((byte) (mChecked ? 1 : 0));
dest.writeByte((byte) (mMultiSimDialog ? 1 : 0));
dest.writeInt(mSubId);
}
public static final Creator<CellDataState> CREATOR = new Creator<CellDataState>() {
@Override
public CellDataState createFromParcel(Parcel source) {
return new CellDataState(source);
}
@Override
public CellDataState[] newArray(int size) {
return new CellDataState[size];
}
};
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright (C) 2016 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.graphics.Color;
import android.net.NetworkPolicy;
import android.net.NetworkStatsHistory;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceViewHolder;
import android.util.AttributeSet;
import com.android.settings.R;
import com.android.settings.widget.ChartDataUsageView;
import com.android.settings.widget.ChartNetworkSeriesView;
public class ChartDataUsagePreference extends Preference {
private NetworkPolicy mPolicy;
private long mStart;
private long mEnd;
private NetworkStatsHistory mNetwork;
private int mSecondaryColor;
private int mSeriesColor;
public ChartDataUsagePreference(Context context, AttributeSet attrs) {
super(context, attrs);
setLayoutResource(R.layout.data_usage_chart);
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
ChartDataUsageView chart = (ChartDataUsageView) holder.itemView;
chart.setVisibleRange(mStart, mEnd);
chart.bindNetworkPolicy(mPolicy);
chart.bindNetworkStats(mNetwork);
ChartNetworkSeriesView series = (ChartNetworkSeriesView) holder.findViewById(R.id.series);
series.setChartColor(Color.BLACK, mSeriesColor, mSecondaryColor);
}
public void bindNetworkPolicy(NetworkPolicy policy) {
mPolicy = policy;
notifyChanged();
}
public void setVisibleRange(long start, long end) {
mStart = start;
mEnd = end;
notifyChanged();
}
public long getInspectStart() {
return mStart;
}
public long getInspectEnd() {
return mEnd;
}
public void bindNetworkStats(NetworkStatsHistory network) {
mNetwork = network;
notifyChanged();
}
public void setColors(int seriesColor, int secondaryColor) {
mSeriesColor = seriesColor;
mSecondaryColor = secondaryColor;
notifyChanged();
}
}

View File

@@ -0,0 +1,188 @@
/*
* Copyright (C) 2016 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 com.android.settings.Utils;
import com.android.settingslib.net.ChartData;
import android.content.Context;
import android.net.NetworkPolicy;
import android.net.NetworkStatsHistory;
import android.text.format.DateUtils;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import libcore.util.Objects;
import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
import static android.net.NetworkPolicyManager.computeNextCycleBoundary;
public class CycleAdapter extends ArrayAdapter<CycleAdapter.CycleItem> {
private final Spinner mSpinner;
private final AdapterView.OnItemSelectedListener mListener;
public CycleAdapter(Context context, Spinner spinner,
AdapterView.OnItemSelectedListener listener) {
super(context, com.android.settings.R.layout.filter_spinner_item);
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mSpinner = spinner;
mListener = listener;
mSpinner.setAdapter(this);
mSpinner.setOnItemSelectedListener(mListener);
}
/**
* Find position of {@link CycleItem} in this adapter which is nearest
* the given {@link CycleItem}.
*/
public int findNearestPosition(CycleItem target) {
if (target != null) {
final int count = getCount();
for (int i = count - 1; i >= 0; i--) {
final CycleItem item = getItem(i);
if (item.compareTo(target) >= 0) {
return i;
}
}
}
return 0;
}
/**
* Rebuild list based on {@link NetworkPolicy#cycleDay}
* and available {@link NetworkStatsHistory} data. Always selects the newest
* item, updating the inspection range on chartData.
*/
public boolean updateCycleList(NetworkPolicy policy, ChartData chartData) {
// stash away currently selected cycle to try restoring below
final CycleAdapter.CycleItem previousItem = (CycleAdapter.CycleItem)
mSpinner.getSelectedItem();
clear();
final Context context = mSpinner.getContext();
NetworkStatsHistory.Entry entry = null;
long historyStart = Long.MAX_VALUE;
long historyEnd = Long.MIN_VALUE;
if (chartData != null) {
historyStart = chartData.network.getStart();
historyEnd = chartData.network.getEnd();
}
final long now = System.currentTimeMillis();
if (historyStart == Long.MAX_VALUE) historyStart = now;
if (historyEnd == Long.MIN_VALUE) historyEnd = now + 1;
boolean hasCycles = false;
if (policy != null) {
// find the next cycle boundary
long cycleEnd = computeNextCycleBoundary(historyEnd, policy);
// walk backwards, generating all valid cycle ranges
while (cycleEnd > historyStart) {
final long cycleStart = computeLastCycleBoundary(cycleEnd, policy);
final boolean includeCycle;
if (chartData != null) {
entry = chartData.network.getValues(cycleStart, cycleEnd, entry);
includeCycle = (entry.rxBytes + entry.txBytes) > 0;
} else {
includeCycle = true;
}
if (includeCycle) {
add(new CycleAdapter.CycleItem(context, cycleStart, cycleEnd));
hasCycles = true;
}
cycleEnd = cycleStart;
}
}
if (!hasCycles) {
// no policy defined cycles; show entry for each four-week period
long cycleEnd = historyEnd;
while (cycleEnd > historyStart) {
final long cycleStart = cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4);
final boolean includeCycle;
if (chartData != null) {
entry = chartData.network.getValues(cycleStart, cycleEnd, entry);
includeCycle = (entry.rxBytes + entry.txBytes) > 0;
} else {
includeCycle = true;
}
if (includeCycle) {
add(new CycleAdapter.CycleItem(context, cycleStart, cycleEnd));
}
cycleEnd = cycleStart;
}
}
// force pick the current cycle (first item)
if (getCount() > 0) {
final int position = findNearestPosition(previousItem);
mSpinner.setSelection(position);
// only force-update cycle when changed; skipping preserves any
// user-defined inspection region.
final CycleAdapter.CycleItem selectedItem = getItem(position);
if (!Objects.equal(selectedItem, previousItem)) {
mListener.onItemSelected(mSpinner, null, position, 0);
return false;
}
}
return true;
}
/**
* List item that reflects a specific data usage cycle.
*/
public static class CycleItem implements Comparable<CycleItem> {
public CharSequence label;
public long start;
public long end;
public CycleItem(CharSequence label) {
this.label = label;
}
public CycleItem(Context context, long start, long end) {
this.label = Utils.formatDateRange(context, start, end);
this.start = start;
this.end = end;
}
@Override
public String toString() {
return label.toString();
}
@Override
public boolean equals(Object o) {
if (o instanceof CycleItem) {
final CycleItem another = (CycleItem) o;
return start == another.start && end == another.end;
}
return false;
}
@Override
public int compareTo(CycleItem another) {
return Long.compare(start, another.start);
}
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright (C) 2016 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 com.android.settings.SettingsPreferenceFragment;
import com.android.settingslib.NetworkPolicyEditor;
import android.content.Context;
import android.net.INetworkStatsService;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
import android.os.Bundle;
import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Log;
public abstract class DataUsageBase extends SettingsPreferenceFragment {
private static final String TAG = "DataUsageBase";
protected final TemplatePreference.NetworkServices services =
new TemplatePreference.NetworkServices();
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
final Context context = getActivity();
services.mNetworkService = INetworkManagementService.Stub.asInterface(
ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
services.mStatsService = INetworkStatsService.Stub.asInterface(
ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
services.mPolicyManager = NetworkPolicyManager.from(context);
services.mPolicyEditor = new NetworkPolicyEditor(services.mPolicyManager);
services.mTelephonyManager = TelephonyManager.from(context);
services.mSubscriptionManager = SubscriptionManager.from(context);
services.mUserManager = UserManager.get(context);
}
@Override
public void onResume() {
super.onResume();
services.mPolicyEditor.read();
}
protected boolean isAdmin() {
return services.mUserManager.isAdminUser();
}
protected boolean isMobileDataAvailable(int subId) {
return services.mSubscriptionManager.getActiveSubscriptionInfo(subId) != null;
}
protected boolean isNetworkPolicyModifiable(NetworkPolicy policy, int subId) {
return policy != null && isBandwidthControlEnabled() && services.mUserManager.isAdminUser()
&& isDataEnabled(subId);
}
private boolean isDataEnabled(int subId) {
if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
return true;
}
return services.mTelephonyManager.getDataEnabled(subId);
}
protected boolean isBandwidthControlEnabled() {
try {
return services.mNetworkService.isBandwidthControlEnabled();
} catch (RemoteException e) {
Log.w(TAG, "problem talking with INetworkManagementService: " + e);
return false;
}
}
}

View File

@@ -0,0 +1,550 @@
/*
* Copyright (C) 2016 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.app.ActivityManager;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.Context;
import android.content.Loader;
import android.content.pm.UserInfo;
import android.graphics.Color;
import android.net.ConnectivityManager;
import android.net.INetworkStatsSession;
import android.net.NetworkPolicy;
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.TrafficStats;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceGroup;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.format.DateUtils;
import android.text.format.Formatter;
import android.util.Log;
import android.util.SparseArray;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.Spinner;
import com.android.settings.InstrumentedFragment;
import com.android.settings.R;
import com.android.settingslib.AppItem;
import com.android.settingslib.net.ChartData;
import com.android.settingslib.net.ChartDataLoader;
import com.android.settingslib.net.SummaryForAllUidLoader;
import com.android.settingslib.net.UidDetailProvider;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
import static android.net.TrafficStats.UID_REMOVED;
import static android.net.TrafficStats.UID_TETHERING;
import static android.telephony.TelephonyManager.SIM_STATE_READY;
import static com.android.settings.datausage.DataUsageSummary.TEST_RADIOS;
import static com.android.settings.datausage.DataUsageSummary.TEST_RADIOS_PROP;
/**
* Panel showing data usage history across various networks, including options
* to inspect based on usage cycle and control through {@link NetworkPolicy}.
*/
public class DataUsageList extends DataUsageBase {
private static final String TAG = "DataUsage";
private static final boolean LOGD = false;
private static final String KEY_USAGE_AMOUNT = "usage_amount";
private static final String KEY_CHART_DATA = "chart_data";
private static final String KEY_APPS_GROUP = "apps_group";
private static final int LOADER_CHART_DATA = 2;
private static final int LOADER_SUMMARY = 3;
public static final String EXTRA_SUB_ID = "sub_id";
public static final String EXTRA_NETWORK_TEMPLATE = "network_template";
private INetworkStatsSession mStatsSession;
private ChartDataUsagePreference mChart;
private NetworkTemplate mTemplate;
private int mSubId;
private ChartData mChartData;
/** Flag used to ignore listeners during binding. */
private boolean mBinding;
private UidDetailProvider mUidDetailProvider;
/**
* Local cache of data enabled for subId, used to work around delays.
*/
private final Map<String, Boolean> mMobileDataEnabled = new HashMap<String, Boolean>();
private CycleAdapter mCycleAdapter;
private Spinner mCycleSpinner;
private Preference mUsageAmount;
private PreferenceGroup mApps;
private View mHeader;
@Override
protected int getMetricsCategory() {
return InstrumentedFragment.DATA_USAGE_LIST;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Context context = getActivity();
if (!isBandwidthControlEnabled()) {
Log.w(TAG, "No bandwidth control; leaving");
getActivity().finish();
}
try {
mStatsSession = services.mStatsService.openSession();
} catch (RemoteException e) {
throw new RuntimeException(e);
}
mUidDetailProvider = new UidDetailProvider(context);
addPreferencesFromResource(R.xml.data_usage_list);
mUsageAmount = findPreference(KEY_USAGE_AMOUNT);
mChart = (ChartDataUsagePreference) findPreference(KEY_CHART_DATA);
mApps = (PreferenceGroup) findPreference(KEY_APPS_GROUP);
final Bundle args = getArguments();
mSubId = args.getInt(EXTRA_SUB_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
mTemplate = args.getParcelable(EXTRA_NETWORK_TEMPLATE);
}
@Override
public void onViewCreated(View v, Bundle savedInstanceState) {
super.onViewCreated(v, savedInstanceState);
mHeader = setPinnedHeaderView(R.layout.apps_filter_spinner);
mCycleSpinner = (Spinner) mHeader.findViewById(R.id.filter_spinner);
mCycleAdapter = new CycleAdapter(getContext(), mCycleSpinner, mCycleListener);
setLoading(true, false);
}
@Override
public void onResume() {
super.onResume();
updateBody();
// kick off background task to update stats
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
try {
// wait a few seconds before kicking off
Thread.sleep(2 * DateUtils.SECOND_IN_MILLIS);
services.mStatsService.forceUpdate();
} catch (InterruptedException e) {
} catch (RemoteException e) {
}
return null;
}
@Override
protected void onPostExecute(Void result) {
if (isAdded()) {
updateBody();
}
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
@Override
public void onDestroy() {
mUidDetailProvider.clearCache();
mUidDetailProvider = null;
TrafficStats.closeQuietly(mStatsSession);
super.onDestroy();
}
/**
* Update body content based on current tab. Loads
* {@link NetworkStatsHistory} and {@link NetworkPolicy} from system, and
* binds them to visible controls.
*/
private void updateBody() {
mBinding = true;
if (!isAdded()) return;
final Context context = getActivity();
// kick off loader for network history
// TODO: consider chaining two loaders together instead of reloading
// network history when showing app detail.
getLoaderManager().restartLoader(LOADER_CHART_DATA,
ChartDataLoader.buildArgs(mTemplate, null), mChartDataCallbacks);
// detail mode can change visible menus, invalidate
getActivity().invalidateOptionsMenu();
mBinding = false;
int seriesColor = context.getColor(R.color.sim_noitification);
if (mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID){
final SubscriptionInfo sir = services.mSubscriptionManager
.getActiveSubscriptionInfo(mSubId);
if (sir != null) {
seriesColor = sir.getIconTint();
}
}
final int secondaryColor = Color.argb(127, Color.red(seriesColor), Color.green(seriesColor),
Color.blue(seriesColor));
mChart.setColors(seriesColor, secondaryColor);
}
/**
* Update chart sweeps and cycle list to reflect {@link NetworkPolicy} for
* current {@link #mTemplate}.
*/
private void updatePolicy(boolean refreshCycle) {
final NetworkPolicy policy = services.mPolicyEditor.getPolicy(mTemplate);
//SUB SELECT
if (isNetworkPolicyModifiable(policy, mSubId) && isMobileDataAvailable(mSubId)) {
mChart.bindNetworkPolicy(policy);
mHeader.findViewById(R.id.filter_settings).setVisibility(View.VISIBLE);
mHeader.findViewById(R.id.filter_settings).setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
Bundle args = new Bundle();
args.putParcelable(DataUsageList.EXTRA_NETWORK_TEMPLATE, mTemplate);
startFragment(DataUsageList.this, BillingCycleSettings.class.getName(),
R.string.billing_cycle, 0, args);
}
});
} else {
// controls are disabled; don't bind warning/limit sweeps
mChart.bindNetworkPolicy(null);
mHeader.findViewById(R.id.filter_settings).setVisibility(View.GONE);
}
if (refreshCycle) {
// generate cycle list based on policy and available history
if (mCycleAdapter.updateCycleList(policy, mChartData)) {
updateDetailData();
}
}
}
/**
* Update details based on {@link #mChart} inspection range depending on
* current mode. Updates {@link #mAdapter} with sorted list
* of applications data usage.
*/
private void updateDetailData() {
if (LOGD) Log.d(TAG, "updateDetailData()");
final long start = mChart.getInspectStart();
final long end = mChart.getInspectEnd();
final long now = System.currentTimeMillis();
final Context context = getActivity();
NetworkStatsHistory.Entry entry = null;
if (mChartData != null) {
entry = mChartData.network.getValues(start, end, now, null);
}
// kick off loader for detailed stats
getLoaderManager().restartLoader(LOADER_SUMMARY,
SummaryForAllUidLoader.buildArgs(mTemplate, start, end), mSummaryCallbacks);
final long totalBytes = entry != null ? entry.rxBytes + entry.txBytes : 0;
final String totalPhrase = Formatter.formatFileSize(context, totalBytes);
mUsageAmount.setTitle(getString(R.string.data_used_template, totalPhrase));
}
/**
* Bind the given {@link NetworkStats}, or {@code null} to clear list.
*/
public void bindStats(NetworkStats stats, int[] restrictedUids) {
ArrayList<AppItem> items = new ArrayList<>();
long largest = 0;
final int currentUserId = ActivityManager.getCurrentUser();
UserManager userManager = UserManager.get(getContext());
final List<UserHandle> profiles = userManager.getUserProfiles();
final SparseArray<AppItem> knownItems = new SparseArray<AppItem>();
NetworkStats.Entry entry = null;
final int size = stats != null ? stats.size() : 0;
for (int i = 0; i < size; i++) {
entry = stats.getValues(i, entry);
// Decide how to collapse items together
final int uid = entry.uid;
final int collapseKey;
final int category;
final int userId = UserHandle.getUserId(uid);
if (UserHandle.isApp(uid)) {
if (profiles.contains(new UserHandle(userId))) {
if (userId != currentUserId) {
// Add to a managed user item.
final int managedKey = UidDetailProvider.buildKeyForUser(userId);
largest = accumulate(managedKey, knownItems, entry, AppItem.CATEGORY_USER,
items, largest);
}
// Add to app item.
collapseKey = uid;
category = AppItem.CATEGORY_APP;
} else {
// If it is a removed user add it to the removed users' key
final UserInfo info = userManager.getUserInfo(userId);
if (info == null) {
collapseKey = UID_REMOVED;
category = AppItem.CATEGORY_APP;
} else {
// Add to other user item.
collapseKey = UidDetailProvider.buildKeyForUser(userId);
category = AppItem.CATEGORY_USER;
}
}
} else if (uid == UID_REMOVED || uid == UID_TETHERING) {
collapseKey = uid;
category = AppItem.CATEGORY_APP;
} else {
collapseKey = android.os.Process.SYSTEM_UID;
category = AppItem.CATEGORY_APP;
}
largest = accumulate(collapseKey, knownItems, entry, category, items, largest);
}
final int restrictedUidsMax = restrictedUids.length;
for (int i = 0; i < restrictedUidsMax; ++i) {
final int uid = restrictedUids[i];
// Only splice in restricted state for current user or managed users
if (!profiles.contains(new UserHandle(UserHandle.getUserId(uid)))) {
continue;
}
AppItem item = knownItems.get(uid);
if (item == null) {
item = new AppItem(uid);
item.total = -1;
items.add(item);
knownItems.put(item.key, item);
}
item.restricted = true;
}
Collections.sort(items);
mApps.removeAll();
for (int i = 0; i < items.size(); i++) {
final int percentTotal = largest != 0 ? (int) (items.get(i).total * 100 / largest) : 0;
AppDataUsagePreference preference = new AppDataUsagePreference(getContext(),
items.get(i), percentTotal, mUidDetailProvider);
preference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
AppDataUsagePreference pref = (AppDataUsagePreference) preference;
AppItem item = pref.getItem();
startAppDataUsage(item);
return true;
}
});
mApps.addPreference(preference);
}
}
private void startAppDataUsage(AppItem item) {
Bundle args = new Bundle();
args.putParcelable(AppDataUsage.ARG_APP_ITEM, item);
args.putParcelable(AppDataUsage.ARG_NETWORK_TEMPLATE, mTemplate);
startFragment(this, AppDataUsage.class.getName(), R.string.app_data_usage, 0, args);
}
/**
* Accumulate data usage of a network stats entry for the item mapped by the collapse key.
* Creates the item if needed.
* @param collapseKey the collapse key used to map the item.
* @param knownItems collection of known (already existing) items.
* @param entry the network stats entry to extract data usage from.
* @param itemCategory the item is categorized on the list view by this category. Must be
*/
private static long accumulate(int collapseKey, final SparseArray<AppItem> knownItems,
NetworkStats.Entry entry, int itemCategory, ArrayList<AppItem> items, long largest) {
final int uid = entry.uid;
AppItem item = knownItems.get(collapseKey);
if (item == null) {
item = new AppItem(collapseKey);
item.category = itemCategory;
items.add(item);
knownItems.put(item.key, item);
}
item.addUid(uid);
item.total += entry.rxBytes + entry.txBytes;
return Math.max(largest, item.total);
}
/**
* Test if device has a mobile data radio with SIM in ready state.
*/
public static boolean hasReadyMobileRadio(Context context) {
if (TEST_RADIOS) {
return SystemProperties.get(TEST_RADIOS_PROP).contains("mobile");
}
final ConnectivityManager conn = ConnectivityManager.from(context);
final TelephonyManager tele = TelephonyManager.from(context);
final List<SubscriptionInfo> subInfoList =
SubscriptionManager.from(context).getActiveSubscriptionInfoList();
// No activated Subscriptions
if (subInfoList == null) {
if (LOGD) Log.d(TAG, "hasReadyMobileRadio: subInfoList=null");
return false;
}
// require both supported network and ready SIM
boolean isReady = true;
for (SubscriptionInfo subInfo : subInfoList) {
isReady = isReady & tele.getSimState(subInfo.getSimSlotIndex()) == SIM_STATE_READY;
if (LOGD) Log.d(TAG, "hasReadyMobileRadio: subInfo=" + subInfo);
}
boolean retVal = conn.isNetworkSupported(TYPE_MOBILE) && isReady;
if (LOGD) {
Log.d(TAG, "hasReadyMobileRadio:"
+ " conn.isNetworkSupported(TYPE_MOBILE)="
+ conn.isNetworkSupported(TYPE_MOBILE)
+ " isReady=" + isReady);
}
return retVal;
}
/*
* TODO: consider adding to TelephonyManager or SubscriptionManager.
*/
public static boolean hasReadyMobileRadio(Context context, int subId) {
if (TEST_RADIOS) {
return SystemProperties.get(TEST_RADIOS_PROP).contains("mobile");
}
final ConnectivityManager conn = ConnectivityManager.from(context);
final TelephonyManager tele = TelephonyManager.from(context);
final int slotId = SubscriptionManager.getSlotId(subId);
final boolean isReady = tele.getSimState(slotId) == SIM_STATE_READY;
boolean retVal = conn.isNetworkSupported(TYPE_MOBILE) && isReady;
if (LOGD) Log.d(TAG, "hasReadyMobileRadio: subId=" + subId
+ " conn.isNetworkSupported(TYPE_MOBILE)=" + conn.isNetworkSupported(TYPE_MOBILE)
+ " isReady=" + isReady);
return retVal;
}
private OnItemSelectedListener mCycleListener = new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
final CycleAdapter.CycleItem cycle = (CycleAdapter.CycleItem)
parent.getItemAtPosition(position);
if (LOGD) {
Log.d(TAG, "showing cycle " + cycle + ", start=" + cycle.start + ", end="
+ cycle.end + "]");
}
// update chart to show selected cycle, and update detail data
// to match updated sweep bounds.
mChart.setVisibleRange(cycle.start, cycle.end);
updateDetailData();
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
// ignored
}
};
private final LoaderCallbacks<ChartData> mChartDataCallbacks = new LoaderCallbacks<
ChartData>() {
@Override
public Loader<ChartData> onCreateLoader(int id, Bundle args) {
return new ChartDataLoader(getActivity(), mStatsSession, args);
}
@Override
public void onLoadFinished(Loader<ChartData> loader, ChartData data) {
setLoading(false, true);
mChartData = data;
mChart.bindNetworkStats(mChartData.network);
// calcuate policy cycles based on available data
updatePolicy(true);
}
@Override
public void onLoaderReset(Loader<ChartData> loader) {
mChartData = null;
mChart.bindNetworkStats(null);
}
};
private final LoaderCallbacks<NetworkStats> mSummaryCallbacks = new LoaderCallbacks<
NetworkStats>() {
@Override
public Loader<NetworkStats> onCreateLoader(int id, Bundle args) {
return new SummaryForAllUidLoader(getActivity(), mStatsSession, args);
}
@Override
public void onLoadFinished(Loader<NetworkStats> loader, NetworkStats data) {
final int[] restrictedUids = services.mPolicyManager.getUidsWithPolicy(
POLICY_REJECT_METERED_BACKGROUND);
bindStats(data, restrictedUids);
updateEmptyVisible();
}
@Override
public void onLoaderReset(Loader<NetworkStats> loader) {
bindStats(null, new int[0]);
updateEmptyVisible();
}
private void updateEmptyVisible() {
if ((mApps.getPreferenceCount() != 0) !=
(getPreferenceScreen().getPreferenceCount() != 0)) {
if (mApps.getPreferenceCount() != 0) {
getPreferenceScreen().addPreference(mUsageAmount);
getPreferenceScreen().addPreference(mApps);
} else {
getPreferenceScreen().removeAll();
}
}
}
};
}

View File

@@ -1,20 +1,18 @@
/*
* Copyright (C) 2012 The Android Open Source Project
* Copyright (C) 2016 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
* 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.
* 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.net;
package com.android.settings.datausage;
import android.content.Context;
import android.content.res.Resources;
@@ -42,8 +40,8 @@ import java.util.List;
import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.wifi.WifiInfo.removeDoubleQuotes;
import static com.android.settings.DataUsageSummary.hasReadyMobileRadio;
import static com.android.settings.DataUsageSummary.hasWifiRadio;
import static com.android.settings.datausage.DataUsageList.hasReadyMobileRadio;
import static com.android.settings.datausage.DataUsageSummary.hasWifiRadio;
/**
* Panel to configure {@link NetworkPolicy#metered} for networks.

View File

@@ -0,0 +1,58 @@
/*
* Copyright (C) 2016 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 android.os.Bundle;
import android.support.v7.preference.Preference;
import android.telephony.SubscriptionManager;
import android.text.format.Formatter;
import android.util.AttributeSet;
import com.android.settings.Utils;
import com.android.settingslib.net.DataUsageController;
import com.android.settings.R;
public class DataUsagePreference extends Preference implements TemplatePreference {
private NetworkTemplate mTemplate;
private int mSubId;
public DataUsagePreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void setTemplate(NetworkTemplate template, int subId,
NetworkServices services) {
mTemplate = template;
mSubId = subId;
DataUsageController controller = new DataUsageController(getContext());
DataUsageController.DataUsageInfo usageInfo = controller.getDataUsageInfo(mTemplate);
setSummary(getContext().getString(R.string.data_usage_template,
Formatter.formatFileSize(getContext(), usageInfo.usageLevel), usageInfo.period));
setIntent(getIntent());
}
@Override
public Intent getIntent() {
Bundle args = new Bundle();
args.putParcelable(DataUsageList.EXTRA_NETWORK_TEMPLATE, mTemplate);
args.putInt(DataUsageList.EXTRA_SUB_ID, mSubId);
return Utils.onBuildStartFragmentIntent(getContext(), DataUsageList.class.getName(), args,
getContext().getPackageName(), 0, getTitle(), false);
}
}

View File

@@ -0,0 +1,375 @@
/*
* Copyright (C) 2016 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.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.INetworkStatsSession;
import android.net.NetworkTemplate;
import android.net.TrafficStats;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.provider.SearchIndexableResource;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.format.Formatter;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import com.android.internal.logging.MetricsLogger;
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;
import com.android.settings.search.Indexable;
import com.android.settingslib.net.DataUsageController;
import java.util.ArrayList;
import java.util.List;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_WIFI;
public class DataUsageSummary extends DataUsageBase implements Indexable {
private static final String TAG = "DataUsageSummary";
static final boolean LOGD = false;
public static final boolean TEST_RADIOS = false;
public static final String TEST_RADIOS_PROP = "test.radios";
private static final String KEY_STATUS_HEADER = "status_header";
private static final String KEY_LIMIT_SUMMARY = "limit_summary";
private static final String KEY_RESTRICT_BACKGROUND = "restrict_background";
private DataUsageController mDataUsageController;
private SummaryPreference mSummaryPreference;
private Preference mLimitPreference;
private NetworkTemplate mDefaultTemplate;
private int mDataUsageTemplate;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
boolean hasMobileData = hasMobileData(getContext());
mDataUsageController = new DataUsageController(getContext());
addPreferencesFromResource(R.xml.data_usage);
int defaultSubId = getDefaultSubscriptionId(getContext());
mDefaultTemplate = getDefaultTemplate(getContext(), defaultSubId);
if (hasMobileData) {
mLimitPreference = findPreference(KEY_LIMIT_SUMMARY);
} else {
removePreference(KEY_LIMIT_SUMMARY);
}
if (!hasMobileData || !isAdmin()) {
removePreference(KEY_RESTRICT_BACKGROUND);
}
if (hasMobileData) {
List<SubscriptionInfo> subscriptions =
services.mSubscriptionManager.getActiveSubscriptionInfoList();
if (subscriptions.size() == 0) {
addMobileSection(defaultSubId);
}
for (int i = 0; i < subscriptions.size(); i++) {
addMobileSection(subscriptions.get(i).getSubscriptionId());
}
}
boolean hasWifiRadio = hasWifiRadio(getContext());
if (hasWifiRadio) {
addWifiSection();
}
if (hasEthernet(getContext())) {
addEthernetSection();
}
mDataUsageTemplate = hasMobileData ? R.string.cell_data_template
: hasWifiRadio ? R.string.wifi_data_template
: R.string.ethernet_data_template;
mSummaryPreference = (SummaryPreference) findPreference(KEY_STATUS_HEADER);
setHasOptionsMenu(true);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.data_usage, menu);
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.data_usage_menu_cellular_networks: {
final Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setComponent(new ComponentName("com.android.phone",
"com.android.phone.MobileNetworkSettings"));
startActivity(intent);
return true;
}
}
return false;
}
private void addMobileSection(int subId) {
TemplatePreferenceCategory category = (TemplatePreferenceCategory)
inflatePreferences(R.xml.data_usage_cellular);
category.setTemplate(getNetworkTemplate(subId), subId, services);
}
private void addWifiSection() {
TemplatePreferenceCategory category = (TemplatePreferenceCategory)
inflatePreferences(R.xml.data_usage_wifi);
category.setTemplate(NetworkTemplate.buildTemplateWifiWildcard(), 0, services);
}
private void addEthernetSection() {
TemplatePreferenceCategory category = (TemplatePreferenceCategory)
inflatePreferences(R.xml.data_usage_ethernet);
category.setTemplate(NetworkTemplate.buildTemplateEthernet(), 0, services);
}
private Preference inflatePreferences(int resId) {
PreferenceScreen rootPreferences = getPreferenceManager().inflateFromResource(
getPrefContext(), resId, null);
Preference pref = rootPreferences.getPreference(0);
rootPreferences.removeAll();
PreferenceScreen screen = getPreferenceScreen();
pref.setOrder(screen.getPreferenceCount());
screen.addPreference(pref);
return pref;
}
private NetworkTemplate getNetworkTemplate(int subscriptionId) {
NetworkTemplate mobileAll = NetworkTemplate.buildTemplateMobileAll(
services.mTelephonyManager.getSubscriberId(subscriptionId));
return NetworkTemplate.normalize(mobileAll,
services.mTelephonyManager.getMergedSubscriberIds());
}
@Override
public void onResume() {
super.onResume();
updateState();
}
private void updateState() {
DataUsageController.DataUsageInfo info = mDataUsageController.getDataUsageInfo(
mDefaultTemplate);
Context context = getContext();
if (mSummaryPreference != null) {
Formatter.BytesResult usedResult = Formatter.formatBytes(context.getResources(),
info.usageLevel, Formatter.FLAG_SHORTER);
mSummaryPreference.setAmount(usedResult.value);
mSummaryPreference.setUnits(getString(mDataUsageTemplate, usedResult.units));
long limit = info.limitLevel;
if (limit <= 0) {
limit = info.warningLevel;
}
if (info.usageLevel > limit) {
limit = info.usageLevel;
}
mSummaryPreference.setSummary(info.period);
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) {
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));
}
PreferenceScreen screen = getPreferenceScreen();
for (int i = 1; i < screen.getPreferenceCount(); i++) {
((TemplatePreferenceCategory) screen.getPreference(i)).pushTemplates(services);
}
}
@Override
protected int getMetricsCategory() {
return MetricsLogger.DATA_USAGE_SUMMARY;
}
/**
* Test if device has an ethernet network connection.
*/
public boolean hasEthernet(Context context) {
if (TEST_RADIOS) {
return SystemProperties.get(TEST_RADIOS_PROP).contains("ethernet");
}
final ConnectivityManager conn = ConnectivityManager.from(context);
final boolean hasEthernet = conn.isNetworkSupported(TYPE_ETHERNET);
final long ethernetBytes;
try {
INetworkStatsSession statsSession = services.mStatsService.openSession();
if (statsSession != null) {
ethernetBytes = statsSession.getSummaryForNetwork(
NetworkTemplate.buildTemplateEthernet(), Long.MIN_VALUE, Long.MAX_VALUE)
.getTotalBytes();
TrafficStats.closeQuietly(statsSession);
} else {
ethernetBytes = 0;
}
} catch (RemoteException e) {
throw new RuntimeException(e);
}
// only show ethernet when both hardware present and traffic has occurred
return hasEthernet && ethernetBytes > 0;
}
public static boolean hasMobileData(Context context) {
return ConnectivityManager.from(context).isNetworkSupported(
ConnectivityManager.TYPE_MOBILE);
}
/**
* Test if device has a Wi-Fi data radio.
*/
public static boolean hasWifiRadio(Context context) {
if (TEST_RADIOS) {
return SystemProperties.get(TEST_RADIOS_PROP).contains("wifi");
}
final ConnectivityManager conn = ConnectivityManager.from(context);
return conn.isNetworkSupported(TYPE_WIFI);
}
public static int getDefaultSubscriptionId(Context context) {
SubscriptionManager subManager = SubscriptionManager.from(context);
if (subManager == null) {
return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
}
SubscriptionInfo subscriptionInfo = subManager.getDefaultDataSubscriptionInfo();
if (subscriptionInfo == null) {
List<SubscriptionInfo> list = subManager.getAllSubscriptionInfoList();
if (list.size() == 0) {
return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
}
subscriptionInfo = list.get(0);
}
return subscriptionInfo.getSubscriptionId();
}
public static NetworkTemplate getDefaultTemplate(Context context, int defaultSubId) {
if (hasMobileData(context)) {
TelephonyManager telephonyManager = TelephonyManager.from(context);
NetworkTemplate mobileAll = NetworkTemplate.buildTemplateMobileAll(
telephonyManager.getSubscriberId(defaultSubId));
return NetworkTemplate.normalize(mobileAll,
telephonyManager.getMergedSubscriberIds());
} else if (hasWifiRadio(context)) {
return NetworkTemplate.buildTemplateWifiWildcard();
} else {
return NetworkTemplate.buildTemplateEthernet();
}
}
private static class SummaryProvider
implements SummaryLoader.SummaryProvider {
private final Activity mActivity;
private final SummaryLoader mSummaryLoader;
private final DataUsageController mDataController;
public SummaryProvider(Activity activity, SummaryLoader summaryLoader) {
mActivity = activity;
mSummaryLoader = summaryLoader;
mDataController = new DataUsageController(activity);
}
@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));
}
}
}
public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
= new SummaryLoader.SummaryProviderFactory() {
@Override
public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
SummaryLoader summaryLoader) {
return new SummaryProvider(activity, summaryLoader);
}
};
/**
* For search
*/
public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider() {
@Override
public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
boolean enabled) {
ArrayList<SearchIndexableResource> resources = new ArrayList<>();
SearchIndexableResource resource = new SearchIndexableResource(context);
resource.xmlResId = R.xml.data_usage;
resources.add(resource);
if (hasMobileData(context)) {
resource = new SearchIndexableResource(context);
resource.xmlResId = R.xml.data_usage_cellular;
resources.add(resource);
}
if (hasWifiRadio(context)) {
resource = new SearchIndexableResource(context);
resource.xmlResId = R.xml.data_usage_wifi;
resources.add(resource);
}
return resources;
}
@Override
public List<String> getNonIndexableKeys(Context context) {
ArrayList<String> keys = new ArrayList<>();
boolean hasMobileData = ConnectivityManager.from(context).isNetworkSupported(
ConnectivityManager.TYPE_MOBILE);
if (hasMobileData) {
keys.add(KEY_RESTRICT_BACKGROUND);
}
return keys;
}
};
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2016 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.net.NetworkTemplate;
import android.support.v7.preference.Preference;
import android.util.AttributeSet;
public class NetworkRestrictionsPreference extends Preference implements TemplatePreference {
public NetworkRestrictionsPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void setTemplate(NetworkTemplate template, int subId,
NetworkServices services) {
// TODO: Summary
}
}

View File

@@ -0,0 +1,99 @@
/*
* Copyright (C) 2016 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.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.net.NetworkPolicyManager;
import android.support.v7.preference.PreferenceViewHolder;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Checkable;
import com.android.settings.CustomDialogPreference;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.dashboard.conditional.BackgroundDataCondition;
import com.android.settings.dashboard.conditional.ConditionManager;
public class RestrictBackgroundDataPreference extends CustomDialogPreference {
private NetworkPolicyManager mPolicyManager;
private boolean mChecked;
public RestrictBackgroundDataPreference(Context context, AttributeSet attrs) {
super(context, attrs, android.R.attr.switchPreferenceStyle);
}
@Override
public void onAttached() {
super.onAttached();
mPolicyManager = NetworkPolicyManager.from(getContext());
setChecked(mPolicyManager.getRestrictBackground());
}
public void setRestrictBackground(boolean restrictBackground) {
mPolicyManager.setRestrictBackground(restrictBackground);
setChecked(restrictBackground);
ConditionManager.get(getContext()).getCondition(BackgroundDataCondition.class)
.refreshState();
}
private void setChecked(boolean checked) {
if (mChecked == checked) return;
mChecked = checked;
notifyChanged();
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
View switchView = holder.findViewById(android.R.id.switch_widget);
switchView.setClickable(false);
((Checkable) switchView).setChecked(mChecked);
}
@Override
protected void performClick(View view) {
if (mChecked) {
setRestrictBackground(false);
} else {
super.performClick(view);
}
}
@Override
protected void onPrepareDialogBuilder(AlertDialog.Builder builder,
DialogInterface.OnClickListener listener) {
super.onPrepareDialogBuilder(builder, listener);
builder.setTitle(R.string.data_usage_restrict_background_title);
if (Utils.hasMultipleUsers(getContext())) {
builder.setMessage(R.string.data_usage_restrict_background_multiuser);
} else {
builder.setMessage(R.string.data_usage_restrict_background);
}
builder.setPositiveButton(android.R.string.ok, listener);
builder.setNegativeButton(android.R.string.cancel, null);
}
@Override
protected void onClick(DialogInterface dialog, int which) {
if (which != DialogInterface.BUTTON_POSITIVE) {
return;
}
setRestrictBackground(true);
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2016 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.net.INetworkStatsService;
import android.net.NetworkPolicyManager;
import android.net.NetworkTemplate;
import android.os.INetworkManagementService;
import android.os.UserManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import com.android.settingslib.NetworkPolicyEditor;
public interface TemplatePreference {
void setTemplate(NetworkTemplate template, int subId, NetworkServices services);
class NetworkServices {
INetworkManagementService mNetworkService;
INetworkStatsService mStatsService;
NetworkPolicyManager mPolicyManager;
TelephonyManager mTelephonyManager;
SubscriptionManager mSubscriptionManager;
UserManager mUserManager;
NetworkPolicyEditor mPolicyEditor;
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright (C) 2016 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.net.NetworkTemplate;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceCategory;
import android.util.AttributeSet;
public class TemplatePreferenceCategory extends PreferenceCategory implements TemplatePreference {
private NetworkTemplate mTemplate;
private int mSubId;
public TemplatePreferenceCategory(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void setTemplate(NetworkTemplate template, int subId,
NetworkServices services) {
mTemplate = template;
mSubId = subId;
}
@Override
public boolean addPreference(Preference preference) {
if (!(preference instanceof TemplatePreference)) {
throw new IllegalArgumentException(
"TemplatePreferenceCategories can only hold TemplatePreferences");
}
return super.addPreference(preference);
}
public void pushTemplates(NetworkServices services) {
if (mTemplate == null) {
throw new RuntimeException("null mTemplate for " + getKey());
}
for (int i = 0; i < getPreferenceCount(); i++) {
((TemplatePreference) getPreference(i)).setTemplate(mTemplate, mSubId, services);
}
}
}

View File

@@ -17,7 +17,6 @@
package com.android.settings.search;
import com.android.settings.ChooseLockGeneric;
import com.android.settings.DataUsageSummary;
import com.android.settings.DateTimeSettings;
import com.android.settings.DevelopmentSettings;
import com.android.settings.DeviceInfoSettings;
@@ -35,6 +34,8 @@ import com.android.settings.accounts.AccountSettings;
import com.android.settings.applications.AdvancedAppSettings;
import com.android.settings.applications.ManageDefaultApps;
import com.android.settings.bluetooth.BluetoothSettings;
import com.android.settings.datausage.DataUsageMeteredSettings;
import com.android.settings.datausage.DataUsageSummary;
import com.android.settings.deviceinfo.StorageSettings;
import com.android.settings.display.ScreenZoomSettings;
import com.android.settings.fuelgauge.BatterySaverSettings;
@@ -42,7 +43,6 @@ import com.android.settings.fuelgauge.PowerUsageSummary;
import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
import com.android.settings.location.LocationSettings;
import com.android.settings.location.ScanningSettings;
import com.android.settings.net.DataUsageMeteredSettings;
import com.android.settings.notification.ConfigureNotificationSettings;
import com.android.settings.notification.OtherSoundSettings;
import com.android.settings.notification.SoundSettings;

View File

@@ -16,9 +16,6 @@
package com.android.settings.search;
import android.provider.SearchIndexableResource;
import com.android.settings.DataUsageSummary;
import com.android.settings.DateTimeSettings;
import com.android.settings.DevelopmentSettings;
import com.android.settings.DeviceInfoSettings;
@@ -37,6 +34,8 @@ import com.android.settings.accounts.AccountSettings;
import com.android.settings.applications.AdvancedAppSettings;
import com.android.settings.applications.ManageDefaultApps;
import com.android.settings.bluetooth.BluetoothSettings;
import com.android.settings.datausage.DataUsageMeteredSettings;
import com.android.settings.datausage.DataUsageSummary;
import com.android.settings.deviceinfo.StorageSettings;
import com.android.settings.display.ScreenZoomSettings;
import com.android.settings.fuelgauge.BatterySaverSettings;
@@ -44,7 +43,6 @@ import com.android.settings.fuelgauge.PowerUsageSummary;
import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
import com.android.settings.location.LocationSettings;
import com.android.settings.location.ScanningSettings;
import com.android.settings.net.DataUsageMeteredSettings;
import com.android.settings.notification.ConfigureNotificationSettings;
import com.android.settings.notification.OtherSoundSettings;
import com.android.settings.notification.SoundSettings;
@@ -58,6 +56,8 @@ import com.android.settings.wifi.AdvancedWifiSettings;
import com.android.settings.wifi.SavedAccessPointsWifiSettings;
import com.android.settings.wifi.WifiSettings;
import android.provider.SearchIndexableResource;
import java.util.Collection;
import java.util.HashMap;

View File

@@ -32,7 +32,7 @@ import android.view.View;
import com.android.internal.util.Preconditions;
import com.android.settings.R;
import static com.android.settings.DataUsageSummary.formatDateRange;
import static com.android.settings.Utils.formatDateRange;
/**
* Background of {@link ChartView} that renders grid lines as requested by

View File

@@ -143,7 +143,6 @@ public class ChartSweepView extends View {
a.recycle();
setClickable(true);
setFocusable(true);
setOnClickListener(mClickListener);
setWillNotDraw(false);