Add data saver to settings

Bug: 22817899

Change-Id: Ic3055aa6a5baae1653db350313366f180c049cc7
This commit is contained in:
Jason Monk
2016-01-21 14:12:11 -05:00
parent 3d71918c58
commit 764f45db91
18 changed files with 755 additions and 53 deletions

View File

@@ -5,6 +5,7 @@
-keep class com.android.settings.wifi.*Settings
-keep class com.android.settings.deviceinfo.*
-keep class com.android.settings.bluetooth.*
-keep class com.android.settings.datausage.*
-keep class com.android.settings.applications.*
-keep class com.android.settings.inputmethod.*
-keep class com.android.settings.ResetNetwork

View File

@@ -0,0 +1,28 @@
<!--
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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24.0dp"
android:height="24.0dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="
M9.0,16.0l2.0,0.0L11.0,8.0L9.0,8.0l0.0,8.0z
m3.0,-14.0C6.48,2.0 2.0,6.48 2.0,12.0s4.48,10.0 10.0,10.0 10.0,-4.48 10.0,-10.0S17.52,2.0 12.0,2.0z
m0.0,18.0c-4.41,0.0 -8.0,-3.59 -8.0,-8.0s3.59,-8.0 8.0,-8.0 8.0,3.59 8.0,8.0 -3.59,8.0 -8.0,8.0z
m1.0,-4.0l2.0,0.0l0.0,-8.0l-2.0,0.0l0.0,8.0z"/>
</vector>

View File

@@ -17,7 +17,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:minHeight="?android:attr/listPreferredItemHeight"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:orientation="horizontal">

View File

@@ -4914,9 +4914,9 @@
<!-- Button title for launching application-specific data usage settings. [CHAR LIMIT=32] -->
<string name="data_usage_app_settings">App settings</string>
<!-- Checkbox label that restricts background data usage of a specific application. [CHAR LIMIT=40] -->
<string name="data_usage_app_restrict_background">Restrict app background data</string>
<!-- Summary message for checkbox that restricts background data usage of a specific application. [CHAR LIMIT=64] -->
<string name="data_usage_app_restrict_background_summary">Disable background data on cellular networks.</string>
<string name="data_usage_app_restrict_background">Background data</string>
<!-- Summary message for checkbox that restricts background data usage of a specific application. [CHAR LIMIT=NONE] -->
<string name="data_usage_app_restrict_background_summary">Enable usage of cellular data in the background</string>
<!-- Summary message for checkbox that restricts background data usage of a specific application when no networks have been limited. [CHAR LIMIT=84] -->
<string name="data_usage_app_restrict_background_summary_disabled">To restrict background data for this app, first set a cellular data limit.</string>
<!-- Title of dialog shown when user restricts background data usage of a specific application. [CHAR LIMIT=48] -->
@@ -6870,7 +6870,7 @@
<string name="condition_cellular_summary">Internet is available only via Wi-Fi</string>
<!-- Title of condition that background data is off [CHAR LIMIT=30] -->
<string name="condition_bg_data_title">Background data is off</string>
<string name="condition_bg_data_title">Data Saver is on</string>
<!-- Summary of condition that background data is off [CHAR LIMIT=NONE] -->
<string name="condition_bg_data_summary">Background data is only available via Wi-Fi. This may affect some apps or services when Wi-Fi is not available.</string>
@@ -6966,4 +6966,28 @@
the code to do that -->
<string name="data_usage_other_apps" translatable="false">Other apps included in usage</string>
<!-- Description of number of apps allowed to ignore data saver [CHAR LIMIT=NONE] -->
<plurals name="data_saver_unrestricted_summary">
<item quantity="one">1 app allowed to use unrestricted data when Data Saver is on</item>
<item quantity="other"><xliff:g id="count" example="10">%1$d</xliff:g> apps allowed to use unrestricted data when Data Saver is on</item>
</plurals>
<!-- Name of Data Saver screens [CHAR LIMIT=30] -->
<string name="data_saver_title">Data Saver</string>
<!-- Button that leads to list of apps with unrestricted data access [CHAR LIMIT=60] -->
<string name="unrestricted_data_saver">Unrestricted data access</string>
<!-- Summary for the data saver feature being on [CHAR LIMIT=NONE] -->
<string name="data_saver_on">On</string>
<!-- Summary for the data saver feature being off [CHAR LIMIT=NONE] -->
<string name="data_saver_off">Off</string>
<!-- Title for switch to allow app unrestricted data usage [CHAR LIMIT=30] -->
<string name="unrestricted_app_title">Unrestricted data usage</string>
<!-- Title for switch to allow app unrestricted data usage [CHAR LIMIT=30] -->
<string name="unrestricted_app_summary">Allow unrestricted data access when Data Saver is on</string>
</resources>

