diff --git a/AndroidManifest.xml b/AndroidManifest.xml index c1e0c608a2a..a275be0b753 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -726,7 +726,6 @@ android:label="@string/zen_mode_settings_title" android:icon="@drawable/ic_notifications" android:exported="true" - android:taskAffinity="com.android.settings" android:parentActivityName="Settings"> @@ -897,7 +896,6 @@ android:label="@string/night_display_title" android:enabled="@*android:bool/config_nightDisplayAvailable" android:icon="@drawable/ic_settings_night_display" - android:taskAffinity="com.android.settings" android:parentActivityName="Settings"> diff --git a/res/values/arrays.xml b/res/values/arrays.xml index 0a058d576cb..e57cbfa00f2 100644 --- a/res/values/arrays.xml +++ b/res/values/arrays.xml @@ -1119,4 +1119,18 @@ 0 + + + Off + Debug + Verbose + + + + + 0 + 2 + 4 + + diff --git a/res/values/bools.xml b/res/values/bools.xml index b4066059aa8..08bb08cafd7 100644 --- a/res/values/bools.xml +++ b/res/values/bools.xml @@ -32,6 +32,9 @@ Can be overridden for specific product builds. --> false + + true + false diff --git a/res/values/strings.xml b/res/values/strings.xml index 2bef192f82d..06f4123fe39 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -5091,6 +5091,9 @@ Limiting battery usage for %1$d apps + + Restricted %1$s + These apps have been using battery in the background. Restricted apps may not work properly and notifications may be delayed. @@ -9861,6 +9864,24 @@ ]]> + + Autofill + + + Logging level + + + Max partitions + + + Max visible datasets + + + Reset to default values + + + Autofill developer options have been reset + Device theme diff --git a/res/values/themes_suw.xml b/res/values/themes_suw.xml index 11ef373f486..f09b2b1427f 100644 --- a/res/values/themes_suw.xml +++ b/res/values/themes_suw.xml @@ -18,8 +18,8 @@ - - @@ -190,4 +190,16 @@ @style/PreferenceTheme @style/ThemeOverlay.SwitchBar.Settings + + + + \ No newline at end of file diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml index 3d5f198b3b3..b0c7c9c5e43 100644 --- a/res/xml/development_settings.xml +++ b/res/xml/development_settings.xml @@ -506,4 +506,29 @@ android:title="@string/reset_shortcut_manager_throttling" /> + + + + + + + + + + + + diff --git a/res/xml/power_usage_summary.xml b/res/xml/power_usage_summary.xml index 4ff67b7650d..b4db4ed155e 100644 --- a/res/xml/power_usage_summary.xml +++ b/res/xml/power_usage_summary.xml @@ -23,12 +23,16 @@ + android:layout="@layout/battery_header" + settings:controller="com.android.settings.fuelgauge.BatteryHeaderPreferenceController" /> + android:title="@string/summary_placeholder" + android:layout="@layout/preference_category_no_label" + settings:controller="com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController" /> + android:summary="@string/battery_percentage_description" + settings:controller="com.android.settings.display.BatteryPercentagePreferenceController" /> { View v = getLayoutInflater().inflate( @@ -59,6 +61,18 @@ public class FallbackHome extends Activity { getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON); }; + private final OnColorsChangedListener mColorsChangedListener = new OnColorsChangedListener() { + @Override + public void onColorsChanged(WallpaperColors colors, int which) { + if (colors != null) { + View decorView = getWindow().getDecorView(); + decorView.setSystemUiVisibility( + updateVisibilityFlagsFromColors(colors, decorView.getSystemUiVisibility())); + mWallManager.removeOnColorsChangedListener(this); + } + } + }; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -78,13 +92,17 @@ public class FallbackHome extends Activity { } // Set the system ui flags to light status bar if the wallpaper supports dark text to match - // current system ui color tints. - final WallpaperColors colors = getSystemService(WallpaperManager.class) - .getWallpaperColors(WallpaperManager.FLAG_SYSTEM); - if (colors != null - && (colors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) != 0) { - flags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR - | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; + // current system ui color tints. Use a listener to wait for colors if not ready yet. + mWallManager = getSystemService(WallpaperManager.class); + if (mWallManager == null) { + Log.w(TAG, "Wallpaper manager isn't ready, can't listen to color changes!"); + } else { + WallpaperColors colors = mWallManager.getWallpaperColors(WallpaperManager.FLAG_SYSTEM); + if (colors == null) { + mWallManager.addOnColorsChangedListener(mColorsChangedListener, null /* handler */); + } else { + flags = updateVisibilityFlagsFromColors(colors, flags); + } } getWindow().getDecorView().setSystemUiVisibility(flags); @@ -109,6 +127,9 @@ public class FallbackHome extends Activity { protected void onDestroy() { super.onDestroy(); unregisterReceiver(mReceiver); + if (mWallManager != null) { + mWallManager.removeOnColorsChangedListener(mColorsChangedListener); + } } private BroadcastReceiver mReceiver = new BroadcastReceiver() { @@ -141,6 +162,15 @@ public class FallbackHome extends Activity { } } + private int updateVisibilityFlagsFromColors(WallpaperColors colors, int flags) { + if ((colors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) != 0) { + return flags | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR + | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; + } + return flags & ~(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) + & ~(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR); + } + private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { diff --git a/src/com/android/settings/dashboard/DashboardAdapter.java b/src/com/android/settings/dashboard/DashboardAdapter.java index d93f2ed5683..b278f60530e 100644 --- a/src/com/android/settings/dashboard/DashboardAdapter.java +++ b/src/com/android/settings/dashboard/DashboardAdapter.java @@ -315,7 +315,7 @@ public class DashboardAdapter extends RecyclerView.Adapter updateSummary()); + } + + @Override + public void onAttached() { + super.onAttached(); + + mObserver.register(); + updateSummary(); + } + + @Override + public void onDetached() { + mObserver.unregister(); + + super.onDetached(); + } + + private String getCurrentValue() { + final int value = Settings.Global.getInt(getContext().getContentResolver(), + mKey, mDefaultValue); + + return Integer.toString(value); + } + + private void updateSummary() { + setSummary(getCurrentValue()); + + } + + @Override + protected void onBindDialogView(View view) { + super.onBindDialogView(view); + + EditText editText = view.findViewById(android.R.id.edit); + if (editText != null) { + editText.setInputType(InputType.TYPE_CLASS_NUMBER); + editText.setText(getCurrentValue()); + Utils.setEditTextCursorPosition(editText); + } + } + + @Override + protected void onDialogClosed(boolean positiveResult) { + if (positiveResult) { + final String stringValue = getText(); + int newValue = mDefaultValue; + try { + newValue = Integer.parseInt(stringValue); + } catch (Exception e) { + Log.e(TAG, "Error converting '" + stringValue + "' to integer. Using " + + mDefaultValue + " instead"); + } + Settings.Global.putInt(getContext().getContentResolver(), mKey, newValue); + } + } +} diff --git a/src/com/android/settings/development/autofill/AutofillDeveloperSettingsObserver.java b/src/com/android/settings/development/autofill/AutofillDeveloperSettingsObserver.java new file mode 100644 index 00000000000..ae8e246b0b4 --- /dev/null +++ b/src/com/android/settings/development/autofill/AutofillDeveloperSettingsObserver.java @@ -0,0 +1,59 @@ +/* + * 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.development.autofill; + +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Handler; +import android.os.UserHandle; +import android.provider.Settings; + +final class AutofillDeveloperSettingsObserver extends ContentObserver { + + private final Runnable mChangeCallback; + private final ContentResolver mResolver; + + public AutofillDeveloperSettingsObserver(Context context, Runnable changeCallback) { + super(new Handler()); + + mResolver = context.getContentResolver(); + mChangeCallback = changeCallback; + } + + public void register() { + mResolver.registerContentObserver(Settings.Global.getUriFor( + Settings.Global.AUTOFILL_LOGGING_LEVEL), false, this, + UserHandle.USER_ALL); + mResolver.registerContentObserver(Settings.Global.getUriFor( + Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE), false, this, + UserHandle.USER_ALL); + mResolver.registerContentObserver(Settings.Global.getUriFor( + Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS), false, this, + UserHandle.USER_ALL); + } + + public void unregister() { + mResolver.unregisterContentObserver(this); + } + + @Override + public void onChange(boolean selfChange, Uri uri, int userId) { + mChangeCallback.run(); // Run Forrest, Run! + } +} diff --git a/src/com/android/settings/development/autofill/AutofillLoggingLevelPreferenceController.java b/src/com/android/settings/development/autofill/AutofillLoggingLevelPreferenceController.java new file mode 100644 index 00000000000..a22295c557f --- /dev/null +++ b/src/com/android/settings/development/autofill/AutofillLoggingLevelPreferenceController.java @@ -0,0 +1,100 @@ +/* + * 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.development.autofill; + +import android.content.Context; +import android.content.res.Resources; +import android.provider.Settings; +import android.view.autofill.AutofillManager; + +import com.android.settings.R; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settingslib.development.DeveloperOptionsPreferenceController; + +import androidx.preference.ListPreference; +import androidx.preference.Preference; + +public final class AutofillLoggingLevelPreferenceController + extends DeveloperOptionsPreferenceController + implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener { + + private static final String AUTOFILL_LOGGING_LEVEL_KEY = "autofill_logging_level"; + + private final String[] mListValues; + private final String[] mListSummaries; + private final AutofillDeveloperSettingsObserver mObserver; + + public AutofillLoggingLevelPreferenceController(Context context) { + super(context); + + Resources resources = context.getResources(); + mListValues = resources.getStringArray(R.array.autofill_logging_level_values); + mListSummaries = resources.getStringArray(R.array.autofill_logging_level_entries); + mObserver = new AutofillDeveloperSettingsObserver(mContext, () -> updateOptions()); + mObserver.register(); + // TODO: there should be a hook on AbstractPreferenceController where we could unregister it + } + + @Override + public String getPreferenceKey() { + return AUTOFILL_LOGGING_LEVEL_KEY; + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + writeLevel(newValue); + updateOptions(); + return true; + } + + @Override + public void updateState(Preference preference) { + updateOptions(); + } + + @Override + protected void onDeveloperOptionsSwitchDisabled() { + super.onDeveloperOptionsSwitchDisabled(); + writeLevel(null); + } + + private void updateOptions() { + final int level = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.AUTOFILL_LOGGING_LEVEL, AutofillManager.DEFAULT_LOGGING_LEVEL); + + final int index; + if (level == AutofillManager.FLAG_ADD_CLIENT_DEBUG) { + index = 1; + } else if (level == AutofillManager.FLAG_ADD_CLIENT_VERBOSE) { + index = 2; + } else { + index = 0; + } + final ListPreference listPreference = (ListPreference) mPreference; + listPreference.setValue(mListValues[index]); + listPreference.setSummary(mListSummaries[index]); + } + + private void writeLevel(Object newValue) { + int level = AutofillManager.NO_LOGGING; + if (newValue instanceof String) { + level = Integer.parseInt((String) newValue); + } + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.AUTOFILL_LOGGING_LEVEL, level); + } +} diff --git a/src/com/android/settings/development/autofill/AutofillMaxPartitionsPreference.java b/src/com/android/settings/development/autofill/AutofillMaxPartitionsPreference.java new file mode 100644 index 00000000000..ab0cec9338f --- /dev/null +++ b/src/com/android/settings/development/autofill/AutofillMaxPartitionsPreference.java @@ -0,0 +1,30 @@ +/* + * 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.development.autofill; + +import android.content.Context; +import android.provider.Settings; +import android.util.AttributeSet; +import android.view.autofill.AutofillManager; + +import java.text.NumberFormat; + +public final class AutofillMaxPartitionsPreference extends AbstractGlobalSettingsPreference { + + public AutofillMaxPartitionsPreference(Context context, AttributeSet attrs) { + super(context, attrs, Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE, + AutofillManager.DEFAULT_MAX_PARTITIONS_SIZE); + } +} diff --git a/src/com/android/settings/development/autofill/AutofillPreferenceCategory.java b/src/com/android/settings/development/autofill/AutofillPreferenceCategory.java new file mode 100644 index 00000000000..ed07f629070 --- /dev/null +++ b/src/com/android/settings/development/autofill/AutofillPreferenceCategory.java @@ -0,0 +1,85 @@ +/* + * 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.development.autofill; + +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Handler; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.AttributeSet; +import android.util.Log; +import android.view.autofill.AutofillManager; + +import androidx.preference.PreferenceCategory; + +public final class AutofillPreferenceCategory extends PreferenceCategory { + + private static final String TAG = "AutofillPreferenceCategory"; + + private final ContentResolver mContentResolver; + private final ContentObserver mSettingsObserver; + + public AutofillPreferenceCategory(Context context, AttributeSet attrs) { + super(context, attrs); + + mSettingsObserver = new ContentObserver(new Handler()) { + @Override + public void onChange(boolean selfChange, Uri uri, int userId) { + Log.w(TAG, "Autofill Service changed, but UI cannot be refreshed"); + // TODO(b/111838239): we cannot update the UI because AFM.isEnabled() will return + // the previous value. Once that's fixed, we'll need to call one of the 2 callbacks + // below: + // notifyChanged(); + // notifyDependencyChange(shouldDisableDependents()); + } + }; + mContentResolver = context.getContentResolver(); + } + + @Override + public void onAttached() { + super.onAttached(); + + mContentResolver.registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.AUTOFILL_SERVICE), false, + mSettingsObserver); + } + + @Override + public void onDetached() { + mContentResolver.unregisterContentObserver(mSettingsObserver); + + super.onDetached(); + } + + // PreferenceCategory.isEnabled() always return false, so we rather not change that logic + // decide whether the children should be shown using isAutofillEnabled() instead. + private boolean isAutofillEnabled() { + final AutofillManager afm = getContext().getSystemService(AutofillManager.class); + final boolean enabled = afm != null && afm.isEnabled(); + Log.v(TAG, "isAutofillEnabled(): " + enabled); + return enabled; + } + + @Override + public boolean shouldDisableDependents() { + final boolean shouldIt = !isAutofillEnabled(); + Log.v(TAG, "shouldDisableDependents(): " + shouldIt); + return shouldIt; + } +} diff --git a/src/com/android/settings/development/autofill/AutofillResetOptionsPreferenceController.java b/src/com/android/settings/development/autofill/AutofillResetOptionsPreferenceController.java new file mode 100644 index 00000000000..42f7a48e923 --- /dev/null +++ b/src/com/android/settings/development/autofill/AutofillResetOptionsPreferenceController.java @@ -0,0 +1,64 @@ +/* + * 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.development.autofill; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.res.Resources; +import android.provider.Settings; +import android.text.TextUtils; +import android.view.autofill.AutofillManager; +import android.widget.Toast; + +import com.android.settings.R; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settingslib.development.DeveloperOptionsPreferenceController; + +import androidx.preference.ListPreference; +import androidx.preference.Preference; + +public final class AutofillResetOptionsPreferenceController + extends DeveloperOptionsPreferenceController + implements PreferenceControllerMixin { + + private static final String AUTOFILL_RESET_OPTIONS_KEY = "autofill_reset_developer_options"; + + public AutofillResetOptionsPreferenceController(Context context) { + super(context); + } + + @Override + public String getPreferenceKey() { + return AUTOFILL_RESET_OPTIONS_KEY; + } + + @Override + public boolean handlePreferenceTreeClick(Preference preference) { + if (!TextUtils.equals(AUTOFILL_RESET_OPTIONS_KEY, preference.getKey())) { + return false; + } + final ContentResolver contentResolver = mContext.getContentResolver(); + Settings.Global.putInt(contentResolver, Settings.Global.AUTOFILL_LOGGING_LEVEL, + AutofillManager.DEFAULT_LOGGING_LEVEL); + Settings.Global.putInt(contentResolver, Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE, + AutofillManager.DEFAULT_MAX_PARTITIONS_SIZE); + Settings.Global.putInt(contentResolver, Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS, 0); + Toast.makeText(mContext, R.string.autofill_reset_developer_options_complete, + Toast.LENGTH_SHORT).show(); + return true; + } +} diff --git a/src/com/android/settings/development/autofill/AutofillVisibleDatasetsPreference.java b/src/com/android/settings/development/autofill/AutofillVisibleDatasetsPreference.java new file mode 100644 index 00000000000..2f0d15f9fb3 --- /dev/null +++ b/src/com/android/settings/development/autofill/AutofillVisibleDatasetsPreference.java @@ -0,0 +1,26 @@ +/* + * 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.development.autofill; + +import android.content.Context; +import android.provider.Settings; +import android.util.AttributeSet; + +public final class AutofillVisibleDatasetsPreference extends AbstractGlobalSettingsPreference { + + public AutofillVisibleDatasetsPreference(Context context, AttributeSet attrs) { + super(context, attrs, Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS, 0); + } +} diff --git a/src/com/android/settings/deviceinfo/DeviceNamePreferenceController.java b/src/com/android/settings/deviceinfo/DeviceNamePreferenceController.java index 0d4df995058..558e7cbff00 100644 --- a/src/com/android/settings/deviceinfo/DeviceNamePreferenceController.java +++ b/src/com/android/settings/deviceinfo/DeviceNamePreferenceController.java @@ -27,6 +27,7 @@ import android.text.SpannedString; import com.android.settings.bluetooth.BluetoothLengthDeviceNameFilter; import com.android.settings.core.BasePreferenceController; +import com.android.settings.R; import com.android.settings.widget.ValidatedEditTextPreference; import com.android.settings.wifi.tether.WifiDeviceNameTextValidator; import com.android.settingslib.bluetooth.LocalBluetoothAdapter; @@ -90,7 +91,9 @@ public class DeviceNamePreferenceController extends BasePreferenceController @Override public int getAvailabilityStatus() { - return AVAILABLE; + return mContext.getResources().getBoolean(R.bool.config_show_device_name) + ? AVAILABLE + : UNSUPPORTED_ON_DEVICE; } @Override diff --git a/src/com/android/settings/display/BatteryPercentagePreferenceController.java b/src/com/android/settings/display/BatteryPercentagePreferenceController.java index 0bd4445f180..50fbc649fb7 100644 --- a/src/com/android/settings/display/BatteryPercentagePreferenceController.java +++ b/src/com/android/settings/display/BatteryPercentagePreferenceController.java @@ -21,8 +21,8 @@ import android.content.Context; import android.provider.Settings; import com.android.internal.R; +import com.android.settings.core.BasePreferenceController; import com.android.settings.core.PreferenceControllerMixin; -import com.android.settingslib.core.AbstractPreferenceController; import androidx.preference.Preference; import androidx.preference.SwitchPreference; @@ -31,24 +31,18 @@ import androidx.preference.SwitchPreference; * A controller to manage the switch for showing battery percentage in the status bar. */ -public class BatteryPercentagePreferenceController extends AbstractPreferenceController implements +public class BatteryPercentagePreferenceController extends BasePreferenceController implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener { - private static final String KEY_BATTERY_PERCENTAGE = "battery_percentage"; - - public BatteryPercentagePreferenceController(Context context) { - super(context); + public BatteryPercentagePreferenceController(Context context, String preferenceKey) { + super(context, preferenceKey); } @Override - public boolean isAvailable() { - return mContext.getResources() - .getBoolean(R.bool.config_battery_percentage_setting_available); - } - - @Override - public String getPreferenceKey() { - return KEY_BATTERY_PERCENTAGE; + public int getAvailabilityStatus() { + return mContext.getResources().getBoolean( + R.bool.config_battery_percentage_setting_available) ? AVAILABLE + : UNSUPPORTED_ON_DEVICE; } @Override diff --git a/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java index 71eb554dfe2..5b5f50afd18 100644 --- a/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java +++ b/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java @@ -27,6 +27,7 @@ import android.widget.TextView; import com.android.settings.R; import com.android.settings.applications.LayoutPreference; +import com.android.settings.core.BasePreferenceController; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.widget.EntityHeaderController; import com.android.settingslib.Utils; @@ -42,7 +43,7 @@ import androidx.preference.PreferenceScreen; /** * Controller that update the battery header view */ -public class BatteryHeaderPreferenceController extends AbstractPreferenceController +public class BatteryHeaderPreferenceController extends BasePreferenceController implements PreferenceControllerMixin, LifecycleObserver, OnStart { @VisibleForTesting static final String KEY_BATTERY_HEADER = "battery_header"; @@ -56,30 +57,35 @@ public class BatteryHeaderPreferenceController extends AbstractPreferenceControl @VisibleForTesting TextView mSummary2; - private final Activity mActivity; - private final PreferenceFragmentCompat mHost; - private final Lifecycle mLifecycle; + private Activity mActivity; + private PreferenceFragmentCompat mHost; + private Lifecycle mLifecycle; private final PowerManager mPowerManager; private LayoutPreference mBatteryLayoutPref; - public BatteryHeaderPreferenceController(Context context, Activity activity, - PreferenceFragmentCompat host, Lifecycle lifecycle) { - super(context); - mActivity = activity; - mHost = host; - mLifecycle = lifecycle; - if (mLifecycle != null) { - mLifecycle.addObserver(this); - } + public BatteryHeaderPreferenceController(Context context, String key) { + super(context, key); mPowerManager = context.getSystemService(PowerManager.class); } + public void setActivity(Activity activity) { + mActivity = activity; + } + + public void setFragment(PreferenceFragmentCompat fragment) { + mHost = fragment; + } + + public void setLifecycle(Lifecycle lifecycle) { + mLifecycle = lifecycle; + } + @Override public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); - mBatteryLayoutPref = (LayoutPreference) screen.findPreference(KEY_BATTERY_HEADER); - mBatteryMeterView = (BatteryMeterView) mBatteryLayoutPref + mBatteryLayoutPref = (LayoutPreference) screen.findPreference(getPreferenceKey()); + mBatteryMeterView = mBatteryLayoutPref .findViewById(R.id.battery_header_icon); mBatteryPercentText = mBatteryLayoutPref.findViewById(R.id.battery_percent); mSummary1 = mBatteryLayoutPref.findViewById(R.id.summary1); @@ -89,13 +95,8 @@ public class BatteryHeaderPreferenceController extends AbstractPreferenceControl } @Override - public boolean isAvailable() { - return true; - } - - @Override - public String getPreferenceKey() { - return KEY_BATTERY_HEADER; + public int getAvailabilityStatus() { + return AVAILABLE_UNSEARCHABLE; } @Override diff --git a/src/com/android/settings/fuelgauge/BatteryUtils.java b/src/com/android/settings/fuelgauge/BatteryUtils.java index 9e920c47978..3676761af78 100644 --- a/src/com/android/settings/fuelgauge/BatteryUtils.java +++ b/src/com/android/settings/fuelgauge/BatteryUtils.java @@ -38,11 +38,14 @@ import com.android.internal.os.BatterySipper; import com.android.internal.os.BatteryStatsHelper; import com.android.internal.util.ArrayUtils; import com.android.settings.R; +import com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper; import com.android.settings.fuelgauge.batterytip.AnomalyInfo; +import com.android.settings.fuelgauge.batterytip.BatteryDatabaseManager; import com.android.settings.fuelgauge.batterytip.StatsManagerConfig; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.fuelgauge.PowerWhitelistBackend; import com.android.settingslib.utils.PowerUtil; +import com.android.settingslib.utils.ThreadUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -101,8 +104,8 @@ public class BatteryUtils { mContext = context; mPackageManager = context.getPackageManager(); mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); - mPowerUsageFeatureProvider = FeatureFactory.getFactory( - context).getPowerUsageFeatureProvider(context); + mPowerUsageFeatureProvider = FeatureFactory.getFactory(context) + .getPowerUsageFeatureProvider(context); } public long getProcessTimeMs(@StatusType int type, @Nullable BatteryStats.Uid uid, @@ -400,6 +403,18 @@ public class BatteryUtils { } // Control whether app could run jobs in the background mAppOpsManager.setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName, mode); + + ThreadUtils.postOnBackgroundThread(() -> { + final BatteryDatabaseManager batteryDatabaseManager = BatteryDatabaseManager + .getInstance(mContext); + if (mode == AppOpsManager.MODE_IGNORED) { + batteryDatabaseManager.insertAction(AnomalyDatabaseHelper.ActionType.RESTRICTION, + uid, packageName, System.currentTimeMillis()); + } else if (mode == AppOpsManager.MODE_ALLOWED) { + batteryDatabaseManager.deleteAction(AnomalyDatabaseHelper.ActionType.RESTRICTION, + uid, packageName); + } + }); } public boolean isForceAppStandbyEnabled(int uid, String packageName) { diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java index 843aeec5a03..2ae58763ba4 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java +++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java @@ -25,7 +25,6 @@ import android.os.Bundle; import android.provider.SearchIndexableResource; import android.text.BidiFormatter; import android.text.format.Formatter; -import android.util.SparseArray; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; @@ -40,19 +39,15 @@ import com.android.settings.Utils; import com.android.settings.applications.LayoutPreference; import com.android.settings.core.SubSettingLauncher; import com.android.settings.dashboard.SummaryLoader; -import com.android.settings.display.BatteryPercentagePreferenceController; import com.android.settings.fuelgauge.batterytip.BatteryTipLoader; import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController; import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; import com.android.settings.overlay.FeatureFactory; import com.android.settings.search.BaseSearchIndexProvider; -import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.search.SearchIndexable; import com.android.settingslib.utils.PowerUtil; import com.android.settingslib.utils.StringUtil; -import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -197,6 +192,22 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList } }; + @Override + public void onAttach(Context context) { + super.onAttach(context); + final SettingsActivity activity = (SettingsActivity) getActivity(); + + mBatteryHeaderPreferenceController = use(BatteryHeaderPreferenceController.class); + mBatteryHeaderPreferenceController.setActivity(activity); + mBatteryHeaderPreferenceController.setFragment(this); + mBatteryHeaderPreferenceController.setLifecycle(getSettingsLifecycle()); + + mBatteryTipPreferenceController = use(BatteryTipPreferenceController.class); + mBatteryTipPreferenceController.setActivity(activity); + mBatteryTipPreferenceController.setFragment(this); + mBatteryTipPreferenceController.setBatteryTipListener(this::onBatteryTipHandled); + } + @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); @@ -231,22 +242,6 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList return R.xml.power_usage_summary; } - @Override - protected List createPreferenceControllers(Context context) { - final Lifecycle lifecycle = getSettingsLifecycle(); - final SettingsActivity activity = (SettingsActivity) getActivity(); - final List controllers = new ArrayList<>(); - mBatteryHeaderPreferenceController = new BatteryHeaderPreferenceController( - context, activity, this /* host */, lifecycle); - controllers.add(mBatteryHeaderPreferenceController); - mBatteryTipPreferenceController = new BatteryTipPreferenceController(context, - KEY_BATTERY_TIP, (SettingsActivity) getActivity(), this /* fragment */, this /* - BatteryTipListener */); - controllers.add(mBatteryTipPreferenceController); - controllers.add(new BatteryPercentagePreferenceController(context)); - return controllers; - } - @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { if (DEBUG) { diff --git a/src/com/android/settings/fuelgauge/RestrictedAppDetails.java b/src/com/android/settings/fuelgauge/RestrictedAppDetails.java index 4934bce700d..b64a70775ed 100644 --- a/src/com/android/settings/fuelgauge/RestrictedAppDetails.java +++ b/src/com/android/settings/fuelgauge/RestrictedAppDetails.java @@ -22,6 +22,8 @@ import android.content.pm.PackageManager; import android.os.Bundle; import android.os.UserHandle; import android.util.IconDrawableFactory; +import android.util.Log; +import android.util.SparseLongArray; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; @@ -29,7 +31,9 @@ import com.android.settings.Utils; import com.android.settings.core.InstrumentedPreferenceFragment; import com.android.settings.core.SubSettingLauncher; import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper; import com.android.settings.fuelgauge.batterytip.AppInfo; +import com.android.settings.fuelgauge.batterytip.BatteryDatabaseManager; import com.android.settings.fuelgauge.batterytip.BatteryTipDialogFragment; import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController; import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; @@ -37,6 +41,7 @@ import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip; import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip; import com.android.settings.widget.AppCheckBoxPreference; import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.utils.StringUtil; import com.android.settingslib.widget.FooterPreferenceMixinCompat; import java.util.List; @@ -57,6 +62,7 @@ public class RestrictedAppDetails extends DashboardFragment implements @VisibleForTesting static final String EXTRA_APP_INFO_LIST = "app_info_list"; private static final String KEY_PREF_RESTRICTED_APP_LIST = "restrict_app_list"; + private static final long TIME_NULL = -1; @VisibleForTesting List mAppInfos; @@ -68,6 +74,8 @@ public class RestrictedAppDetails extends DashboardFragment implements BatteryUtils mBatteryUtils; @VisibleForTesting PackageManager mPackageManager; + @VisibleForTesting + BatteryDatabaseManager mBatteryDatabaseManager; private final FooterPreferenceMixinCompat mFooterPreferenceMixin = new FooterPreferenceMixinCompat(this, getSettingsLifecycle()); @@ -96,6 +104,7 @@ public class RestrictedAppDetails extends DashboardFragment implements mPackageManager = context.getPackageManager(); mIconDrawableFactory = IconDrawableFactory.newInstance(context); mBatteryUtils = BatteryUtils.getInstance(context); + mBatteryDatabaseManager = BatteryDatabaseManager.getInstance(context); refreshUi(); } @@ -135,6 +144,9 @@ public class RestrictedAppDetails extends DashboardFragment implements void refreshUi() { mRestrictedAppListGroup.removeAll(); final Context context = getPrefContext(); + final SparseLongArray timestampArray = mBatteryDatabaseManager + .queryActionTime(AnomalyDatabaseHelper.ActionType.RESTRICTION); + final long now = System.currentTimeMillis(); for (int i = 0, size = mAppInfos.size(); i < size; i++) { final CheckBoxPreference checkBoxPreference = new AppCheckBoxPreference(context); @@ -158,9 +170,16 @@ public class RestrictedAppDetails extends DashboardFragment implements return false; }); + + final long timestamp = timestampArray.get(appInfo.uid, TIME_NULL); + if (timestamp != TIME_NULL) { + checkBoxPreference.setSummary(getString(R.string.restricted_app_time_summary, + StringUtil.formatRelativeTime(context, now - timestamp, false))); + } + final CharSequence test = checkBoxPreference.getSummaryOn(); mRestrictedAppListGroup.addPreference(checkBoxPreference); } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); + Log.e(TAG, "Can't find package: " + appInfo.packageName); } } } diff --git a/src/com/android/settings/fuelgauge/batterytip/AnomalyDatabaseHelper.java b/src/com/android/settings/fuelgauge/batterytip/AnomalyDatabaseHelper.java index bc332b6d765..bd1633faf5d 100644 --- a/src/com/android/settings/fuelgauge/batterytip/AnomalyDatabaseHelper.java +++ b/src/com/android/settings/fuelgauge/batterytip/AnomalyDatabaseHelper.java @@ -33,7 +33,7 @@ public class AnomalyDatabaseHelper extends SQLiteOpenHelper { private static final String TAG = "BatteryDatabaseHelper"; private static final String DATABASE_NAME = "battery_settings.db"; - private static final int DATABASE_VERSION = 4; + private static final int DATABASE_VERSION = 5; @Retention(RetentionPolicy.SOURCE) @IntDef({State.NEW, @@ -45,8 +45,15 @@ public class AnomalyDatabaseHelper extends SQLiteOpenHelper { int AUTO_HANDLED = 2; } + @Retention(RetentionPolicy.SOURCE) + @IntDef({ActionType.RESTRICTION}) + public @interface ActionType { + int RESTRICTION = 0; + } + public interface Tables { String TABLE_ANOMALY = "anomaly"; + String TABLE_ACTION = "action"; } public interface AnomalyColumns { @@ -91,6 +98,42 @@ public class AnomalyDatabaseHelper extends SQLiteOpenHelper { + AnomalyColumns.ANOMALY_STATE + "," + AnomalyColumns.TIME_STAMP_MS + ")" + ")"; + + public interface ActionColumns { + /** + * The package name of an app been performed an action + */ + String PACKAGE_NAME = "package_name"; + /** + * The uid of an app been performed an action + */ + String UID = "uid"; + /** + * The type of user action + * @see ActionType + */ + String ACTION_TYPE = "action_type"; + /** + * The time when action been performed + */ + String TIME_STAMP_MS = "time_stamp_ms"; + } + + private static final String CREATE_ACTION_TABLE = + "CREATE TABLE " + Tables.TABLE_ACTION + + "(" + + ActionColumns.UID + + " INTEGER NOT NULL, " + + ActionColumns.PACKAGE_NAME + + " TEXT, " + + ActionColumns.ACTION_TYPE + + " INTEGER NOT NULL, " + + ActionColumns.TIME_STAMP_MS + + " INTEGER NOT NULL, " + + " PRIMARY KEY (" + ActionColumns.ACTION_TYPE + "," + ActionColumns.UID + "," + + ActionColumns.PACKAGE_NAME + ")" + + ")"; + private static AnomalyDatabaseHelper sSingleton; public static synchronized AnomalyDatabaseHelper getInstance(Context context) { @@ -109,11 +152,6 @@ public class AnomalyDatabaseHelper extends SQLiteOpenHelper { bootstrapDB(db); } - private void bootstrapDB(SQLiteDatabase db) { - db.execSQL(CREATE_ANOMALY_TABLE); - Log.i(TAG, "Bootstrapped database"); - } - @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (oldVersion < DATABASE_VERSION) { @@ -137,7 +175,14 @@ public class AnomalyDatabaseHelper extends SQLiteOpenHelper { bootstrapDB(db); } + private void bootstrapDB(SQLiteDatabase db) { + db.execSQL(CREATE_ANOMALY_TABLE); + db.execSQL(CREATE_ACTION_TABLE); + Log.i(TAG, "Bootstrapped database"); + } + private void dropTables(SQLiteDatabase db) { db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_ANOMALY); + db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_ACTION); } } diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryDatabaseManager.java b/src/com/android/settings/fuelgauge/batterytip/BatteryDatabaseManager.java index 910b3680965..513244e39de 100644 --- a/src/com/android/settings/fuelgauge/batterytip/BatteryDatabaseManager.java +++ b/src/com/android/settings/fuelgauge/batterytip/BatteryDatabaseManager.java @@ -17,6 +17,8 @@ package com.android.settings.fuelgauge.batterytip; import static android.database.sqlite.SQLiteDatabase.CONFLICT_IGNORE; +import static android.database.sqlite.SQLiteDatabase.CONFLICT_REPLACE; + import static com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper.AnomalyColumns .ANOMALY_STATE; import static com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper.AnomalyColumns @@ -26,6 +28,7 @@ import static com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper.An import static com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper.AnomalyColumns .TIME_STAMP_MS; import static com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper.AnomalyColumns.UID; +import static com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper.Tables.TABLE_ACTION; import static com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper.Tables.TABLE_ANOMALY; import android.content.ContentValues; @@ -34,12 +37,15 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.text.TextUtils; import android.util.ArrayMap; +import android.util.SparseLongArray; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper.ActionColumns; + import androidx.annotation.VisibleForTesting; /** @@ -158,4 +164,65 @@ public class BatteryDatabaseManager { } } } + + /** + * Query latest timestamps when an app has been performed action {@code type} + * + * @param type of action been performed + * @return {@link SparseLongArray} where key is uid and value is timestamp + */ + public synchronized SparseLongArray queryActionTime( + @AnomalyDatabaseHelper.ActionType int type) { + final SparseLongArray timeStamps = new SparseLongArray(); + try (SQLiteDatabase db = mDatabaseHelper.getReadableDatabase()) { + final String[] projection = {ActionColumns.UID, ActionColumns.TIME_STAMP_MS}; + final String selection = ActionColumns.ACTION_TYPE + " = ? "; + final String[] selectionArgs = new String[]{String.valueOf(type)}; + + try (Cursor cursor = db.query(TABLE_ACTION, projection, selection, selectionArgs, + null /* groupBy */, null /* having */, null /* orderBy */)) { + final int uidIndex = cursor.getColumnIndex(ActionColumns.UID); + final int timestampIndex = cursor.getColumnIndex(ActionColumns.TIME_STAMP_MS); + + while (cursor.moveToNext()) { + final int uid = cursor.getInt(uidIndex); + final long timeStamp = cursor.getLong(timestampIndex); + timeStamps.append(uid, timeStamp); + } + } + } + + return timeStamps; + } + + /** + * Insert an action, or update it if already existed + */ + public synchronized boolean insertAction(@AnomalyDatabaseHelper.ActionType int type, + int uid, String packageName, long timestampMs) { + try (SQLiteDatabase db = mDatabaseHelper.getWritableDatabase()) { + final ContentValues values = new ContentValues(); + values.put(ActionColumns.UID, uid); + values.put(ActionColumns.PACKAGE_NAME, packageName); + values.put(ActionColumns.ACTION_TYPE, type); + values.put(ActionColumns.TIME_STAMP_MS, timestampMs); + return db.insertWithOnConflict(TABLE_ACTION, null, values, CONFLICT_REPLACE) != -1; + } + } + + /** + * Remove an action + */ + public synchronized boolean deleteAction(@AnomalyDatabaseHelper.ActionType int type, + int uid, String packageName) { + try (SQLiteDatabase db = mDatabaseHelper.getWritableDatabase()) { + final String where = + ActionColumns.ACTION_TYPE + " = ? AND " + ActionColumns.UID + " = ? AND " + + ActionColumns.PACKAGE_NAME + " = ? "; + final String[] whereArgs = new String[]{String.valueOf(type), String.valueOf(uid), + String.valueOf(packageName)}; + + return db.delete(TABLE_ACTION, where, whereArgs) != 0; + } + } } diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceController.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceController.java index 0a9a4c75259..158ffd47382 100644 --- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceController.java +++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceController.java @@ -59,24 +59,27 @@ public class BatteryTipPreferenceController extends BasePreferenceController { InstrumentedPreferenceFragment mFragment; public BatteryTipPreferenceController(Context context, String preferenceKey) { - this(context, preferenceKey, null, null, null); - } - - public BatteryTipPreferenceController(Context context, String preferenceKey, - SettingsActivity settingsActivity, InstrumentedPreferenceFragment fragment, - BatteryTipListener batteryTipListener) { super(context, preferenceKey); - mBatteryTipListener = batteryTipListener; mBatteryTipMap = new HashMap<>(); - mFragment = fragment; - mSettingsActivity = settingsActivity; mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider(); mNeedUpdate = true; } + public void setActivity(SettingsActivity activity) { + mSettingsActivity = activity; + } + + public void setFragment(InstrumentedPreferenceFragment fragment) { + mFragment = fragment; + } + + public void setBatteryTipListener(BatteryTipListener lsn) { + mBatteryTipListener = lsn; + } + @Override public int getAvailabilityStatus() { - return AVAILABLE; + return AVAILABLE_UNSEARCHABLE; } @Override diff --git a/src/com/android/settings/security/ChangeScreenLockPreferenceController.java b/src/com/android/settings/security/ChangeScreenLockPreferenceController.java index 156e230c6b5..1284b6ad6e0 100644 --- a/src/com/android/settings/security/ChangeScreenLockPreferenceController.java +++ b/src/com/android/settings/security/ChangeScreenLockPreferenceController.java @@ -84,8 +84,7 @@ public class ChangeScreenLockPreferenceController extends AbstractPreferenceCont @Override public void updateState(Preference preference) { if (mPreference != null && mPreference instanceof GearPreference) { - if (mLockPatternUtils.isSecure(mUserId) - || !mLockPatternUtils.isLockScreenDisabled(mUserId)) { + if (mLockPatternUtils.isSecure(mUserId)) { ((GearPreference) mPreference).setOnGearClickListener(this); } else { ((GearPreference) mPreference).setOnGearClickListener(null); diff --git a/src/com/android/settings/widget/AppCheckBoxPreference.java b/src/com/android/settings/widget/AppCheckBoxPreference.java index a0d95e94c39..bd643ba2481 100644 --- a/src/com/android/settings/widget/AppCheckBoxPreference.java +++ b/src/com/android/settings/widget/AppCheckBoxPreference.java @@ -17,11 +17,16 @@ package com.android.settings.widget; import android.content.Context; +import android.text.TextUtils; import android.util.AttributeSet; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; import com.android.settings.R; import androidx.preference.CheckBoxPreference; +import androidx.preference.PreferenceViewHolder; /** * {@link CheckBoxPreference} that used only to display app @@ -36,4 +41,20 @@ public class AppCheckBoxPreference extends CheckBoxPreference { super(context); setLayoutResource(R.layout.preference_app); } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + + final TextView appendix = (TextView) holder.findViewById(R.id.appendix); + if (appendix != null) { + appendix.setVisibility(View.GONE); + } + + final LinearLayout layout = (LinearLayout) holder.findViewById(R.id.summary_container); + if (layout != null) { + // If summary doesn't exist, make it gone + layout.setVisibility(TextUtils.isEmpty(getSummary()) ? View.GONE : View.VISIBLE); + } + } } diff --git a/src/com/android/settings/wifi/LinkablePreference.java b/src/com/android/settings/wifi/LinkablePreference.java index 9c00ee2218f..9581e7a6edb 100644 --- a/src/com/android/settings/wifi/LinkablePreference.java +++ b/src/com/android/settings/wifi/LinkablePreference.java @@ -23,12 +23,14 @@ import android.text.style.TextAppearanceSpan; import android.util.AttributeSet; import android.widget.TextView; -import com.android.settings.LinkifyUtils; - import androidx.annotation.Nullable; +import androidx.core.content.res.TypedArrayUtils; import androidx.preference.Preference; import androidx.preference.PreferenceViewHolder; +import com.android.settings.LinkifyUtils; +import com.android.settingslib.R; + /** * A preference with a title that can have linkable content on click. */ @@ -38,19 +40,20 @@ public class LinkablePreference extends Preference { private CharSequence mContentTitle; private CharSequence mContentDescription; + public LinkablePreference(Context ctx, AttributeSet attrs, int defStyle) { super(ctx, attrs, defStyle); + setIcon(R.drawable.ic_info_outline_24dp); setSelectable(false); } public LinkablePreference(Context ctx, AttributeSet attrs) { - super(ctx, attrs); - setSelectable(false); + this(ctx, attrs, TypedArrayUtils.getAttr( + ctx, R.attr.footerPreferenceStyle, android.R.attr.preferenceStyle)); } public LinkablePreference(Context ctx) { - super(ctx); - setSelectable(false); + this(ctx, null); } @Override @@ -75,21 +78,20 @@ public class LinkablePreference extends Preference { boolean linked = LinkifyUtils.linkify(textView, contentBuilder, mClickListener); if (linked && mContentTitle != null) { - // Embolden and enlarge the title. - Spannable boldSpan = (Spannable) textView.getText(); - boldSpan.setSpan( - new TextAppearanceSpan( - getContext(), android.R.style.TextAppearance_Medium), + Spannable spannableContent = (Spannable) textView.getText(); + spannableContent.setSpan( + new TextAppearanceSpan(getContext(), android.R.style.TextAppearance_Small), 0, mContentTitle.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); - textView.setText(boldSpan); + textView.setText(spannableContent); textView.setMovementMethod(new LinkMovementMethod()); } } /** * Sets the linkable text for the Preference title. + * * @param contentTitle text to set the Preference title. * @param contentDescription description text to append underneath title, can be null. * @param clickListener OnClickListener for the link portion of the text. diff --git a/tests/robotests/assets/grandfather_not_sharing_pref_controllers_with_search_provider b/tests/robotests/assets/grandfather_not_sharing_pref_controllers_with_search_provider index b3290725c98..e69de29bb2d 100644 --- a/tests/robotests/assets/grandfather_not_sharing_pref_controllers_with_search_provider +++ b/tests/robotests/assets/grandfather_not_sharing_pref_controllers_with_search_provider @@ -1 +0,0 @@ -com.android.settings.fuelgauge.PowerUsageSummary diff --git a/tests/robotests/res/values-mcc999/config.xml b/tests/robotests/res/values-mcc999/config.xml index 2d95b657905..6d82451bad9 100644 --- a/tests/robotests/res/values-mcc999/config.xml +++ b/tests/robotests/res/values-mcc999/config.xml @@ -61,6 +61,7 @@ false false true + false diff --git a/tests/robotests/src/com/android/settings/bluetooth/RemoteDeviceNameDialogFragmentTest.java b/tests/robotests/src/com/android/settings/bluetooth/RemoteDeviceNameDialogFragmentTest.java index 0ecd2952ddc..c9edc001264 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/RemoteDeviceNameDialogFragmentTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/RemoteDeviceNameDialogFragmentTest.java @@ -37,6 +37,7 @@ import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.testutils.FragmentTestUtils; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Answers; @@ -80,6 +81,7 @@ public class RemoteDeviceNameDialogFragmentTest { } @Test + @Ignore public void deviceNameDisplayIsCorrect() { String deviceName = "ABC Corp Headphones"; AlertDialog dialog = startDialog(deviceName); @@ -95,6 +97,7 @@ public class RemoteDeviceNameDialogFragmentTest { } @Test + @Ignore public void deviceNameEditSucceeds() { String deviceNameInitial = "ABC Corp Headphones"; String deviceNameModified = "My Headphones"; @@ -117,6 +120,7 @@ public class RemoteDeviceNameDialogFragmentTest { } @Test + @Ignore public void deviceNameEditThenCancelDoesntRename() { String deviceNameInitial = "ABC Corp Headphones"; String deviceNameModified = "My Headphones"; diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardAdapterTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardAdapterTest.java index 79be6cf54a5..c82343325b6 100644 --- a/tests/robotests/src/com/android/settings/dashboard/DashboardAdapterTest.java +++ b/tests/robotests/src/com/android/settings/dashboard/DashboardAdapterTest.java @@ -203,9 +203,9 @@ public class DashboardAdapterTest { new DashboardAdapter.DashboardItemHolder(view); final Tile tile = spy(new Tile(mActivityInfo)); doReturn(Icon.createWithResource(context, R.drawable.ic_settings)) - .when(tile).getIcon(); + .when(tile).getIcon(context); final IconCache iconCache = mock(IconCache.class); - when(iconCache.getIcon(tile.getIcon())) + when(iconCache.getIcon(tile.getIcon(context))) .thenReturn(context.getDrawable(R.drawable.ic_settings)); mDashboardAdapter = new DashboardAdapter(context, null /* savedInstanceState */, @@ -224,7 +224,7 @@ public class DashboardAdapterTest { new DashboardAdapter.DashboardItemHolder(view); final Tile tile = spy(new Tile(mActivityInfo)); final Icon icon = Icon.createWithResource(context, R.drawable.ic_settings); - doReturn(icon).when(tile).getIcon(); + doReturn(icon).when(tile).getIcon(context); final IconCache iconCache = new IconCache(context); @@ -235,7 +235,8 @@ public class DashboardAdapterTest { doReturn("another.package").when(context).getPackageName(); mDashboardAdapter.onBindTile(holder, tile); - assertThat(iconCache.getIcon(tile.getIcon())).isInstanceOf(RoundedHomepageIcon.class); + assertThat(iconCache.getIcon(tile.getIcon(context))) + .isInstanceOf(RoundedHomepageIcon.class); } @Test @@ -249,7 +250,7 @@ public class DashboardAdapterTest { tile.metaData.putInt(TileUtils.META_DATA_PREFERENCE_ICON_BACKGROUND_HINT, R.color.memory_critical); doReturn(Icon.createWithResource(context, R.drawable.ic_settings)) - .when(tile).getIcon(); + .when(tile).getIcon(context); final IconCache iconCache = new IconCache(context); mDashboardAdapter = new DashboardAdapter(context, null /* savedInstanceState */, null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */); @@ -259,7 +260,7 @@ public class DashboardAdapterTest { mDashboardAdapter.onBindTile(holder, tile); final RoundedHomepageIcon homepageIcon = (RoundedHomepageIcon) iconCache.getIcon( - tile.getIcon()); + tile.getIcon(context)); assertThat(homepageIcon.mBackgroundColor) .isEqualTo(RuntimeEnvironment.application.getColor(R.color.memory_critical)); } @@ -271,11 +272,11 @@ public class DashboardAdapterTest { final DashboardAdapter.DashboardItemHolder holder = new DashboardAdapter.DashboardItemHolder(view); final Tile tile = spy(new Tile(mActivityInfo)); - doReturn(mock(Icon.class)).when(tile).getIcon(); - when(tile.getIcon().getResPackage()).thenReturn("another.package"); + doReturn(mock(Icon.class)).when(tile).getIcon(context); + when(tile.getIcon(context).getResPackage()).thenReturn("another.package"); final IconCache iconCache = mock(IconCache.class); - when(iconCache.getIcon(tile.getIcon())).thenReturn(mock(RoundedHomepageIcon.class)); + when(iconCache.getIcon(tile.getIcon(context))).thenReturn(mock(RoundedHomepageIcon.class)); mDashboardAdapter = new DashboardAdapter(context, null /* savedInstanceState */, null /* conditions */, null /* suggestionControllerMixin */, null /* lifecycle */); @@ -283,7 +284,8 @@ public class DashboardAdapterTest { mDashboardAdapter.onBindTile(holder, tile); - verify(iconCache, never()).updateIcon(eq(tile.getIcon()), any(RoundedHomepageIcon.class)); + verify(iconCache, never()).updateIcon(eq(tile.getIcon(context)), + any(RoundedHomepageIcon.class)); } private List makeSuggestionsV2(String... pkgNames) { diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java index e67711981b1..963411d37ad 100644 --- a/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java +++ b/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java @@ -120,7 +120,7 @@ public class DashboardFeatureProviderImplTest { tile.title = "title"; tile.summary = "summary"; doReturn(Icon.createWithBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565))) - .when(tile).getIcon(); + .when(tile).getIcon(any(Context.class)); tile.metaData = new Bundle(); tile.metaData.putString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS, "HI"); tile.priority = 10; diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java index e7453fa4039..8c1b99b6e8e 100644 --- a/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java +++ b/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java @@ -17,6 +17,7 @@ package com.android.settings.dashboard; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -183,7 +184,7 @@ public class DashboardFragmentTest { @Test public void tintTileIcon_hasMetadata_shouldReturnIconTintableMetadata() { final Tile tile = spy(new Tile(mActivityInfo)); - doReturn(mock(Icon.class)).when(tile).getIcon(); + doReturn(mock(Icon.class)).when(tile).getIcon(any(Context.class)); final Bundle metaData = new Bundle(); tile.metaData = metaData; @@ -205,7 +206,7 @@ public class DashboardFragmentTest { @Test public void tintTileIcon_noMetadata_shouldReturnPackageNameCheck() { final Tile tile = spy(new Tile(mActivityInfo)); - doReturn(mock(Icon.class)).when(tile).getIcon(); + doReturn(mock(Icon.class)).when(tile).getIcon(any(Context.class)); final Intent intent = new Intent(); tile.intent = intent; intent.setComponent( diff --git a/tests/robotests/src/com/android/settings/development/autofill/AutofillLoggingLevelPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/autofill/AutofillLoggingLevelPreferenceControllerTest.java new file mode 100644 index 00000000000..b9da71d3f29 --- /dev/null +++ b/tests/robotests/src/com/android/settings/development/autofill/AutofillLoggingLevelPreferenceControllerTest.java @@ -0,0 +1,144 @@ +/* + * 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.development.autofill; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.res.Resources; +import android.os.RemoteException; +import android.view.autofill.AutofillManager; + +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.R; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; + +import androidx.preference.PreferenceScreen; +import androidx.preference.ListPreference; + +@RunWith(SettingsRobolectricTestRunner.class) +public class AutofillLoggingLevelPreferenceControllerTest { + + private static final int IDX_OFF = 0; + private static final int IDX_DEBUG = 1; + private static final int IDX_VERBOSE = 2; + + @Mock + private ListPreference mPreference; + @Mock + private PreferenceScreen mPreferenceScreen; + + private Context mContext; + private AutofillLoggingLevelPreferenceController mController; + private AutofillTestingHelper mHelper; + + private String[] mListValues; + private String[] mListSummaries; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); // TODO: use @Rule + mContext = RuntimeEnvironment.application; + mHelper = new AutofillTestingHelper(mContext); + final Resources resources = mContext.getResources(); + mListValues = resources.getStringArray(R.array.autofill_logging_level_values); + mListSummaries = resources.getStringArray(R.array.autofill_logging_level_entries); + mController = new AutofillLoggingLevelPreferenceController(mContext); + when(mPreferenceScreen.findPreference(mController.getPreferenceKey())) + .thenReturn(mPreference); + mController.displayPreference(mPreferenceScreen); + } + + @Test + public void handlePreferenceTreeClick_differentPreferenceKey_shouldNotTrigger() + throws Exception { + when(mPreference.getKey()).thenReturn("SomeRandomKey"); + + mHelper.setLoggingLevel(108); + + assertThat(mController.handlePreferenceTreeClick(mPreference)).isFalse(); + + assertThat(mHelper.getLoggingLevel()).isEqualTo(108); + } + + @Test + public void onPreferenceChange_off() throws Exception { + mHelper.setLoggingLevel(108); + + mController.onPreferenceChange(mPreference, mListValues[IDX_OFF]); + + assertThat(mHelper.getLoggingLevel()).isEqualTo(AutofillManager.NO_LOGGING); + } + + @Test + public void onPreferenceChange_debug() throws Exception { + mHelper.setLoggingLevel(108); + + mController.onPreferenceChange(mPreference, mListValues[IDX_DEBUG]); + + assertThat(mHelper.getLoggingLevel()) + .isEqualTo(AutofillManager.FLAG_ADD_CLIENT_DEBUG); + } + + @Test + public void onPreferenceChange_verbose() throws Exception { + mHelper.setLoggingLevel(108); + + mController.onPreferenceChange(mPreference, mListValues[IDX_VERBOSE]); + + assertThat(mHelper.getLoggingLevel()) + .isEqualTo(AutofillManager.FLAG_ADD_CLIENT_VERBOSE); + } + + @Test + public void onSettingsChange_off() throws Exception { + mHelper.setLoggingLevel(AutofillManager.NO_LOGGING); + + mController.updateState(mPreference); + + verify(mPreference).setValue(mListValues[IDX_OFF]); + verify(mPreference).setSummary(mListSummaries[IDX_OFF]); + } + + @Test + public void onSettingsChange_debug() throws Exception { + mHelper.setLoggingLevel(AutofillManager.FLAG_ADD_CLIENT_DEBUG); + + mController.updateState(mPreference); + + verify(mPreference).setValue(mListValues[IDX_DEBUG]); + verify(mPreference).setSummary(mListSummaries[IDX_DEBUG]); + } + + @Test + public void onSettingsChange_verbose() throws Exception { + mHelper.setLoggingLevel(AutofillManager.FLAG_ADD_CLIENT_VERBOSE); + + mController.updateState(mPreference); + + verify(mPreference).setValue(mListValues[IDX_VERBOSE]); + verify(mPreference).setSummary(mListSummaries[IDX_VERBOSE]); + } +} diff --git a/tests/robotests/src/com/android/settings/development/autofill/AutofillResetOptionsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/autofill/AutofillResetOptionsPreferenceControllerTest.java new file mode 100644 index 00000000000..e560a836d38 --- /dev/null +++ b/tests/robotests/src/com/android/settings/development/autofill/AutofillResetOptionsPreferenceControllerTest.java @@ -0,0 +1,94 @@ +/* + * 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.development.autofill; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.os.RemoteException; +import android.view.autofill.AutofillManager; + +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; + +import androidx.preference.PreferenceScreen; +import androidx.preference.SwitchPreference; + +@RunWith(SettingsRobolectricTestRunner.class) +public class AutofillResetOptionsPreferenceControllerTest { + + @Mock + private SwitchPreference mPreference; + @Mock + private PreferenceScreen mPreferenceScreen; + + private Context mContext; + private AutofillResetOptionsPreferenceController mController; + private AutofillTestingHelper mHelper; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); // TODO: use @Rule + mContext = RuntimeEnvironment.application; + mHelper = new AutofillTestingHelper(mContext); + mController = new AutofillResetOptionsPreferenceController(mContext); + when(mPreferenceScreen.findPreference(mController.getPreferenceKey())) + .thenReturn(mPreference); + mController.displayPreference(mPreferenceScreen); + } + + @Test + public void handlePreferenceTreeClick_differentPreferenceKey_shouldNotReset() throws Exception { + when(mPreference.getKey()).thenReturn("SomeRandomKey"); + + mHelper.setLoggingLevel(4); + mHelper.setMaxPartitionsSize(8); + mHelper.setMaxVisibleDatasets(15); + + assertThat(mController.handlePreferenceTreeClick(mPreference)).isFalse(); + + assertThat(mHelper.getLoggingLevel()).isEqualTo(4); + assertThat(mHelper.getMaxPartitionsSize()).isEqualTo(8); + assertThat(mHelper.getMaxVisibleDatasets()).isEqualTo(15); + } + + @Test + public void handlePreferenceTreeClick_correctPreferenceKey_shouldReset() throws Exception { + when(mPreference.getKey()).thenReturn(mController.getPreferenceKey()); + + mHelper.setMaxPartitionsSize(16); + mHelper.setMaxVisibleDatasets(23); + mHelper.setLoggingLevel(42); + + assertThat(mController.handlePreferenceTreeClick(mPreference)).isTrue(); + + assertThat(mHelper.getLoggingLevel()) + .isEqualTo(AutofillManager.DEFAULT_LOGGING_LEVEL); + assertThat(mHelper.getMaxPartitionsSize()) + .isEqualTo(AutofillManager.DEFAULT_MAX_PARTITIONS_SIZE); + assertThat(mHelper.getMaxVisibleDatasets()) + .isEqualTo(0); + } +} diff --git a/tests/robotests/src/com/android/settings/development/autofill/AutofillTestingHelper.java b/tests/robotests/src/com/android/settings/development/autofill/AutofillTestingHelper.java new file mode 100644 index 00000000000..59090bce743 --- /dev/null +++ b/tests/robotests/src/com/android/settings/development/autofill/AutofillTestingHelper.java @@ -0,0 +1,62 @@ +/* + * 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.development.autofill; + +import android.content.ContentResolver; +import android.content.Context; +import android.provider.Settings; +import android.provider.Settings.SettingNotFoundException; + +final class AutofillTestingHelper { + private final ContentResolver mResolver; + + public AutofillTestingHelper(Context context) { + mResolver = context.getContentResolver(); + } + + public void setLoggingLevel(int max) { + setGlobal(Settings.Global.AUTOFILL_LOGGING_LEVEL, max); + } + + public void setMaxPartitionsSize(int max) { + setGlobal(Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE, max); + } + + public void setMaxVisibleDatasets(int level) { + setGlobal(Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS, level); + } + + public int getLoggingLevel() throws SettingNotFoundException { + return getGlobal(Settings.Global.AUTOFILL_LOGGING_LEVEL); + } + + public int getMaxPartitionsSize() throws SettingNotFoundException { + return getGlobal(Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE); + } + + public int getMaxVisibleDatasets() throws SettingNotFoundException { + return getGlobal(Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS); + } + + private void setGlobal(String key, int value) { + Settings.Global.putInt(mResolver, key, value); + } + + private int getGlobal(String key) throws SettingNotFoundException { + return Settings.Global.getInt(mResolver, key); + } +} diff --git a/tests/robotests/src/com/android/settings/deviceinfo/DeviceNamePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/DeviceNamePreferenceControllerTest.java index 0e6bf8d3229..68b48a8203a 100644 --- a/tests/robotests/src/com/android/settings/deviceinfo/DeviceNamePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/deviceinfo/DeviceNamePreferenceControllerTest.java @@ -16,6 +16,8 @@ package com.android.settings.deviceinfo; +import static com.android.settings.core.BasePreferenceController.AVAILABLE; +import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -41,6 +43,7 @@ import org.mockito.Answers; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowApplication; import androidx.preference.PreferenceScreen; @@ -79,6 +82,17 @@ public class DeviceNamePreferenceControllerTest { mController.setLocalBluetoothManager(mBluetoothManager); } + @Test + public void getAvailibilityStatus_availableByDefault() { + assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); + } + + @Test + @Config(qualifiers = "mcc999") + public void getAvailabilityStatus_unsupportedWhenSet() { + assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE); + } + @Test public void constructor_defaultDeviceNameIsModelName() { assertThat(mController.getSummary()).isEqualTo(Build.MODEL); diff --git a/tests/robotests/src/com/android/settings/display/BatteryPercentagePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/BatteryPercentagePreferenceControllerTest.java index 673abb0e93a..140fc5cb222 100644 --- a/tests/robotests/src/com/android/settings/display/BatteryPercentagePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/display/BatteryPercentagePreferenceControllerTest.java @@ -32,13 +32,15 @@ import org.robolectric.RuntimeEnvironment; @RunWith(SettingsRobolectricTestRunner.class) public class BatteryPercentagePreferenceControllerTest { + private static final String PREF_KEY = "battery_percentage"; + private Context mContext; private BatteryPercentagePreferenceController mController; @Before public void setup() { mContext = RuntimeEnvironment.application; - mController = new BatteryPercentagePreferenceController(mContext); + mController = new BatteryPercentagePreferenceController(mContext, PREF_KEY); } private int getPercentageSetting() { diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryDatabaseManagerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryDatabaseManagerTest.java index 58bfe0eabfa..41e77e9a8b0 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryDatabaseManagerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryDatabaseManagerTest.java @@ -22,6 +22,7 @@ import static org.mockito.Mockito.spy; import android.content.Context; import android.text.format.DateUtils; +import android.util.SparseLongArray; import com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper; import com.android.settings.fuelgauge.batterytip.AppInfo; @@ -88,7 +89,7 @@ public class BatteryDatabaseManagerTest { } @Test - public void testAllFunctions() { + public void allAnomalyFunctions() { mBatteryDatabaseManager.insertAnomaly(UID_NEW, PACKAGE_NAME_NEW, TYPE_NEW, AnomalyDatabaseHelper.State.NEW, NOW); mBatteryDatabaseManager.insertAnomaly(UID_OLD, PACKAGE_NAME_OLD, TYPE_OLD, @@ -113,7 +114,7 @@ public class BatteryDatabaseManagerTest { } @Test - public void testUpdateAnomalies_updateSuccessfully() { + public void updateAnomalies_updateSuccessfully() { mBatteryDatabaseManager.insertAnomaly(UID_NEW, PACKAGE_NAME_NEW, TYPE_NEW, AnomalyDatabaseHelper.State.NEW, NOW); mBatteryDatabaseManager.insertAnomaly(UID_OLD, PACKAGE_NAME_OLD, TYPE_OLD, @@ -138,7 +139,7 @@ public class BatteryDatabaseManagerTest { } @Test - public void testQueryAnomalies_removeDuplicateByUid() { + public void queryAnomalies_removeDuplicateByUid() { mBatteryDatabaseManager.insertAnomaly(UID_NEW, PACKAGE_NAME_NEW, TYPE_NEW, AnomalyDatabaseHelper.State.NEW, NOW); mBatteryDatabaseManager.insertAnomaly(UID_NEW, PACKAGE_NAME_NEW, TYPE_OLD, @@ -149,4 +150,28 @@ public class BatteryDatabaseManagerTest { AnomalyDatabaseHelper.State.NEW); assertThat(newAppInfos).containsExactly(mCombinedAppInfo); } + + @Test + public void allActionFunctions() { + final long timestamp = System.currentTimeMillis(); + mBatteryDatabaseManager.insertAction(AnomalyDatabaseHelper.ActionType.RESTRICTION, UID_OLD, + PACKAGE_NAME_OLD, 0); + mBatteryDatabaseManager.insertAction(AnomalyDatabaseHelper.ActionType.RESTRICTION, UID_OLD, + PACKAGE_NAME_OLD, 1); + mBatteryDatabaseManager.insertAction(AnomalyDatabaseHelper.ActionType.RESTRICTION, UID_NEW, + PACKAGE_NAME_NEW, timestamp); + + final SparseLongArray timeArray = mBatteryDatabaseManager.queryActionTime( + AnomalyDatabaseHelper.ActionType.RESTRICTION); + assertThat(timeArray.size()).isEqualTo(2); + assertThat(timeArray.get(UID_OLD)).isEqualTo(1); + assertThat(timeArray.get(UID_NEW)).isEqualTo(timestamp); + + mBatteryDatabaseManager.deleteAction(AnomalyDatabaseHelper.ActionType.RESTRICTION, UID_NEW, + PACKAGE_NAME_NEW); + final SparseLongArray recentTimeArray = mBatteryDatabaseManager.queryActionTime( + AnomalyDatabaseHelper.ActionType.RESTRICTION); + assertThat(recentTimeArray.size()).isEqualTo(1); + assertThat(timeArray.get(UID_OLD)).isEqualTo(1); + } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java index 91cb078d548..337b950fe49 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java @@ -36,6 +36,7 @@ import android.widget.TextView; import com.android.settings.R; import com.android.settings.applications.LayoutPreference; +import com.android.settings.core.BasePreferenceController; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.shadow.SettingsShadowResources; import com.android.settings.testutils.shadow.SettingsShadowResourcesImpl; @@ -68,6 +69,7 @@ import androidx.recyclerview.widget.RecyclerView; }) public class BatteryHeaderPreferenceControllerTest { + private static final String PREF_KEY = "battery_header"; private static final int BATTERY_LEVEL = 60; private static final String TIME_LEFT = "2h30min"; private static final String BATTERY_STATUS = "Charging"; @@ -121,8 +123,11 @@ public class BatteryHeaderPreferenceControllerTest { mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); - mController = new BatteryHeaderPreferenceController( - mContext, mActivity, mPreferenceFragment, mLifecycle); + mController = new BatteryHeaderPreferenceController(mContext, PREF_KEY); + mLifecycle.addObserver(mController); + mController.setActivity(mActivity); + mController.setFragment(mPreferenceFragment); + mController.setLifecycle(mLifecycle); mController.mBatteryMeterView = mBatteryMeterView; mController.mBatteryPercentText = mBatteryPercentText; mController.mSummary1 = mSummary; @@ -207,4 +212,10 @@ public class BatteryHeaderPreferenceControllerTest { assertThat(mBatteryMeterView.getPowerSave()).isEqualTo(value); } } + + @Test + public void getAvailabilityStatus_returnAvailableUnsearchable() { + assertThat(mController.getAvailabilityStatus()).isEqualTo( + BasePreferenceController.AVAILABLE_UNSEARCHABLE); + } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java index df34b785e6c..b274492c75e 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java @@ -51,9 +51,12 @@ import android.text.format.DateUtils; import com.android.internal.os.BatterySipper; import com.android.internal.os.BatteryStatsHelper; +import com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper; import com.android.settings.fuelgauge.batterytip.AnomalyInfo; +import com.android.settings.fuelgauge.batterytip.BatteryDatabaseManager; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settings.testutils.shadow.ShadowThreadUtils; import com.android.settingslib.fuelgauge.PowerWhitelistBackend; import org.junit.Before; @@ -148,6 +151,8 @@ public class BatteryUtilsTest { private ApplicationInfo mLowApplicationInfo; @Mock private PowerWhitelistBackend mPowerWhitelistBackend; + @Mock + private BatteryDatabaseManager mBatteryDatabaseManager; private AnomalyInfo mAnomalyInfo; private BatteryUtils mBatteryUtils; private FakeFeatureFactory mFeatureFactory; @@ -225,6 +230,8 @@ public class BatteryUtilsTest { .thenReturn(TOTAL_BATTERY_USAGE + BATTERY_SCREEN_USAGE); when(mBatteryStatsHelper.getStats().getDischargeAmount(anyInt())) .thenReturn(DISCHARGE_AMOUNT); + BatteryDatabaseManager.setUpForTest(mBatteryDatabaseManager); + ShadowThreadUtils.setIsMainThread(true); } @Test @@ -569,6 +576,23 @@ public class BatteryUtilsTest { HIGH_SDK_PACKAGE, AppOpsManager.MODE_IGNORED); } + @Test + public void testSetForceAppStandby_restrictApp_recordTime() { + mBatteryUtils.setForceAppStandby(UID, HIGH_SDK_PACKAGE, AppOpsManager.MODE_IGNORED); + + verify(mBatteryDatabaseManager).insertAction( + eq(AnomalyDatabaseHelper.ActionType.RESTRICTION), eq(UID), + eq(HIGH_SDK_PACKAGE), anyLong()); + } + + @Test + public void testSetForceAppStandby_unrestrictApp_deleteTime() { + mBatteryUtils.setForceAppStandby(UID, HIGH_SDK_PACKAGE, AppOpsManager.MODE_ALLOWED); + + verify(mBatteryDatabaseManager).deleteAction(AnomalyDatabaseHelper.ActionType.RESTRICTION, + UID, HIGH_SDK_PACKAGE); + } + @Test public void testIsForceAppStandbyEnabled_enabled_returnTrue() { when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID, diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java index 97e272bf07d..68d9994cf9c 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java @@ -222,21 +222,6 @@ public class PowerUsageSummaryTest { assertThat(keys).containsAllIn(niks); } - @Test - public void preferenceControllers_getPreferenceKeys_existInPreferenceScreen() { - final Context context = RuntimeEnvironment.application; - final PowerUsageSummary fragment = new PowerUsageSummary(); - final List preferenceScreenKeys = - XmlTestUtils.getKeysFromPreferenceXml(context, fragment.getPreferenceScreenResId()); - final List preferenceKeys = new ArrayList<>(); - - for (AbstractPreferenceController controller : fragment.createPreferenceControllers(context)) { - preferenceKeys.add(controller.getPreferenceKey()); - } - - assertThat(preferenceScreenKeys).containsAllIn(preferenceKeys); - } - @Test public void restartBatteryTipLoader() { //TODO: add policy logic here when BatteryTipPolicy is implemented diff --git a/tests/robotests/src/com/android/settings/fuelgauge/RestrictedAppDetailsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/RestrictedAppDetailsTest.java index b9ed5098116..7219f183b60 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/RestrictedAppDetailsTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/RestrictedAppDetailsTest.java @@ -28,10 +28,13 @@ import android.content.pm.PackageManager; import android.os.Bundle; import android.os.UserHandle; import android.util.IconDrawableFactory; +import android.util.SparseLongArray; import com.android.settings.SettingsActivity; import com.android.settings.core.InstrumentedPreferenceFragment; +import com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper; import com.android.settings.fuelgauge.batterytip.AppInfo; +import com.android.settings.fuelgauge.batterytip.BatteryDatabaseManager; import com.android.settings.fuelgauge.batterytip.BatteryTipDialogFragment; import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip; @@ -52,6 +55,7 @@ import org.robolectric.annotation.Config; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.TimeUnit; import androidx.appcompat.app.AlertDialog; import androidx.preference.CheckBoxPreference; @@ -76,6 +80,8 @@ public class RestrictedAppDetailsTest { private IconDrawableFactory mIconDrawableFactory; @Mock private InstrumentedPreferenceFragment mFragment; + @Mock + private BatteryDatabaseManager mBatteryDatabaseManager; private PreferenceManager mPreferenceManager; private RestrictedAppDetails mRestrictedAppDetails; private Context mContext; @@ -98,12 +104,14 @@ public class RestrictedAppDetailsTest { doReturn(mPreferenceManager).when(mRestrictedAppDetails).getPreferenceManager(); doReturn(mContext).when(mFragment).getContext(); + doReturn(mContext).when(mRestrictedAppDetails).getContext(); mRestrictedAppDetails.mPackageManager = mPackageManager; mRestrictedAppDetails.mIconDrawableFactory = mIconDrawableFactory; mRestrictedAppDetails.mAppInfos = new ArrayList<>(); mRestrictedAppDetails.mAppInfos.add(mAppInfo); mRestrictedAppDetails.mRestrictedAppListGroup = spy(new PreferenceCategory(mContext)); mRestrictedAppDetails.mBatteryUtils = spy(new BatteryUtils(mContext)); + mRestrictedAppDetails.mBatteryDatabaseManager = mBatteryDatabaseManager; doReturn(mPreferenceManager).when( mRestrictedAppDetails.mRestrictedAppListGroup).getPreferenceManager(); @@ -118,6 +126,10 @@ public class RestrictedAppDetailsTest { doReturn(APP_NAME).when(mPackageManager).getApplicationLabel(mApplicationInfo); doReturn(true).when(mRestrictedAppDetails.mBatteryUtils).isForceAppStandbyEnabled(UID, PACKAGE_NAME); + final SparseLongArray timestampArray = new SparseLongArray(); + timestampArray.put(UID, System.currentTimeMillis() - TimeUnit.HOURS.toMillis(5)); + doReturn(timestampArray).when(mBatteryDatabaseManager) + .queryActionTime(AnomalyDatabaseHelper.ActionType.RESTRICTION); mRestrictedAppDetails.refreshUi(); @@ -126,6 +138,7 @@ public class RestrictedAppDetailsTest { (CheckBoxPreference) mRestrictedAppDetails.mRestrictedAppListGroup.getPreference(0); assertThat(preference.getTitle()).isEqualTo(APP_NAME); assertThat(preference.isChecked()).isTrue(); + assertThat(preference.getSummary()).isEqualTo("Restricted 5 hours ago"); } @Test diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceControllerTest.java index 7e9ffeb3d8d..e7aafef9792 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceControllerTest.java @@ -31,6 +31,7 @@ import android.text.format.DateUtils; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; import com.android.settings.SettingsActivity; +import com.android.settings.core.BasePreferenceController; import com.android.settings.core.InstrumentedPreferenceFragment; import com.android.settings.fuelgauge.batterytip.tips.BatteryTip; import com.android.settings.fuelgauge.batterytip.tips.SummaryTip; @@ -100,8 +101,7 @@ public class BatteryTipPreferenceControllerTest { mNewBatteryTips = new ArrayList<>(); mNewBatteryTips.add(new SummaryTip(BatteryTip.StateType.INVISIBLE, AVERAGE_TIME_MS)); - mBatteryTipPreferenceController = new BatteryTipPreferenceController(mContext, KEY_PREF, - mSettingsActivity, mFragment, mBatteryTipListener); + mBatteryTipPreferenceController = buildBatteryTipPreferenceController(); mBatteryTipPreferenceController.mPreferenceGroup = mPreferenceGroup; mBatteryTipPreferenceController.mPrefContext = mContext; } @@ -139,8 +139,7 @@ public class BatteryTipPreferenceControllerTest { final Bundle bundle = new Bundle(); mBatteryTipPreferenceController.saveInstanceState(bundle); - final BatteryTipPreferenceController controller = new BatteryTipPreferenceController( - mContext, KEY_PREF, mSettingsActivity, mFragment, mBatteryTipListener); + final BatteryTipPreferenceController controller = buildBatteryTipPreferenceController(); controller.mPreferenceGroup = mPreferenceGroup; controller.mPrefContext = mContext; controller.restoreInstanceState(bundle); @@ -154,8 +153,7 @@ public class BatteryTipPreferenceControllerTest { // Battery tip list is null at this time mBatteryTipPreferenceController.saveInstanceState(bundle); - final BatteryTipPreferenceController controller = new BatteryTipPreferenceController( - mContext, KEY_PREF, mSettingsActivity, mFragment, mBatteryTipListener); + final BatteryTipPreferenceController controller = buildBatteryTipPreferenceController(); // Should not crash controller.restoreInstanceState(bundle); @@ -176,6 +174,12 @@ public class BatteryTipPreferenceControllerTest { verify(mBatteryTipListener).onBatteryTipHandled(mBatteryTip); } + @Test + public void getAvailabilityStatus_returnAvailableUnsearchable() { + assertThat(mBatteryTipPreferenceController.getAvailabilityStatus()).isEqualTo( + BasePreferenceController.AVAILABLE_UNSEARCHABLE); + } + private void assertOnlyContainsSummaryTip(final PreferenceGroup preferenceGroup) { assertThat(preferenceGroup.getPreferenceCount()).isEqualTo(1); @@ -185,4 +189,14 @@ public class BatteryTipPreferenceControllerTest { assertThat(preference.getSummary()).isEqualTo( mContext.getString(R.string.battery_tip_summary_summary)); } + + private BatteryTipPreferenceController buildBatteryTipPreferenceController() { + final BatteryTipPreferenceController controller = new BatteryTipPreferenceController( + mContext, KEY_PREF); + controller.setActivity(mSettingsActivity); + controller.setFragment(mFragment); + controller.setBatteryTipListener(mBatteryTipListener); + + return controller; + } } diff --git a/tests/robotests/src/com/android/settings/security/ChangeScreenLockPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/security/ChangeScreenLockPreferenceControllerTest.java index 6cc1704bf39..fda5942b212 100644 --- a/tests/robotests/src/com/android/settings/security/ChangeScreenLockPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/security/ChangeScreenLockPreferenceControllerTest.java @@ -17,17 +17,28 @@ package com.android.settings.security; import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import android.app.admin.DevicePolicyManager; import android.content.Context; import android.os.UserManager; +import android.view.LayoutInflater; +import android.view.View; + +import androidx.preference.PreferenceScreen; +import androidx.preference.PreferenceViewHolder; import com.android.internal.widget.LockPatternUtils; +import com.android.settings.R; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.shadow.ShadowUtils; +import com.android.settings.widget.GearPreference; import org.junit.Before; import org.junit.Test; @@ -47,10 +58,15 @@ public class ChangeScreenLockPreferenceControllerTest { private UserManager mUserManager; @Mock private DevicePolicyManager mDevicePolicyManager; + @Mock + private PreferenceScreen mPreferenceScreen; private Context mContext; private FakeFeatureFactory mFeatureFactory; private ChangeScreenLockPreferenceController mController; + private View mGearView; + private GearPreference mGearPreference; + private PreferenceViewHolder mPreferenceViewHolder; @Before public void setUp() { @@ -75,4 +91,149 @@ public class ChangeScreenLockPreferenceControllerTest { public void testDeviceAdministrators_ifDisabled_shouldNotBeShown() { assertThat(mController.isAvailable()).isFalse(); } + + @Test + public void updateState_notSecureDisableKeyguard_shouldNotShowGear() { + when(mLockPatternUtils.isSecure(anyInt())).thenReturn(false); + when(mLockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(true); + mockGearPreferenceAndViewHolder(); + + showPreference(); + + assertThat(mGearView.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void updateState_notSecureDisableKeyguard_summaryShouldShowOff() { + when(mLockPatternUtils.isSecure(anyInt())).thenReturn(false); + when(mLockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(true); + mockGearPreferenceAndViewHolder(); + + showPreference(); + + assertThat(mGearPreference.getSummary()) + .isEqualTo(mContext.getString(R.string.unlock_set_unlock_mode_off)); + } + + @Test + public void updateState_notSecureWithSwipeKeyguard_shouldNotShowGear() { + when(mLockPatternUtils.isSecure(anyInt())).thenReturn(false); + when(mLockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false); + mockGearPreferenceAndViewHolder(); + + showPreference(); + + assertThat(mGearView.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void updateState_notSecureWithSwipeKeyguard_summaryShouldShowSwipe() { + when(mLockPatternUtils.isSecure(anyInt())).thenReturn(false); + when(mLockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false); + mockGearPreferenceAndViewHolder(); + + showPreference(); + + assertThat(mGearPreference.getSummary()) + .isEqualTo(mContext.getString(R.string.unlock_set_unlock_mode_none)); + } + + @Test + public void updateState_secureWithPinKeyguard_shouldShowGear() { + when(mLockPatternUtils.isSecure(anyInt())).thenReturn(true); + when(mLockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false); + doReturn(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX).when(mLockPatternUtils) + .getKeyguardStoredPasswordQuality(anyInt()); + mockGearPreferenceAndViewHolder(); + + showPreference(); + + assertThat(mGearView.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void updateState_secureWithPinKeyguard_summaryShouldShowPin() { + when(mLockPatternUtils.isSecure(anyInt())).thenReturn(true); + when(mLockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false); + doReturn(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX).when(mLockPatternUtils) + .getKeyguardStoredPasswordQuality(anyInt()); + + mockGearPreferenceAndViewHolder(); + + showPreference(); + + assertThat(mGearPreference.getSummary()) + .isEqualTo(mContext.getString(R.string.unlock_set_unlock_mode_pin)); + } + + @Test + public void updateState_secureWithPasswordKeyguard_shouldShowGear() { + when(mLockPatternUtils.isSecure(anyInt())).thenReturn(true); + when(mLockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false); + doReturn(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX).when(mLockPatternUtils) + .getKeyguardStoredPasswordQuality(anyInt()); + mockGearPreferenceAndViewHolder(); + + showPreference(); + + assertThat(mGearView.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void updateState_secureWithPasswordKeyguard_summaryShouldShowPassword() { + when(mLockPatternUtils.isSecure(anyInt())).thenReturn(true); + when(mLockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false); + doReturn(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX).when(mLockPatternUtils) + .getKeyguardStoredPasswordQuality(anyInt()); + mockGearPreferenceAndViewHolder(); + + showPreference(); + + assertThat(mGearPreference.getSummary()) + .isEqualTo(mContext.getString(R.string.unlock_set_unlock_mode_password)); + } + + @Test + public void updateState_secureWithPatternKeyguard_shouldShowGear() { + when(mLockPatternUtils.isSecure(anyInt())).thenReturn(true); + when(mLockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false); + doReturn(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING).when(mLockPatternUtils) + .getKeyguardStoredPasswordQuality(anyInt()); + mockGearPreferenceAndViewHolder(); + + showPreference(); + + assertThat(mGearView.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void updateState_secureWithPatternKeyguard_summaryShouldShowPattern() { + when(mLockPatternUtils.isSecure(anyInt())).thenReturn(true); + when(mLockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false); + doReturn(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING).when(mLockPatternUtils) + .getKeyguardStoredPasswordQuality(anyInt()); + mockGearPreferenceAndViewHolder(); + + showPreference(); + + assertThat(mGearPreference.getSummary()) + .isEqualTo(mContext.getString(R.string.unlock_set_unlock_mode_pattern)); + } + + private void mockGearPreferenceAndViewHolder() { + mGearPreference = new GearPreference(mContext, null); + mGearView = new View(mContext); + PreferenceViewHolder viewHolder = PreferenceViewHolder.createInstanceForTests( + LayoutInflater.from(mContext).inflate( + mGearPreference.getLayoutResource(), null, false)); + mPreferenceViewHolder = spy(viewHolder); + doReturn(mGearView).when(mPreferenceViewHolder).findViewById(R.id.settings_button); + when(mPreferenceScreen.findPreference(anyString())).thenReturn(mGearPreference); + } + + private void showPreference() { + mController.displayPreference(mPreferenceScreen); + mController.updateState(mGearPreference); + mGearPreference.onBindViewHolder(mPreferenceViewHolder); + } } \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/wallpaper/FallbackHomeActivityTest.java b/tests/robotests/src/com/android/settings/wallpaper/FallbackHomeActivityTest.java new file mode 100644 index 00000000000..57d77989496 --- /dev/null +++ b/tests/robotests/src/com/android/settings/wallpaper/FallbackHomeActivityTest.java @@ -0,0 +1,107 @@ +/* + * 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.wallpaper; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.when; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.WallpaperColors; +import android.app.WallpaperManager; +import android.app.WallpaperManager.OnColorsChangedListener; +import android.os.Handler; + +import com.android.settings.FallbackHome; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.robolectric.Robolectric; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.android.controller.ActivityController; +import org.robolectric.annotation.Config; +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; +import org.robolectric.shadow.api.Shadow; + +import java.util.ArrayList; +import java.util.List; + +/** + * Build/Install/Run: + * make RunSettingsRoboTests -j40 ROBOTEST_FILTER=FallbackHomeActivityTest + */ +@RunWith(SettingsRobolectricTestRunner.class) +public class FallbackHomeActivityTest { + + private ActivityController mController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mController = Robolectric.buildActivity(FallbackHome.class); + } + + @Test + @Config(shadows = ShadowWallpaperManager.class) + public void wallpaperColorsChangedListener_ensured_removed() { + // onCreate adds the first color listener by WallpaperManager returning null colors + ActivityController controller = mController.setup(); + ShadowWallpaperManager shadowManager = Shadow.extract(RuntimeEnvironment.application + .getSystemService(WallpaperManager.class)); + assertThat(shadowManager.size()).isEqualTo(1); + + // Assert onDestroy will remove the original listener + controller.destroy(); + assertThat(shadowManager.size()).isEqualTo(0); + } + + @Implements(WallpaperManager.class) + public static class ShadowWallpaperManager { + + private final List mListener = new ArrayList<>(); + + public int size() { + return mListener.size(); + } + + @Implementation + public boolean isWallpaperServiceEnabled() { + return true; + } + + @Implementation + public @Nullable WallpaperColors getWallpaperColors(int which) { + return null; + } + + @Implementation + public void addOnColorsChangedListener(@NonNull OnColorsChangedListener listener, + @NonNull Handler handler) { + mListener.add(listener); + } + + @Implementation + public void removeOnColorsChangedListener(@NonNull OnColorsChangedListener listener) { + mListener.remove(listener); + } + } +} diff --git a/tests/robotests/src/com/android/settings/widget/AppCheckBoxPreferenceTest.java b/tests/robotests/src/com/android/settings/widget/AppCheckBoxPreferenceTest.java index af867e6c011..e20520f3ea4 100644 --- a/tests/robotests/src/com/android/settings/widget/AppCheckBoxPreferenceTest.java +++ b/tests/robotests/src/com/android/settings/widget/AppCheckBoxPreferenceTest.java @@ -19,6 +19,8 @@ package com.android.settings.widget; import static com.google.common.truth.Truth.assertThat; import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; import com.android.settings.R; import com.android.settings.testutils.SettingsRobolectricTestRunner; @@ -28,18 +30,25 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RuntimeEnvironment; +import androidx.preference.PreferenceViewHolder; + @RunWith(SettingsRobolectricTestRunner.class) public class AppCheckBoxPreferenceTest { + private static final String SUMMARY = "summary info"; + private Context mContext; private AppCheckBoxPreference mPreference; private AppCheckBoxPreference mAttrPreference; + private PreferenceViewHolder mPreferenceViewHolder; @Before public void setUp() { mContext = RuntimeEnvironment.application; mPreference = new AppCheckBoxPreference(mContext); mAttrPreference = new AppCheckBoxPreference(mContext, null /* attrs */); + mPreferenceViewHolder = PreferenceViewHolder.createInstanceForTests( + LayoutInflater.from(mContext).inflate(R.layout.preference_app, null)); } @Test @@ -47,4 +56,32 @@ public class AppCheckBoxPreferenceTest { assertThat(mPreference.getLayoutResource()).isEqualTo(R.layout.preference_app); assertThat(mAttrPreference.getLayoutResource()).isEqualTo(R.layout.preference_app); } + + @Test + public void onBindViewHolder_noSummary_layoutGone() { + mPreference.setSummary(""); + + mPreference.onBindViewHolder(mPreferenceViewHolder); + + assertThat(mPreferenceViewHolder.findViewById(R.id.summary_container).getVisibility()) + .isEqualTo(View.GONE); + } + + @Test + public void onBindViewHolder_hasSummary_layoutVisible() { + mPreference.setSummary(SUMMARY); + + mPreference.onBindViewHolder(mPreferenceViewHolder); + + assertThat(mPreferenceViewHolder.findViewById(R.id.summary_container).getVisibility()) + .isEqualTo(View.VISIBLE); + } + + @Test + public void onBindViewHolder_appendixGone() { + mPreference.onBindViewHolder(mPreferenceViewHolder); + + assertThat(mPreferenceViewHolder.findViewById(R.id.appendix).getVisibility()) + .isEqualTo(View.GONE); + } } diff --git a/tests/unit/src/com/android/settings/core/PreferenceControllerContractTest.java b/tests/unit/src/com/android/settings/core/PreferenceControllerContractTest.java index 176d3c44ab5..59f22dbdce0 100644 --- a/tests/unit/src/com/android/settings/core/PreferenceControllerContractTest.java +++ b/tests/unit/src/com/android/settings/core/PreferenceControllerContractTest.java @@ -19,6 +19,7 @@ package com.android.settings.core; import static junit.framework.Assert.fail; import android.content.Context; +import android.os.Looper; import android.platform.test.annotations.Presubmit; import android.support.test.InstrumentationRegistry; import android.support.test.filters.MediumTest; @@ -52,6 +53,7 @@ public class PreferenceControllerContractTest { @Test @Presubmit public void controllersInSearchShouldImplementPreferenceControllerMixin() { + Looper.prepare(); // Required by AutofillLoggingLevelPreferenceController final Set errorClasses = new ArraySet<>(); final SearchIndexableResources resources =