Snap for 4560043 from 442a53c5fb to pi-release
Change-Id: I73749a08a6fbd6d4674716abefa4b045905c01d6
This commit is contained in:
@@ -3311,6 +3311,25 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="Settings$AdvancedConnectedDeviceActivity"
|
||||
android:label="@string/connected_device_connections_title"
|
||||
android:taskAffinity="com.android.settings"
|
||||
android:parentActivityName="Settings$ConnectedDeviceDashboardActivity"
|
||||
android:enabled="false">
|
||||
<intent-filter android:priority="1">
|
||||
<action android:name="com.android.settings.ADVANCED_CONNECTED_DEVICE_SETTINGS" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
|
||||
android:value="com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment" />
|
||||
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
|
||||
android:value="true" />
|
||||
</activity>
|
||||
|
||||
<provider android:name=".slices.SettingsSliceProvider"
|
||||
android:authorities="com.android.settings.slices"
|
||||
android:exported="true">
|
||||
|
||||
@@ -115,6 +115,10 @@
|
||||
android:text="@string/erase_external_storage_description" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
<include layout="@layout/reset_esim_checkbox"
|
||||
android:id="@+id/erase_esim_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
<Button
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
@@ -21,7 +22,8 @@
|
||||
android:clickable="true"
|
||||
android:visibility="gone">
|
||||
|
||||
<CheckBox android:id="@+id/erase_esim"
|
||||
<CheckBox
|
||||
android:id="@+id/erase_esim"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
@@ -30,12 +32,14 @@
|
||||
android:clickable="false"
|
||||
android:duplicateParentState="true" />
|
||||
|
||||
<LinearLayout android:layout_width="match_parent"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView android:id="@+id/erase_esim_title"
|
||||
<TextView
|
||||
android:id="@+id/erase_esim_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="@dimen/reset_checkbox_title_padding_top"
|
||||
|
||||
@@ -3248,7 +3248,7 @@
|
||||
<!-- SD card & phone storage settings screen, list of items in user data storage (USB storage or SD card) that will be erased during this operation [CHAR LIMIT=NONE] -->
|
||||
<string name="master_clear_desc_also_erases_external">"<li>Music</li>\n<li>Photos</li>\n<li>Other user data</li>"</string>
|
||||
<!-- SD card & phone storage settings screen, list of items on an eSIM (embedded SIM) that will be erased during this operation [CHAR LIMIT=NONE] -->
|
||||
<string name="master_clear_desc_also_erases_esim">"<li>Carriers on eSIM</li>"</string>
|
||||
<string name="master_clear_desc_also_erases_esim">"<li>eSIMs</li>"</string>
|
||||
<!-- SD card & phone storage settings screen, notification if there are eSIM (embedded SIM) profiles present that the user's mobile service plan will not be canceled [CHAR LIMIT=NONE] -->
|
||||
<string name="master_clear_desc_no_cancel_mobile_plan">"\n\nThis will not cancel your mobile service plan.</string>
|
||||
<!-- SD card & phone storage settings screen, instructions about whether to also erase the external storage (SD card) when erasing the internal storage [CHAR LIMIT=NONE] -->
|
||||
@@ -4719,7 +4719,7 @@
|
||||
<string name="power_charge_remaining"><xliff:g id="until_charged">%1$s</xliff:g> to charge</string>
|
||||
|
||||
<!-- Title for the background activity setting, which allows a user to control whether an app can run in the background [CHAR_LIMIT=40] -->
|
||||
<string name="background_activity_title">Background activity</string>
|
||||
<string name="background_activity_title">Restricted</string>
|
||||
<!-- Summary for the background activity [CHAR_LIMIT=120] -->
|
||||
<string name="background_activity_summary">Allow the app to run in the background</string>
|
||||
<!-- Summary for the background activity when it is on [CHAR_LIMIT=120] -->
|
||||
@@ -7602,6 +7602,11 @@
|
||||
<!-- Button label to say no to the question of whether to require PIN/password/pattern to start your device. [CHAR LIMIT=20] -->
|
||||
<string name="encryption_interstitial_no">No</string>
|
||||
|
||||
<!-- Label to say yes to the question of whether app is restricted. [CHAR LIMIT=20] -->
|
||||
<string name="restricted_true_label">Yes</string>
|
||||
<!-- Label to say no to the question of whether app is restricted. [CHAR LIMIT=20] -->
|
||||
<string name="restricted_false_label">No</string>
|
||||
|
||||
<!-- Title for encryption dialog that disables TalkBack. [CHAR_LIMIT=25] -->
|
||||
<string name="encrypt_talkback_dialog_require_pin">Require PIN?</string>
|
||||
|
||||
@@ -8335,6 +8340,8 @@
|
||||
<string name="disabled_by_policy_title_camera">Camera not allowed</string>
|
||||
<!-- Title for dialog displayed to tell user that screenshots are disabled by an admin [CHAR LIMIT=50] -->
|
||||
<string name="disabled_by_policy_title_screen_capture">Screenshot not allowed</string>
|
||||
<!-- Title for dialog displayed to tell user that turning off backups is disallowed by an admin [CHAR LIMIT=50] -->
|
||||
<string name="disabled_by_policy_title_turn_off_backups">Can\'t turn off backups</string>
|
||||
<!-- Shown when the user tries to change a settings locked by an admin [CHAR LIMIT=200] -->
|
||||
<string name="default_admin_support_msg">This action is disabled. To learn more, contact your
|
||||
organization\'s admin.</string>
|
||||
|
||||
20
res/xml/directory_access_details.xml
Normal file
20
res/xml/directory_access_details.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2018 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||
android:key="directory_access_details"
|
||||
android:title="@string/directory_access"/>
|
||||
@@ -27,6 +27,22 @@
|
||||
android:key="action_buttons"
|
||||
android:order="-9999"/>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/battery_detail_manage_title">
|
||||
|
||||
<Preference
|
||||
android:key="background_activity"
|
||||
android:title="@string/background_activity_title"
|
||||
android:selectable="true"/>
|
||||
|
||||
<Preference
|
||||
android:key="battery_optimization"
|
||||
android:title="@string/high_power_apps"
|
||||
android:summary="@string/high_power_off"
|
||||
android:selectable="true"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<Preference
|
||||
android:key="high_usage"
|
||||
android:icon="@drawable/ic_battery_alert_24dp"
|
||||
@@ -52,20 +68,4 @@
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/battery_detail_manage_title">
|
||||
|
||||
<SwitchPreference
|
||||
android:key="background_activity"
|
||||
android:title="@string/background_activity_title"
|
||||
android:selectable="true"/>
|
||||
|
||||
<Preference
|
||||
android:key="battery_optimization"
|
||||
android:title="@string/high_power_apps"
|
||||
android:summary="@string/high_power_off"
|
||||
android:selectable="true"/>
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
@@ -111,7 +111,6 @@
|
||||
android:value="com.android.settings.Settings$VrListenersSettingsActivity" />
|
||||
</Preference>
|
||||
|
||||
<!-- TODO(b/63720392): add when ready
|
||||
<Preference
|
||||
android:key="special_app_directory_access"
|
||||
android:title="@string/directory_access"
|
||||
@@ -119,8 +118,7 @@
|
||||
settings:keywords="@string/keywords_directory_access">
|
||||
<extra
|
||||
android:name="classname"
|
||||
android:value="com.android.settings.Settings$DirectoryeAccessSettingsActivity" />
|
||||
android:value="com.android.settings.Settings$DirectoryAccessSettingsActivity" />
|
||||
</Preference>
|
||||
-->
|
||||
|
||||
</PreferenceScreen>
|
||||
|
||||
@@ -76,7 +76,10 @@ public class MasterClear extends InstrumentedPreferenceFragment {
|
||||
private static final String TAG = "MasterClear";
|
||||
|
||||
private static final int KEYGUARD_REQUEST = 55;
|
||||
private static final int CREDENTIAL_CONFIRM_REQUEST = 56;
|
||||
@VisibleForTesting static final int CREDENTIAL_CONFIRM_REQUEST = 56;
|
||||
|
||||
private static final String KEY_SHOW_ESIM_RESET_CHECKBOX
|
||||
= "masterclear.allow_retain_esim_profiles_after_fdr";
|
||||
|
||||
static final String ERASE_EXTERNAL_EXTRA = "erase_sd";
|
||||
static final String ERASE_ESIMS_EXTRA = "erase_esim";
|
||||
@@ -85,6 +88,8 @@ public class MasterClear extends InstrumentedPreferenceFragment {
|
||||
private Button mInitiateButton;
|
||||
private View mExternalStorageContainer;
|
||||
@VisibleForTesting CheckBox mExternalStorage;
|
||||
private View mEsimStorageContainer;
|
||||
@VisibleForTesting CheckBox mEsimStorage;
|
||||
private ScrollView mScrollView;
|
||||
|
||||
private final OnGlobalLayoutListener mOnGlobalLayoutListener = new OnGlobalLayoutListener() {
|
||||
@@ -134,8 +139,7 @@ public class MasterClear extends InstrumentedPreferenceFragment {
|
||||
void showFinalConfirmation() {
|
||||
Bundle args = new Bundle();
|
||||
args.putBoolean(ERASE_EXTERNAL_EXTRA, mExternalStorage.isChecked());
|
||||
// TODO: Offer the user a choice to wipe eSIMs when it is technically feasible to do so.
|
||||
args.putBoolean(ERASE_ESIMS_EXTRA, true);
|
||||
args.putBoolean(ERASE_ESIMS_EXTRA, mEsimStorage.isChecked());
|
||||
((SettingsActivity) getActivity()).startPreferencePanel(
|
||||
this, MasterClearConfirm.class.getName(),
|
||||
args, R.string.master_clear_confirm_title, null, null, 0);
|
||||
@@ -157,9 +161,12 @@ public class MasterClear extends InstrumentedPreferenceFragment {
|
||||
.setAction("android.accounts.action.PRE_FACTORY_RESET");
|
||||
// Check to make sure that the intent is supported.
|
||||
final PackageManager pm = context.getPackageManager();
|
||||
final List<ResolveInfo> resolutions =
|
||||
pm.queryIntentActivities(requestAccountConfirmation, 0);
|
||||
if (resolutions != null && resolutions.size() > 0) {
|
||||
final ResolveInfo resolution = pm.resolveActivity(requestAccountConfirmation, 0);
|
||||
if (resolution != null
|
||||
&& resolution.activityInfo != null
|
||||
&& packageName.equals(resolution.activityInfo.packageName)) {
|
||||
// Note that we need to check the packagename to make sure that an Activity resolver
|
||||
// wasn't returned.
|
||||
getActivity().startActivityForResult(
|
||||
requestAccountConfirmation, CREDENTIAL_CONFIRM_REQUEST);
|
||||
return true;
|
||||
@@ -214,6 +221,8 @@ public class MasterClear extends InstrumentedPreferenceFragment {
|
||||
mInitiateButton.setOnClickListener(mInitiateListener);
|
||||
mExternalStorageContainer = mContentView.findViewById(R.id.erase_external_container);
|
||||
mExternalStorage = (CheckBox) mContentView.findViewById(R.id.erase_external);
|
||||
mEsimStorageContainer = mContentView.findViewById(R.id.erase_esim_container);
|
||||
mEsimStorage = (CheckBox) mContentView.findViewById(R.id.erase_esim);
|
||||
mScrollView = (ScrollView) mContentView.findViewById(R.id.master_clear_scrollview);
|
||||
|
||||
/*
|
||||
@@ -248,11 +257,25 @@ public class MasterClear extends InstrumentedPreferenceFragment {
|
||||
}
|
||||
|
||||
if (showWipeEuicc()) {
|
||||
final View esimAlsoErased = mContentView.findViewById(R.id.also_erases_esim);
|
||||
esimAlsoErased.setVisibility(View.VISIBLE);
|
||||
if (showWipeEuiccCheckbox()) {
|
||||
TextView title = mContentView.findViewById(R.id.erase_esim_title);
|
||||
title.setText(R.string.erase_esim_storage);
|
||||
mEsimStorageContainer.setVisibility(View.VISIBLE);
|
||||
mEsimStorageContainer.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mEsimStorage.toggle();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
final View esimAlsoErased = mContentView.findViewById(R.id.also_erases_esim);
|
||||
esimAlsoErased.setVisibility(View.VISIBLE);
|
||||
|
||||
final View noCancelMobilePlan = mContentView.findViewById(R.id.no_cancel_mobile_plan);
|
||||
noCancelMobilePlan.setVisibility(View.VISIBLE);
|
||||
final View noCancelMobilePlan = mContentView.findViewById(
|
||||
R.id.no_cancel_mobile_plan);
|
||||
noCancelMobilePlan.setVisibility(View.VISIBLE);
|
||||
mEsimStorage.setChecked(true /* checked */);
|
||||
}
|
||||
}
|
||||
|
||||
final UserManager um = (UserManager) getActivity().getSystemService(Context.USER_SERVICE);
|
||||
@@ -293,6 +316,12 @@ public class MasterClear extends InstrumentedPreferenceFragment {
|
||||
return Settings.Global.getInt(cr, Settings.Global.EUICC_PROVISIONED, 0) != 0;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
boolean showWipeEuiccCheckbox() {
|
||||
return SystemProperties
|
||||
.getBoolean(KEY_SHOW_ESIM_RESET_CHECKBOX, false /* def */);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected boolean isEuiccEnabled(Context context) {
|
||||
EuiccManager euiccManager = (EuiccManager) context.getSystemService(Context.EUICC_SERVICE);
|
||||
|
||||
@@ -81,8 +81,7 @@ public class ResetNetworkConfirm extends InstrumentedPreferenceFragment {
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
return mRecoverySystem.wipeEuiccData(
|
||||
mContext, true /* isWipeEuicc */, mPackageName);
|
||||
return mRecoverySystem.wipeEuiccData(mContext, mPackageName);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -178,5 +178,10 @@ public class Settings extends SettingsActivity {
|
||||
public static class StorageDashboardActivity extends SettingsActivity {}
|
||||
public static class UserAndAccountDashboardActivity extends SettingsActivity {}
|
||||
public static class SystemDashboardActivity extends SettingsActivity {}
|
||||
public static class AdvancedConnectedDeviceActivity extends SettingsActivity {
|
||||
public static final boolean isEnabled() {
|
||||
return FeatureFlagUtils.isEnabled(null /* context */, CONNECTED_DEVICE_V2);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -150,6 +150,9 @@ public class ShowAdminSupportDetailsDialog extends Activity
|
||||
case DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE:
|
||||
titleView.setText(R.string.disabled_by_policy_title_screen_capture);
|
||||
break;
|
||||
case DevicePolicyManager.POLICY_MANDATORY_BACKUPS:
|
||||
titleView.setText(R.string.disabled_by_policy_title_turn_off_backups);
|
||||
break;
|
||||
default:
|
||||
// Use general text if no specialized title applies
|
||||
titleView.setText(R.string.disabled_by_policy_title);
|
||||
|
||||
@@ -514,7 +514,8 @@ public final class Utils extends com.android.settingslib.Utils {
|
||||
Fragment resultTo, int resultRequestCode, String titleResPackageName, int titleResId,
|
||||
CharSequence title, boolean isShortcut, int metricsCategory) {
|
||||
startWithFragment(context, fragmentName, args, resultTo, resultRequestCode,
|
||||
titleResPackageName, titleResId, title, isShortcut, metricsCategory, 0);
|
||||
titleResPackageName, titleResId, title, isShortcut, metricsCategory,
|
||||
Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -33,11 +33,15 @@ import com.android.settingslib.applications.ApplicationsState.AppFilter;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
// TODO(b/63720392): add unit tests
|
||||
// TODO(b/72055774): add unit tests
|
||||
public class AppStateDirectoryAccessBridge extends AppStateBaseBridge {
|
||||
|
||||
private static final String TAG = "DirectoryAccessBridge";
|
||||
|
||||
// TODO(b/72055774): set to false once feature is ready (or use Log.isLoggable)
|
||||
static final boolean DEBUG = true;
|
||||
static final boolean VERBOSE = true;
|
||||
|
||||
public AppStateDirectoryAccessBridge(ApplicationsState appState, Callback callback) {
|
||||
super(appState, callback);
|
||||
}
|
||||
@@ -59,27 +63,34 @@ public class AppStateDirectoryAccessBridge extends AppStateBaseBridge {
|
||||
|
||||
@Override
|
||||
public void init(Context context) {
|
||||
try (Cursor cursor = context.getContentResolver().query(
|
||||
new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
|
||||
.authority(AUTHORITY).appendPath(TABLE_PACKAGES).appendPath("*")
|
||||
.build(), TABLE_PACKAGES_COLUMNS, null, null)) {
|
||||
mPackages = null;
|
||||
final Uri providerUri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
|
||||
.authority(AUTHORITY).appendPath(TABLE_PACKAGES).appendPath("*")
|
||||
.build();
|
||||
try (Cursor cursor = context.getContentResolver().query(providerUri,
|
||||
TABLE_PACKAGES_COLUMNS, null, null)) {
|
||||
if (cursor == null) {
|
||||
Log.w(TAG, "didn't get cursor");
|
||||
Log.w(TAG, "Didn't get cursor for " + providerUri);
|
||||
return;
|
||||
}
|
||||
final int count = cursor.getCount();
|
||||
if (count == 0) {
|
||||
Log.d(TAG, "no packages");
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "No packages anymore (was " + mPackages + ")");
|
||||
}
|
||||
return;
|
||||
}
|
||||
mPackages = new ArraySet<>(count);
|
||||
while (cursor.moveToNext()) {
|
||||
mPackages.add(cursor.getString(TABLE_PACKAGES_COL_PACKAGE));
|
||||
}
|
||||
Log.v(TAG, "init(): " + mPackages);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "init(): " + mPackages);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean filterApp(AppEntry info) {
|
||||
return mPackages != null && mPackages.contains(info.info.packageName);
|
||||
|
||||
@@ -17,84 +17,236 @@
|
||||
package com.android.settings.applications;
|
||||
|
||||
import static android.os.storage.StorageVolume.ScopedAccessProviderContract.AUTHORITY;
|
||||
import static android.os.storage.StorageVolume.ScopedAccessProviderContract.COL_DIRECTORY;
|
||||
import static android.os.storage.StorageVolume.ScopedAccessProviderContract.COL_GRANTED;
|
||||
import static android.os.storage.StorageVolume.ScopedAccessProviderContract.COL_PACKAGE;
|
||||
import static android.os.storage.StorageVolume.ScopedAccessProviderContract.COL_VOLUME_UUID;
|
||||
import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS;
|
||||
import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS_COLUMNS;
|
||||
import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS_COL_DIRECTORY;
|
||||
import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS_COL_GRANTED;
|
||||
import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS_COL_PACKAGE;
|
||||
import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS_COL_VOLUME_UUID;
|
||||
|
||||
import static com.android.settings.applications.AppStateDirectoryAccessBridge.DEBUG;
|
||||
import static com.android.settings.applications.AppStateDirectoryAccessBridge.VERBOSE;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.os.storage.VolumeInfo;
|
||||
import android.support.v14.preference.SwitchPreference;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.support.v7.preference.Preference.OnPreferenceChangeListener;
|
||||
import android.support.v7.preference.Preference.OnPreferenceClickListener;
|
||||
import android.text.TextUtils;
|
||||
import android.support.v7.preference.PreferenceManager;
|
||||
import android.support.v7.preference.PreferenceScreen;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.IconDrawableFactory;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.widget.EntityHeaderController;
|
||||
import com.android.settings.widget.EntityHeaderController.ActionType;
|
||||
import com.android.settingslib.applications.AppUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Detailed settings for an app's directory access permissions (A.K.A Scoped Directory Access).
|
||||
*
|
||||
* <p>Currently, it shows the entry for which the user denied access with the "Do not ask again"
|
||||
* flag checked on: the user than can use the settings toggle to reset that deniel.
|
||||
*
|
||||
* <p>This fragments dynamically lists all such permissions, starting with one preference per
|
||||
* directory in the primary storage, then adding additional entries for the external volumes (one
|
||||
* entry for the whole volume).
|
||||
*/
|
||||
// TODO(b/63720392): explain its layout
|
||||
// TODO(b/63720392): add unit tests
|
||||
public class DirectoryAccessDetails extends AppInfoWithHeader implements OnPreferenceChangeListener,
|
||||
OnPreferenceClickListener {
|
||||
private static final String MY_TAG = "DirectoryAccessDetails";
|
||||
// TODO(b/72055774): add unit tests
|
||||
public class DirectoryAccessDetails extends AppInfoBase {
|
||||
|
||||
@SuppressWarnings("hiding")
|
||||
private static final String TAG = "DirectoryAccessDetails";
|
||||
|
||||
private boolean mCreated;
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
if (mCreated) {
|
||||
Log.w(TAG, "onActivityCreated(): ignoring duplicate call");
|
||||
return;
|
||||
}
|
||||
mCreated = true;
|
||||
if (mPackageInfo == null) {
|
||||
Log.w(TAG, "onActivityCreated(): no package info");
|
||||
return;
|
||||
}
|
||||
final Activity activity = getActivity();
|
||||
final Preference pref = EntityHeaderController
|
||||
.newInstance(activity, this, /* header= */ null )
|
||||
.setRecyclerView(getListView(), getLifecycle())
|
||||
.setIcon(IconDrawableFactory.newInstance(getPrefContext())
|
||||
.getBadgedIcon(mPackageInfo.applicationInfo))
|
||||
.setLabel(mPackageInfo.applicationInfo.loadLabel(mPm))
|
||||
.setIsInstantApp(AppUtils.isInstant(mPackageInfo.applicationInfo))
|
||||
.setPackageName(mPackageName)
|
||||
.setUid(mPackageInfo.applicationInfo.uid)
|
||||
.setHasAppInfoLink(false)
|
||||
.setButtonActions(ActionType.ACTION_NONE, ActionType.ACTION_NONE)
|
||||
.done(activity, getPrefContext());
|
||||
getPreferenceScreen().addPreference(pref);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (true) {
|
||||
// TODO(b/63720392): temporarily hack so the screen doesn't crash..
|
||||
addPreferencesFromResource(R.xml.app_ops_permissions_details);
|
||||
// ... we need to dynamically create the preferences by calling the provider instead:
|
||||
try (Cursor cursor = getContentResolver().query(
|
||||
new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
|
||||
.authority(AUTHORITY).appendPath(TABLE_PERMISSIONS).appendPath("*")
|
||||
.build(),
|
||||
TABLE_PERMISSIONS_COLUMNS, null, null)) {
|
||||
if (cursor == null) {
|
||||
Log.w(TAG, "didn't get cursor");
|
||||
return;
|
||||
final Context context = getPrefContext();
|
||||
addPreferencesFromResource(R.xml.directory_access_details);
|
||||
final PreferenceScreen prefsGroup = getPreferenceScreen();
|
||||
|
||||
// Set external directory UUIDs.
|
||||
ArraySet<String> externalDirectoryUuids = null;
|
||||
|
||||
final Uri providerUri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
|
||||
.authority(AUTHORITY).appendPath(TABLE_PERMISSIONS).appendPath("*")
|
||||
.build();
|
||||
// Query provider for entries.
|
||||
try (Cursor cursor = context.getContentResolver().query(providerUri,
|
||||
TABLE_PERMISSIONS_COLUMNS, null, new String[] { mPackageName }, null)) {
|
||||
if (cursor == null) {
|
||||
Log.w(TAG, "Didn't get cursor for " + mPackageName);
|
||||
return;
|
||||
}
|
||||
final int count = cursor.getCount();
|
||||
if (count == 0) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "No permissions for " + mPackageName);
|
||||
}
|
||||
final int count = cursor.getCount();
|
||||
if (count == 0) {
|
||||
Log.d(TAG, "no permissions");
|
||||
return;
|
||||
// TODO(b/72055774): display empty message
|
||||
return;
|
||||
}
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
final String pkg = cursor.getString(TABLE_PERMISSIONS_COL_PACKAGE);
|
||||
final String uuid = cursor.getString(TABLE_PERMISSIONS_COL_VOLUME_UUID);
|
||||
final String dir = cursor.getString(TABLE_PERMISSIONS_COL_DIRECTORY);
|
||||
if (VERBOSE) {
|
||||
Log.v(TAG, "Pkg:" + pkg + " uuid: " + uuid + " dir: " + dir);
|
||||
}
|
||||
while (cursor.moveToNext()) {
|
||||
final String pkg = cursor.getString(TABLE_PERMISSIONS_COL_PACKAGE);
|
||||
final String uuid = cursor.getString(TABLE_PERMISSIONS_COL_VOLUME_UUID);
|
||||
final String dir = cursor.getString(TABLE_PERMISSIONS_COL_DIRECTORY);
|
||||
final boolean granted = cursor.getInt(TABLE_PERMISSIONS_COL_GRANTED) == 1;
|
||||
Log.v(MY_TAG, "pkg:" + pkg + " uuid: " + uuid + " dir: " + dir + "> "
|
||||
+ granted);
|
||||
|
||||
if (!mPackageName.equals(pkg)) {
|
||||
// Sanity check, shouldn't happen
|
||||
Log.w(TAG, "Ignoring " + uuid + "/" + dir + " due to package mismatch: "
|
||||
+ "expected " + mPackageName + ", got " + pkg);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (uuid == null) {
|
||||
// Primary storage entry: add right away
|
||||
prefsGroup.addPreference(
|
||||
newPreference(context, dir, providerUri, /* uuid= */ null, dir));
|
||||
} else {
|
||||
// External volume entry: save it for later.
|
||||
if (externalDirectoryUuids == null) {
|
||||
externalDirectoryUuids = new ArraySet<>(1);
|
||||
}
|
||||
externalDirectoryUuids.add(uuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add entries from external volumes
|
||||
if (externalDirectoryUuids != null) {
|
||||
if (VERBOSE) {
|
||||
Log.v(TAG, "adding external directories: " + externalDirectoryUuids);
|
||||
}
|
||||
|
||||
// Query StorageManager to get the user-friendly volume names.
|
||||
final StorageManager sm = context.getSystemService(StorageManager.class);
|
||||
final List<VolumeInfo> volumes = sm.getVolumes();
|
||||
if (volumes.isEmpty()) {
|
||||
Log.w(TAG, "StorageManager returned no secondary volumes");
|
||||
return;
|
||||
}
|
||||
final Map<String, String> volumeNames = new HashMap<>(volumes.size());
|
||||
for (VolumeInfo volume : volumes) {
|
||||
final String uuid = volume.getFsUuid();
|
||||
if (uuid == null) continue; // Primary storage; not used.
|
||||
|
||||
String name = sm.getBestVolumeDescription(volume);
|
||||
if (name == null) {
|
||||
Log.w(TAG, "No description for " + volume + "; using uuid instead: " + uuid);
|
||||
name = uuid;
|
||||
}
|
||||
volumeNames.put(uuid, name);
|
||||
}
|
||||
if (VERBOSE) {
|
||||
Log.v(TAG, "UUID -> name mapping: " + volumeNames);
|
||||
}
|
||||
|
||||
externalDirectoryUuids.forEach((uuid) ->{
|
||||
final String name = volumeNames.get(uuid);
|
||||
// TODO(b/72055774): add separator
|
||||
prefsGroup.addPreference(
|
||||
newPreference(context, name, providerUri, uuid, /* dir= */ null));
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
private SwitchPreference newPreference(Context context, String title, Uri providerUri,
|
||||
String uuid, String dir) {
|
||||
final SwitchPreference pref = new SwitchPreference(context);
|
||||
pref.setKey(String.format("%s:%s", uuid, dir));
|
||||
pref.setTitle(title);
|
||||
pref.setChecked(false);
|
||||
pref.setOnPreferenceChangeListener((unused, value) -> {
|
||||
resetDoNotAskAgain(context, value, providerUri, uuid, dir);
|
||||
return true;
|
||||
});
|
||||
return pref;
|
||||
}
|
||||
|
||||
private void resetDoNotAskAgain(Context context, Object value, Uri providerUri,
|
||||
@Nullable String uuid, @Nullable String directory) {
|
||||
if (!Boolean.class.isInstance(value)) {
|
||||
// Sanity check
|
||||
Log.wtf(TAG, "Invalid value from switch: " + value);
|
||||
return;
|
||||
}
|
||||
final boolean newValue = ((Boolean) value).booleanValue();
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Asking " + providerUri + " to update " + uuid + "/" + directory + " to "
|
||||
+ newValue);
|
||||
}
|
||||
final ContentValues values = new ContentValues(1);
|
||||
values.put(COL_GRANTED, newValue);
|
||||
final int updated = context.getContentResolver().update(providerUri, values,
|
||||
null, new String[] { mPackageName, uuid, directory });
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Updated " + updated + " entries for " + uuid + "/" + directory);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
// TODO(b/63720392): implement or remove listener
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
// TODO(b/63720392): implement or remove listener
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean refreshUi() {
|
||||
// TODO(b/63720392): implement
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,14 +27,19 @@ import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.connecteddevice.DevicePreferenceCallback;
|
||||
import com.android.settings.widget.GearPreference;
|
||||
import com.android.settingslib.bluetooth.A2dpProfile;
|
||||
import com.android.settingslib.bluetooth.BluetoothCallback;
|
||||
import com.android.settingslib.bluetooth.BluetoothDeviceFilter;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.bluetooth.HeadsetProfile;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Update the bluetooth devices. It gets bluetooth event from {@link LocalBluetoothManager} using
|
||||
@@ -45,6 +50,7 @@ import java.util.Map;
|
||||
* whether the {@link CachedBluetoothDevice} is relevant.
|
||||
*/
|
||||
public abstract class BluetoothDeviceUpdater implements BluetoothCallback {
|
||||
private static final String TAG = "BluetoothDeviceUpdater";
|
||||
private static final String BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY =
|
||||
"persist.bluetooth.showdeviceswithoutnames";
|
||||
|
||||
@@ -55,6 +61,7 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback {
|
||||
|
||||
private final boolean mShowDeviceWithoutNames;
|
||||
private DashboardFragment mFragment;
|
||||
private Preference.OnPreferenceClickListener mDevicePreferenceClickListener = null;
|
||||
|
||||
@VisibleForTesting
|
||||
final GearPreference.OnGearClickListener mDeviceProfilesListener = pref -> {
|
||||
@@ -73,6 +80,38 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback {
|
||||
|
||||
};
|
||||
|
||||
private class PreferenceClickListener implements
|
||||
Preference.OnPreferenceClickListener {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
final CachedBluetoothDevice device =
|
||||
((BluetoothDevicePreference) preference).getBluetoothDevice();
|
||||
if (device == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set the device as active per profile only if the device supports that profile
|
||||
// TODO: The active device selector location might change in the future
|
||||
Log.i(TAG, "OnPreferenceClickListener: device=" + device);
|
||||
boolean result = false;
|
||||
A2dpProfile a2dpProfile = mLocalManager.getProfileManager().getA2dpProfile();
|
||||
if ((a2dpProfile != null) && device.isConnectedProfile(a2dpProfile)) {
|
||||
if (a2dpProfile.setActiveDevice(device.getDevice())) {
|
||||
Log.i(TAG, "OnPreferenceClickListener: A2DP active device=" + device);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
HeadsetProfile headsetProfile = mLocalManager.getProfileManager().getHeadsetProfile();
|
||||
if ((headsetProfile != null) && device.isConnectedProfile(headsetProfile)) {
|
||||
if (headsetProfile.setActiveDevice(device.getDevice())) {
|
||||
Log.i(TAG, "OnPreferenceClickListener: Headset active device=" + device);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public BluetoothDeviceUpdater(DashboardFragment fragment,
|
||||
DevicePreferenceCallback devicePreferenceCallback) {
|
||||
this(fragment, devicePreferenceCallback, Utils.getLocalBtManager(fragment.getContext()));
|
||||
@@ -87,6 +126,7 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback {
|
||||
BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY, false);
|
||||
mPreferenceMap = new HashMap<>();
|
||||
mLocalManager = localManager;
|
||||
mDevicePreferenceClickListener = new PreferenceClickListener();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -141,6 +181,18 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback {
|
||||
@Override
|
||||
public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {}
|
||||
|
||||
@Override
|
||||
public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {
|
||||
Collection<CachedBluetoothDevice> cachedDevices =
|
||||
mLocalManager.getCachedDeviceManager().getCachedDevicesCopy();
|
||||
// TODO: The state update of the Cached Bluetooth Devices should be
|
||||
// moved to the device manager: b/72316092
|
||||
for (CachedBluetoothDevice cachedBluetoothDevice : cachedDevices) {
|
||||
boolean isActive = Objects.equals(cachedBluetoothDevice, activeDevice);
|
||||
cachedBluetoothDevice.setActiveDevice(isActive, bluetoothProfile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the context to generate the {@link Preference}, so it could get the correct theme.
|
||||
*/
|
||||
@@ -176,6 +228,7 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback {
|
||||
new BluetoothDevicePreference(mPrefContext, cachedDevice,
|
||||
mShowDeviceWithoutNames);
|
||||
btPreference.setOnGearClickListener(mDeviceProfilesListener);
|
||||
btPreference.setOnPreferenceClickListener(mDevicePreferenceClickListener);
|
||||
mPreferenceMap.put(device, btPreference);
|
||||
mDevicePreferenceCallback.onDeviceAdded(btPreference);
|
||||
}
|
||||
|
||||
@@ -76,6 +76,10 @@ public final class BluetoothSummaryUpdater extends SummaryUpdater implements Blu
|
||||
public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(boolean listening) {
|
||||
if (mBluetoothAdapter == null) {
|
||||
|
||||
@@ -277,6 +277,9 @@ public abstract class DeviceListPreferenceFragment extends
|
||||
|
||||
public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { }
|
||||
|
||||
@Override
|
||||
public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) { }
|
||||
|
||||
/**
|
||||
* Return the key of the {@link PreferenceGroup} that contains the bluetooth devices
|
||||
*/
|
||||
|
||||
@@ -57,6 +57,7 @@ import com.android.settings.applications.assist.ManageAssist;
|
||||
import com.android.settings.applications.manageapplications.ManageApplications;
|
||||
import com.android.settings.bluetooth.BluetoothDeviceDetailsFragment;
|
||||
import com.android.settings.bluetooth.BluetoothSettings;
|
||||
import com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment;
|
||||
import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
|
||||
import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragmentOld;
|
||||
import com.android.settings.datausage.DataPlanUsageSummary;
|
||||
@@ -254,7 +255,8 @@ public class SettingsGateway {
|
||||
LockscreenDashboardFragment.class.getName(),
|
||||
BluetoothDeviceDetailsFragment.class.getName(),
|
||||
DataUsageList.class.getName(),
|
||||
DirectoryAccessDetails.class.getName()
|
||||
DirectoryAccessDetails.class.getName(),
|
||||
AdvancedConnectedDeviceDashboardFragment.class.getName()
|
||||
};
|
||||
|
||||
public static final String[] SETTINGS_FOR_RESTRICTED = {
|
||||
|
||||
@@ -31,6 +31,7 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Spinner;
|
||||
import com.android.internal.logging.nano.MetricsProto;
|
||||
import com.android.settings.R;
|
||||
@@ -56,11 +57,14 @@ public class ZonePicker extends InstrumentedFragment
|
||||
private List<RegionInfo> mRegions;
|
||||
private Map<String, List<TimeZoneInfo>> mZoneInfos;
|
||||
private List<TimeZoneInfo> mFixedOffsetTimeZones;
|
||||
private TimeZoneAdapter mTimeZoneAdapter;
|
||||
private String mSelectedTimeZone;
|
||||
private boolean mSelectByRegion;
|
||||
private DataLoader mDataLoader;
|
||||
private TimeZoneAdapter mTimeZoneAdapter;
|
||||
|
||||
private RecyclerView mRecyclerView;
|
||||
private LinearLayout mRegionSpinnerLayout;
|
||||
private Spinner mRegionSpinner;
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
@@ -88,15 +92,17 @@ public class ZonePicker extends InstrumentedFragment
|
||||
final ArrayAdapter<RegionInfo> regionAdapter = new ArrayAdapter<>(getContext(),
|
||||
R.layout.filter_spinner_item, mRegions);
|
||||
regionAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
final Spinner spinner = view.findViewById(R.id.tz_region_spinner);
|
||||
spinner.setAdapter(regionAdapter);
|
||||
spinner.setOnItemSelectedListener(this);
|
||||
setupForCurrentTimeZone(spinner);
|
||||
|
||||
mRegionSpinnerLayout = view.findViewById(R.id.tz_region_spinner_layout);
|
||||
mRegionSpinner = view.findViewById(R.id.tz_region_spinner);
|
||||
mRegionSpinner.setAdapter(regionAdapter);
|
||||
mRegionSpinner.setOnItemSelectedListener(this);
|
||||
setupForCurrentTimeZone();
|
||||
setHasOptionsMenu(true);
|
||||
return view;
|
||||
}
|
||||
|
||||
private void setupForCurrentTimeZone(Spinner spinner) {
|
||||
private void setupForCurrentTimeZone() {
|
||||
final String localeRegionId = mLocale.getCountry().toUpperCase(Locale.ROOT);
|
||||
final String currentTimeZone = TimeZone.getDefault().getID();
|
||||
boolean fixedOffset = currentTimeZone.startsWith("Etc/GMT") ||
|
||||
@@ -105,12 +111,12 @@ public class ZonePicker extends InstrumentedFragment
|
||||
for (int regionIndex = 0; regionIndex < mRegions.size(); regionIndex++) {
|
||||
final RegionInfo region = mRegions.get(regionIndex);
|
||||
if (localeRegionId.equals(region.getId())) {
|
||||
spinner.setSelection(regionIndex);
|
||||
mRegionSpinner.setSelection(regionIndex);
|
||||
}
|
||||
if (!fixedOffset) {
|
||||
for (String timeZoneId: region.getTimeZoneIds()) {
|
||||
if (TextUtils.equals(timeZoneId, mSelectedTimeZone)) {
|
||||
spinner.setSelection(regionIndex);
|
||||
mRegionSpinner.setSelection(regionIndex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -179,16 +185,15 @@ public class ZonePicker extends InstrumentedFragment
|
||||
|
||||
private void setSelectByRegion(boolean selectByRegion) {
|
||||
mSelectByRegion = selectByRegion;
|
||||
getView().findViewById(R.id.tz_region_spinner_layout).setVisibility(
|
||||
mRegionSpinnerLayout.setVisibility(
|
||||
mSelectByRegion ? View.VISIBLE : View.GONE);
|
||||
List<TimeZoneInfo> tzInfos;
|
||||
if (selectByRegion) {
|
||||
Spinner regionSpinner = getView().findViewById(R.id.tz_region_spinner);
|
||||
int selectedRegion = regionSpinner.getSelectedItemPosition();
|
||||
int selectedRegion = mRegionSpinner.getSelectedItemPosition();
|
||||
if (selectedRegion == -1) {
|
||||
// Arbitrarily pick the first item if no region was selected above.
|
||||
selectedRegion = 0;
|
||||
regionSpinner.setSelection(selectedRegion);
|
||||
mRegionSpinner.setSelection(selectedRegion);
|
||||
}
|
||||
tzInfos = getTimeZoneInfos(mRegions.get(selectedRegion));
|
||||
} else {
|
||||
|
||||
@@ -374,7 +374,7 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
|
||||
|
||||
@Override
|
||||
public void onLimitBackgroundActivity() {
|
||||
mBackgroundActivityPreferenceController.setUnchecked(
|
||||
mBackgroundActivityPreferenceController.setRestricted(
|
||||
findPreference(mBackgroundActivityPreferenceController.getPreferenceKey()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,12 +43,12 @@ import com.android.settingslib.fuelgauge.PowerWhitelistBackend;
|
||||
* Controller to control whether an app can run in the background
|
||||
*/
|
||||
public class BackgroundActivityPreferenceController extends AbstractPreferenceController
|
||||
implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
|
||||
implements PreferenceControllerMixin {
|
||||
|
||||
private static final String TAG = "BgActivityPrefContr";
|
||||
private static final String KEY_BACKGROUND_ACTIVITY = "background_activity";
|
||||
@VisibleForTesting
|
||||
static final String KEY_BACKGROUND_ACTIVITY = "background_activity";
|
||||
|
||||
private final PackageManager mPackageManager;
|
||||
private final AppOpsManager mAppOpsManager;
|
||||
private final UserManager mUserManager;
|
||||
private final int mUid;
|
||||
@@ -70,7 +70,6 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo
|
||||
int uid, String packageName, PowerWhitelistBackend backend) {
|
||||
super(context);
|
||||
mPowerWhitelistBackend = backend;
|
||||
mPackageManager = context.getPackageManager();
|
||||
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
|
||||
mDpm = new DevicePolicyManagerWrapper(
|
||||
(DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE));
|
||||
@@ -86,11 +85,6 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo
|
||||
final int mode = mAppOpsManager
|
||||
.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mTargetPackage);
|
||||
final boolean whitelisted = mPowerWhitelistBackend.isWhitelisted(mTargetPackage);
|
||||
// Set checked or not before we may set it disabled
|
||||
if (mode != AppOpsManager.MODE_ERRORED) {
|
||||
final boolean checked = whitelisted || mode != AppOpsManager.MODE_IGNORED;
|
||||
((SwitchPreference) preference).setChecked(checked);
|
||||
}
|
||||
if (whitelisted || mode == AppOpsManager.MODE_ERRORED
|
||||
|| Utils.isProfileOrDeviceOwner(mUserManager, mDpm, mTargetPackage)) {
|
||||
preference.setEnabled(false);
|
||||
@@ -109,9 +103,8 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo
|
||||
* Called from the warning dialog, if the user decides to go ahead and disable background
|
||||
* activity for this package
|
||||
*/
|
||||
public void setUnchecked(Preference preference) {
|
||||
public void setRestricted(Preference preference) {
|
||||
mBatteryUtils.setForceAppStandby(mUid, mTargetPackage, AppOpsManager.MODE_IGNORED);
|
||||
((SwitchPreference) preference).setChecked(false);
|
||||
updateSummary(preference);
|
||||
}
|
||||
|
||||
@@ -121,17 +114,21 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
final boolean switchOn = (Boolean) newValue;
|
||||
if (!switchOn) {
|
||||
final WarningDialogFragment dialogFragment = new WarningDialogFragment();
|
||||
dialogFragment.setTargetFragment(mFragment, 0);
|
||||
dialogFragment.show(mFragment.getFragmentManager(), TAG);
|
||||
return false;
|
||||
public boolean handlePreferenceTreeClick(Preference preference) {
|
||||
if (KEY_BACKGROUND_ACTIVITY.equals(preference.getKey())) {
|
||||
final int mode = mAppOpsManager
|
||||
.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mTargetPackage);
|
||||
final boolean restricted = mode == AppOpsManager.MODE_IGNORED;
|
||||
if (!restricted) {
|
||||
showDialog();
|
||||
return false;
|
||||
}
|
||||
mBatteryUtils.setForceAppStandby(mUid, mTargetPackage, AppOpsManager.MODE_ALLOWED);
|
||||
updateSummary(preference);
|
||||
return true;
|
||||
}
|
||||
mBatteryUtils.setForceAppStandby(mUid, mTargetPackage, AppOpsManager.MODE_ALLOWED);
|
||||
updateSummary(preference);
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -146,12 +143,19 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo
|
||||
if (mode == AppOpsManager.MODE_ERRORED) {
|
||||
preference.setSummary(R.string.background_activity_summary_disabled);
|
||||
} else {
|
||||
final boolean checked = mode != AppOpsManager.MODE_IGNORED;
|
||||
preference.setSummary(checked ? R.string.background_activity_summary_on
|
||||
: R.string.background_activity_summary_off);
|
||||
final boolean restricted = mode == AppOpsManager.MODE_IGNORED;
|
||||
preference.setSummary(restricted ? R.string.restricted_true_label
|
||||
: R.string.restricted_false_label);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void showDialog() {
|
||||
final WarningDialogFragment dialogFragment = new WarningDialogFragment();
|
||||
dialogFragment.setTargetFragment(mFragment, 0 /* requestCode */);
|
||||
dialogFragment.show(mFragment.getFragmentManager(), TAG);
|
||||
}
|
||||
|
||||
interface WarningConfirmationListener {
|
||||
void onLimitBackgroundActivity();
|
||||
}
|
||||
|
||||
@@ -272,7 +272,7 @@ public class BatteryInfo {
|
||||
void onParsingDone();
|
||||
}
|
||||
|
||||
private static void parse(BatteryStats stats, BatteryDataParser... parsers) {
|
||||
public static void parse(BatteryStats stats, BatteryDataParser... parsers) {
|
||||
long startWalltime = 0;
|
||||
long endWalltime = 0;
|
||||
long historyStart = 0;
|
||||
|
||||
@@ -19,9 +19,12 @@ package com.android.settings.fuelgauge.batterytip;
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.text.format.DateUtils;
|
||||
import android.util.KeyValueListParser;
|
||||
import android.util.Log;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
/**
|
||||
* Class to store the policy for battery tips, which comes from
|
||||
* {@link Settings.Global}
|
||||
@@ -34,6 +37,8 @@ public class BatteryTipPolicy {
|
||||
private static final String KEY_BATTERY_SAVER_TIP_ENABLED = "battery_saver_tip_enabled";
|
||||
private static final String KEY_HIGH_USAGE_ENABLED = "high_usage_enabled";
|
||||
private static final String KEY_HIGH_USAGE_APP_COUNT = "high_usage_app_count";
|
||||
private static final String KEY_HIGH_USAGE_PERIOD_MS = "high_usage_period_ms";
|
||||
private static final String KEY_HIGH_USAGE_BATTERY_DRAINING = "high_usage_battery_draining";
|
||||
private static final String KEY_APP_RESTRICTION_ENABLED = "app_restriction_enabled";
|
||||
private static final String KEY_REDUCED_BATTERY_ENABLED = "reduced_battery_enabled";
|
||||
private static final String KEY_REDUCED_BATTERY_PERCENT = "reduced_battery_percent";
|
||||
@@ -80,6 +85,24 @@ public class BatteryTipPolicy {
|
||||
*/
|
||||
public final int highUsageAppCount;
|
||||
|
||||
/**
|
||||
* The size of the window(milliseconds) for checking if the device is being heavily used
|
||||
*
|
||||
* @see Settings.Global#BATTERY_TIP_CONSTANTS
|
||||
* @see #KEY_HIGH_USAGE_PERIOD_MS
|
||||
*/
|
||||
public final long highUsagePeriodMs;
|
||||
|
||||
/**
|
||||
* The battery draining threshold to detect whether device is heavily used.
|
||||
* If battery drains more than {@link #highUsageBatteryDraining} in last {@link
|
||||
* #highUsagePeriodMs}, treat device as heavily used.
|
||||
*
|
||||
* @see Settings.Global#BATTERY_TIP_CONSTANTS
|
||||
* @see #KEY_HIGH_USAGE_BATTERY_DRAINING
|
||||
*/
|
||||
public final int highUsageBatteryDraining;
|
||||
|
||||
/**
|
||||
* {@code true} if app restriction tip is enabled
|
||||
*
|
||||
@@ -143,6 +166,9 @@ public class BatteryTipPolicy {
|
||||
batterySaverTipEnabled = mParser.getBoolean(KEY_BATTERY_SAVER_TIP_ENABLED, true);
|
||||
highUsageEnabled = mParser.getBoolean(KEY_HIGH_USAGE_ENABLED, true);
|
||||
highUsageAppCount = mParser.getInt(KEY_HIGH_USAGE_APP_COUNT, 3);
|
||||
highUsagePeriodMs = mParser.getLong(KEY_HIGH_USAGE_PERIOD_MS,
|
||||
Duration.ofHours(2).toMillis());
|
||||
highUsageBatteryDraining = mParser.getInt(KEY_HIGH_USAGE_BATTERY_DRAINING, 25);
|
||||
appRestrictionEnabled = mParser.getBoolean(KEY_APP_RESTRICTION_ENABLED, true);
|
||||
reducedBatteryEnabled = mParser.getBoolean(KEY_REDUCED_BATTERY_ENABLED, false);
|
||||
reducedBatteryPercent = mParser.getInt(KEY_REDUCED_BATTERY_PERCENT, 50);
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.fuelgauge.batterytip;
|
||||
|
||||
import android.os.BatteryStats;
|
||||
|
||||
import com.android.settings.fuelgauge.BatteryInfo;
|
||||
|
||||
/**
|
||||
* DataParser used to go through battery data and detect whether battery is
|
||||
* heavily used.
|
||||
*/
|
||||
public class HighUsageDataParser implements BatteryInfo.BatteryDataParser {
|
||||
/**
|
||||
* time period to check the battery usage
|
||||
*/
|
||||
private final long mTimePeriodMs;
|
||||
/**
|
||||
* treat device as heavily used if battery usage is more than {@code threshold}. 1 means 1%
|
||||
* battery usage.
|
||||
*/
|
||||
private int mThreshold;
|
||||
private long mEndTimeMs;
|
||||
private byte mEndBatteryLevel;
|
||||
private byte mLastPeriodBatteryLevel;
|
||||
private int mBatteryDrain;
|
||||
|
||||
public HighUsageDataParser(long timePeriodMs, int threshold) {
|
||||
mTimePeriodMs = timePeriodMs;
|
||||
mThreshold = threshold;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onParsingStarted(long startTime, long endTime) {
|
||||
mEndTimeMs = endTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataPoint(long time, BatteryStats.HistoryItem record) {
|
||||
if (record.currentTime <= mEndTimeMs - mTimePeriodMs) {
|
||||
// Since onDataPoint is invoked sorted by time, so we could use this way to get the
|
||||
// closet battery level 'mTimePeriodMs' time ago.
|
||||
mLastPeriodBatteryLevel = record.batteryLevel;
|
||||
}
|
||||
mEndBatteryLevel = record.batteryLevel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataGap() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onParsingDone() {
|
||||
mBatteryDrain = mLastPeriodBatteryLevel - mEndBatteryLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@code true} if the battery drain in {@link #mTimePeriodMs} is too much
|
||||
*/
|
||||
public boolean isDeviceHeavilyUsed() {
|
||||
return mBatteryDrain > mThreshold;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,13 +19,14 @@ package com.android.settings.fuelgauge.batterytip.detectors;
|
||||
import android.content.Context;
|
||||
import android.os.BatteryStats;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.text.format.DateUtils;
|
||||
|
||||
import com.android.internal.os.BatterySipper;
|
||||
import com.android.internal.os.BatteryStatsHelper;
|
||||
import com.android.settings.fuelgauge.BatteryUtils;
|
||||
import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
|
||||
import com.android.settings.fuelgauge.batterytip.AppInfo;
|
||||
import com.android.settings.fuelgauge.BatteryInfo;
|
||||
import com.android.settings.fuelgauge.batterytip.HighUsageDataParser;
|
||||
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
|
||||
import com.android.settings.fuelgauge.batterytip.tips.HighUsageTip;
|
||||
|
||||
@@ -43,6 +44,8 @@ public class HighUsageDetector implements BatteryTipDetector {
|
||||
private List<AppInfo> mHighUsageAppList;
|
||||
private Context mContext;
|
||||
@VisibleForTesting
|
||||
HighUsageDataParser mDataParser;
|
||||
@VisibleForTesting
|
||||
BatteryUtils mBatteryUtils;
|
||||
|
||||
public HighUsageDetector(Context context, BatteryTipPolicy policy,
|
||||
@@ -52,32 +55,42 @@ public class HighUsageDetector implements BatteryTipDetector {
|
||||
mBatteryStatsHelper = batteryStatsHelper;
|
||||
mHighUsageAppList = new ArrayList<>();
|
||||
mBatteryUtils = BatteryUtils.getInstance(context);
|
||||
mDataParser = new HighUsageDataParser(mPolicy.highUsagePeriodMs,
|
||||
mPolicy.highUsageBatteryDraining);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BatteryTip detect() {
|
||||
final long screenUsageTimeMs = mBatteryUtils.calculateScreenUsageTime(mBatteryStatsHelper);
|
||||
//TODO(b/70570352): Change it to detect whether battery drops 25% in last 2 hours
|
||||
if (mPolicy.highUsageEnabled && screenUsageTimeMs > DateUtils.HOUR_IN_MILLIS) {
|
||||
final List<BatterySipper> batterySippers = mBatteryStatsHelper.getUsageList();
|
||||
for (int i = 0, size = batterySippers.size(); i < size; i++) {
|
||||
final BatterySipper batterySipper = batterySippers.get(i);
|
||||
if (!mBatteryUtils.shouldHideSipper(batterySipper)) {
|
||||
final long foregroundTimeMs = mBatteryUtils.getProcessTimeMs(
|
||||
BatteryUtils.StatusType.FOREGROUND, batterySipper.uidObj,
|
||||
BatteryStats.STATS_SINCE_CHARGED);
|
||||
mHighUsageAppList.add(new AppInfo.Builder()
|
||||
.setPackageName(mBatteryUtils.getPackageName(batterySipper.getUid()))
|
||||
.setScreenOnTimeMs(foregroundTimeMs)
|
||||
.build());
|
||||
if (mPolicy.highUsageEnabled) {
|
||||
parseBatteryData();
|
||||
if (mDataParser.isDeviceHeavilyUsed()) {
|
||||
final List<BatterySipper> batterySippers = mBatteryStatsHelper.getUsageList();
|
||||
for (int i = 0, size = batterySippers.size(); i < size; i++) {
|
||||
final BatterySipper batterySipper = batterySippers.get(i);
|
||||
if (!mBatteryUtils.shouldHideSipper(batterySipper)) {
|
||||
final long foregroundTimeMs = mBatteryUtils.getProcessTimeMs(
|
||||
BatteryUtils.StatusType.FOREGROUND, batterySipper.uidObj,
|
||||
BatteryStats.STATS_SINCE_CHARGED);
|
||||
mHighUsageAppList.add(new AppInfo.Builder()
|
||||
.setPackageName(
|
||||
mBatteryUtils.getPackageName(batterySipper.getUid()))
|
||||
.setScreenOnTimeMs(foregroundTimeMs)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mHighUsageAppList = mHighUsageAppList.subList(0,
|
||||
Math.min(mPolicy.highUsageAppCount, mHighUsageAppList.size()));
|
||||
Collections.sort(mHighUsageAppList, Collections.reverseOrder());
|
||||
mHighUsageAppList = mHighUsageAppList.subList(0,
|
||||
Math.min(mPolicy.highUsageAppCount, mHighUsageAppList.size()));
|
||||
Collections.sort(mHighUsageAppList, Collections.reverseOrder());
|
||||
}
|
||||
}
|
||||
|
||||
return new HighUsageTip(screenUsageTimeMs, mHighUsageAppList);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void parseBatteryData() {
|
||||
BatteryInfo.parse(mBatteryStatsHelper.getStats(), mDataParser);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,7 +280,7 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController
|
||||
.setButton1Text(R.string.forget)
|
||||
.setButton1Positive(false)
|
||||
.setButton1OnClickListener(view -> forgetNetwork())
|
||||
.setButton2Text(R.string.support_sign_in_button_text)
|
||||
.setButton2Text(R.string.wifi_sign_in_button_text)
|
||||
.setButton2Positive(true)
|
||||
.setButton2OnClickListener(view -> signIntoNetwork());
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.net.ConnectivityManager;
|
||||
import android.net.wifi.WifiConfiguration;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.provider.Settings;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.support.v7.preference.PreferenceScreen;
|
||||
import android.text.BidiFormatter;
|
||||
|
||||
@@ -51,7 +52,11 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController
|
||||
private final WifiManager mWifiManager;
|
||||
private final Lifecycle mLifecycle;
|
||||
private WifiTetherSwitchBarController mSwitchController;
|
||||
private MasterSwitchPreference mPreference;
|
||||
private int mSoftApState;
|
||||
@VisibleForTesting
|
||||
MasterSwitchPreference mPreference;
|
||||
@VisibleForTesting
|
||||
WifiTetherSoftApManager mWifiTetherSoftApManager;
|
||||
|
||||
static {
|
||||
WIFI_TETHER_INTENT_FILTER = new IntentFilter(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
|
||||
@@ -60,6 +65,12 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController
|
||||
}
|
||||
|
||||
public WifiTetherPreferenceController(Context context, Lifecycle lifecycle) {
|
||||
this(context, lifecycle, true /* initSoftApManager */);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
WifiTetherPreferenceController(Context context, Lifecycle lifecycle,
|
||||
boolean initSoftApManager) {
|
||||
super(context);
|
||||
mConnectivityManager =
|
||||
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
@@ -69,6 +80,9 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController
|
||||
if (lifecycle != null) {
|
||||
lifecycle.addObserver(this);
|
||||
}
|
||||
if (initSoftApManager) {
|
||||
initWifiTetherSoftApManager();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -101,6 +115,9 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController
|
||||
if (mPreference != null) {
|
||||
mContext.registerReceiver(mReceiver, WIFI_TETHER_INTENT_FILTER);
|
||||
clearSummaryForAirplaneMode();
|
||||
if (mWifiTetherSoftApManager != null) {
|
||||
mWifiTetherSoftApManager.registerSoftApCallback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,9 +125,36 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController
|
||||
public void onStop() {
|
||||
if (mPreference != null) {
|
||||
mContext.unregisterReceiver(mReceiver);
|
||||
if (mWifiTetherSoftApManager != null) {
|
||||
mWifiTetherSoftApManager.unRegisterSoftApCallback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void initWifiTetherSoftApManager() {
|
||||
// This manager only handles the number of connected devices, other parts are handled by
|
||||
// normal BroadcastReceiver in this controller
|
||||
mWifiTetherSoftApManager = new WifiTetherSoftApManager(mWifiManager,
|
||||
new WifiTetherSoftApManager.WifiTetherSoftApCallback() {
|
||||
@Override
|
||||
public void onStateChanged(int state, int failureReason) {
|
||||
mSoftApState = state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNumClientsChanged(int numClients) {
|
||||
if (mPreference != null
|
||||
&& mSoftApState == WifiManager.WIFI_AP_STATE_ENABLED) {
|
||||
// Only show the number of clients when state is on
|
||||
mPreference.setSummary(mContext.getResources().getQuantityString(
|
||||
R.plurals.wifi_tether_connected_summary, numClients,
|
||||
numClients));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// Everything below is copied from WifiApEnabler
|
||||
//
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.android.settings.wifi.tether;
|
||||
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.Handler;
|
||||
|
||||
/**
|
||||
* Wrapper for {@link android.net.wifi.WifiManager.SoftApCallback} to pass the robo test
|
||||
*/
|
||||
public class WifiTetherSoftApManager {
|
||||
|
||||
private WifiManager mWifiManager;
|
||||
private WifiTetherSoftApCallback mWifiTetherSoftApCallback;
|
||||
|
||||
private WifiManager.SoftApCallback mSoftApCallback = new WifiManager.SoftApCallback() {
|
||||
@Override
|
||||
public void onStateChanged(int state, int failureReason) {
|
||||
mWifiTetherSoftApCallback.onStateChanged(state, failureReason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNumClientsChanged(int numClients) {
|
||||
mWifiTetherSoftApCallback.onNumClientsChanged(numClients);
|
||||
}
|
||||
};
|
||||
private Handler mHandler;
|
||||
|
||||
WifiTetherSoftApManager(WifiManager wifiManager,
|
||||
WifiTetherSoftApCallback wifiTetherSoftApCallback) {
|
||||
mWifiManager = wifiManager;
|
||||
mWifiTetherSoftApCallback = wifiTetherSoftApCallback;
|
||||
mHandler = new Handler();
|
||||
}
|
||||
|
||||
public void registerSoftApCallback() {
|
||||
mWifiManager.registerSoftApCallback(mSoftApCallback, mHandler);
|
||||
}
|
||||
|
||||
public void unRegisterSoftApCallback() {
|
||||
mWifiManager.unregisterSoftApCallback(mSoftApCallback);
|
||||
}
|
||||
|
||||
public interface WifiTetherSoftApCallback {
|
||||
void onStateChanged(int state, int failureReason);
|
||||
|
||||
void onNumClientsChanged(int numClients);
|
||||
}
|
||||
}
|
||||
@@ -29,11 +29,10 @@ public class RecoverySystemWrapper {
|
||||
/**
|
||||
* Returns whether wipe Euicc data successfully or not.
|
||||
*
|
||||
* @param isWipeEuicc whether we want to wipe Euicc data or not
|
||||
* @param packageName the package name of the caller app.
|
||||
*/
|
||||
public boolean wipeEuiccData(
|
||||
Context context, final boolean isWipeEuicc, final String packageName) {
|
||||
return RecoverySystem.wipeEuiccData(context, isWipeEuicc, packageName);
|
||||
Context context, final String packageName) {
|
||||
return RecoverySystem.wipeEuiccData(context, packageName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,20 +19,29 @@ package com.android.settings;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.robolectric.Shadows.shadowOf;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.app.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ScrollView;
|
||||
|
||||
@@ -46,6 +55,7 @@ import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadows.ShadowAccountManager;
|
||||
import org.robolectric.shadows.ShadowActivity;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
@@ -55,6 +65,9 @@ import org.robolectric.shadows.ShadowActivity;
|
||||
shadows = {ShadowUtils.class}
|
||||
)
|
||||
public class MasterClearTest {
|
||||
private static final String TEST_ACCOUNT_TYPE = "android.test.account.type";
|
||||
private static final String TEST_CONFIRMATION_PACKAGE = "android.test.confirmation.pkg";
|
||||
private static final String TEST_ACCOUNT_NAME = "test@example.com";
|
||||
|
||||
@Mock
|
||||
private MasterClear mMasterClear;
|
||||
@@ -62,7 +75,18 @@ public class MasterClearTest {
|
||||
private ScrollView mScrollView;
|
||||
@Mock
|
||||
private LinearLayout mLinearLayout;
|
||||
|
||||
@Mock
|
||||
private PackageManager mPackageManager;
|
||||
|
||||
@Mock
|
||||
private AccountManager mAccountManager;
|
||||
|
||||
@Mock
|
||||
private Activity mMockActivity;
|
||||
|
||||
private ShadowActivity mShadowActivity;
|
||||
private ShadowAccountManager mShadowAccountManager;
|
||||
private Activity mActivity;
|
||||
private View mContentView;
|
||||
|
||||
@@ -86,6 +110,7 @@ public class MasterClearTest {
|
||||
mMasterClear = spy(new MasterClear());
|
||||
mActivity = Robolectric.setupActivity(Activity.class);
|
||||
mShadowActivity = shadowOf(mActivity);
|
||||
// mShadowAccountManager = shadowOf(AccountManager.get(mActivity));
|
||||
mContentView = LayoutInflater.from(mActivity).inflate(R.layout.master_clear, null);
|
||||
|
||||
// Make scrollView only have one child
|
||||
@@ -93,6 +118,32 @@ public class MasterClearTest {
|
||||
when(mScrollView.getChildCount()).thenReturn(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShowFinalConfirmation_eraseEsimChecked() {
|
||||
ActivityForTest testActivity = new ActivityForTest();
|
||||
when(mMasterClear.getActivity()).thenReturn(testActivity);
|
||||
|
||||
mMasterClear.mEsimStorage = mContentView.findViewById(R.id.erase_esim);
|
||||
mMasterClear.mExternalStorage = mContentView.findViewById(R.id.erase_external);
|
||||
mMasterClear.mEsimStorage.setChecked(true);
|
||||
mMasterClear.showFinalConfirmation();
|
||||
assertThat(testActivity.getArgs().getBoolean(MasterClear.ERASE_ESIMS_EXTRA, false))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShowFinalConfirmation_eraseEsimUnchecked() {
|
||||
ActivityForTest testActivity = new ActivityForTest();
|
||||
when(mMasterClear.getActivity()).thenReturn(testActivity);
|
||||
|
||||
mMasterClear.mEsimStorage = mContentView.findViewById(R.id.erase_esim);
|
||||
mMasterClear.mExternalStorage = mContentView.findViewById(R.id.erase_external);
|
||||
mMasterClear.mEsimStorage.setChecked(false);
|
||||
mMasterClear.showFinalConfirmation();
|
||||
assertThat(testActivity.getArgs().getBoolean(MasterClear.ERASE_ESIMS_EXTRA, true))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShowWipeEuicc_euiccDisabled() {
|
||||
prepareEuiccState(
|
||||
@@ -162,9 +213,61 @@ public class MasterClearTest {
|
||||
|
||||
@Test
|
||||
public void testTryShowAccountConfirmation_unsupported() {
|
||||
doReturn(mActivity).when(mMasterClear).getActivity();
|
||||
/* Using the default resources, account confirmation shouldn't trigger */
|
||||
assertThat(mMasterClear.tryShowAccountConfirmation()).isFalse();
|
||||
when(mMasterClear.getActivity()).thenReturn(mActivity);
|
||||
/* Using the default resources, account confirmation shouldn't trigger */
|
||||
assertThat(mMasterClear.tryShowAccountConfirmation()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTryShowAccountConfirmation_no_relevant_accounts() {
|
||||
when(mMasterClear.getActivity()).thenReturn(mMockActivity);
|
||||
when(mMockActivity.getString(R.string.account_type)).thenReturn(TEST_ACCOUNT_TYPE);
|
||||
when(mMockActivity.getString(R.string.account_confirmation_package)).thenReturn(TEST_CONFIRMATION_PACKAGE);
|
||||
|
||||
Account[] accounts = new Account[0];
|
||||
when(mMockActivity.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mAccountManager);
|
||||
when(mAccountManager.getAccountsByType(TEST_ACCOUNT_TYPE)).thenReturn(accounts);
|
||||
assertThat(mMasterClear.tryShowAccountConfirmation()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTryShowAccountConfirmation_unresolved() {
|
||||
when(mMasterClear.getActivity()).thenReturn(mMockActivity);
|
||||
when(mMockActivity.getString(R.string.account_type)).thenReturn(TEST_ACCOUNT_TYPE);
|
||||
when(mMockActivity.getString(R.string.account_confirmation_package)).thenReturn(TEST_CONFIRMATION_PACKAGE);
|
||||
Account[] accounts = new Account[] { new Account(TEST_ACCOUNT_NAME, TEST_ACCOUNT_TYPE) };
|
||||
when(mMockActivity.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mAccountManager);
|
||||
when(mAccountManager.getAccountsByType(TEST_ACCOUNT_TYPE)).thenReturn(accounts);
|
||||
// The package manager should not resolve the confirmation intent targeting the non-existent
|
||||
// confirmation package.
|
||||
when(mMockActivity.getPackageManager()).thenReturn(mPackageManager);
|
||||
assertThat(mMasterClear.tryShowAccountConfirmation()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTryShowAccountConfirmation_ok() {
|
||||
when(mMasterClear.getActivity()).thenReturn(mMockActivity);
|
||||
// Only try to show account confirmation if the appropriate resource overlays are available.
|
||||
when(mMockActivity.getString(R.string.account_type)).thenReturn(TEST_ACCOUNT_TYPE);
|
||||
when(mMockActivity.getString(R.string.account_confirmation_package)).thenReturn(TEST_CONFIRMATION_PACKAGE);
|
||||
// Add accounts to trigger the search for a resolving intent.
|
||||
Account[] accounts = new Account[] { new Account(TEST_ACCOUNT_NAME, TEST_ACCOUNT_TYPE) };
|
||||
when(mMockActivity.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mAccountManager);
|
||||
when(mAccountManager.getAccountsByType(TEST_ACCOUNT_TYPE)).thenReturn(accounts);
|
||||
// The package manager should not resolve the confirmation intent targeting the non-existent
|
||||
// confirmation package.
|
||||
when(mMockActivity.getPackageManager()).thenReturn(mPackageManager);
|
||||
|
||||
ActivityInfo activityInfo = new ActivityInfo();
|
||||
activityInfo.packageName = TEST_CONFIRMATION_PACKAGE;
|
||||
ResolveInfo resolveInfo = new ResolveInfo();
|
||||
resolveInfo.activityInfo = activityInfo;
|
||||
when(mPackageManager.resolveActivity(any(), eq(0))).thenReturn(resolveInfo);
|
||||
|
||||
// Finally mock out the startActivityForResultCall
|
||||
doNothing().when(mMockActivity).startActivityForResult(any(), eq(MasterClear.CREDENTIAL_CONFIRM_REQUEST));
|
||||
|
||||
assertThat(mMasterClear.tryShowAccountConfirmation()).isTrue();
|
||||
}
|
||||
|
||||
private void initScrollView(int height, int scrollY, int childBottom) {
|
||||
|
||||
@@ -66,7 +66,7 @@ public class ResetNetworkConfirmTest {
|
||||
public void testResetNetworkData_resetEsim() {
|
||||
mResetNetworkConfirm.mEraseEsim = true;
|
||||
doReturn(true)
|
||||
.when(mRecoverySystem).wipeEuiccData(any(Context.class), anyBoolean(), anyString());
|
||||
.when(mRecoverySystem).wipeEuiccData(any(Context.class), anyString());
|
||||
|
||||
mResetNetworkConfirm.esimFactoryReset(mActivity, "" /* packageName */);
|
||||
try {
|
||||
@@ -77,7 +77,7 @@ public class ResetNetworkConfirmTest {
|
||||
}
|
||||
|
||||
Assert.assertNotNull(mResetNetworkConfirm.mEraseEsimTask);
|
||||
verify(mRecoverySystem).wipeEuiccData(any(Context.class), anyBoolean(), anyString());
|
||||
verify(mRecoverySystem).wipeEuiccData(any(Context.class), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -88,6 +88,6 @@ public class ResetNetworkConfirmTest {
|
||||
|
||||
Assert.assertNull(mResetNetworkConfirm.mEraseEsimTask);
|
||||
verify(mRecoverySystem, never())
|
||||
.wipeEuiccData(any(Context.class), anyBoolean(), anyString());
|
||||
.wipeEuiccData(any(Context.class), anyString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -38,6 +39,7 @@ import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.UserManager;
|
||||
import android.support.v14.preference.SwitchPreference;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.widget.Button;
|
||||
|
||||
import com.android.settings.R;
|
||||
@@ -92,12 +94,12 @@ public class BackgroundActivityPreferenceControllerTest {
|
||||
private DevicePolicyManager mDevicePolicyManager;
|
||||
@Mock
|
||||
private DevicePolicyManagerWrapper mDevicePolicyManagerWrapper;
|
||||
@Mock
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private AdvancedPowerUsageDetail mFragment;
|
||||
@Mock
|
||||
private PowerWhitelistBackend mPowerWhitelistBackend;
|
||||
private BackgroundActivityPreferenceController mController;
|
||||
private SwitchPreference mPreference;
|
||||
private Preference mPreference;
|
||||
private Context mShadowContext;
|
||||
private BatteryUtils mBatteryUtils;
|
||||
|
||||
@@ -125,7 +127,8 @@ public class BackgroundActivityPreferenceControllerTest {
|
||||
mBatteryUtils = spy(new BatteryUtils(mShadowContext));
|
||||
doNothing().when(mBatteryUtils).setForceAppStandby(anyInt(), anyString(), anyInt());
|
||||
|
||||
mPreference = new SwitchPreference(mShadowContext);
|
||||
mPreference = new Preference(mShadowContext);
|
||||
mPreference.setKey(BackgroundActivityPreferenceController.KEY_BACKGROUND_ACTIVITY);
|
||||
mController = spy(new BackgroundActivityPreferenceController(
|
||||
mContext, mFragment, UID_LOW_SDK, LOW_SDK_PACKAGE, mPowerWhitelistBackend));
|
||||
mController.mDpm = mDevicePolicyManagerWrapper;
|
||||
@@ -133,49 +136,33 @@ public class BackgroundActivityPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnPreferenceChange_TurnOnCheck_MethodInvoked() {
|
||||
mController.onPreferenceChange(mPreference, true);
|
||||
public void testHandlePreferenceTreeClick_restrictApp_showDialog() {
|
||||
doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager).checkOpNoThrow(anyInt(),
|
||||
anyInt(), anyString());
|
||||
|
||||
mController.handlePreferenceTreeClick(mPreference);
|
||||
|
||||
verify(mController).showDialog();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandlePreferenceTreeClick_unRestrictApp_setModeAllowed() {
|
||||
doReturn(AppOpsManager.MODE_IGNORED).when(mAppOpsManager).checkOpNoThrow(anyInt(),
|
||||
anyInt(), anyString());
|
||||
|
||||
mController.handlePreferenceTreeClick(mPreference);
|
||||
|
||||
verify(mBatteryUtils).setForceAppStandby(UID_LOW_SDK, LOW_SDK_PACKAGE,
|
||||
AppOpsManager.MODE_ALLOWED);
|
||||
assertThat(mPreference.getSummary())
|
||||
.isEqualTo(mShadowContext.getText(R.string.background_activity_summary_on));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnPreferenceChange_TurnOnCheckHighSDK_MethodInvoked() {
|
||||
mController = new BackgroundActivityPreferenceController(mContext, mFragment, UID_HIGH_SDK,
|
||||
HIGH_SDK_PACKAGE, mPowerWhitelistBackend);
|
||||
mController.mBatteryUtils = mBatteryUtils;
|
||||
|
||||
mController.onPreferenceChange(mPreference, true);
|
||||
|
||||
verify(mBatteryUtils).setForceAppStandby(UID_HIGH_SDK, HIGH_SDK_PACKAGE,
|
||||
AppOpsManager.MODE_ALLOWED);
|
||||
assertThat(mPreference.getSummary())
|
||||
.isEqualTo(mShadowContext.getText(R.string.background_activity_summary_on));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateState_CheckOn_SetCheckedTrue() {
|
||||
public void testUpdateState_noError_setEnabled() {
|
||||
when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK,
|
||||
LOW_SDK_PACKAGE)).thenReturn(AppOpsManager.MODE_ALLOWED);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mPreference.isChecked()).isTrue();
|
||||
assertThat(mPreference.isEnabled()).isTrue();
|
||||
verify(mController).updateSummary(mPreference);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateState_CheckOff_SetCheckedFalse() {
|
||||
when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK,
|
||||
LOW_SDK_PACKAGE)).thenReturn(AppOpsManager.MODE_IGNORED);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mPreference.isChecked()).isFalse();
|
||||
assertThat(mPreference.isEnabled()).isTrue();
|
||||
verify(mController).updateSummary(mPreference);
|
||||
}
|
||||
@@ -184,7 +171,6 @@ public class BackgroundActivityPreferenceControllerTest {
|
||||
public void testUpdateState_whitelisted() {
|
||||
when(mPowerWhitelistBackend.isWhitelisted(LOW_SDK_PACKAGE)).thenReturn(true);
|
||||
mController.updateState(mPreference);
|
||||
assertThat(mPreference.isChecked()).isTrue();
|
||||
assertThat(mPreference.isEnabled()).isFalse();
|
||||
assertThat(mPreference.getSummary()).isEqualTo(
|
||||
mShadowContext.getText(R.string.background_activity_summary_whitelisted));
|
||||
@@ -202,27 +188,23 @@ public class BackgroundActivityPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateSummary_modeDefault_showSummaryOn() {
|
||||
public void testUpdateSummary_modeDefault_showNotRestricted() {
|
||||
when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK,
|
||||
LOW_SDK_PACKAGE)).thenReturn(AppOpsManager.MODE_DEFAULT);
|
||||
final CharSequence expectedSummary = mShadowContext.getText(
|
||||
R.string.background_activity_summary_on);
|
||||
|
||||
mController.updateSummary(mPreference);
|
||||
|
||||
assertThat(mPreference.getSummary()).isEqualTo(expectedSummary);
|
||||
assertThat(mPreference.getSummary()).isEqualTo("No");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateSummary_modeIgnored_showSummaryOff() {
|
||||
public void testUpdateSummary_modeIgnored_showRestricted() {
|
||||
when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK,
|
||||
LOW_SDK_PACKAGE)).thenReturn(AppOpsManager.MODE_IGNORED);
|
||||
final CharSequence expectedSummary = mShadowContext.getText(
|
||||
R.string.background_activity_summary_off);
|
||||
|
||||
mController.updateSummary(mPreference);
|
||||
|
||||
assertThat(mPreference.getSummary()).isEqualTo(expectedSummary);
|
||||
assertThat(mPreference.getSummary()).isEqualTo("Yes");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -37,6 +37,7 @@ import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
@@ -54,7 +55,7 @@ public class BatteryTipLoaderTest {
|
||||
BatteryTip.TipType.BATTERY_SAVER,
|
||||
BatteryTip.TipType.LOW_BATTERY,
|
||||
BatteryTip.TipType.SUMMARY};
|
||||
@Mock
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private BatteryStatsHelper mBatteryStatsHelper;
|
||||
@Mock
|
||||
private PowerManager mPowerManager;
|
||||
|
||||
@@ -24,6 +24,7 @@ import static org.mockito.Mockito.spy;
|
||||
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
import android.text.format.DateUtils;
|
||||
|
||||
import com.android.settings.TestConfig;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
@@ -42,6 +43,8 @@ public class BatteryTipPolicyTest {
|
||||
+ ",battery_saver_tip_enabled=false"
|
||||
+ ",high_usage_enabled=true"
|
||||
+ ",high_usage_app_count=5"
|
||||
+ ",high_usage_period_ms=2000"
|
||||
+ ",high_usage_battery_draining=30"
|
||||
+ ",app_restriction_enabled=true"
|
||||
+ ",reduced_battery_enabled=true"
|
||||
+ ",reduced_battery_percent=30"
|
||||
@@ -66,6 +69,8 @@ public class BatteryTipPolicyTest {
|
||||
assertThat(batteryTipPolicy.batterySaverTipEnabled).isFalse();
|
||||
assertThat(batteryTipPolicy.highUsageEnabled).isTrue();
|
||||
assertThat(batteryTipPolicy.highUsageAppCount).isEqualTo(5);
|
||||
assertThat(batteryTipPolicy.highUsagePeriodMs).isEqualTo(2000);
|
||||
assertThat(batteryTipPolicy.highUsageBatteryDraining).isEqualTo(30);
|
||||
assertThat(batteryTipPolicy.appRestrictionEnabled).isTrue();
|
||||
assertThat(batteryTipPolicy.reducedBatteryEnabled).isTrue();
|
||||
assertThat(batteryTipPolicy.reducedBatteryPercent).isEqualTo(30);
|
||||
@@ -85,6 +90,8 @@ public class BatteryTipPolicyTest {
|
||||
assertThat(batteryTipPolicy.batterySaverTipEnabled).isTrue();
|
||||
assertThat(batteryTipPolicy.highUsageEnabled).isTrue();
|
||||
assertThat(batteryTipPolicy.highUsageAppCount).isEqualTo(3);
|
||||
assertThat(batteryTipPolicy.highUsagePeriodMs).isEqualTo(2 * DateUtils.HOUR_IN_MILLIS);
|
||||
assertThat(batteryTipPolicy.highUsageBatteryDraining).isEqualTo(25);
|
||||
assertThat(batteryTipPolicy.appRestrictionEnabled).isTrue();
|
||||
assertThat(batteryTipPolicy.reducedBatteryEnabled).isFalse();
|
||||
assertThat(batteryTipPolicy.reducedBatteryPercent).isEqualTo(50);
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.fuelgauge.batterytip;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.os.BatteryStats;
|
||||
import android.text.format.DateUtils;
|
||||
|
||||
import com.android.settings.TestConfig;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
@RunWith(SettingsRobolectricTestRunner.class)
|
||||
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
||||
public class HighUsageDataParserTest {
|
||||
private static final long PERIOD_ONE_MINUTE_MS = Duration.ofMinutes(1).toMillis();
|
||||
private static final long END_TIME_MS = 2 * PERIOD_ONE_MINUTE_MS;
|
||||
private static final int THRESHOLD_LOW = 10;
|
||||
private static final int THRESHOLD_HIGH = 20;
|
||||
private HighUsageDataParser mDataParser;
|
||||
private BatteryStats.HistoryItem mFirstItem;
|
||||
private BatteryStats.HistoryItem mSecondItem;
|
||||
private BatteryStats.HistoryItem mThirdItem;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mFirstItem = new BatteryStats.HistoryItem();
|
||||
mFirstItem.batteryLevel = 100;
|
||||
mFirstItem.currentTime = 0;
|
||||
mSecondItem = new BatteryStats.HistoryItem();
|
||||
mSecondItem.batteryLevel = 95;
|
||||
mSecondItem.currentTime = PERIOD_ONE_MINUTE_MS;
|
||||
mThirdItem = new BatteryStats.HistoryItem();
|
||||
mThirdItem.batteryLevel = 80;
|
||||
mThirdItem.currentTime = END_TIME_MS;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDataParser_thresholdLow_isHeavilyUsed() {
|
||||
mDataParser = new HighUsageDataParser(PERIOD_ONE_MINUTE_MS, THRESHOLD_LOW);
|
||||
parseData();
|
||||
|
||||
assertThat(mDataParser.isDeviceHeavilyUsed()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDataParser_thresholdHigh_notHeavilyUsed() {
|
||||
mDataParser = new HighUsageDataParser(PERIOD_ONE_MINUTE_MS, THRESHOLD_HIGH);
|
||||
parseData();
|
||||
|
||||
assertThat(mDataParser.isDeviceHeavilyUsed()).isFalse();
|
||||
}
|
||||
|
||||
private void parseData() {
|
||||
mDataParser.onParsingStarted(0, END_TIME_MS);
|
||||
mDataParser.onDataPoint(0, mFirstItem);
|
||||
mDataParser.onDataPoint(PERIOD_ONE_MINUTE_MS, mSecondItem);
|
||||
mDataParser.onDataPoint(END_TIME_MS, mThirdItem);
|
||||
|
||||
mDataParser.onParsingDone();
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,8 @@ package com.android.settings.fuelgauge.batterytip.detectors;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
@@ -31,6 +33,7 @@ import com.android.settings.TestConfig;
|
||||
import com.android.settings.fuelgauge.BatteryInfo;
|
||||
import com.android.settings.fuelgauge.BatteryUtils;
|
||||
import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
|
||||
import com.android.settings.fuelgauge.batterytip.HighUsageDataParser;
|
||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -55,6 +58,8 @@ public class HighUsageDetectorTest {
|
||||
private BatteryUtils mBatteryUtils;
|
||||
@Mock
|
||||
private BatterySipper mBatterySipper;
|
||||
@Mock
|
||||
private HighUsageDataParser mDataParser;
|
||||
|
||||
private BatteryTipPolicy mPolicy;
|
||||
private HighUsageDetector mHighUsageDetector;
|
||||
@@ -66,8 +71,10 @@ public class HighUsageDetectorTest {
|
||||
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mPolicy = spy(new BatteryTipPolicy(mContext));
|
||||
mHighUsageDetector = new HighUsageDetector(mContext, mPolicy, mBatteryStatsHelper);
|
||||
mHighUsageDetector = spy(new HighUsageDetector(mContext, mPolicy, mBatteryStatsHelper));
|
||||
mHighUsageDetector.mBatteryUtils = mBatteryUtils;
|
||||
mHighUsageDetector.mDataParser = mDataParser;
|
||||
doNothing().when(mHighUsageDetector).parseBatteryData();
|
||||
|
||||
mUsageList = new ArrayList<>();
|
||||
mUsageList.add(mBatterySipper);
|
||||
@@ -82,8 +89,7 @@ public class HighUsageDetectorTest {
|
||||
|
||||
@Test
|
||||
public void testDetect_containsHighUsageApp_tipVisible() {
|
||||
doReturn(2 * DateUtils.HOUR_IN_MILLIS).when(mBatteryUtils).calculateScreenUsageTime(
|
||||
mBatteryStatsHelper);
|
||||
doReturn(true).when(mDataParser).isDeviceHeavilyUsed();
|
||||
doReturn(mUsageList).when(mBatteryStatsHelper).getUsageList();
|
||||
doReturn(DateUtils.HOUR_IN_MILLIS).when(mBatteryUtils).getProcessTimeMs(
|
||||
BatteryUtils.StatusType.FOREGROUND, mBatterySipper.uidObj,
|
||||
|
||||
@@ -18,11 +18,15 @@ package com.android.settings.wifi.tether;
|
||||
|
||||
import static android.arch.lifecycle.Lifecycle.Event.ON_START;
|
||||
import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@@ -65,6 +69,7 @@ import java.util.ArrayList;
|
||||
shadows = {
|
||||
WifiTetherPreferenceControllerTest.ShadowWifiTetherSettings.class,
|
||||
WifiTetherPreferenceControllerTest.ShadowWifiTetherSwitchBarController.class,
|
||||
WifiTetherPreferenceControllerTest.ShadowWifiTetherSoftApManager.class
|
||||
})
|
||||
public class WifiTetherPreferenceControllerTest {
|
||||
|
||||
@@ -94,8 +99,9 @@ public class WifiTetherPreferenceControllerTest {
|
||||
when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager);
|
||||
when(mScreen.findPreference(anyString())).thenReturn(mPreference);
|
||||
|
||||
when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[] {"1", "2"});
|
||||
mController = new WifiTetherPreferenceController(mContext, mLifecycle);
|
||||
when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[]{"1", "2"});
|
||||
mController = new WifiTetherPreferenceController(mContext, mLifecycle,
|
||||
false /* initSoftApManager */);
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -105,8 +111,9 @@ public class WifiTetherPreferenceControllerTest {
|
||||
|
||||
@Test
|
||||
public void isAvailable_noTetherRegex_shouldReturnFalse() {
|
||||
when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[] {});
|
||||
mController = new WifiTetherPreferenceController(mContext, mLifecycle);
|
||||
when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[]{});
|
||||
mController = new WifiTetherPreferenceController(mContext, mLifecycle,
|
||||
false /* initSoftApManager */);
|
||||
|
||||
assertThat(mController.isAvailable()).isFalse();
|
||||
}
|
||||
@@ -244,6 +251,19 @@ public class WifiTetherPreferenceControllerTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Implements(WifiTetherSoftApManager.class)
|
||||
public static final class ShadowWifiTetherSoftApManager {
|
||||
@Implementation
|
||||
public void registerSoftApCallback() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Implementation
|
||||
public void unRegisterSoftApCallback() {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
@Implements(WifiTetherSwitchBarController.class)
|
||||
public static final class ShadowWifiTetherSwitchBarController {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user