View File

@@ -17,6 +17,9 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/data_usage_summary_title">
<com.android.settings.datausage.SpinnerPreference
android:key="cycle" />
<com.android.settings.applications.SpacePreference
android:layout_height="8dp" />
@@ -50,6 +53,11 @@
android:title="@string/data_usage_app_restrict_background"
android:summary="@string/data_usage_app_restrict_background_summary" />
<SwitchPreference
android:key="unrestricted_data_saver"
android:title="@string/unrestricted_app_title"
android:summary="@string/unrestricted_app_summary" />
<PreferenceCategory
android:key="app_list"
android:title="@string/data_usage_other_apps" />

View File

@@ -1,5 +1,5 @@
<!--
Copyright (C) 2016 The Android Open Source Project
<?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.
@@ -14,18 +14,12 @@
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">
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/data_saver_title">
<include layout="@layout/app_header" />
<Preference
android:key="unrestricted_access"
android:title="@string/unrestricted_data_saver"
android:fragment="com.android.settings.datausage.UnrestrictedDataAccess" />
<View
android:layout_width="match_parent"
android:layout_height=".5dp"
android:background="@android:color/white" />
<include layout="@layout/apps_filter_spinner" />
</LinearLayout>
</PreferenceScreen>

View File

@@ -29,9 +29,10 @@
android:key="limit_summary"
android:selectable="false" />
<com.android.settings.datausage.RestrictBackgroundDataPreference
<com.android.settings.datausage.DataSaverPreference
android:key="restrict_background"
android:title="@string/data_usage_menu_restrict_background" />
android:title="@string/data_saver_title"
android:fragment="com.android.settings.datausage.DataSaverSummary" />
</PreferenceCategory>

View File

@@ -41,6 +41,8 @@ public abstract class InstrumentedFragment extends PreferenceFragment {
public static final int VIRTUAL_KEYBOARDS = UNDECLARED + 11;
public static final int PHYSICAL_KEYBOARDS = UNDECLARED + 12;
public static final int ENABLE_VIRTUAL_KEYBOARDS = UNDECLARED + 13;
public static final int DATA_SAVER_SUMMARY = UNDECLARED + 14;
public static final int DATA_USAGE_UNRESTRICTED_ACCESS = UNDECLARED + 15;
/**
* Declare the view of this category.

View File

@@ -34,7 +34,7 @@ public class BackgroundDataCondition extends Condition {
@Override
public Icon getIcon() {
return Icon.createWithResource(mManager.getContext(), R.drawable.ic_cellular_off);
return Icon.createWithResource(mManager.getContext(), R.drawable.ic_data_saver);
}
@Override

View File

@@ -14,14 +14,6 @@
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;
@@ -43,10 +35,15 @@ 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 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 static android.net.NetworkPolicyManager.POLICY_NONE;
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
@@ -62,6 +59,8 @@ public class AppDataUsage extends DataUsageBase implements Preference.OnPreferen
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 String KEY_CYCLE = "cycle";
private static final String KEY_UNRESTRICTED_DATA = "unrestricted_data_saver";
private static final int LOADER_CHART_DATA = 2;
@@ -76,7 +75,6 @@ public class AppDataUsage extends DataUsageBase implements Preference.OnPreferen
private Drawable mIcon;
private CharSequence mLabel;
private INetworkStatsSession mStatsSession;
private Spinner mCycleSpinner;
private CycleAdapter mCycleAdapter;
private long mStart;
@@ -86,6 +84,9 @@ public class AppDataUsage extends DataUsageBase implements Preference.OnPreferen
private NetworkPolicy mPolicy;
private AppItem mAppItem;
private Intent mAppSettingsIntent;
private SpinnerPreference mCycle;
private SwitchPreference mUnrestrictedData;
private DataSaverBackend mDataSaverBackend;
@Override
public void onCreate(Bundle icicle) {
@@ -137,9 +138,15 @@ public class AppDataUsage extends DataUsageBase implements Preference.OnPreferen
mForegroundUsage = findPreference(KEY_FOREGROUND_USAGE);
mBackgroundUsage = findPreference(KEY_BACKGROUND_USAGE);
mCycle = (SpinnerPreference) findPreference(KEY_CYCLE);
mCycleAdapter = new CycleAdapter(getContext(), mCycle, mCycleListener, false);
if (UserHandle.isApp(mAppItem.key)) {
mRestrictBackground = (SwitchPreference) findPreference(KEY_RESTRICT_BACKGROUND);
mRestrictBackground.setOnPreferenceChangeListener(this);
mUnrestrictedData = (SwitchPreference) findPreference(KEY_UNRESTRICTED_DATA);
mUnrestrictedData.setOnPreferenceChangeListener(this);
mDataSaverBackend = new DataSaverBackend(getContext());
mAppSettings = findPreference(KEY_APP_SETTINGS);
mAppSettingsIntent = new Intent(Intent.ACTION_MANAGE_NETWORK_USAGE);
@@ -169,6 +176,7 @@ public class AppDataUsage extends DataUsageBase implements Preference.OnPreferen
removePreference(KEY_APP_LIST);
}
} else {
removePreference(KEY_UNRESTRICTED_DATA);
removePreference(KEY_APP_SETTINGS);
removePreference(KEY_RESTRICT_BACKGROUND);
removePreference(KEY_APP_LIST);
@@ -195,6 +203,9 @@ public class AppDataUsage extends DataUsageBase implements Preference.OnPreferen
if (preference == mRestrictBackground) {
setAppRestrictBackground((Boolean) newValue);
return true;
} else if (preference == mUnrestrictedData) {
mDataSaverBackend.setIsWhitelisted(mAppItem.key, (Boolean) newValue);
return true;
}
return false;
}
@@ -214,6 +225,9 @@ public class AppDataUsage extends DataUsageBase implements Preference.OnPreferen
if (mRestrictBackground != null) {
mRestrictBackground.setChecked(getAppRestrictBackground());
}
if (mUnrestrictedData != null) {
mUnrestrictedData.setChecked(mDataSaverBackend.isWhitelisted(mAppItem.key));
}
}
private void addUid(int uid) {
@@ -260,7 +274,7 @@ public class AppDataUsage extends DataUsageBase implements Preference.OnPreferen
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
View header = setPinnedHeaderView(R.layout.data_usage_app_header);
View header = setPinnedHeaderView(R.layout.app_header);
String pkg = mPackages.size() != 0 ? mPackages.valueAt(0) : null;
int uid = 0;
try {
@@ -269,9 +283,6 @@ public class AppDataUsage extends DataUsageBase implements Preference.OnPreferen
}
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
@@ -283,8 +294,7 @@ public class AppDataUsage extends DataUsageBase implements Preference.OnPreferen
new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
final CycleAdapter.CycleItem cycle =
(CycleAdapter.CycleItem) parent.getItemAtPosition(position);
final CycleAdapter.CycleItem cycle = (CycleAdapter.CycleItem) mCycle.getSelectedItem();
mStart = cycle.start;
mEnd = cycle.end;

View File

@@ -0,0 +1,56 @@
/*
* 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.applications.AppStateBaseBridge;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import java.util.ArrayList;
public class AppStateDataUsageBridge extends AppStateBaseBridge {
private static final String TAG = "AppStateDataUsageBridge";
private final DataSaverBackend mDataSaverBackend;
public AppStateDataUsageBridge(ApplicationsState appState, Callback callback,
DataSaverBackend backend) {
super(appState, callback);
mDataSaverBackend = backend;
}
@Override
protected void loadAllExtraInfo() {
ArrayList<AppEntry> apps = mAppSession.getAllApps();
final int N = apps.size();
for (int i = 0; i < N; i++) {
AppEntry app = apps.get(i);
app.extraInfo = new DataUsageState(mDataSaverBackend.isWhitelisted(app.info.uid));
}
}
@Override
protected void updateExtraInfo(AppEntry app, String pkg, int uid) {
app.extraInfo = new DataUsageState(mDataSaverBackend.isWhitelisted(uid));
}
public static class DataUsageState {
public boolean isDataSaverWhitelisted;
public DataUsageState(boolean isDataSaverWhitelisted) {
this.isDataSaverWhitelisted = isDataSaverWhitelisted;
}
}
}

View File

@@ -13,17 +13,15 @@
*/
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 com.android.settings.R;
import com.android.settings.Utils;
import com.android.settingslib.net.ChartData;
import libcore.util.Objects;
import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
@@ -31,12 +29,13 @@ import static android.net.NetworkPolicyManager.computeNextCycleBoundary;
public class CycleAdapter extends ArrayAdapter<CycleAdapter.CycleItem> {
private final Spinner mSpinner;
private final SpinnerInterface 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);
public CycleAdapter(Context context, SpinnerInterface spinner,
AdapterView.OnItemSelectedListener listener, boolean isHeader) {
super(context, isHeader ? R.layout.filter_spinner_item
: R.layout.data_usage_cycle_item);
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mSpinner = spinner;
mListener = listener;
@@ -72,7 +71,7 @@ public class CycleAdapter extends ArrayAdapter<CycleAdapter.CycleItem> {
mSpinner.getSelectedItem();
clear();
final Context context = mSpinner.getContext();
final Context context = getContext();
NetworkStatsHistory.Entry entry = null;
long historyStart = Long.MAX_VALUE;
@@ -141,7 +140,7 @@ public class CycleAdapter extends ArrayAdapter<CycleAdapter.CycleItem> {
// user-defined inspection region.
final CycleAdapter.CycleItem selectedItem = getItem(position);
if (!Objects.equal(selectedItem, previousItem)) {
mListener.onItemSelected(mSpinner, null, position, 0);
mListener.onItemSelected(null, null, position, 0);
return false;
}
}
@@ -185,4 +184,11 @@ public class CycleAdapter extends ArrayAdapter<CycleAdapter.CycleItem> {
return Long.compare(start, another.start);
}
}
public interface SpinnerInterface {
void setAdapter(CycleAdapter cycleAdapter);
void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener);
Object getSelectedItem();
void setSelection(int position);
}
}

View File

@@ -0,0 +1,148 @@
/*
* 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.INetworkPolicyListener;
import android.net.INetworkPolicyManager;
import android.net.NetworkPolicyManager;
import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
import android.util.SparseBooleanArray;
import java.util.ArrayList;
public class DataSaverBackend {
private static final String TAG = "DataSaverBackend";
private final Context mContext;
private final Handler mHandler = new Handler();
private final NetworkPolicyManager mPolicyManager;
private final INetworkPolicyManager mIPolicyManager;
private final ArrayList<Listener> mListeners = new ArrayList<>();
private SparseBooleanArray mWhitelist;
// TODO: Staticize into only one.
public DataSaverBackend(Context context) {
mContext = context;
mIPolicyManager = INetworkPolicyManager.Stub.asInterface(
ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
mPolicyManager = NetworkPolicyManager.from(context);
}
public void addListener(Listener listener) {
mListeners.add(listener);
if (mListeners.size() == 1) {
mPolicyManager.registerListener(mPolicyListener);
}
listener.onDataSaverChanged(isDataSaverEnabled());
}
public void remListener(Listener listener) {
mListeners.remove(listener);
if (mListeners.size() == 0) {
mPolicyManager.unregisterListener(mPolicyListener);
}
}
public boolean isDataSaverEnabled() {
return mPolicyManager.getRestrictBackground();
}
public void setDataSaverEnabled(boolean enabled) {
mPolicyManager.setRestrictBackground(enabled);
}
public void refreshWhitelist() {
loadWhitelist();
}
public void setIsWhitelisted(int uid, boolean whitelisted) {
mWhitelist.put(uid, whitelisted);
try {
if (whitelisted) {
mIPolicyManager.addRestrictBackgroundWhitelistedUid(uid);
} else {
mIPolicyManager.removeRestrictBackgroundWhitelistedUid(uid);
}
} catch (RemoteException e) {
Log.w(TAG, "Can't reach policy manager", e);
}
}
public boolean isWhitelisted(int uid) {
if (mWhitelist == null) {
loadWhitelist();
}
return mWhitelist.get(uid);
}
public int getWhitelistedCount() {
int count = 0;
if (mWhitelist == null) {
loadWhitelist();
}
for (int i = 0; i < mWhitelist.size(); i++) {
if (mWhitelist.valueAt(i)) {
count++;
}
}
return count;
}
private void loadWhitelist() {
mWhitelist = new SparseBooleanArray();
try {
for (int uid : mIPolicyManager.getRestrictBackgroundWhitelistedUids()) {
mWhitelist.put(uid, true);
}
} catch (RemoteException e) {
}
}
private void handleRestrictBackgroundChanged(boolean isDataSaving) {
for (int i = 0; i < mListeners.size(); i++) {
mListeners.get(i).onDataSaverChanged(isDataSaving);
}
}
private final INetworkPolicyListener mPolicyListener = new INetworkPolicyListener.Stub() {
@Override
public void onUidRulesChanged(int i, int i1) throws RemoteException {
}
@Override
public void onMeteredIfacesChanged(String[] strings) throws RemoteException {
}
@Override
public void onRestrictBackgroundChanged(final boolean isDataSaving) throws RemoteException {
mHandler.post(new Runnable() {
@Override
public void run() {
handleRestrictBackgroundChanged(isDataSaving);
}
});
}
};
public interface Listener {
void onDataSaverChanged(boolean isDataSaving);
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.support.v7.preference.Preference;
import android.util.AttributeSet;
import com.android.settings.R;
public class DataSaverPreference extends Preference implements DataSaverBackend.Listener {
private final DataSaverBackend mDataSaverBackend;
public DataSaverPreference(Context context, AttributeSet attrs) {
super(context, attrs);
mDataSaverBackend = new DataSaverBackend(context);
}
@Override
public void onAttached() {
super.onAttached();
mDataSaverBackend.addListener(this);
}
@Override
public void onDetached() {
super.onDetached();
mDataSaverBackend.addListener(this);
}
@Override
public void onDataSaverChanged(boolean isDataSaving) {
setSummary(isDataSaving ? R.string.data_saver_on : R.string.data_saver_off);
}
}

View File

@@ -0,0 +1,83 @@
/*
* 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.os.Bundle;
import android.support.v7.preference.Preference;
import android.util.Log;
import android.widget.Switch;
import com.android.settings.InstrumentedFragment;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.widget.SwitchBar;
public class DataSaverSummary extends SettingsPreferenceFragment
implements SwitchBar.OnSwitchChangeListener, DataSaverBackend.Listener {
private static final String KEY_UNRESTRICTED_ACCESS = "unrestricted_access";
private SwitchBar mSwitchBar;
private DataSaverBackend mDataSaverBackend;
private Preference mUnrestrictedAccess;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
addPreferencesFromResource(R.xml.data_saver);
mUnrestrictedAccess = findPreference(KEY_UNRESTRICTED_ACCESS);
mDataSaverBackend = new DataSaverBackend(getContext());
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mSwitchBar = ((SettingsActivity) getActivity()).getSwitchBar();
mSwitchBar.show();
mSwitchBar.addOnSwitchChangeListener(this);
}
@Override
public void onResume() {
super.onResume();
mDataSaverBackend.addListener(this);
mDataSaverBackend.refreshWhitelist();
int count = mDataSaverBackend.getWhitelistedCount();
mUnrestrictedAccess.setSummary(getResources().getQuantityString(
R.plurals.data_saver_unrestricted_summary, count, count));
}
@Override
public void onPause() {
super.onPause();
mDataSaverBackend.remListener(this);
}
@Override
public void onSwitchChanged(Switch switchView, boolean isChecked) {
mDataSaverBackend.setDataSaverEnabled(isChecked);
}
@Override
protected int getMetricsCategory() {
return InstrumentedFragment.DATA_SAVER_SUMMARY;
}
@Override
public void onDataSaverChanged(boolean isDataSaving) {
mSwitchBar.setChecked(isDataSaving);
}
}

View File

@@ -147,7 +147,27 @@ public class DataUsageList extends DataUsageBase {
mHeader = setPinnedHeaderView(R.layout.apps_filter_spinner);
mCycleSpinner = (Spinner) mHeader.findViewById(R.id.filter_spinner);
mCycleAdapter = new CycleAdapter(getContext(), mCycleSpinner, mCycleListener);
mCycleAdapter = new CycleAdapter(getContext(), new CycleAdapter.SpinnerInterface() {
@Override
public void setAdapter(CycleAdapter cycleAdapter) {
mCycleSpinner.setAdapter(cycleAdapter);
}
@Override
public void setOnItemSelectedListener(OnItemSelectedListener listener) {
mCycleSpinner.setOnItemSelectedListener(listener);
}
@Override
public Object getSelectedItem() {
return mCycleSpinner.getSelectedItem();
}
@Override
public void setSelection(int position) {
mCycleSpinner.setSelection(position);
}
}, mCycleListener, true);
setLoading(true, false);
}
@@ -470,7 +490,7 @@ public class DataUsageList extends DataUsageBase {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
final CycleAdapter.CycleItem cycle = (CycleAdapter.CycleItem)
parent.getItemAtPosition(position);
mCycleSpinner.getSelectedItem();
if (LOGD) {
Log.d(TAG, "showing cycle " + cycle + ", start=" + cycle.start + ", end="

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.Context;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceViewHolder;
import android.util.AttributeSet;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Spinner;
import com.android.settings.R;
public class SpinnerPreference extends Preference implements CycleAdapter.SpinnerInterface {
private CycleAdapter mAdapter;
private AdapterView.OnItemSelectedListener mListener;
private Object mCurrentObject;
private int mPosition;
public SpinnerPreference(Context context, AttributeSet attrs) {
super(context, attrs);
setLayoutResource(R.layout.data_usage_cycles);
}
@Override
public void setAdapter(CycleAdapter cycleAdapter) {
mAdapter = cycleAdapter;
notifyChanged();
}
@Override
public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener) {
mListener = listener;
}
@Override
public Object getSelectedItem() {
return mCurrentObject;
}
@Override
public void setSelection(int position) {
mPosition = position;
mCurrentObject = mAdapter.getItem(mPosition);
notifyChanged();
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
Spinner spinner = (Spinner) holder.findViewById(R.id.cycles_spinner);
spinner.setAdapter(mAdapter);
spinner.setSelection(mPosition);
spinner.setOnItemSelectedListener(mOnSelectedListener);
}
@Override
protected void performClick(View view) {
view.findViewById(R.id.cycles_spinner).performClick();
}
private final AdapterView.OnItemSelectedListener mOnSelectedListener
= new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (mPosition == position) return;
mPosition = position;
mCurrentObject = mAdapter.getItem(position);
mListener.onItemSelected(parent, view, position, id);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
mListener.onNothingSelected(parent);
}
};
}

View File

@@ -0,0 +1,184 @@
/*
* 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.Application;
import android.content.Context;
import android.os.Bundle;
import android.support.v14.preference.SwitchPreference;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceViewHolder;
import android.view.View;
import com.android.settings.InstrumentedFragment;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.applications.AppStateBaseBridge;
import com.android.settingslib.applications.ApplicationsState;
import java.util.ArrayList;
public class UnrestrictedDataAccess extends SettingsPreferenceFragment
implements ApplicationsState.Callbacks, AppStateBaseBridge.Callback, Preference.OnPreferenceChangeListener {
private ApplicationsState mApplicationsState;
private AppStateDataUsageBridge mDataUsageBridge;
private ApplicationsState.Session mSession;
private DataSaverBackend mDataSaverBackend;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getContext()));
getPreferenceScreen().setOrderingAsAdded(false);
mApplicationsState = ApplicationsState.getInstance(
(Application) getContext().getApplicationContext());
mDataSaverBackend = new DataSaverBackend(getContext());
mDataUsageBridge = new AppStateDataUsageBridge(mApplicationsState, this, mDataSaverBackend);
mSession = mApplicationsState.newSession(this);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
setLoading(true, false);
}
@Override
public void onResume() {
super.onResume();
mSession.resume();
mDataUsageBridge.resume();
}
@Override
public void onPause() {
super.onPause();
mDataUsageBridge.pause();
mSession.pause();
}
@Override
public void onDestroy() {
super.onDestroy();
mSession.release();
mDataUsageBridge.release();
}
@Override
public void onExtraInfoUpdated() {
ArrayList<ApplicationsState.AppEntry> apps = mSession.getAllApps();
final int N = apps.size();
for (int i = 0; i < N; i++) {
ApplicationsState.AppEntry entry = apps.get(i);
String key = entry.info.packageName + "|" + entry.info.uid;
AccessPreference preference = (AccessPreference) findPreference(key);
if (preference == null) {
preference = new AccessPreference(getContext(), entry);
preference.setKey(key);
preference.setOnPreferenceChangeListener(this);
getPreferenceScreen().addPreference(preference);
}
AppStateDataUsageBridge.DataUsageState state =
(AppStateDataUsageBridge.DataUsageState) entry.extraInfo;
preference.setChecked(state.isDataSaverWhitelisted);
}
setLoading(false, true);
}
@Override
public void onRunningStateChanged(boolean running) {
}
@Override
public void onPackageListChanged() {
}
@Override
public void onRebuildComplete(ArrayList<ApplicationsState.AppEntry> apps) {
}
@Override
public void onPackageIconChanged() {
}
@Override
public void onPackageSizeChanged(String packageName) {
}
@Override
public void onAllSizesComputed() {
}
@Override
public void onLauncherInfoChanged() {
}
@Override
public void onLoadEntriesCompleted() {
}
@Override
protected int getMetricsCategory() {
return InstrumentedFragment.DATA_USAGE_UNRESTRICTED_ACCESS;
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (preference instanceof AccessPreference) {
AccessPreference accessPreference = (AccessPreference) preference;
boolean whitelisted = newValue == Boolean.TRUE;
mDataSaverBackend.setIsWhitelisted(accessPreference.mEntry.info.uid, whitelisted);
((AppStateDataUsageBridge.DataUsageState) accessPreference.mEntry.extraInfo)
.isDataSaverWhitelisted = whitelisted;
return true;
}
return false;
}
private class AccessPreference extends SwitchPreference {
private final ApplicationsState.AppEntry mEntry;
public AccessPreference(Context context, ApplicationsState.AppEntry entry) {
super(context);
mEntry = entry;
mEntry.ensureLabel(getContext());
setTitle(entry.label);
setChecked(((AppStateDataUsageBridge.DataUsageState) entry.extraInfo)
.isDataSaverWhitelisted);
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
holder.itemView.post(new Runnable() {
@Override
public void run() {
// Ensure we have an icon before binding.
mApplicationsState.ensureIcon(mEntry);
// This might trigger us to bind again, but it gives an easy way to only load the icon
// once its needed, so its probably worth it.
setIcon(mEntry.icon);
}
});
super.onBindViewHolder(holder);
}
}
}