summaryrefslogtreecommitdiffstats
path: root/src/com
diff options
context:
space:
mode:
Diffstat (limited to 'src/com')
-rw-r--r--src/com/android/settings/SettingsActivity.java6
-rw-r--r--src/com/android/settings/Utils.java9
-rw-r--r--src/com/android/settings/accessibility/AccessibilitySearchFeatureProvider.java37
-rw-r--r--src/com/android/settings/accessibility/AccessibilitySearchFeatureProviderImpl.java34
-rw-r--r--src/com/android/settings/accessibility/AccessibilitySettings.java13
-rw-r--r--src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java65
-rw-r--r--src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java10
-rw-r--r--src/com/android/settings/accessibility/ReduceBrightColorsIntensityPreferenceController.java12
-rw-r--r--src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java10
-rw-r--r--src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java2
-rw-r--r--src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java2
-rw-r--r--src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java2
-rw-r--r--src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java2
-rw-r--r--src/com/android/settings/accounts/AvatarViewMixin.java9
-rw-r--r--src/com/android/settings/applications/AppStateAlarmsAndRemindersBridge.java14
-rw-r--r--src/com/android/settings/applications/HibernatedAppsPreferenceController.java12
-rw-r--r--src/com/android/settings/applications/RunningServices.java6
-rw-r--r--src/com/android/settings/applications/appinfo/AlarmsAndRemindersDetailPreferenceController.java75
-rw-r--r--src/com/android/settings/applications/appinfo/AlarmsAndRemindersDetails.java27
-rw-r--r--src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java64
-rwxr-xr-xsrc/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java13
-rw-r--r--src/com/android/settings/applications/intentpicker/AppLaunchSettings.java23
-rw-r--r--src/com/android/settings/applications/manageapplications/ManageApplications.java98
-rw-r--r--src/com/android/settings/biometrics/BiometricEnrollActivity.java283
-rw-r--r--src/com/android/settings/biometrics/BiometricEnrollBase.java21
-rw-r--r--src/com/android/settings/biometrics/BiometricEnrollIntroduction.java122
-rw-r--r--src/com/android/settings/biometrics/BiometricHandoffActivity.java75
-rw-r--r--src/com/android/settings/biometrics/BiometricUtils.java13
-rw-r--r--src/com/android/settings/biometrics/ParentalConsentHelper.java168
-rw-r--r--src/com/android/settings/biometrics/ParentalControlsUtils.java80
-rw-r--r--src/com/android/settings/biometrics/combination/CombinedBiometricStatusPreferenceController.java33
-rw-r--r--src/com/android/settings/biometrics/face/BiometricLockscreenBypassPreferenceController.java41
-rw-r--r--src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java107
-rw-r--r--src/com/android/settings/biometrics/face/FaceEnrollParentalConsent.java110
-rw-r--r--src/com/android/settings/biometrics/face/FaceSettingsLockscreenBypassPreferenceController.java4
-rw-r--r--src/com/android/settings/biometrics/face/FaceStatusPreferenceController.java32
-rw-r--r--src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java89
-rw-r--r--src/com/android/settings/biometrics/fingerprint/FingerprintEnrollParentalConsent.java104
-rw-r--r--src/com/android/settings/biometrics/fingerprint/FingerprintErrorDialog.java2
-rw-r--r--src/com/android/settings/biometrics/fingerprint/FingerprintStatusPreferenceController.java32
-rw-r--r--src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java5
-rw-r--r--src/com/android/settings/core/CategoryMixin.java225
-rw-r--r--src/com/android/settings/core/SettingsBaseActivity.java247
-rw-r--r--src/com/android/settings/core/SubSettingLauncher.java5
-rw-r--r--src/com/android/settings/dashboard/DashboardFragment.java23
-rw-r--r--src/com/android/settings/datausage/AppDataUsage.java10
-rw-r--r--src/com/android/settings/datetime/timezone/BaseTimeZonePicker.java20
-rw-r--r--src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java5
-rw-r--r--src/com/android/settings/development/transcode/TranscodeNotificationPreferenceController.java2
-rw-r--r--src/com/android/settings/deviceinfo/storage/StorageAsyncLoader.java6
-rw-r--r--src/com/android/settings/display/AdaptiveSleepPreferenceController.java1
-rw-r--r--src/com/android/settings/display/ScreenTimeoutSettings.java1
-rw-r--r--src/com/android/settings/emergency/MoreSettingsPreferenceController.java9
-rw-r--r--src/com/android/settings/enterprise/ActionDisabledByAdminDialog.java2
-rw-r--r--src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelper.java11
-rw-r--r--src/com/android/settings/enterprise/DeviceAdminStringProviderImpl.java10
-rw-r--r--src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java7
-rw-r--r--src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java6
-rw-r--r--src/com/android/settings/fuelgauge/BatteryUtils.java18
-rw-r--r--src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarController.java20
-rw-r--r--src/com/android/settings/gestures/GesturePreferenceController.java5
-rw-r--r--src/com/android/settings/gestures/OneHandedActionPullDownPrefController.java101
-rw-r--r--src/com/android/settings/gestures/OneHandedActionShowNotificationPrefController.java101
-rw-r--r--src/com/android/settings/gestures/OneHandedEnablePreferenceController.java34
-rw-r--r--src/com/android/settings/gestures/OneHandedMainSwitchPreferenceController.java (renamed from src/com/android/settings/gestures/OneHandedAppTapsExitPreferenceController.java)63
-rw-r--r--src/com/android/settings/gestures/OneHandedSettings.java68
-rw-r--r--src/com/android/settings/gestures/OneHandedSettingsUtils.java40
-rw-r--r--src/com/android/settings/gestures/OneHandedTimeoutPreferenceController.java138
-rw-r--r--src/com/android/settings/gestures/PreventRingingGesturePreferenceController.java13
-rw-r--r--src/com/android/settings/gestures/SwipeBottomToNotificationPreferenceController.java71
-rw-r--r--src/com/android/settings/gestures/SwipeBottomToNotificationSettings.java59
-rw-r--r--src/com/android/settings/homepage/SettingsHomepageActivity.java34
-rw-r--r--src/com/android/settings/media/MediaDeviceUpdateWorker.java4
-rw-r--r--src/com/android/settings/media/RemoteMediaSlice.java6
-rw-r--r--src/com/android/settings/network/CarrierWifiTogglePreferenceController.java22
-rw-r--r--src/com/android/settings/network/NetworkDashboardFragment.java5
-rw-r--r--src/com/android/settings/network/NetworkProviderSettings.java35
-rw-r--r--src/com/android/settings/network/ProviderModelSlice.java86
-rw-r--r--src/com/android/settings/network/ProviderModelSliceHelper.java2
-rw-r--r--src/com/android/settings/network/SubscriptionUtil.java6
-rw-r--r--src/com/android/settings/network/SubscriptionsPreferenceController.java10
-rwxr-xr-xsrc/com/android/settings/network/apn/ApnSettings.java23
-rw-r--r--src/com/android/settings/network/telephony/NetworkProviderWorker.java2
-rw-r--r--src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java5
-rw-r--r--src/com/android/settings/notification/ConfigureNotificationSettings.java5
-rw-r--r--src/com/android/settings/notification/RemoteVolumeGroupController.java1
-rw-r--r--src/com/android/settings/notification/app/AppConversationListPreferenceController.java3
-rw-r--r--src/com/android/settings/notification/app/AppNotificationSettings.java31
-rw-r--r--src/com/android/settings/notification/app/ChannelListPreferenceController.java4
-rw-r--r--src/com/android/settings/notification/app/ChannelNotificationSettings.java6
-rw-r--r--src/com/android/settings/notification/app/ConversationListPreferenceController.java4
-rw-r--r--src/com/android/settings/notification/app/ConversationNotificationSettings.java4
-rw-r--r--src/com/android/settings/notification/app/HeaderPreferenceController.java36
-rw-r--r--src/com/android/settings/notification/app/RecentConversationsPreferenceController.java2
-rw-r--r--src/com/android/settings/notification/zen/AbstractZenModeAutomaticRulePreferenceController.java54
-rw-r--r--src/com/android/settings/notification/zen/ZenRulePreference.java2
-rw-r--r--src/com/android/settings/overlay/FeatureFactory.java6
-rw-r--r--src/com/android/settings/overlay/FeatureFactoryImpl.java11
-rw-r--r--src/com/android/settings/panel/InternetConnectivityPanel.java68
-rw-r--r--src/com/android/settings/password/BiometricFragment.java20
-rw-r--r--src/com/android/settings/password/ChooseLockPattern.java3
-rw-r--r--src/com/android/settings/password/ConfirmDeviceCredentialActivity.java11
-rw-r--r--src/com/android/settings/search/SearchFeatureProvider.java5
-rw-r--r--src/com/android/settings/sim/ChooseSimActivity.java19
-rw-r--r--src/com/android/settings/widget/LoadingViewController.java68
-rw-r--r--src/com/android/settings/widget/SeekBarPreference.java11
-rw-r--r--src/com/android/settings/widget/SettingsMainSwitchPreference.java12
-rw-r--r--src/com/android/settings/wifi/WifiPickerTrackerHelper.java6
108 files changed, 2633 insertions, 1186 deletions
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 7dd5fe40e4..12f63ea226 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -36,10 +36,8 @@ import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
-import android.util.FeatureFlagUtils;
import android.util.Log;
import android.view.View;
-import android.view.Window;
import android.widget.Button;
import androidx.annotation.Nullable;
@@ -55,7 +53,6 @@ import androidx.preference.PreferenceManager;
import com.android.internal.util.ArrayUtils;
import com.android.settings.Settings.WifiSettingsActivity;
import com.android.settings.applications.manageapplications.ManageApplications;
-import com.android.settings.core.FeatureFlags;
import com.android.settings.core.OnActivityResultListener;
import com.android.settings.core.SettingsBaseActivity;
import com.android.settings.core.SubSettingLauncher;
@@ -70,7 +67,6 @@ import com.android.settingslib.core.instrumentation.SharedPreferencesLogger;
import com.android.settingslib.development.DevelopmentSettingsEnabler;
import com.android.settingslib.drawer.DashboardCategory;
-import com.google.android.material.transition.platform.MaterialSharedAxis;
import com.google.android.setupcompat.util.WizardManagerHelper;
import java.util.ArrayList;
@@ -689,7 +685,7 @@ public class SettingsActivity extends SettingsBaseActivity
if (somethingChanged) {
Log.d(LOG_TAG, "Enabled state changed for some tiles, reloading all categories "
+ changedList.toString());
- updateCategories();
+ mCategoryMixin.updateCategories();
} else {
Log.d(LOG_TAG, "No enabled state changed, skipping updateCategory call");
}
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index 4f0515c02d..708dbeddec 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -77,7 +77,6 @@ import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.Profile;
import android.provider.ContactsContract.RawContacts;
-import android.provider.Settings;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.Spannable;
@@ -164,9 +163,6 @@ public final class Utils extends com.android.settingslib.Utils {
public static final String PROPERTY_HIBERNATION_TARGETS_PRE_S_APPS =
"app_hibernation_targets_pre_s_apps";
- /** Whether or not Settings Shared Axis transition is enabled */
- public static final String SETTINGS_SHARED_AXIS_ENABLED = "settings_shared_axis_enabled";
-
/**
* Finds a matching activity for a preference's intent. If a matching
* activity is not found, it will remove the preference.
@@ -1225,9 +1221,4 @@ public final class Utils extends com.android.settingslib.Utils {
public static boolean isProviderModelEnabled(Context context) {
return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
}
-
- public static boolean isPageTransitionEnabled(Context context) {
- return Settings.Global.getInt(context.getContentResolver(),
- SETTINGS_SHARED_AXIS_ENABLED, 0) == 1;
- }
}
diff --git a/src/com/android/settings/accessibility/AccessibilitySearchFeatureProvider.java b/src/com/android/settings/accessibility/AccessibilitySearchFeatureProvider.java
new file mode 100644
index 0000000000..6aa8c841ed
--- /dev/null
+++ b/src/com/android/settings/accessibility/AccessibilitySearchFeatureProvider.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021 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.accessibility;
+
+import android.content.Context;
+
+import com.android.settingslib.search.SearchIndexableRaw;
+
+import java.util.List;
+
+/**
+ * Provider for Accessibility Search related features.
+ */
+public interface AccessibilitySearchFeatureProvider {
+
+ /**
+ * Returns a list of raw data for indexing. See {@link SearchIndexableRaw}
+ *
+ * @param context a valid context {@link Context} instance
+ * @return a list of {@link SearchIndexableRaw} references. Can be null.
+ */
+ List<SearchIndexableRaw> getSearchIndexableRawData(Context context);
+}
diff --git a/src/com/android/settings/accessibility/AccessibilitySearchFeatureProviderImpl.java b/src/com/android/settings/accessibility/AccessibilitySearchFeatureProviderImpl.java
new file mode 100644
index 0000000000..c358af11d0
--- /dev/null
+++ b/src/com/android/settings/accessibility/AccessibilitySearchFeatureProviderImpl.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2021 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.accessibility;
+
+import android.content.Context;
+
+import com.android.settingslib.search.SearchIndexableRaw;
+
+import java.util.List;
+
+/**
+ * Provider implementation for Accessibility Search related features.
+ */
+public class AccessibilitySearchFeatureProviderImpl implements AccessibilitySearchFeatureProvider {
+
+ @Override
+ public List<SearchIndexableRaw> getSearchIndexableRawData(Context context) {
+ return null;
+ }
+}
diff --git a/src/com/android/settings/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java
index d4db3953f7..78bea0f00a 100644
--- a/src/com/android/settings/accessibility/AccessibilitySettings.java
+++ b/src/com/android/settings/accessibility/AccessibilitySettings.java
@@ -50,12 +50,14 @@ import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.accessibility.AccessibilityUtil.AccessibilityServiceFragmentType;
import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.RestrictedPreference;
import com.android.settingslib.accessibility.AccessibilityUtils;
import com.android.settingslib.search.SearchIndexable;
+import com.android.settingslib.search.SearchIndexableRaw;
import java.util.ArrayList;
import java.util.Collection;
@@ -494,7 +496,15 @@ public class AccessibilitySettings extends DashboardFragment {
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
- new BaseSearchIndexProvider(R.xml.accessibility_settings);
+ new BaseSearchIndexProvider(R.xml.accessibility_settings) {
+ @Override
+ public List<SearchIndexableRaw> getRawDataToIndex(Context context,
+ boolean enabled) {
+ return FeatureFactory.getFactory(context)
+ .getAccessibilitySearchFeatureProvider().getSearchIndexableRawData(
+ context);
+ }
+ };
/**
* This class helps setup RestrictedPreference.
@@ -559,7 +569,6 @@ public class AccessibilitySettings extends DashboardFragment {
setRestrictedPreferenceEnabled(preference, packageName, serviceAllowed,
serviceEnabled);
-
final String prefKey = preference.getKey();
final int imageRes = info.getAnimatedImageRes();
final CharSequence description = getServiceDescription(mContext, info,
diff --git a/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java b/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java
index 709e1657b7..127c7c68a3 100644
--- a/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java
@@ -17,6 +17,7 @@
package com.android.settings.accessibility;
import static com.android.settings.accessibility.AccessibilityDialogUtils.DialogEnums;
+import static com.android.settings.accessibility.ToggleFeaturePreferenceFragment.KEY_GENERAL_CATEGORY;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
@@ -34,11 +35,13 @@ import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
import android.widget.CheckBox;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
-import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.utils.LocaleUtils;
import com.google.android.setupcompat.util.WizardManagerHelper;
@@ -50,7 +53,7 @@ import java.util.Locale;
/**
* Base class for accessibility fragments shortcut functions and dialog management.
*/
-public abstract class AccessibilityShortcutPreferenceFragment extends SettingsPreferenceFragment
+public abstract class AccessibilityShortcutPreferenceFragment extends DashboardFragment
implements ShortcutPreference.OnClickCallback {
private static final String KEY_SHORTCUT_PREFERENCE = "shortcut_preference";
protected static final String KEY_SAVED_USER_SHORTCUT_TYPE = "shortcut_type";
@@ -88,6 +91,10 @@ public abstract class AccessibilityShortcutPreferenceFragment extends SettingsPr
setPreferenceScreen(preferenceScreen);
}
+ if (showGeneralCategory()) {
+ initGeneralCategory();
+ }
+
final List<String> shortcutFeatureKeys = new ArrayList<>();
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
@@ -151,6 +158,7 @@ public abstract class AccessibilityShortcutPreferenceFragment extends SettingsPr
@Override
public Dialog onCreateDialog(int dialogId) {
+ final Dialog dialog;
switch (dialogId) {
case DialogEnums.EDIT_SHORTCUT:
final CharSequence dialogTitle = getPrefContext().getString(
@@ -158,11 +166,17 @@ public abstract class AccessibilityShortcutPreferenceFragment extends SettingsPr
final int dialogType = WizardManagerHelper.isAnySetupWizard(getIntent())
? AccessibilityDialogUtils.DialogType.EDIT_SHORTCUT_GENERIC_SUW :
AccessibilityDialogUtils.DialogType.EDIT_SHORTCUT_GENERIC;
- final Dialog dialog = AccessibilityDialogUtils.showEditShortcutDialog(
+ dialog = AccessibilityDialogUtils.showEditShortcutDialog(
getPrefContext(), dialogType, dialogTitle,
this::callOnAlertDialogCheckboxClicked);
setupEditShortcutDialog(dialog);
return dialog;
+ case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL:
+ dialog = AccessibilityGestureNavigationTutorial
+ .createAccessibilityTutorialDialog(getPrefContext(),
+ getUserShortcutTypes());
+ dialog.setCanceledOnTouchOutside(false);
+ return dialog;
default:
throw new IllegalArgumentException("Unsupported dialogId " + dialogId);
}
@@ -173,6 +187,8 @@ public abstract class AccessibilityShortcutPreferenceFragment extends SettingsPr
switch (dialogId) {
case DialogEnums.EDIT_SHORTCUT:
return SettingsEnums.DIALOG_ACCESSIBILITY_SERVICE_EDIT_SHORTCUT;
+ case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL:
+ return SettingsEnums.DIALOG_ACCESSIBILITY_TUTORIAL;
default:
return SettingsEnums.ACTION_UNKNOWN;
}
@@ -202,6 +218,11 @@ public abstract class AccessibilityShortcutPreferenceFragment extends SettingsPr
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
}
+ /**
+ * Overrides to return specific shortcut preference key
+ *
+ * @return String The specific shortcut preference key
+ */
protected String getShortcutPreferenceKey() {
return KEY_SHORTCUT_PREFERENCE;
}
@@ -238,6 +259,13 @@ public abstract class AccessibilityShortcutPreferenceFragment extends SettingsPr
return value;
}
+ /**
+ * Returns the shortcut type list which has been checked by user.
+ */
+ protected int getUserShortcutTypes() {
+ return AccessibilityUtil.getUserShortcutTypesFromSettings(getPrefContext(),
+ getComponentName());
+ };
/**
* This method will be invoked when a button in the edit shortcut dialog is clicked.
@@ -260,6 +288,15 @@ public abstract class AccessibilityShortcutPreferenceFragment extends SettingsPr
}
@VisibleForTesting
+ void initGeneralCategory() {
+ final PreferenceCategory generalCategory = new PreferenceCategory(getPrefContext());
+ generalCategory.setKey(KEY_GENERAL_CATEGORY);
+ generalCategory.setTitle(getGeneralCategoryDescription(null));
+
+ getPreferenceScreen().addPreference(generalCategory);
+ }
+
+ @VisibleForTesting
void saveNonEmptyUserShortcutType(int type) {
if (type == AccessibilityUtil.UserShortcutType.EMPTY) {
return;
@@ -270,6 +307,28 @@ public abstract class AccessibilityShortcutPreferenceFragment extends SettingsPr
PreferredShortcuts.saveUserShortcutType(getPrefContext(), shortcut);
}
+ /**
+ * Overrides to return customized description for general category above shortcut
+ *
+ * @return CharSequence The customized description for general category
+ */
+ protected CharSequence getGeneralCategoryDescription(@Nullable CharSequence title) {
+ if (title == null || title.toString().isEmpty()) {
+ // Return default 'Options' string for category
+ return getContext().getString(R.string.accessibility_screen_option);
+ }
+ return title;
+ }
+
+ /**
+ * Overrides to determinate if showing additional category description above shortcut
+ *
+ * @return boolean true to show category, false otherwise.
+ */
+ protected boolean showGeneralCategory() {
+ return false;
+ }
+
private void setDialogTextAreaClickListener(View dialogView, CheckBox checkBox) {
final View dialogTextArea = dialogView.findViewById(R.id.container);
dialogTextArea.setOnClickListener(v -> checkBox.toggle());
diff --git a/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java b/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java
index fe08d6b5bd..98090ac1db 100644
--- a/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java
@@ -78,10 +78,12 @@ public class LaunchAccessibilityActivityPreferenceFragment extends ToggleFeature
// Settings animated image.
final int animatedImageRes = arguments.getInt(
AccessibilitySettings.EXTRA_ANIMATED_IMAGE_RES);
- mImageUri = new Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
- .authority(mComponentName.getPackageName())
- .appendPath(String.valueOf(animatedImageRes))
- .build();
+ if (animatedImageRes > 0) {
+ mImageUri = new Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
+ .authority(mComponentName.getPackageName())
+ .appendPath(String.valueOf(animatedImageRes))
+ .build();
+ }
// Settings html description.
mHtmlDescription = arguments.getCharSequence(AccessibilitySettings.EXTRA_HTML_DESCRIPTION);
diff --git a/src/com/android/settings/accessibility/ReduceBrightColorsIntensityPreferenceController.java b/src/com/android/settings/accessibility/ReduceBrightColorsIntensityPreferenceController.java
index 7e6b42daac..156b942176 100644
--- a/src/com/android/settings/accessibility/ReduceBrightColorsIntensityPreferenceController.java
+++ b/src/com/android/settings/accessibility/ReduceBrightColorsIntensityPreferenceController.java
@@ -28,6 +28,7 @@ import com.android.settings.widget.SeekBarPreference;
/** PreferenceController for feature intensity. */
public class ReduceBrightColorsIntensityPreferenceController extends SliderPreferenceController {
+ private static final int INVERSE_PERCENTAGE_BASE = 100;
private final ColorDisplayManager mColorDisplayManager;
public ReduceBrightColorsIntensityPreferenceController(Context context, String key) {
@@ -66,21 +67,24 @@ public class ReduceBrightColorsIntensityPreferenceController extends SliderPrefe
@Override
public int getSliderPosition() {
- return mColorDisplayManager.getReduceBrightColorsStrength();
+ return INVERSE_PERCENTAGE_BASE - mColorDisplayManager.getReduceBrightColorsStrength();
}
@Override
public boolean setSliderPosition(int position) {
- return mColorDisplayManager.setReduceBrightColorsStrength(position);
+ return mColorDisplayManager.setReduceBrightColorsStrength(
+ INVERSE_PERCENTAGE_BASE - position);
}
@Override
public int getMax() {
- return ColorDisplayManager.getMaximumReduceBrightColorsStrength(mContext);
+ return INVERSE_PERCENTAGE_BASE
+ - ColorDisplayManager.getMinimumReduceBrightColorsStrength(mContext);
}
@Override
public int getMin() {
- return ColorDisplayManager.getMinimumReduceBrightColorsStrength(mContext);
+ return INVERSE_PERCENTAGE_BASE
+ - ColorDisplayManager.getMaximumReduceBrightColorsStrength(mContext);
}
}
diff --git a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java
index 9d948583fd..934907181a 100644
--- a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java
@@ -396,10 +396,12 @@ public class ToggleAccessibilityServicePreferenceFragment extends
// Settings animated image.
final int animatedImageRes = arguments.getInt(
AccessibilitySettings.EXTRA_ANIMATED_IMAGE_RES);
- mImageUri = new Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
- .authority(mComponentName.getPackageName())
- .appendPath(String.valueOf(animatedImageRes))
- .build();
+ if (animatedImageRes > 0) {
+ mImageUri = new Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
+ .authority(mComponentName.getPackageName())
+ .appendPath(String.valueOf(animatedImageRes))
+ .build();
+ }
// Get Accessibility service name.
mPackageName = getAccessibilityServiceInfo().getResolveInfo().loadLabel(
diff --git a/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java
index 7fd30c6755..b41bafda51 100644
--- a/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java
@@ -80,7 +80,7 @@ public class ToggleColorInversionPreferenceFragment extends
mHtmlDescription = getText(R.string.accessibility_display_inversion_preference_subtitle);
mImageUri = new Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
.authority(getPrefContext().getPackageName())
- .appendPath(String.valueOf(R.drawable.accessibility_color_inversion_banner))
+ .appendPath(String.valueOf(R.raw.accessibility_color_inversion_banner))
.build();
final List<String> enableServiceFeatureKeys = new ArrayList<>(/* initialCapacity= */ 1);
enableServiceFeatureKeys.add(ENABLED);
diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
index 1002a8491c..640ae53216 100644
--- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
@@ -90,7 +90,7 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
private static final String DRAWABLE_FOLDER = "drawable";
protected static final String KEY_USE_SERVICE_PREFERENCE = "use_service";
- protected static final String KEY_GENERAL_CATEGORY = "general_categories";
+ public static final String KEY_GENERAL_CATEGORY = "general_categories";
protected static final String KEY_HTML_DESCRIPTION_PREFERENCE = "html_description";
private static final String KEY_SHORTCUT_PREFERENCE = "shortcut_preference";
protected static final String KEY_SAVED_USER_SHORTCUT_TYPE = "shortcut_type";
diff --git a/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java
index b629eaa102..e60751e56b 100644
--- a/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java
@@ -62,7 +62,7 @@ public class ToggleReduceBrightColorsPreferenceFragment extends ToggleFeaturePre
mImageUri = new Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
.authority(getPrefContext().getPackageName())
- .appendPath(String.valueOf(R.drawable.extra_dim_banner))
+ .appendPath(String.valueOf(R.raw.extra_dim_banner))
.build();
mComponentName = AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME;
mPackageName = getText(R.string.reduce_bright_colors_preference_title);
diff --git a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
index ca33a96472..9266f720ca 100644
--- a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
@@ -89,7 +89,7 @@ public class ToggleScreenMagnificationPreferenceFragment extends
mPackageName = getString(R.string.accessibility_screen_magnification_title);
mImageUri = new Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
.authority(getPrefContext().getPackageName())
- .appendPath(String.valueOf(R.drawable.accessibility_magnification_banner))
+ .appendPath(String.valueOf(R.raw.accessibility_magnification_banner))
.build();
mTouchExplorationStateChangeListener = isTouchExplorationEnabled -> {
removeDialog(DialogEnums.EDIT_SHORTCUT);
diff --git a/src/com/android/settings/accounts/AvatarViewMixin.java b/src/com/android/settings/accounts/AvatarViewMixin.java
index c4ab55a46b..7a2565c45b 100644
--- a/src/com/android/settings/accounts/AvatarViewMixin.java
+++ b/src/com/android/settings/accounts/AvatarViewMixin.java
@@ -39,7 +39,6 @@ import androidx.lifecycle.OnLifecycleEvent;
import com.android.settings.R;
import com.android.settings.homepage.SettingsHomepageActivity;
import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.utils.ThreadUtils;
import java.net.URISyntaxException;
@@ -59,6 +58,7 @@ public class AvatarViewMixin implements LifecycleObserver {
private static final String METHOD_GET_ACCOUNT_AVATAR = "getAccountAvatar";
private static final String KEY_AVATAR_BITMAP = "account_avatar";
private static final String KEY_ACCOUNT_NAME = "account_name";
+ private static final String KEY_AVATAR_ICON = "avatar_icon";
private static final String EXTRA_ACCOUNT_NAME = "extra.accountName";
private final Context mContext;
@@ -105,11 +105,8 @@ public class AvatarViewMixin implements LifecycleObserver {
return;
}
- final MetricsFeatureProvider metricsFeatureProvider = FeatureFactory.getFactory(
- mContext).getMetricsFeatureProvider();
- metricsFeatureProvider.action(SettingsEnums.PAGE_UNKNOWN,
- SettingsEnums.CLICK_ACCOUNT_AVATAR, SettingsEnums.SETTINGS_HOMEPAGE,
- null /* key */, Integer.MIN_VALUE /* value */);
+ FeatureFactory.getFactory(mContext).getMetricsFeatureProvider()
+ .logSettingsTileClick(KEY_AVATAR_ICON, SettingsEnums.SETTINGS_HOMEPAGE);
// Here may have two different UI while start the activity.
// It will display adding account UI when device has no any account.
diff --git a/src/com/android/settings/applications/AppStateAlarmsAndRemindersBridge.java b/src/com/android/settings/applications/AppStateAlarmsAndRemindersBridge.java
index 4e9a96efb5..cf938a5e3d 100644
--- a/src/com/android/settings/applications/AppStateAlarmsAndRemindersBridge.java
+++ b/src/com/android/settings/applications/AppStateAlarmsAndRemindersBridge.java
@@ -19,6 +19,7 @@ package com.android.settings.applications;
import android.Manifest;
import android.app.AlarmManager;
import android.app.AppGlobals;
+import android.app.compat.CompatChanges;
import android.content.Context;
import android.content.pm.IPackageManager;
import android.os.RemoteException;
@@ -63,14 +64,21 @@ public class AppStateAlarmsAndRemindersBridge extends AppStateBaseBridge {
}
}
+ private boolean isChangeEnabled(String packageName, int userId) {
+ return CompatChanges.isChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION,
+ packageName, UserHandle.of(userId));
+ }
+
/**
* Returns information regarding {@link Manifest.permission#SCHEDULE_EXACT_ALARM} for the given
* package and uid.
*/
public AlarmsAndRemindersState createPermissionState(String packageName, int uid) {
- final boolean permissionRequested = ArrayUtils.contains(mRequesterPackages, packageName);
- final boolean permissionGranted = mAlarmManager.hasScheduleExactAlarm(packageName,
- UserHandle.getUserId(uid));
+ final int userId = UserHandle.getUserId(uid);
+
+ final boolean permissionRequested = ArrayUtils.contains(mRequesterPackages, packageName)
+ && isChangeEnabled(packageName, userId);
+ final boolean permissionGranted = mAlarmManager.hasScheduleExactAlarm(packageName, userId);
return new AlarmsAndRemindersState(permissionRequested, permissionGranted);
}
diff --git a/src/com/android/settings/applications/HibernatedAppsPreferenceController.java b/src/com/android/settings/applications/HibernatedAppsPreferenceController.java
index bf12b86c00..898d7096ee 100644
--- a/src/com/android/settings/applications/HibernatedAppsPreferenceController.java
+++ b/src/com/android/settings/applications/HibernatedAppsPreferenceController.java
@@ -61,6 +61,7 @@ public final class HibernatedAppsPreferenceController extends BasePreferenceCont
private PreferenceScreen mScreen;
private int mUnusedCount = 0;
private boolean mLoadingUnusedApps;
+ private boolean mLoadedUnusedCount;
private final Executor mBackgroundExecutor;
private final Executor mMainExecutor;
@@ -79,14 +80,15 @@ public final class HibernatedAppsPreferenceController extends BasePreferenceCont
@Override
public int getAvailabilityStatus() {
- return isHibernationEnabled() && mUnusedCount > 0
- ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+ return isHibernationEnabled() ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
}
@Override
public CharSequence getSummary() {
- return mContext.getResources().getQuantityString(
- R.plurals.unused_apps_summary, mUnusedCount, mUnusedCount);
+ return mLoadedUnusedCount
+ ? mContext.getResources().getQuantityString(
+ R.plurals.unused_apps_summary, mUnusedCount, mUnusedCount)
+ : mContext.getResources().getString(R.string.summary_placeholder);
}
@Override
@@ -111,8 +113,8 @@ public final class HibernatedAppsPreferenceController extends BasePreferenceCont
loadUnusedCount(unusedCount -> {
mUnusedCount = unusedCount;
mLoadingUnusedApps = false;
+ mLoadedUnusedCount = true;
mMainExecutor.execute(() -> {
- super.displayPreference(mScreen);
Preference pref = mScreen.findPreference(mPreferenceKey);
refreshSummary(pref);
});
diff --git a/src/com/android/settings/applications/RunningServices.java b/src/com/android/settings/applications/RunningServices.java
index 4d13241126..b1689d5c59 100644
--- a/src/com/android/settings/applications/RunningServices.java
+++ b/src/com/android/settings/applications/RunningServices.java
@@ -72,7 +72,11 @@ public class RunningServices extends SettingsPreferenceFragment {
public void onResume() {
super.onResume();
boolean haveData = mRunningProcessesView.doResume(this, mRunningProcessesAvail);
- mLoadingViewController.handleLoadingContainer(haveData /* done */, false /* animate */);
+ if (haveData) {
+ mLoadingViewController.showContent(false /* animate */);
+ } else {
+ mLoadingViewController.showLoadingView();
+ }
}
@Override
diff --git a/src/com/android/settings/applications/appinfo/AlarmsAndRemindersDetailPreferenceController.java b/src/com/android/settings/applications/appinfo/AlarmsAndRemindersDetailPreferenceController.java
new file mode 100644
index 0000000000..cfd4bf1c26
--- /dev/null
+++ b/src/com/android/settings/applications/appinfo/AlarmsAndRemindersDetailPreferenceController.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2021 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.applications.appinfo;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.applications.AppStateAlarmsAndRemindersBridge;
+
+/**
+ * Preference controller for
+ * {@link com.android.settings.applications.appinfo.AlarmsAndRemindersDetails} Settings fragment.
+ */
+public class AlarmsAndRemindersDetailPreferenceController extends AppInfoPreferenceControllerBase {
+
+ private String mPackageName;
+
+ public AlarmsAndRemindersDetailPreferenceController(Context context, String key) {
+ super(context, key);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return isCandidate() ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ preference.setSummary(getPreferenceSummary());
+ }
+
+ @Override
+ protected Class<? extends SettingsPreferenceFragment> getDetailFragmentClass() {
+ return AlarmsAndRemindersDetails.class;
+ }
+
+ @VisibleForTesting
+ CharSequence getPreferenceSummary() {
+ return AlarmsAndRemindersDetails.getSummary(mContext, mParent.getAppEntry());
+ }
+
+ @VisibleForTesting
+ boolean isCandidate() {
+ final PackageInfo packageInfo = mParent.getPackageInfo();
+ if (packageInfo == null) {
+ return false;
+ }
+ final AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState appState =
+ new AppStateAlarmsAndRemindersBridge(mContext, null, null).createPermissionState(
+ mPackageName, packageInfo.applicationInfo.uid);
+ return appState.shouldBeVisible();
+ }
+
+ void setPackageName(String packageName) {
+ mPackageName = packageName;
+ }
+}
diff --git a/src/com/android/settings/applications/appinfo/AlarmsAndRemindersDetails.java b/src/com/android/settings/applications/appinfo/AlarmsAndRemindersDetails.java
index 3765dd9b68..648696ba25 100644
--- a/src/com/android/settings/applications/appinfo/AlarmsAndRemindersDetails.java
+++ b/src/com/android/settings/applications/appinfo/AlarmsAndRemindersDetails.java
@@ -18,7 +18,6 @@ package com.android.settings.applications.appinfo;
import static android.app.Activity.RESULT_CANCELED;
import static android.app.Activity.RESULT_OK;
-import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.settings.SettingsEnums;
import android.content.Context;
@@ -49,25 +48,20 @@ public class AlarmsAndRemindersDetails extends AppInfoWithHeader
private AppOpsManager mAppOpsManager;
private RestrictedSwitchPreference mSwitchPref;
private AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState mPermissionState;
- private ActivityManager mActivityManager;
private volatile Boolean mUncommittedState;
/**
* Returns the string that states whether the app has access to
* {@link android.Manifest.permission#SCHEDULE_EXACT_ALARM}.
*/
- public static int getSummary(Context context, AppEntry entry) {
- final AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState state;
- if (entry.extraInfo instanceof AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState) {
- state = (AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState) entry.extraInfo;
- } else {
- state = new AppStateAlarmsAndRemindersBridge(context, /*appState=*/null,
- /*callback=*/null).createPermissionState(entry.info.packageName,
- entry.info.uid);
- }
-
- return state.isAllowed() ? R.string.app_permission_summary_allowed
- : R.string.app_permission_summary_not_allowed;
+ public static CharSequence getSummary(Context context, AppEntry entry) {
+ final AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState state =
+ new AppStateAlarmsAndRemindersBridge(context, /*appState=*/null,
+ /*callback=*/null).createPermissionState(entry.info.packageName,
+ entry.info.uid);
+
+ return context.getString(state.isAllowed() ? R.string.app_permission_summary_allowed
+ : R.string.app_permission_summary_not_allowed);
}
@Override
@@ -77,7 +71,6 @@ public class AlarmsAndRemindersDetails extends AppInfoWithHeader
final Context context = getActivity();
mAppBridge = new AppStateAlarmsAndRemindersBridge(context, mState, /*callback=*/null);
mAppOpsManager = context.getSystemService(AppOpsManager.class);
- mActivityManager = context.getSystemService(ActivityManager.class);
if (savedInstanceState != null) {
mUncommittedState = (Boolean) savedInstanceState.get(UNCOMMITTED_STATE_KEY);
@@ -115,10 +108,6 @@ public class AlarmsAndRemindersDetails extends AppInfoWithHeader
final int uid = mPackageInfo.applicationInfo.uid;
mAppOpsManager.setUidMode(AppOpsManager.OPSTR_SCHEDULE_EXACT_ALARM, uid,
newState ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_ERRORED);
- if (!newState) {
- mActivityManager.killUid(uid,
- AppOpsManager.OPSTR_SCHEDULE_EXACT_ALARM + " no longer allowed.");
- }
}
private void logPermissionChange(boolean newState, String packageName) {
diff --git a/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java b/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java
index 1f0777a5ff..d83f5d147d 100644
--- a/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java
@@ -18,11 +18,14 @@ package com.android.settings.applications.appinfo;
import android.content.Context;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.BatteryUsageStats;
import android.os.Bundle;
import android.os.UidBatteryConsumer;
+import android.os.UserHandle;
import android.os.UserManager;
+import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
@@ -40,6 +43,8 @@ import com.android.settings.fuelgauge.BatteryDiffEntry;
import com.android.settings.fuelgauge.BatteryEntry;
import com.android.settings.fuelgauge.BatteryUsageStatsLoader;
import com.android.settings.fuelgauge.BatteryUtils;
+import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
+import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnPause;
@@ -50,6 +55,7 @@ import java.util.List;
public class AppBatteryPreferenceController extends BasePreferenceController
implements LifecycleObserver, OnResume, OnPause {
+ private static final String TAG = "AppBatteryPreferenceController";
private static final String KEY_BATTERY = "battery";
@VisibleForTesting
@@ -61,13 +67,16 @@ public class AppBatteryPreferenceController extends BasePreferenceController
BatteryUsageStats mBatteryUsageStats;
@VisibleForTesting
UidBatteryConsumer mUidBatteryConsumer;
+ @VisibleForTesting
+ BatteryDiffEntry mBatteryDiffEntry;
+ @VisibleForTesting
+ boolean mIsChartGraphEnabled;
private Preference mPreference;
private final AppInfoDashboardFragment mParent;
private String mBatteryPercent;
private final String mPackageName;
private final int mUid;
- private BatteryDiffEntry mBatteryDiffEntry;
private boolean mBatteryUsageStatsLoaded = false;
private boolean mBatteryDiffEntriesLoaded = false;
@@ -78,6 +87,7 @@ public class AppBatteryPreferenceController extends BasePreferenceController
mBatteryUtils = BatteryUtils.getInstance(mContext);
mPackageName = packageName;
mUid = uid;
+ refreshFeatureFlag(mContext);
if (lifecycle != null) {
lifecycle.addObserver(this);
}
@@ -108,7 +118,8 @@ public class AppBatteryPreferenceController extends BasePreferenceController
mParent.getActivity(),
mParent,
mBatteryDiffEntry,
- mBatteryPercent,
+ Utils.formatPercentage(
+ mBatteryDiffEntry.getPercentOfTotal(), /* round */ true),
/*isValidToShowSummary=*/ true,
/*slotInformation=*/ null);
return true;
@@ -120,8 +131,9 @@ public class AppBatteryPreferenceController extends BasePreferenceController
final BatteryEntry entry = new BatteryEntry(mContext, /* handler */null, userManager,
mUidBatteryConsumer, /* isHidden */ false,
mUidBatteryConsumer.getUid(), /* packages */ null, mPackageName);
- AdvancedPowerUsageDetail.startBatteryDetailPage(mParent.getActivity(), mParent,
- entry, mBatteryPercent);
+ AdvancedPowerUsageDetail.startBatteryDetailPage(mParent.getActivity(), mParent, entry,
+ mIsChartGraphEnabled ? Utils.formatPercentage(0) : mBatteryPercent,
+ !mIsChartGraphEnabled);
} else {
AdvancedPowerUsageDetail.startBatteryDetailPage(mParent.getActivity(), mParent,
mPackageName);
@@ -161,12 +173,29 @@ public class AppBatteryPreferenceController extends BasePreferenceController
@Override
protected void onPostExecute(BatteryDiffEntry batteryDiffEntry) {
mBatteryDiffEntry = batteryDiffEntry;
- mBatteryDiffEntriesLoaded = true;
- mPreference.setEnabled(mBatteryUsageStatsLoaded);
+ updateBatteryWithDiffEntry();
}
}.execute();
}
+ @VisibleForTesting
+ void updateBatteryWithDiffEntry() {
+ if (mIsChartGraphEnabled) {
+ if (mBatteryDiffEntry != null && mBatteryDiffEntry.mConsumePower > 0) {
+ mBatteryPercent = Utils.formatPercentage(
+ mBatteryDiffEntry.getPercentOfTotal(), /* round */ true);
+ mPreference.setSummary(mContext.getString(
+ R.string.battery_summary_24hr, mBatteryPercent));
+ } else {
+ mPreference.setSummary(
+ mContext.getString(R.string.no_battery_summary_24hr));
+ }
+ }
+
+ mBatteryDiffEntriesLoaded = true;
+ mPreference.setEnabled(mBatteryUsageStatsLoaded);
+ }
+
private void onLoadFinished() {
if (mBatteryUsageStats == null) {
return;
@@ -182,10 +211,33 @@ public class AppBatteryPreferenceController extends BasePreferenceController
}
}
+ private void refreshFeatureFlag(Context context) {
+ if (isWorkProfile(context)) {
+ try {
+ context = context.createPackageContextAsUser(
+ context.getPackageName(), 0, UserHandle.OWNER);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "context.createPackageContextAsUser() fail: " + e);
+ }
+ }
+
+ final PowerUsageFeatureProvider powerUsageFeatureProvider =
+ FeatureFactory.getFactory(context).getPowerUsageFeatureProvider(context);
+ mIsChartGraphEnabled = powerUsageFeatureProvider.isChartGraphEnabled(context);
+ }
+
+ private boolean isWorkProfile(Context context) {
+ final UserManager userManager = context.getSystemService(UserManager.class);
+ return userManager.isManagedProfile() && !userManager.isSystemUser();
+ }
+
@VisibleForTesting
void updateBattery() {
mBatteryUsageStatsLoaded = true;
mPreference.setEnabled(mBatteryDiffEntriesLoaded);
+ if (mIsChartGraphEnabled) {
+ return;
+ }
if (isBatteryStatsAvailable()) {
final int percentOfMax = (int) mBatteryUtils.calculateBatteryPercent(
mUidBatteryConsumer.getConsumedPower(), mBatteryUsageStats.getConsumedPower(),
diff --git a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java
index cb0ed07b4a..6d3aaa3c2c 100755
--- a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java
+++ b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java
@@ -197,8 +197,14 @@ public class AppInfoDashboardFragment extends DashboardFragment
acrossProfiles.setPackageName(packageName);
acrossProfiles.setParentFragment(this);
+ final AlarmsAndRemindersDetailPreferenceController alarmsAndReminders =
+ use(AlarmsAndRemindersDetailPreferenceController.class);
+ alarmsAndReminders.setPackageName(packageName);
+ alarmsAndReminders.setParentFragment(this);
+
use(AdvancedAppInfoPreferenceCategoryController.class).setChildren(Arrays.asList(
- writeSystemSettings, drawOverlay, pip, externalSource, acrossProfiles));
+ writeSystemSettings, drawOverlay, pip, externalSource, acrossProfiles,
+ alarmsAndReminders));
}
@Override
@@ -303,11 +309,6 @@ public class AppInfoDashboardFragment extends DashboardFragment
return controllers;
}
- @Override
- protected boolean isParalleledControllers() {
- return true;
- }
-
void addToCallbackList(Callback callback) {
if (callback != null) {
mCallbacks.add(callback);
diff --git a/src/com/android/settings/applications/intentpicker/AppLaunchSettings.java b/src/com/android/settings/applications/intentpicker/AppLaunchSettings.java
index 439a6a31b4..43c377aff7 100644
--- a/src/com/android/settings/applications/intentpicker/AppLaunchSettings.java
+++ b/src/com/android/settings/applications/intentpicker/AppLaunchSettings.java
@@ -46,7 +46,6 @@ import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.applications.AppInfoBase;
import com.android.settings.applications.ClearDefaultsPreference;
-import com.android.settings.utils.AnnotationSpan;
import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.applications.AppUtils;
import com.android.settingslib.widget.FooterPreference;
@@ -73,8 +72,6 @@ public class AppLaunchSettings extends AppInfoBase implements
"open_by_default_selected_links_category";
private static final String OTHER_DETAILS_PREF_CATEGORY_KEY = "app_launch_other_defaults";
- // Url and Uri
- private static final String ANNOTATION_URL = "url";
private static final String LEARN_MORE_URI =
"https://developer.android.com/training/app-links/verify-site-associations";
@@ -356,16 +353,22 @@ public class AppLaunchSettings extends AppInfoBase implements
}
private void initFooter() {
- // learn more
- final AnnotationSpan.LinkInfo linkInfo =
- new AnnotationSpan.LinkInfo(ANNOTATION_URL, v -> {
- final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(LEARN_MORE_URI));
- mContext.startActivity(intent);
- });
final CharSequence footerText = mContext.getText(R.string.app_launch_footer);
final FooterPreference footerPreference = (FooterPreference) findPreference(
FOOTER_PREF_KEY);
- footerPreference.setTitle(AnnotationSpan.linkify(footerText, linkInfo));
+ footerPreference.setTitle(footerText);
+ // learn more
+ footerPreference.setLearnMoreAction(view -> {
+ final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(LEARN_MORE_URI));
+ mContext.startActivity(intent);
+ });
+ final String learnMoreContentDescription = mContext.getString(
+ R.string.footer_learn_more_content_description, getLabelName());
+ footerPreference.setLearnMoreContentDescription(learnMoreContentDescription);
+ }
+
+ private String getLabelName() {
+ return mContext.getString(R.string.launch_by_default);
}
private boolean isClearDefaultsEnabled() {
diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java
index 7509a78891..43e929b805 100644
--- a/src/com/android/settings/applications/manageapplications/ManageApplications.java
+++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java
@@ -73,6 +73,8 @@ import android.widget.Spinner;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
+import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -130,6 +132,8 @@ import com.android.settingslib.fuelgauge.PowerAllowlistBackend;
import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.widget.settingsspinner.SettingsSpinnerAdapter;
+import com.google.android.material.appbar.AppBarLayout;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -143,7 +147,8 @@ import java.util.Set;
* intent.
*/
public class ManageApplications extends InstrumentedFragment
- implements View.OnClickListener, OnItemSelectedListener, SearchView.OnQueryTextListener {
+ implements View.OnClickListener, OnItemSelectedListener, SearchView.OnQueryTextListener,
+ MenuItem.OnActionExpandListener {
static final String TAG = "ManageApplications";
static final boolean DEBUG = Build.IS_DEBUGGABLE;
@@ -203,7 +208,6 @@ public class ManageApplications extends InstrumentedFragment
private ApplicationsAdapter mApplications;
private View mLoadingContainer;
- private View mListContainer;
private SearchView mSearchView;
// Size resource used for packages whose size computation failed for some reason
@@ -256,6 +260,7 @@ public class ManageApplications extends InstrumentedFragment
private boolean mIsPersonalOnly;
private View mEmptyView;
private int mFilterType;
+ private AppBarLayout mAppBarLayout;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -396,25 +401,21 @@ public class ManageApplications extends InstrumentedFragment
mRootView = inflater.inflate(R.layout.manage_applications_apps, null);
mLoadingContainer = mRootView.findViewById(R.id.loading_container);
- mListContainer = mRootView.findViewById(R.id.list_container);
- if (mListContainer != null) {
- // Create adapter and list view here
- mEmptyView = mListContainer.findViewById(android.R.id.empty);
+ mEmptyView = mRootView.findViewById(android.R.id.empty);
+ mRecyclerView = mRootView.findViewById(R.id.apps_list);
- mApplications = new ApplicationsAdapter(mApplicationsState, this, mFilter,
- savedInstanceState);
- if (savedInstanceState != null) {
- mApplications.mHasReceivedLoadEntries =
- savedInstanceState.getBoolean(EXTRA_HAS_ENTRIES, false);
- mApplications.mHasReceivedBridgeCallback =
- savedInstanceState.getBoolean(EXTRA_HAS_BRIDGE, false);
- }
- mRecyclerView = mListContainer.findViewById(R.id.apps_list);
- mRecyclerView.setItemAnimator(null);
- mRecyclerView.setLayoutManager(new LinearLayoutManager(
- getContext(), RecyclerView.VERTICAL, false /* reverseLayout */));
- mRecyclerView.setAdapter(mApplications);
+ mApplications = new ApplicationsAdapter(mApplicationsState, this, mFilter,
+ savedInstanceState);
+ if (savedInstanceState != null) {
+ mApplications.mHasReceivedLoadEntries =
+ savedInstanceState.getBoolean(EXTRA_HAS_ENTRIES, false);
+ mApplications.mHasReceivedBridgeCallback =
+ savedInstanceState.getBoolean(EXTRA_HAS_BRIDGE, false);
}
+ mRecyclerView.setItemAnimator(null);
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(
+ getContext(), RecyclerView.VERTICAL, false /* reverseLayout */));
+ mRecyclerView.setAdapter(mApplications);
// We have to do this now because PreferenceFrameLayout looks at it
// only when the view is added.
@@ -426,6 +427,9 @@ public class ManageApplications extends InstrumentedFragment
mResetAppsHelper.onRestoreInstanceState(savedInstanceState);
+ mAppBarLayout = getActivity().findViewById(R.id.app_bar);
+ disableToolBarScrollableBehavior();
+
return mRootView;
}
@@ -659,6 +663,7 @@ public class ManageApplications extends InstrumentedFragment
final MenuItem searchMenuItem = menu.findItem(R.id.search_app_list_menu);
if (searchMenuItem != null) {
+ searchMenuItem.setOnActionExpandListener(this);
mSearchView = (SearchView) searchMenuItem.getActionView();
mSearchView.setQueryHint(getText(R.string.search_settings));
mSearchView.setOnQueryTextListener(this);
@@ -671,6 +676,23 @@ public class ManageApplications extends InstrumentedFragment
}
@Override
+ public boolean onMenuItemActionExpand(MenuItem item) {
+ // To prevent a large space on tool bar.
+ mAppBarLayout.setExpanded(false /*expanded*/, false /*animate*/);
+ // To prevent user can expand the collapsing tool bar view.
+ ViewCompat.setNestedScrollingEnabled(mRecyclerView, false);
+ return true;
+ }
+
+ @Override
+ public boolean onMenuItemActionCollapse(MenuItem item) {
+ // We keep the collapsed status after user cancel the search function.
+ mAppBarLayout.setExpanded(false /*expanded*/, false /*animate*/);
+ ViewCompat.setNestedScrollingEnabled(mRecyclerView, true);
+ return true;
+ }
+
+ @Override
public void onPrepareOptionsMenu(Menu menu) {
updateOptionsMenu();
}
@@ -791,6 +813,9 @@ public class ManageApplications extends InstrumentedFragment
mCurrentPkgName = entry.info.packageName;
mCurrentUid = entry.info.uid;
startApplicationDetailsActivity();
+ // We disable the scrolling ability in onMenuItemActionCollapse, we should recover it
+ // if user selects any app item.
+ ViewCompat.setNestedScrollingEnabled(mRecyclerView, true);
}
}
@@ -842,6 +867,20 @@ public class ManageApplications extends InstrumentedFragment
}
}
+ private void disableToolBarScrollableBehavior() {
+ final CoordinatorLayout.LayoutParams params =
+ (CoordinatorLayout.LayoutParams) mAppBarLayout.getLayoutParams();
+ final AppBarLayout.Behavior behavior = new AppBarLayout.Behavior();
+ behavior.setDragCallback(
+ new AppBarLayout.Behavior.DragCallback() {
+ @Override
+ public boolean canDrag(@NonNull AppBarLayout appBarLayout) {
+ return false;
+ }
+ });
+ params.setBehavior(behavior);
+ }
+
static class FilterSpinnerAdapter extends SettingsSpinnerAdapter<CharSequence> {
private final ManageApplications mManageApplications;
@@ -941,16 +980,8 @@ public class ManageApplications extends InstrumentedFragment
// overlapped by floating filter.
if (hasFilter) {
mManageApplications.mSpinnerHeader.setVisibility(View.VISIBLE);
- mManageApplications.mRecyclerView.setPadding(0 /* left */,
- mContext.getResources().getDimensionPixelSize(
- R.dimen.app_bar_height) /* top */,
- 0 /* right */,
- 0 /* bottom */);
} else {
mManageApplications.mSpinnerHeader.setVisibility(View.GONE);
- mManageApplications.mRecyclerView.setPadding(0 /* left */, 0 /* top */,
- 0 /* right */,
- 0 /* bottom */);
}
}
}
@@ -1000,7 +1031,8 @@ public class ManageApplications extends InstrumentedFragment
mManageApplications = manageApplications;
mLoadingViewController = new LoadingViewController(
mManageApplications.mLoadingContainer,
- mManageApplications.mListContainer
+ mManageApplications.mRecyclerView,
+ mManageApplications.mEmptyView
);
mContext = manageApplications.getActivity();
mIconDrawableFactory = IconDrawableFactory.newInstance(mContext);
@@ -1259,11 +1291,9 @@ public class ManageApplications extends InstrumentedFragment
mOriginalEntries = entries;
notifyDataSetChanged();
if (getItemCount() == 0) {
- mManageApplications.mRecyclerView.setVisibility(View.GONE);
- mManageApplications.mEmptyView.setVisibility(View.VISIBLE);
+ mLoadingViewController.showEmpty(false /* animate */);
} else {
- mManageApplications.mEmptyView.setVisibility(View.GONE);
- mManageApplications.mRecyclerView.setVisibility(View.VISIBLE);
+ mLoadingViewController.showContent(false /* animate */);
if (mManageApplications.mSearchView != null
&& mManageApplications.mSearchView.isVisibleToUser()) {
@@ -1280,10 +1310,6 @@ public class ManageApplications extends InstrumentedFragment
mLastIndex = -1;
}
- if (mSession.getAllApps().size() != 0
- && mManageApplications.mListContainer.getVisibility() != View.VISIBLE) {
- mLoadingViewController.showContent(true /* animate */);
- }
if (mManageApplications.mListType == LIST_TYPE_USAGE_ACCESS) {
// No enabled or disabled filters for usage access.
return;
diff --git a/src/com/android/settings/biometrics/BiometricEnrollActivity.java b/src/com/android/settings/biometrics/BiometricEnrollActivity.java
index 11313fd52d..db5e003456 100644
--- a/src/com/android/settings/biometrics/BiometricEnrollActivity.java
+++ b/src/com/android/settings/biometrics/BiometricEnrollActivity.java
@@ -19,6 +19,9 @@ package com.android.settings.biometrics;
import static android.provider.Settings.ACTION_BIOMETRIC_ENROLL;
import static android.provider.Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED;
+import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_CONSENT_DENIED;
+import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_CONSENT_GRANTED;
+
import android.annotation.NonNull;
import android.app.admin.DevicePolicyManager;
import android.app.settings.SettingsEnums;
@@ -64,6 +67,10 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
private static final int REQUEST_CHOOSE_LOCK = 1;
private static final int REQUEST_CONFIRM_LOCK = 2;
+ // prompt for parental consent options
+ private static final int REQUEST_CHOOSE_OPTIONS = 3;
+ // prompt hand phone back to parent after enrollment
+ private static final int REQUEST_HANDOFF_PARENT = 4;
public static final int RESULT_SKIP = BiometricEnrollBase.RESULT_SKIP;
@@ -71,8 +78,12 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
// this only applies to fingerprint.
public static final String EXTRA_SKIP_INTRO = "skip_intro";
+ // TODO: temporary while waiting for team to add real flag
+ public static final String EXTRA_TEMP_REQUIRE_PARENTAL_CONSENT = "require_consent";
+
private static final String SAVED_STATE_CONFIRMING_CREDENTIALS = "confirming_credentials";
private static final String SAVED_STATE_ENROLL_ACTION_LOGGED = "enroll_action_logged";
+ private static final String SAVED_STATE_PARENTAL_OPTIONS = "enroll_preferences";
private static final String SAVED_STATE_GK_PW_HANDLE = "gk_pw_handle";
public static final class InternalActivity extends BiometricEnrollActivity {}
@@ -80,9 +91,14 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
private int mUserId = UserHandle.myUserId();
private boolean mConfirmingCredentials;
private boolean mIsEnrollActionLogged;
- private boolean mIsFaceEnrollable;
- private boolean mIsFingerprintEnrollable;
+ private boolean mHasFeatureFace = false;
+ private boolean mHasFeatureFingerprint = false;
+ private boolean mIsFaceEnrollable = false;
+ private boolean mIsFingerprintEnrollable = false;
+ private boolean mParentalOptionsRequired = false;
+ private Bundle mParentalOptions;
@Nullable private Long mGkPwHandle;
+ @Nullable private ParentalConsentHelper mParentalConsentHelper;
@Nullable private MultiBiometricEnrollHelper mMultiBiometricEnrollHelper;
@Override
@@ -101,6 +117,7 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
SAVED_STATE_CONFIRMING_CREDENTIALS, false);
mIsEnrollActionLogged = savedInstanceState.getBoolean(
SAVED_STATE_ENROLL_ACTION_LOGGED, false);
+ mParentalOptions = savedInstanceState.getBundle(SAVED_STATE_PARENTAL_OPTIONS);
if (savedInstanceState.containsKey(SAVED_STATE_GK_PW_HANDLE)) {
mGkPwHandle = savedInstanceState.getLong(SAVED_STATE_GK_PW_HANDLE);
}
@@ -141,52 +158,98 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
SetupWizardUtils.getThemeString(intent));
}
- // Default behavior is to enroll BIOMETRIC_WEAK or above. See ACTION_BIOMETRIC_ENROLL.
- final int authenticators = intent.getIntExtra(
- EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, Authenticators.BIOMETRIC_WEAK);
-
- Log.d(TAG, "Authenticators: " + authenticators);
-
final PackageManager pm = getApplicationContext().getPackageManager();
- final boolean hasFeatureFingerprint =
- pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
- final boolean hasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE);
+ mHasFeatureFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
+ mHasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE);
+
+ // determine what can be enrolled
final boolean isSetupWizard = WizardManagerHelper.isAnySetupWizard(getIntent());
+ if (mHasFeatureFace) {
+ final FaceManager faceManager = getSystemService(FaceManager.class);
+ final List<FaceSensorPropertiesInternal> faceProperties =
+ faceManager.getSensorPropertiesInternal();
+ if (!faceProperties.isEmpty()) {
+ final int maxEnrolls =
+ isSetupWizard ? 1 : faceProperties.get(0).maxEnrollmentsPerUser;
+ mIsFaceEnrollable =
+ faceManager.getEnrolledFaces(mUserId).size() < maxEnrolls;
+ }
+ }
+ if (mHasFeatureFingerprint) {
+ final FingerprintManager fpManager = getSystemService(FingerprintManager.class);
+ final List<FingerprintSensorPropertiesInternal> fpProperties =
+ fpManager.getSensorPropertiesInternal();
+ if (!fpProperties.isEmpty()) {
+ final int maxEnrolls =
+ isSetupWizard ? 1 : fpProperties.get(0).maxEnrollmentsPerUser;
+ mIsFingerprintEnrollable =
+ fpManager.getEnrolledFingerprints(mUserId).size() < maxEnrolls;
+ }
+ }
- if (isSetupWizard) {
- if (hasFeatureFace && hasFeatureFingerprint) {
- setupForMultiBiometricEnroll();
- } else if (hasFeatureFace) {
- launchFaceOnlyEnroll();
- } else if (hasFeatureFingerprint) {
- launchFingerprintOnlyEnroll();
- } else {
- Log.e(TAG, "No biometrics but started by SUW?");
- finish();
+ // TODO(b/188847063): replace with real flag when ready
+ mParentalOptionsRequired = intent.getBooleanExtra(
+ BiometricEnrollActivity.EXTRA_TEMP_REQUIRE_PARENTAL_CONSENT, false);
+
+ if (mParentalOptionsRequired && mParentalOptions == null) {
+ mParentalConsentHelper = new ParentalConsentHelper(
+ mIsFaceEnrollable, mIsFingerprintEnrollable, mGkPwHandle);
+ setOrConfirmCredentialsNow();
+ } else {
+ startEnroll();
+ }
+ }
+
+ private void startEnroll() {
+ // TODO(b/188847063): This can be deleted, but log it now until it's wired up for real.
+ if (mParentalOptionsRequired) {
+ if (mParentalOptions == null) {
+ throw new IllegalStateException("consent options required, but not set");
}
+ Log.d(TAG, "consent for face: "
+ + ParentalConsentHelper.hasFaceConsent(mParentalOptions));
+ Log.d(TAG, "consent for fingerprint: "
+ + ParentalConsentHelper.hasFingerprintConsent(mParentalOptions));
} else {
- // If the caller is not setup wizard, and the user has something enrolled, finish.
+ Log.d(TAG, "startEnroll without requiring consent");
+ }
+
+ // Default behavior is to enroll BIOMETRIC_WEAK or above. See ACTION_BIOMETRIC_ENROLL.
+ final int authenticators = getIntent().getIntExtra(
+ EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, Authenticators.BIOMETRIC_WEAK);
+ Log.d(TAG, "Authenticators: " + authenticators);
+
+ startEnrollWith(authenticators, WizardManagerHelper.isAnySetupWizard(getIntent()));
+ }
+
+ private void startEnrollWith(@Authenticators.Types int authenticators, boolean setupWizard) {
+ // If the caller is not setup wizard, and the user has something enrolled, finish.
+ if (!setupWizard) {
final BiometricManager bm = getSystemService(BiometricManager.class);
final @BiometricError int result = bm.canAuthenticate(authenticators);
if (result != BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED) {
- Log.e(TAG, "Unexpected result: " + result);
+ Log.e(TAG, "Unexpected result (has enrollments): " + result);
finish();
return;
}
+ }
- // This will need to be updated if the device has sensors other than BIOMETRIC_STRONG
- if (authenticators == BiometricManager.Authenticators.DEVICE_CREDENTIAL) {
- launchCredentialOnlyEnroll();
- } else if (hasFeatureFace && hasFeatureFingerprint) {
- setupForMultiBiometricEnroll();
- } else if (hasFeatureFingerprint) {
- launchFingerprintOnlyEnroll();
- } else if (hasFeatureFace) {
- launchFaceOnlyEnroll();
+ // This will need to be updated if the device has sensors other than BIOMETRIC_STRONG
+ if (!setupWizard && authenticators == BiometricManager.Authenticators.DEVICE_CREDENTIAL) {
+ launchCredentialOnlyEnroll();
+ } else if (mHasFeatureFace && mHasFeatureFingerprint) {
+ if (mParentalOptionsRequired && mGkPwHandle != null) {
+ launchFaceAndFingerprintEnroll();
} else {
- Log.e(TAG, "Unknown state, finishing");
- finish();
+ setOrConfirmCredentialsNow();
}
+ } else if (mHasFeatureFingerprint) {
+ launchFingerprintOnlyEnroll();
+ } else if (mHasFeatureFace) {
+ launchFaceOnlyEnroll();
+ } else {
+ Log.e(TAG, "Unknown state, finishing (was SUW: " + setupWizard + ")");
+ finish();
}
}
@@ -195,6 +258,9 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
super.onSaveInstanceState(outState);
outState.putBoolean(SAVED_STATE_CONFIRMING_CREDENTIALS, mConfirmingCredentials);
outState.putBoolean(SAVED_STATE_ENROLL_ACTION_LOGGED, mIsEnrollActionLogged);
+ if (mParentalOptions != null) {
+ outState.putBundle(SAVED_STATE_PARENTAL_OPTIONS, mParentalOptions);
+ }
if (mGkPwHandle != null) {
outState.putLong(SAVED_STATE_GK_PW_HANDLE, mGkPwHandle);
}
@@ -204,31 +270,93 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
+ // single enrollment is handled entirely by the launched activity
+ // this handles multi enroll or if parental consent is required
+ if (mParentalConsentHelper != null) {
+ handleOnActivityResultWhileConsenting(requestCode, resultCode, data);
+ } else {
+ handleOnActivityResultWhileEnrollingMultiple(requestCode, resultCode, data);
+ }
+ }
+
+ // handles responses while parental consent is pending
+ private void handleOnActivityResultWhileConsenting(
+ int requestCode, int resultCode, Intent data) {
+ overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out);
+
+ switch (requestCode) {
+ case REQUEST_CHOOSE_LOCK:
+ case REQUEST_CONFIRM_LOCK:
+ mConfirmingCredentials = false;
+ if (isSuccessfulConfirmOrChooseCredential(requestCode, resultCode)) {
+ updateGatekeeperPasswordHandle(data);
+ if (!mParentalConsentHelper.launchNext(this, REQUEST_CHOOSE_OPTIONS)) {
+ Log.e(TAG, "Nothing to prompt for consent (no modalities enabled)!");
+ finish();
+ }
+ } else {
+ Log.d(TAG, "Unknown result for set/choose lock: " + resultCode);
+ setResult(resultCode);
+ finish();
+ }
+ break;
+ case REQUEST_CHOOSE_OPTIONS:
+ if (resultCode == RESULT_CONSENT_GRANTED || resultCode == RESULT_CONSENT_DENIED) {
+ final boolean isStillPrompting = mParentalConsentHelper.launchNext(
+ this, REQUEST_CHOOSE_OPTIONS, resultCode, data);
+ if (!isStillPrompting) {
+ Log.d(TAG, "Enrollment options set, requesting handoff");
+ launchHandoffToParent();
+ }
+ } else {
+ Log.d(TAG, "Unknown or cancelled parental consent");
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+ break;
+ case REQUEST_HANDOFF_PARENT:
+ if (resultCode == RESULT_OK) {
+ Log.d(TAG, "Enrollment options set, starting enrollment");
+ mParentalOptions = mParentalConsentHelper.getConsentResult();
+ mParentalConsentHelper = null;
+ startEnroll();
+ } else {
+ Log.d(TAG, "Unknown or cancelled handoff");
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+ break;
+ default:
+ Log.w(TAG, "Unknown consenting requestCode: " + requestCode + ", finishing");
+ finish();
+ }
+ }
+
+ // handles responses while multi biometric enrollment is pending
+ private void handleOnActivityResultWhileEnrollingMultiple(
+ int requestCode, int resultCode, Intent data) {
if (mMultiBiometricEnrollHelper == null) {
overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out);
switch (requestCode) {
case REQUEST_CHOOSE_LOCK:
- mConfirmingCredentials = false;
- if (resultCode == ChooseLockPattern.RESULT_FINISHED) {
- startMultiBiometricEnroll(data);
- } else {
- Log.d(TAG, "Unknown result for chooseLock: " + resultCode);
- setResult(resultCode);
- finish();
- }
- break;
case REQUEST_CONFIRM_LOCK:
mConfirmingCredentials = false;
- if (resultCode == RESULT_OK) {
- startMultiBiometricEnroll(data);
+ final boolean isOk =
+ isSuccessfulConfirmOrChooseCredential(requestCode, resultCode);
+ // single modality enrollment requests confirmation directly
+ // via BiometricEnrollBase#onCreate and should never get here
+ if (isOk && mHasFeatureFace && mHasFeatureFingerprint) {
+ updateGatekeeperPasswordHandle(data);
+ launchFaceAndFingerprintEnroll();
} else {
- Log.d(TAG, "Unknown result for confirmLock: " + resultCode);
+ Log.d(TAG, "Unknown result for set/choose lock: " + resultCode);
+ setResult(resultCode);
finish();
}
break;
default:
- Log.d(TAG, "Unknown requestCode: " + requestCode + ", finishing");
+ Log.w(TAG, "Unknown enrolling requestCode: " + requestCode + ", finishing");
finish();
}
} else {
@@ -236,18 +364,28 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
}
}
+ private static boolean isSuccessfulConfirmOrChooseCredential(int requestCode, int resultCode) {
+ final boolean okChoose = requestCode == REQUEST_CHOOSE_LOCK
+ && resultCode == ChooseLockPattern.RESULT_FINISHED;
+ final boolean okConfirm = requestCode == REQUEST_CONFIRM_LOCK
+ && resultCode == RESULT_OK;
+ return okChoose || okConfirm;
+ }
+
@Override
protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
- final int new_resid = SetupWizardUtils.getTheme(this, getIntent());
+ final int newResid = SetupWizardUtils.getTheme(this, getIntent());
theme.applyStyle(R.style.SetupWizardPartnerResource, true);
- super.onApplyThemeResource(theme, new_resid, first);
+ super.onApplyThemeResource(theme, newResid, first);
}
@Override
protected void onStop() {
super.onStop();
- if (mConfirmingCredentials || mMultiBiometricEnrollHelper != null) {
+ if (mConfirmingCredentials
+ || mMultiBiometricEnrollHelper != null
+ || mParentalConsentHelper != null) {
return;
}
@@ -257,22 +395,8 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
}
}
- private void setupForMultiBiometricEnroll() {
- final FingerprintManager fingerprintManager = getSystemService(FingerprintManager.class);
- final FaceManager faceManager = getSystemService(FaceManager.class);
- final List<FingerprintSensorPropertiesInternal> fpProperties =
- fingerprintManager.getSensorPropertiesInternal();
- final List<FaceSensorPropertiesInternal> faceProperties =
- faceManager.getSensorPropertiesInternal();
-
- // This would need to be updated for devices with multiple sensors of the same modality
- mIsFaceEnrollable = !faceProperties.isEmpty() &&
- faceManager.getEnrolledFaces(mUserId).size()
- < faceProperties.get(0).maxEnrollmentsPerUser;
- mIsFingerprintEnrollable = !fpProperties.isEmpty() &&
- fingerprintManager.getEnrolledFingerprints(mUserId).size()
- < fpProperties.get(0).maxEnrollmentsPerUser;
+ private void setOrConfirmCredentialsNow() {
if (!mConfirmingCredentials) {
mConfirmingCredentials = true;
if (!userHasPassword(mUserId)) {
@@ -283,11 +407,11 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
}
}
- private void startMultiBiometricEnroll(Intent data) {
+ private void updateGatekeeperPasswordHandle(@NonNull Intent data) {
mGkPwHandle = BiometricUtils.getGatekeeperPasswordHandle(data);
- mMultiBiometricEnrollHelper = new MultiBiometricEnrollHelper(this, mUserId,
- mIsFaceEnrollable, mIsFingerprintEnrollable, mGkPwHandle);
- mMultiBiometricEnrollHelper.startNextStep();
+ if (mParentalConsentHelper != null) {
+ mParentalConsentHelper.updateGatekeeperHandle(data);
+ }
}
private boolean userHasPassword(int userId) {
@@ -299,6 +423,7 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
private void launchChooseLock() {
Log.d(TAG, "launchChooseLock");
+
Intent intent = BiometricUtils.getChooseLockIntent(this, getIntent());
intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS, true);
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, true);
@@ -312,6 +437,7 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
private void launchConfirmLock() {
Log.d(TAG, "launchConfirmLock");
+
final ChooseLockSettingsHelper.Builder builder = new ChooseLockSettingsHelper.Builder(this);
builder.setRequestCode(REQUEST_CONFIRM_LOCK)
.setRequestGatekeeperPasswordHandle(true)
@@ -335,7 +461,7 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
* @param intent Enrollment activity that should be started (e.g. FaceEnrollIntroduction.class,
* etc).
*/
- private void launchEnrollActivity(@NonNull Intent intent) {
+ private void launchSingleSensorEnrollActivity(@NonNull Intent intent) {
intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
byte[] hardwareAuthToken = null;
if (this instanceof InternalActivity) {
@@ -351,7 +477,7 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
// If only device credential was specified, ask the user to only set that up.
intent = new Intent(this, ChooseLockGeneric.class);
intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS, true);
- launchEnrollActivity(intent);
+ launchSingleSensorEnrollActivity(intent);
}
private void launchFingerprintOnlyEnroll() {
@@ -363,12 +489,23 @@ public class BiometricEnrollActivity extends InstrumentedActivity {
} else {
intent = BiometricUtils.getFingerprintIntroIntent(this, getIntent());
}
- launchEnrollActivity(intent);
+ launchSingleSensorEnrollActivity(intent);
}
private void launchFaceOnlyEnroll() {
final Intent intent = BiometricUtils.getFaceIntroIntent(this, getIntent());
- launchEnrollActivity(intent);
+ launchSingleSensorEnrollActivity(intent);
+ }
+
+ private void launchFaceAndFingerprintEnroll() {
+ mMultiBiometricEnrollHelper = new MultiBiometricEnrollHelper(this, mUserId,
+ mIsFaceEnrollable, mIsFingerprintEnrollable, mGkPwHandle);
+ mMultiBiometricEnrollHelper.startNextStep();
+ }
+
+ private void launchHandoffToParent() {
+ final Intent intent = BiometricUtils.getHandoffToParentIntent(this, getIntent());
+ startActivityForResult(intent, REQUEST_HANDOFF_PARENT);
}
@Override
diff --git a/src/com/android/settings/biometrics/BiometricEnrollBase.java b/src/com/android/settings/biometrics/BiometricEnrollBase.java
index b62b35fe20..6e7d04f26b 100644
--- a/src/com/android/settings/biometrics/BiometricEnrollBase.java
+++ b/src/com/android/settings/biometrics/BiometricEnrollBase.java
@@ -26,6 +26,7 @@ import android.graphics.Color;
import android.os.Bundle;
import android.os.UserHandle;
import android.text.TextUtils;
+import android.util.Log;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -50,12 +51,15 @@ import com.google.android.setupdesign.util.ThemeHelper;
*/
public abstract class BiometricEnrollBase extends InstrumentedActivity {
+ private static final String TAG = "BiometricEnrollBase";
+
public static final String EXTRA_FROM_SETTINGS_SUMMARY = "from_settings_summary";
public static final String EXTRA_KEY_LAUNCHED_CONFIRM = "launched_confirm_lock";
public static final String EXTRA_KEY_REQUIRE_VISION = "accessibility_vision";
public static final String EXTRA_KEY_REQUIRE_DIVERSITY = "accessibility_diversity";
public static final String EXTRA_KEY_SENSOR_ID = "sensor_id";
public static final String EXTRA_KEY_CHALLENGE = "challenge";
+ public static final String EXTRA_KEY_MODALITY = "sensor_modality";
/**
* Used by the choose fingerprint wizard to indicate the wizard is
@@ -84,11 +88,26 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity {
*/
public static final int RESULT_TIMEOUT = RESULT_FIRST_USER + 2;
+ /**
+ * Used by consent screens to indicate that consent was granted. Extras, such as
+ * EXTRA_KEY_MODALITY, will be included in the result to provide details about the
+ * consent that was granted.
+ */
+ public static final int RESULT_CONSENT_GRANTED = RESULT_FIRST_USER + 3;
+
+ /**
+ * Used by consent screens to indicate that consent was denied. Extras, such as
+ * EXTRA_KEY_MODALITY, will be included in the result to provide details about the
+ * consent that was not granted.
+ */
+ public static final int RESULT_CONSENT_DENIED = RESULT_FIRST_USER + 4;
+
public static final int CHOOSE_LOCK_GENERIC_REQUEST = 1;
public static final int BIOMETRIC_FIND_SENSOR_REQUEST = 2;
public static final int LEARN_MORE_REQUEST = 3;
public static final int CONFIRM_REQUEST = 4;
public static final int ENROLL_REQUEST = 5;
+
/**
* Request code when starting another biometric enrollment from within a biometric flow. For
* example, when starting fingerprint enroll after face enroll.
@@ -242,6 +261,8 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity {
}
protected void launchConfirmLock(int titleResId) {
+ Log.d(TAG, "launchConfirmLock");
+
final ChooseLockSettingsHelper.Builder builder = new ChooseLockSettingsHelper.Builder(this);
builder.setRequestCode(CONFIRM_REQUEST)
.setTitle(getString(titleResId))
diff --git a/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java b/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java
index de850672bd..c073c3c2ea 100644
--- a/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java
@@ -29,6 +29,7 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.R;
@@ -36,11 +37,12 @@ import com.android.settings.SetupWizardUtils;
import com.android.settings.password.ChooseLockGeneric;
import com.android.settings.password.ChooseLockSettingsHelper;
+import com.google.android.setupcompat.template.FooterBarMixin;
import com.google.android.setupcompat.template.FooterButton;
import com.google.android.setupcompat.util.WizardManagerHelper;
+import com.google.android.setupdesign.GlifLayout;
import com.google.android.setupdesign.span.LinkSpan;
import com.google.android.setupdesign.template.RequireScrollMixin;
-import com.google.android.setupdesign.template.RequireScrollMixin.OnRequireScrollStateChangedListener;
import com.google.android.setupdesign.util.DynamicColorPalette;
/**
@@ -183,32 +185,26 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
}
}
- FooterButton primaryButton = getPrimaryFooterButton();
- FooterButton secondaryButton = getSecondaryFooterButton();
- if (primaryButton == null) {
- Log.d(TAG, "getPrimaryFooterButton() was null");
- return;
- }
-
- if (secondaryButton == null) {
- Log.d(TAG, "getSecondaryFooterButton() was null");
- return;
- }
-
- // Setup scroll mixin
- final RequireScrollMixin requireScrollMixin = getLayout().getMixin(
- RequireScrollMixin.class);
- requireScrollMixin.requireScrollWithButton(this, primaryButton, getScrollCompletedText(),
- this::onNextButtonClick);
+ final GlifLayout layout = getLayout();
+ mFooterBarMixin = layout.getMixin(FooterBarMixin.class);
+ mFooterBarMixin.setPrimaryButton(getPrimaryFooterButton());
+ mFooterBarMixin.setSecondaryButton(getSecondaryFooterButton(), true /* usePrimaryStyle */);
+ mFooterBarMixin.getSecondaryButton().setVisibility(View.INVISIBLE);
- secondaryButton.setVisibility(View.INVISIBLE);
+ final RequireScrollMixin requireScrollMixin = layout.getMixin(RequireScrollMixin.class);
+ requireScrollMixin.requireScrollWithButton(this, getPrimaryFooterButton(),
+ getMoreButtonTextRes(), this::onNextButtonClick);
requireScrollMixin.setOnRequireScrollStateChangedListener(
- new OnRequireScrollStateChangedListener() {
- @Override
- public void onRequireScrollStateChanged(boolean scrollNeeded) {
- if (!scrollNeeded && secondaryButton.getVisibility() == View.INVISIBLE) {
- secondaryButton.setVisibility(View.VISIBLE);
- }
+ scrollNeeded -> {
+ // Update text of primary button from "More" to "Agree".
+ final int primaryButtonTextRes = scrollNeeded
+ ? getMoreButtonTextRes()
+ : getAgreeButtonTextRes();
+ getPrimaryFooterButton().setText(this, primaryButtonTextRes);
+
+ // Show secondary button once scroll is completed.
+ if (!scrollNeeded) {
+ getSecondaryFooterButton().setVisibility(View.VISIBLE);
}
});
}
@@ -298,15 +294,19 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
mConfirmingCredentials = false;
if (resultCode == RESULT_FINISHED) {
updatePasswordQuality();
- overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out);
- getNextButton().setEnabled(false);
- getChallenge(((sensorId, userId, challenge) -> {
- mSensorId = sensorId;
- mChallenge = challenge;
- mToken = BiometricUtils.requestGatekeeperHat(this, data, mUserId, challenge);
- BiometricUtils.removeGatekeeperPasswordHandle(this, data);
- getNextButton().setEnabled(true);
- }));
+ final boolean handled = onSetOrConfirmCredentials(data);
+ if (!handled) {
+ overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out);
+ getNextButton().setEnabled(false);
+ getChallenge(((sensorId, userId, challenge) -> {
+ mSensorId = sensorId;
+ mChallenge = challenge;
+ mToken = BiometricUtils.requestGatekeeperHat(this, data, mUserId,
+ challenge);
+ BiometricUtils.removeGatekeeperPasswordHandle(this, data);
+ getNextButton().setEnabled(true);
+ }));
+ }
} else {
setResult(resultCode, data);
finish();
@@ -314,15 +314,19 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
} else if (requestCode == CONFIRM_REQUEST) {
mConfirmingCredentials = false;
if (resultCode == RESULT_OK && data != null) {
- overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out);
- getNextButton().setEnabled(false);
- getChallenge(((sensorId, userId, challenge) -> {
- mSensorId = sensorId;
- mChallenge = challenge;
- mToken = BiometricUtils.requestGatekeeperHat(this, data, mUserId, challenge);
- BiometricUtils.removeGatekeeperPasswordHandle(this, data);
- getNextButton().setEnabled(true);
- }));
+ final boolean handled = onSetOrConfirmCredentials(data);
+ if (!handled) {
+ overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out);
+ getNextButton().setEnabled(false);
+ getChallenge(((sensorId, userId, challenge) -> {
+ mSensorId = sensorId;
+ mChallenge = challenge;
+ mToken = BiometricUtils.requestGatekeeperHat(this, data, mUserId,
+ challenge);
+ BiometricUtils.removeGatekeeperPasswordHandle(this, data);
+ getNextButton().setEnabled(true);
+ }));
+ }
} else {
setResult(resultCode, data);
finish();
@@ -339,6 +343,18 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
super.onActivityResult(requestCode, resultCode, data);
}
+ /**
+ * Called after confirming credentials. Can be used to prevent the default
+ * behavior of immediately calling #getChallenge (useful to things like intro
+ * consent screens that don't actually do enrollment and will later start an
+ * activity that does).
+ *
+ * @return True if the default behavior should be skipped and handled by this method instead.
+ */
+ protected boolean onSetOrConfirmCredentials(@Nullable Intent data) {
+ return false;
+ }
+
protected void onCancelButtonClick(View view) {
finish();
}
@@ -367,17 +383,15 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
return mIconColorFilter;
}
- @Nullable
- protected FooterButton getPrimaryFooterButton() {
- return null;
- }
+ @NonNull
+ protected abstract FooterButton getPrimaryFooterButton();
- @Nullable
- protected FooterButton getSecondaryFooterButton() {
- return null;
- }
+ @NonNull
+ protected abstract FooterButton getSecondaryFooterButton();
- protected int getScrollCompletedText() {
- return R.string.security_settings_face_enroll_introduction_more;
- }
+ @StringRes
+ protected abstract int getAgreeButtonTextRes();
+
+ @StringRes
+ protected abstract int getMoreButtonTextRes();
}
diff --git a/src/com/android/settings/biometrics/BiometricHandoffActivity.java b/src/com/android/settings/biometrics/BiometricHandoffActivity.java
new file mode 100644
index 0000000000..7f28ced202
--- /dev/null
+++ b/src/com/android/settings/biometrics/BiometricHandoffActivity.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2021 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.biometrics;
+
+import android.app.settings.SettingsEnums;
+import android.os.Bundle;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.settings.R;
+
+import com.google.android.setupcompat.template.FooterBarMixin;
+import com.google.android.setupcompat.template.FooterButton;
+import com.google.android.setupdesign.GlifLayout;
+
+/**
+ * Prompts the user to hand the device to their parent or guardian.
+ */
+public class BiometricHandoffActivity extends BiometricEnrollBase {
+
+ @Nullable
+ private FooterButton mPrimaryFooterButton;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.biometric_handoff);
+
+ setHeaderText(R.string.biometric_settings_hand_back_to_guardian);
+
+ final GlifLayout layout = getLayout();
+ mFooterBarMixin = layout.getMixin(FooterBarMixin.class);
+ mFooterBarMixin.setPrimaryButton(getPrimaryFooterButton());
+ }
+
+ @NonNull
+ protected FooterButton getPrimaryFooterButton() {
+ if (mPrimaryFooterButton == null) {
+ mPrimaryFooterButton = new FooterButton.Builder(this)
+ .setText(R.string.biometric_settings_hand_back_to_guardian_ok)
+ .setButtonType(FooterButton.ButtonType.NEXT)
+ .setListener(this::onNextButtonClick)
+ .setTheme(R.style.SudGlifButton_Primary)
+ .build();
+ }
+ return mPrimaryFooterButton;
+ }
+
+ @Override
+ protected void onNextButtonClick(View view) {
+ setResult(RESULT_OK);
+ finish();
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.BIOMETRIC_CONSENT_PARENT_TO_CHILD;
+ }
+}
diff --git a/src/com/android/settings/biometrics/BiometricUtils.java b/src/com/android/settings/biometrics/BiometricUtils.java
index 03cdb0d653..a98c3555f7 100644
--- a/src/com/android/settings/biometrics/BiometricUtils.java
+++ b/src/com/android/settings/biometrics/BiometricUtils.java
@@ -177,6 +177,19 @@ public class BiometricUtils {
}
/**
+ * Start an activity that prompts the user to hand the device to their parent or guardian.
+ * @param context caller's context
+ * @param activityIntent The intent that started the caller's activity
+ * @return Intent for starting BiometricHandoffActivity
+ */
+ public static Intent getHandoffToParentIntent(@NonNull Context context,
+ @NonNull Intent activityIntent) {
+ final Intent intent = new Intent(context, BiometricHandoffActivity.class);
+ WizardManagerHelper.copyWizardManagerExtras(activityIntent, intent);
+ return intent;
+ }
+
+ /**
* @param activity Reference to the calling activity, used to startActivity
* @param intent Intent pointing to the enrollment activity
* @param requestCode If non-zero, will invoke startActivityForResult instead of startActivity
diff --git a/src/com/android/settings/biometrics/ParentalConsentHelper.java b/src/com/android/settings/biometrics/ParentalConsentHelper.java
new file mode 100644
index 0000000000..905a95527e
--- /dev/null
+++ b/src/com/android/settings/biometrics/ParentalConsentHelper.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2021 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.biometrics;
+
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
+
+import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_KEY_MODALITY;
+import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_CONSENT_DENIED;
+import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_CONSENT_GRANTED;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.settings.biometrics.face.FaceEnrollParentalConsent;
+import com.android.settings.biometrics.fingerprint.FingerprintEnrollParentalConsent;
+import com.android.settings.password.ChooseLockSettingsHelper;
+
+import com.google.android.setupcompat.util.WizardManagerHelper;
+
+/**
+ * Helper for {@link BiometricEnrollActivity} to ask for parental consent prior to actual user
+ * enrollment.
+ */
+public class ParentalConsentHelper {
+
+ private static final String KEY_FACE_CONSENT = "face";
+ private static final String KEY_FINGERPRINT_CONSENT = "fingerprint";
+
+ private final boolean mRequireFace;
+ private final boolean mRequireFingerprint;
+
+ private long mGkPwHandle;
+ @Nullable
+ private Boolean mConsentFace;
+ @Nullable
+ private Boolean mConsentFingerprint;
+
+ /**
+ * Helper for aggregating user consent.
+ *
+ * @param requireFace if face consent should be shown
+ * @param requireFingerprint if fingerprint consent should be shown
+ * @param gkPwHandle for launched intents
+ */
+ public ParentalConsentHelper(boolean requireFace, boolean requireFingerprint,
+ @Nullable Long gkPwHandle) {
+ mRequireFace = requireFace;
+ mRequireFingerprint = requireFingerprint;
+ mGkPwHandle = gkPwHandle != null ? gkPwHandle : 0L;
+ }
+
+ /**
+ * Updated the handle used for launching activities
+ *
+ * @param data result intent for credential verification
+ */
+ public void updateGatekeeperHandle(Intent data) {
+ mGkPwHandle = BiometricUtils.getGatekeeperPasswordHandle(data);
+ }
+
+ /**
+ * Launch the next consent screen.
+ *
+ * @param activity root activity
+ * @param requestCode request code to launch new activity
+ * @param resultCode result code of the last consent launch
+ * @param data result data from the last consent launch
+ * @return true if a consent activity was launched or false when complete
+ */
+ public boolean launchNext(@NonNull Activity activity, int requestCode, int resultCode,
+ @Nullable Intent data) {
+ if (data != null) {
+ switch (data.getIntExtra(EXTRA_KEY_MODALITY, TYPE_NONE)) {
+ case TYPE_FACE:
+ mConsentFace = isConsent(resultCode, mConsentFace);
+ break;
+ case TYPE_FINGERPRINT:
+ mConsentFingerprint = isConsent(resultCode, mConsentFingerprint);
+ break;
+ }
+ }
+ return launchNext(activity, requestCode);
+ }
+
+ @Nullable
+ private static Boolean isConsent(int resultCode, @Nullable Boolean defaultValue) {
+ switch (resultCode) {
+ case RESULT_CONSENT_GRANTED:
+ return true;
+ case RESULT_CONSENT_DENIED:
+ return false;
+ }
+ return defaultValue;
+ }
+
+ /** @see #launchNext(Activity, int, int, Intent) */
+ public boolean launchNext(@NonNull Activity activity, int requestCode) {
+ final Intent intent = getNextConsentIntent(activity);
+ if (intent != null) {
+ WizardManagerHelper.copyWizardManagerExtras(activity.getIntent(), intent);
+ if (mGkPwHandle != 0) {
+ intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, mGkPwHandle);
+ }
+ activity.startActivityForResult(intent, requestCode);
+ return true;
+ }
+ return false;
+ }
+
+ @Nullable
+ private Intent getNextConsentIntent(@NonNull Context context) {
+ if (mRequireFace && mConsentFace == null) {
+ return new Intent(context, FaceEnrollParentalConsent.class);
+ }
+ if (mRequireFingerprint && mConsentFingerprint == null) {
+ return new Intent(context, FingerprintEnrollParentalConsent.class);
+ }
+ return null;
+ }
+
+ /**
+ * Get the result of all consent requests.
+ *
+ * This should be called when {@link #launchNext(Activity, int, int, Intent)} returns false
+ * to indicate that all responses have been recorded.
+ *
+ * @return The aggregate consent status.
+ */
+ @NonNull
+ public Bundle getConsentResult() {
+ final Bundle result = new Bundle();
+ result.putBoolean(KEY_FACE_CONSENT, mConsentFace != null ? mConsentFace : false);
+ result.putBoolean(KEY_FINGERPRINT_CONSENT,
+ mConsentFingerprint != null ? mConsentFingerprint : false);
+ return result;
+ }
+
+ /** @return If the result bundle contains consent for face authentication. */
+ public static boolean hasFaceConsent(@NonNull Bundle bundle) {
+ return bundle.getBoolean(KEY_FACE_CONSENT, false);
+ }
+
+ /** @return If the result bundle contains consent for fingerprint authentication. */
+ public static boolean hasFingerprintConsent(@NonNull Bundle bundle) {
+ return bundle.getBoolean(KEY_FINGERPRINT_CONSENT, false);
+ }
+}
diff --git a/src/com/android/settings/biometrics/ParentalControlsUtils.java b/src/com/android/settings/biometrics/ParentalControlsUtils.java
new file mode 100644
index 0000000000..0959184b16
--- /dev/null
+++ b/src/com/android/settings/biometrics/ParentalControlsUtils.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2021 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.biometrics;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.ParentalControlsUtilsInternal;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.RestrictedLockUtils;
+
+/**
+ * Utilities for things at the cross-section of biometrics and parental controls. For example,
+ * determining if parental consent is required, determining which strings should be shown, etc.
+ */
+public class ParentalControlsUtils {
+
+ private static final String TAG = "ParentalControlsUtils";
+
+ /**
+ * Public version that enables test paths, see
+ * {@link android.hardware.biometrics.ParentalControlsUtilsInternal#isTestModeEnabled(Context)}
+ * @return non-null EnforcedAdmin if parental consent is required
+ */
+ public static RestrictedLockUtils.EnforcedAdmin parentConsentRequired(@NonNull Context context,
+ @BiometricAuthenticator.Modality int modality) {
+
+ final UserHandle userHandle = new UserHandle(UserHandle.myUserId());
+ if (ParentalControlsUtilsInternal.isTestModeEnabled(context)) {
+ Log.d(TAG, "Requiring consent for test flow");
+ return new RestrictedLockUtils.EnforcedAdmin(null /* ComponentName */,
+ UserManager.DISALLOW_BIOMETRIC, userHandle);
+ }
+
+ final DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+ return parentConsentRequiredInternal(dpm, modality, userHandle);
+ }
+
+ /**
+ * Internal testable version.
+ * @return non-null EnforcedAdmin if parental consent is required
+ */
+ @Nullable
+ @VisibleForTesting
+ static RestrictedLockUtils.EnforcedAdmin parentConsentRequiredInternal(
+ @NonNull DevicePolicyManager dpm, @BiometricAuthenticator.Modality int modality,
+ @NonNull UserHandle userHandle) {
+ if (ParentalControlsUtilsInternal.parentConsentRequired(dpm, modality,
+ userHandle)) {
+ final ComponentName cn =
+ ParentalControlsUtilsInternal.getSupervisionComponentName(dpm, userHandle);
+ return new RestrictedLockUtils.EnforcedAdmin(cn, UserManager.DISALLOW_BIOMETRIC,
+ userHandle);
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/src/com/android/settings/biometrics/combination/CombinedBiometricStatusPreferenceController.java b/src/com/android/settings/biometrics/combination/CombinedBiometricStatusPreferenceController.java
index 6e989d8b5e..9723d92a03 100644
--- a/src/com/android/settings/biometrics/combination/CombinedBiometricStatusPreferenceController.java
+++ b/src/com/android/settings/biometrics/combination/CombinedBiometricStatusPreferenceController.java
@@ -16,15 +16,22 @@
package com.android.settings.biometrics.combination;
import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import androidx.annotation.Nullable;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.Settings;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricStatusPreferenceController;
+import com.android.settings.biometrics.ParentalControlsUtils;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedPreference;
/**
* Preference controller for biometrics settings page controlling the ability to unlock the phone
@@ -38,6 +45,8 @@ public class CombinedBiometricStatusPreferenceController extends
FingerprintManager mFingerprintManager;
@Nullable
FaceManager mFaceManager;
+ @VisibleForTesting
+ RestrictedPreference mPreference;
public CombinedBiometricStatusPreferenceController(Context context) {
this(context, KEY_BIOMETRIC_SETTINGS);
@@ -50,6 +59,12 @@ public class CombinedBiometricStatusPreferenceController extends
}
@Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreference = screen.findPreference(KEY_BIOMETRIC_SETTINGS);
+ }
+
+ @Override
protected boolean isDeviceSupported() {
return Utils.hasFingerprintHardware(mContext) && Utils.hasFaceHardware(mContext);
}
@@ -60,6 +75,24 @@ public class CombinedBiometricStatusPreferenceController extends
}
@Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ // This controller currently is shown if fingerprint&face exist on the device. If this
+ // changes in the future, the modalities passed into the below will need to be updated.
+ final RestrictedLockUtils.EnforcedAdmin admin = ParentalControlsUtils
+ .parentConsentRequired(mContext,
+ BiometricAuthenticator.TYPE_FACE | BiometricAuthenticator.TYPE_FINGERPRINT);
+ updateStateInternal(admin);
+ }
+
+ @VisibleForTesting
+ void updateStateInternal(@Nullable RestrictedLockUtils.EnforcedAdmin enforcedAdmin) {
+ if (enforcedAdmin != null && mPreference != null) {
+ mPreference.setDisabledByAdmin(enforcedAdmin);
+ }
+ }
+
+ @Override
protected String getSummaryTextEnrolled() {
// Note that this is currently never called (see the super class)
return mContext.getString(
diff --git a/src/com/android/settings/biometrics/face/BiometricLockscreenBypassPreferenceController.java b/src/com/android/settings/biometrics/face/BiometricLockscreenBypassPreferenceController.java
new file mode 100644
index 0000000000..7af7e3df9c
--- /dev/null
+++ b/src/com/android/settings/biometrics/face/BiometricLockscreenBypassPreferenceController.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 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.biometrics.face;
+
+import android.content.Context;
+
+import com.android.settings.Utils;
+
+/**
+ * Preference controller that controls whether unlocking directly to home.
+ */
+public class BiometricLockscreenBypassPreferenceController extends
+ FaceSettingsLockscreenBypassPreferenceController {
+ public BiometricLockscreenBypassPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ // When the device supports multiple biometrics auth, this preference will be shown
+ // in face unlock category.
+ if (Utils.isMultipleBiometricsSupported(mContext)) {
+ return AVAILABLE;
+ }
+ return UNSUPPORTED_ON_DEVICE;
+ }
+}
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
index b29e2840e2..91cc3a9bfd 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
@@ -26,6 +26,10 @@ import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricEnrollIntroduction;
@@ -34,7 +38,6 @@ import com.android.settings.overlay.FeatureFactory;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settingslib.RestrictedLockUtilsInternal;
-import com.google.android.setupcompat.template.FooterBarMixin;
import com.google.android.setupcompat.template.FooterButton;
import com.google.android.setupcompat.util.WizardManagerHelper;
import com.google.android.setupdesign.span.LinkSpan;
@@ -50,6 +53,8 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
private FaceManager mFaceManager;
private FaceFeatureProvider mFaceFeatureProvider;
+ @Nullable private FooterButton mPrimaryFooterButton;
+ @Nullable private FooterButton mSecondaryFooterButton;
@Override
protected void onCancelButtonClick(View view) {
@@ -71,31 +76,71 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
final ImageView iconGlasses = findViewById(R.id.icon_glasses);
final ImageView iconLooking = findViewById(R.id.icon_looking);
- final ImageView iconSecurity = findViewById(R.id.icon_security);
iconGlasses.getBackground().setColorFilter(getIconColorFilter());
iconLooking.getBackground().setColorFilter(getIconColorFilter());
- iconSecurity.getBackground().setColorFilter(getIconColorFilter());
+
+ final TextView infoMessageGlasses = findViewById(R.id.info_message_glasses);
+ final TextView infoMessageLooking = findViewById(R.id.info_message_looking);
+ final TextView howMessage = findViewById(R.id.how_message);
+ final TextView inControlTitle = findViewById(R.id.title_in_control);
+ final TextView inControlMessage = findViewById(R.id.message_in_control);
+ infoMessageGlasses.setText(getInfoMessageGlasses());
+ infoMessageLooking.setText(getInfoMessageLooking());
+ howMessage.setText(getHowMessage());
+ inControlTitle.setText(getInControlTitle());
+ inControlMessage.setText(getInControlMessage());
mFaceManager = Utils.getFaceManagerOrNull(this);
mFaceFeatureProvider = FeatureFactory.getFactory(getApplicationContext())
.getFaceFeatureProvider();
-
// This path is an entry point for SetNewPasswordController, e.g.
// adb shell am start -a android.app.action.SET_NEW_PASSWORD
if (mToken == null && BiometricUtils.containsGatekeeperPasswordHandle(getIntent())) {
- mFooterBarMixin.getPrimaryButton().setEnabled(false);
- // We either block on generateChallenge, or need to gray out the "next" button until
- // the challenge is ready. Let's just do this for now.
- mFaceManager.generateChallenge(mUserId, (sensorId, userId, challenge) -> {
- mToken = BiometricUtils.requestGatekeeperHat(this, getIntent(), mUserId, challenge);
- mSensorId = sensorId;
- mChallenge = challenge;
- mFooterBarMixin.getPrimaryButton().setEnabled(true);
- });
+ if (generateChallengeOnCreate()) {
+ mFooterBarMixin.getPrimaryButton().setEnabled(false);
+ // We either block on generateChallenge, or need to gray out the "next" button until
+ // the challenge is ready. Let's just do this for now.
+ mFaceManager.generateChallenge(mUserId, (sensorId, userId, challenge) -> {
+ mToken = BiometricUtils.requestGatekeeperHat(this, getIntent(), mUserId,
+ challenge);
+ mSensorId = sensorId;
+ mChallenge = challenge;
+ mFooterBarMixin.getPrimaryButton().setEnabled(true);
+ });
+ }
}
}
+ protected boolean generateChallengeOnCreate() {
+ return true;
+ }
+
+ @StringRes
+ protected int getInfoMessageGlasses() {
+ return R.string.security_settings_face_enroll_introduction_info_glasses;
+ }
+
+ @StringRes
+ protected int getInfoMessageLooking() {
+ return R.string.security_settings_face_enroll_introduction_info_looking;
+ }
+
+ @StringRes
+ protected int getHowMessage() {
+ return R.string.security_settings_face_enroll_introduction_how_message;
+ }
+
+ @StringRes
+ protected int getInControlTitle() {
+ return R.string.security_settings_face_enroll_introduction_control_title;
+ }
+
+ @StringRes
+ protected int getInControlMessage() {
+ return R.string.security_settings_face_enroll_introduction_control_message;
+ }
+
@Override
protected boolean isDisabledByAdmin() {
return RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled(
@@ -207,38 +252,42 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
}
@Override
+ @NonNull
protected FooterButton getPrimaryFooterButton() {
- if (mFooterBarMixin == null) {
- mFooterBarMixin = getLayout().getMixin(FooterBarMixin.class);
- }
-
- if (mFooterBarMixin.getPrimaryButton() == null) {
- final FooterButton nextButtonBuilder = new FooterButton.Builder(this)
+ if (mPrimaryFooterButton == null) {
+ mPrimaryFooterButton = new FooterButton.Builder(this)
.setText(R.string.security_settings_face_enroll_introduction_agree)
.setButtonType(FooterButton.ButtonType.OPT_IN)
.setListener(this::onNextButtonClick)
.setTheme(R.style.SudGlifButton_Primary)
.build();
- mFooterBarMixin.setPrimaryButton(nextButtonBuilder);
}
- return mFooterBarMixin.getPrimaryButton();
+ return mPrimaryFooterButton;
}
@Override
+ @NonNull
protected FooterButton getSecondaryFooterButton() {
- if (mFooterBarMixin == null) {
- mFooterBarMixin = getLayout().getMixin(FooterBarMixin.class);
- }
-
- if (mFooterBarMixin.getSecondaryButton() == null) {
- final FooterButton noThanksButton = new FooterButton.Builder(this)
+ if (mSecondaryFooterButton == null) {
+ mSecondaryFooterButton = new FooterButton.Builder(this)
.setText(R.string.security_settings_face_enroll_introduction_no_thanks)
.setListener(this::onSkipButtonClick)
.setButtonType(FooterButton.ButtonType.NEXT)
.setTheme(R.style.SudGlifButton_Primary)
.build();
- mFooterBarMixin.setSecondaryButton(noThanksButton, true /* usePrimaryStyle */);
}
- return mFooterBarMixin.getSecondaryButton();
+ return mSecondaryFooterButton;
+ }
+
+ @Override
+ @StringRes
+ protected int getAgreeButtonTextRes() {
+ return R.string.security_settings_fingerprint_enroll_introduction_agree;
+ }
+
+ @Override
+ @StringRes
+ protected int getMoreButtonTextRes() {
+ return R.string.security_settings_face_enroll_introduction_more;
}
}
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollParentalConsent.java b/src/com/android/settings/biometrics/face/FaceEnrollParentalConsent.java
new file mode 100644
index 0000000000..7a60a94ad5
--- /dev/null
+++ b/src/com/android/settings/biometrics/face/FaceEnrollParentalConsent.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2021 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.biometrics.face;
+
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
+
+import android.app.settings.SettingsEnums;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+
+import com.android.settings.R;
+
+/**
+ * Displays parental consent information for face authentication.
+ */
+public class FaceEnrollParentalConsent extends FaceEnrollIntroduction {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setDescriptionText(R.string.security_settings_face_enroll_introduction_consent_message);
+ }
+
+ @Override
+ protected void onNextButtonClick(View view) {
+ onConsentResult(true /* granted */);
+ }
+
+ @Override
+ protected void onSkipButtonClick(View view) {
+ onConsentResult(false /* granted */);
+ }
+
+ private void onConsentResult(boolean granted) {
+ final Intent result = new Intent();
+ result.putExtra(EXTRA_KEY_MODALITY, TYPE_FACE);
+ setResult(granted ? RESULT_CONSENT_GRANTED : RESULT_CONSENT_DENIED, result);
+ finish();
+ }
+
+ @Override
+ protected boolean onSetOrConfirmCredentials(@Nullable Intent data) {
+ // prevent challenge from being generated by default
+ return true;
+ }
+
+ @Override
+ protected boolean generateChallengeOnCreate() {
+ return false;
+ }
+
+ @Override
+ @StringRes
+ protected int getInfoMessageGlasses() {
+ return R.string.security_settings_face_enroll_introduction_info_consent_glasses;
+ }
+
+ @Override
+ @StringRes
+ protected int getInfoMessageLooking() {
+ return R.string.security_settings_face_enroll_introduction_info_consent_looking;
+ }
+
+ @Override
+ @StringRes
+ protected int getHowMessage() {
+ return R.string.security_settings_face_enroll_introduction_how_consent_message;
+ }
+
+ @Override
+ @StringRes
+ protected int getInControlTitle() {
+ return R.string.security_settings_face_enroll_introduction_control_consent_title;
+ }
+
+ @Override
+ @StringRes
+ protected int getInControlMessage() {
+ return R.string.security_settings_face_enroll_introduction_control_consent_message;
+ }
+
+ @Override
+ protected int getHeaderResDefault() {
+ return R.string.security_settings_face_enroll_consent_introduction_title;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.FACE_PARENTAL_CONSENT;
+ }
+}
diff --git a/src/com/android/settings/biometrics/face/FaceSettingsLockscreenBypassPreferenceController.java b/src/com/android/settings/biometrics/face/FaceSettingsLockscreenBypassPreferenceController.java
index 2f0ef4f123..c6ef87f92c 100644
--- a/src/com/android/settings/biometrics/face/FaceSettingsLockscreenBypassPreferenceController.java
+++ b/src/com/android/settings/biometrics/face/FaceSettingsLockscreenBypassPreferenceController.java
@@ -83,10 +83,10 @@ public class FaceSettingsLockscreenBypassPreferenceController
@Override
public int getAvailabilityStatus() {
- // When the device supports multiple biometrics auth, this preference will be shown
+ // When the device supports multiple biometrics auth, this preference won't be shown
// in face unlock category.
if (Utils.isMultipleBiometricsSupported(mContext)) {
- return AVAILABLE;
+ return UNSUPPORTED_ON_DEVICE;
}
if (mUserManager.isManagedProfile(UserHandle.myUserId())) {
return UNSUPPORTED_ON_DEVICE;
diff --git a/src/com/android/settings/biometrics/face/FaceStatusPreferenceController.java b/src/com/android/settings/biometrics/face/FaceStatusPreferenceController.java
index 2ceaa0d7db..2b130ae244 100644
--- a/src/com/android/settings/biometrics/face/FaceStatusPreferenceController.java
+++ b/src/com/android/settings/biometrics/face/FaceStatusPreferenceController.java
@@ -17,18 +17,29 @@
package com.android.settings.biometrics.face;
import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.face.FaceManager;
+import androidx.annotation.Nullable;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.Settings;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricStatusPreferenceController;
+import com.android.settings.biometrics.ParentalControlsUtils;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedPreference;
public class FaceStatusPreferenceController extends BiometricStatusPreferenceController {
public static final String KEY_FACE_SETTINGS = "face_settings";
protected final FaceManager mFaceManager;
+ @VisibleForTesting
+ RestrictedPreference mPreference;
public FaceStatusPreferenceController(Context context) {
this(context, KEY_FACE_SETTINGS);
@@ -40,6 +51,12 @@ public class FaceStatusPreferenceController extends BiometricStatusPreferenceCon
}
@Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreference = screen.findPreference(KEY_FACE_SETTINGS);
+ }
+
+ @Override
protected boolean isDeviceSupported() {
return !Utils.isMultipleBiometricsSupported(mContext) && Utils.hasFaceHardware(mContext);
}
@@ -50,6 +67,21 @@ public class FaceStatusPreferenceController extends BiometricStatusPreferenceCon
}
@Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ final RestrictedLockUtils.EnforcedAdmin admin = ParentalControlsUtils
+ .parentConsentRequired(mContext, BiometricAuthenticator.TYPE_FACE);
+ updateStateInternal(admin);
+ }
+
+ @VisibleForTesting
+ void updateStateInternal(@Nullable RestrictedLockUtils.EnforcedAdmin enforcedAdmin) {
+ if (enforcedAdmin != null && mPreference != null) {
+ mPreference.setDisabledByAdmin(enforcedAdmin);
+ }
+ }
+
+ @Override
protected String getSummaryTextEnrolled() {
return mContext.getResources()
.getString(R.string.security_settings_face_preference_summary);
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
index 8a070ab46b..8c3b1ceb89 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
@@ -27,6 +27,10 @@ import android.util.Log;
import android.widget.ImageView;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricEnrollIntroduction;
@@ -35,7 +39,6 @@ import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settingslib.HelpUtils;
import com.android.settingslib.RestrictedLockUtilsInternal;
-import com.google.android.setupcompat.template.FooterBarMixin;
import com.google.android.setupcompat.template.FooterButton;
import com.google.android.setupdesign.span.LinkSpan;
@@ -46,6 +49,8 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
private static final String TAG = "FingerprintIntro";
private FingerprintManager mFingerprintManager;
+ @Nullable private FooterButton mPrimaryFooterButton;
+ @Nullable private FooterButton mSecondaryFooterButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -59,21 +64,62 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
super.onCreate(savedInstanceState);
final ImageView iconFingerprint = findViewById(R.id.icon_fingerprint);
- final ImageView iconLocked = findViewById(R.id.icon_locked);
- final ImageView iconDelete = findViewById(R.id.icon_delete);
final ImageView iconInfo = findViewById(R.id.icon_info);
final ImageView iconLink = findViewById(R.id.icon_link);
iconFingerprint.getDrawable().setColorFilter(getIconColorFilter());
- iconLocked.getDrawable().setColorFilter(getIconColorFilter());
- iconDelete.getDrawable().setColorFilter(getIconColorFilter());
iconInfo.getDrawable().setColorFilter(getIconColorFilter());
iconLink.getDrawable().setColorFilter(getIconColorFilter());
+
+ final TextView footerMessage2 = findViewById(R.id.footer_message_2);
+ final TextView footerMessage3 = findViewById(R.id.footer_message_3);
+ final TextView footerMessage4 = findViewById(R.id.footer_message_4);
+ final TextView footerMessage5 = findViewById(R.id.footer_message_5);
+ footerMessage2.setText(getFooterMessage2());
+ footerMessage3.setText(getFooterMessage3());
+ footerMessage4.setText(getFooterMessage4());
+ footerMessage5.setText(getFooterMessage5());
+
+ final TextView footerTitle1 = findViewById(R.id.footer_title_1);
+ final TextView footerTitle2 = findViewById(R.id.footer_title_2);
+ footerTitle1.setText(getFooterTitle1());
+ footerTitle2.setText(getFooterTitle2());
}
+ @StringRes
int getNegativeButtonTextId() {
return R.string.security_settings_fingerprint_enroll_introduction_skip;
}
+ @StringRes
+ protected int getFooterTitle1() {
+ return R.string.security_settings_fingerprint_enroll_introduction_footer_title_1;
+ }
+
+ @StringRes
+ protected int getFooterTitle2() {
+ return R.string.security_settings_fingerprint_enroll_introduction_footer_title_2;
+ }
+
+ @StringRes
+ protected int getFooterMessage2() {
+ return R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_2;
+ }
+
+ @StringRes
+ protected int getFooterMessage3() {
+ return R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_3;
+ }
+
+ @StringRes
+ protected int getFooterMessage4() {
+ return R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_4;
+ }
+
+ @StringRes
+ protected int getFooterMessage5() {
+ return R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_5;
+ }
+
@Override
protected boolean isDisabledByAdmin() {
return RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled(
@@ -194,43 +240,42 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
}
@Override
+ @NonNull
protected FooterButton getPrimaryFooterButton() {
- if (mFooterBarMixin == null) {
- mFooterBarMixin = getLayout().getMixin(FooterBarMixin.class);
- }
-
- if (mFooterBarMixin.getPrimaryButton() == null) {
- final FooterButton nextButtonBuilder = new FooterButton.Builder(this)
+ if (mPrimaryFooterButton == null) {
+ mPrimaryFooterButton = new FooterButton.Builder(this)
.setText(R.string.security_settings_fingerprint_enroll_introduction_agree)
.setListener(this::onNextButtonClick)
.setButtonType(FooterButton.ButtonType.OPT_IN)
.setTheme(R.style.SudGlifButton_Primary)
.build();
- mFooterBarMixin.setPrimaryButton(nextButtonBuilder);
}
- return mFooterBarMixin.getPrimaryButton();
+ return mPrimaryFooterButton;
}
@Override
+ @NonNull
protected FooterButton getSecondaryFooterButton() {
- if (mFooterBarMixin == null) {
- mFooterBarMixin = getLayout().getMixin(FooterBarMixin.class);
- }
-
- if (mFooterBarMixin.getSecondaryButton() == null) {
- final FooterButton noThanksButton = new FooterButton.Builder(this)
+ if (mSecondaryFooterButton == null) {
+ mSecondaryFooterButton = new FooterButton.Builder(this)
.setText(getNegativeButtonTextId())
.setListener(this::onSkipButtonClick)
.setButtonType(FooterButton.ButtonType.NEXT)
.setTheme(R.style.SudGlifButton_Primary)
.build();
- mFooterBarMixin.setSecondaryButton(noThanksButton, true /* usePrimaryStyle */);
}
- return mFooterBarMixin.getSecondaryButton();
+ return mSecondaryFooterButton;
+ }
+
+ @Override
+ @StringRes
+ protected int getAgreeButtonTextRes() {
+ return R.string.security_settings_fingerprint_enroll_introduction_agree;
}
@Override
- protected int getScrollCompletedText() {
+ @StringRes
+ protected int getMoreButtonTextRes() {
return R.string.security_settings_face_enroll_introduction_more;
}
}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollParentalConsent.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollParentalConsent.java
new file mode 100644
index 0000000000..636e703bae
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollParentalConsent.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2021 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.biometrics.fingerprint;
+
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+
+import android.app.settings.SettingsEnums;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+
+import com.android.settings.R;
+
+/**
+ * Displays parental consent information for fingerprint authentication.
+ */
+public class FingerprintEnrollParentalConsent extends FingerprintEnrollIntroduction {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setDescriptionText(
+ R.string.security_settings_fingerprint_enroll_introduction_consent_message);
+ }
+
+ @Override
+ protected void onNextButtonClick(View view) {
+ onConsentResult(true /* granted */);
+ }
+
+ @Override
+ protected void onSkipButtonClick(View view) {
+ onConsentResult(false /* granted */);
+ }
+
+ private void onConsentResult(boolean granted) {
+ final Intent result = new Intent();
+ result.putExtra(EXTRA_KEY_MODALITY, TYPE_FINGERPRINT);
+ setResult(granted ? RESULT_CONSENT_GRANTED : RESULT_CONSENT_DENIED, result);
+ finish();
+ }
+
+ @Override
+ protected boolean onSetOrConfirmCredentials(@Nullable Intent data) {
+ // prevent challenge from being generated by default
+ return true;
+ }
+
+ @StringRes
+ @Override
+ protected int getFooterTitle1() {
+ return R.string.security_settings_fingerprint_enroll_introduction_footer_title_consent_1;
+ }
+
+ @StringRes
+ @Override
+ protected int getFooterMessage2() {
+ return R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_consent_2;
+ }
+
+ @StringRes
+ @Override
+ protected int getFooterMessage3() {
+ return R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_consent_3;
+ }
+
+ @StringRes
+ protected int getFooterMessage4() {
+ return R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_consent_4;
+ }
+
+ @StringRes
+ protected int getFooterMessage5() {
+ return R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_consent_5;
+ }
+
+ @Override
+ protected int getHeaderResDefault() {
+ return R.string.security_settings_fingerprint_enroll_consent_introduction_title;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.FINGERPRINT_PARENTAL_CONSENT;
+ }
+}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintErrorDialog.java b/src/com/android/settings/biometrics/fingerprint/FingerprintErrorDialog.java
index 416e805682..e4d86a199e 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintErrorDialog.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintErrorDialog.java
@@ -45,6 +45,8 @@ public class FingerprintErrorDialog extends BiometricErrorDialog {
// This message happens when the underlying crypto layer decides to revoke the
// enrollment auth token.
return R.string.security_settings_fingerprint_enroll_error_timeout_dialog_message;
+ case FingerprintManager.FINGERPRINT_ERROR_BAD_CALIBARTION:
+ return R.string.security_settings_fingerprint_bad_calibration;
default:
// There's nothing specific to tell the user about. Ask them to try again.
return R.string.security_settings_fingerprint_enroll_error_generic_dialog_message;
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintStatusPreferenceController.java b/src/com/android/settings/biometrics/fingerprint/FingerprintStatusPreferenceController.java
index e53dbc411a..5166bae274 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintStatusPreferenceController.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintStatusPreferenceController.java
@@ -17,17 +17,28 @@
package com.android.settings.biometrics.fingerprint;
import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.fingerprint.FingerprintManager;
+import androidx.annotation.Nullable;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricStatusPreferenceController;
+import com.android.settings.biometrics.ParentalControlsUtils;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedPreference;
public class FingerprintStatusPreferenceController extends BiometricStatusPreferenceController {
private static final String KEY_FINGERPRINT_SETTINGS = "fingerprint_settings";
protected final FingerprintManager mFingerprintManager;
+ @VisibleForTesting
+ RestrictedPreference mPreference;
public FingerprintStatusPreferenceController(Context context) {
this(context, KEY_FINGERPRINT_SETTINGS);
@@ -39,6 +50,12 @@ public class FingerprintStatusPreferenceController extends BiometricStatusPrefer
}
@Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreference = screen.findPreference(KEY_FINGERPRINT_SETTINGS);
+ }
+
+ @Override
protected boolean isDeviceSupported() {
return !Utils.isMultipleBiometricsSupported(mContext)
&& Utils.hasFingerprintHardware(mContext);
@@ -50,6 +67,21 @@ public class FingerprintStatusPreferenceController extends BiometricStatusPrefer
}
@Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ final RestrictedLockUtils.EnforcedAdmin admin = ParentalControlsUtils
+ .parentConsentRequired(mContext, BiometricAuthenticator.TYPE_FINGERPRINT);
+ updateStateInternal(admin);
+ }
+
+ @VisibleForTesting
+ void updateStateInternal(@Nullable RestrictedLockUtils.EnforcedAdmin enforcedAdmin) {
+ if (enforcedAdmin != null && mPreference != null) {
+ mPreference.setDisabledByAdmin(enforcedAdmin);
+ }
+ }
+
+ @Override
protected String getSummaryTextEnrolled() {
final int numEnrolled = mFingerprintManager.getEnrolledFingerprints(getUserId()).size();
return mContext.getResources().getQuantityString(
diff --git a/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java b/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java
index 5dd769db13..ce980e0bda 100644
--- a/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java
+++ b/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java
@@ -50,11 +50,6 @@ public class ConnectedDeviceDashboardFragment extends DashboardFragment {
}
@Override
- protected boolean isParalleledControllers() {
- return true;
- }
-
- @Override
public int getHelpResource() {
return R.string.help_url_connected_devices;
}
diff --git a/src/com/android/settings/core/CategoryMixin.java b/src/com/android/settings/core/CategoryMixin.java
new file mode 100644
index 0000000000..8d0a412a60
--- /dev/null
+++ b/src/com/android/settings/core/CategoryMixin.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2021 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.core;
+
+import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE;
+import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
+
+import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.AsyncTask;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.OnLifecycleEvent;
+
+import com.android.settings.dashboard.CategoryManager;
+import com.android.settingslib.drawer.Tile;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A mixin that handles live categories for Injection
+ */
+public class CategoryMixin implements LifecycleObserver {
+
+ private static final String TAG = "CategoryMixin";
+ private static final String DATA_SCHEME_PKG = "package";
+
+ // Serves as a temporary list of tiles to ignore until we heard back from the PM that they
+ // are disabled.
+ private static final ArraySet<ComponentName> sTileDenylist = new ArraySet<>();
+
+ private final Context mContext;
+ private final PackageReceiver mPackageReceiver = new PackageReceiver();
+ private final List<CategoryListener> mCategoryListeners = new ArrayList<>();
+ private int mCategoriesUpdateTaskCount;
+
+ public CategoryMixin(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Resume Lifecycle event
+ */
+ @OnLifecycleEvent(ON_RESUME)
+ public void onResume() {
+ final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+ filter.addDataScheme(DATA_SCHEME_PKG);
+ mContext.registerReceiver(mPackageReceiver, filter);
+
+ updateCategories();
+ }
+
+ /**
+ * Pause Lifecycle event
+ */
+ @OnLifecycleEvent(ON_PAUSE)
+ public void onPause() {
+ mContext.unregisterReceiver(mPackageReceiver);
+ }
+
+ /**
+ * Add a category listener
+ */
+ public void addCategoryListener(CategoryListener listener) {
+ mCategoryListeners.add(listener);
+ }
+
+ /**
+ * Remove a category listener
+ */
+ public void removeCategoryListener(CategoryListener listener) {
+ mCategoryListeners.remove(listener);
+ }
+
+ /**
+ * Updates dashboard categories.
+ */
+ public void updateCategories() {
+ updateCategories(false /* fromBroadcast */);
+ }
+
+ void addToDenylist(ComponentName component) {
+ sTileDenylist.add(component);
+ }
+
+ void removeFromDenylist(ComponentName component) {
+ sTileDenylist.remove(component);
+ }
+
+ @VisibleForTesting
+ void onCategoriesChanged(Set<String> categories) {
+ mCategoryListeners.forEach(listener -> listener.onCategoriesChanged(categories));
+ }
+
+ private void updateCategories(boolean fromBroadcast) {
+ // Only allow at most 2 tasks existing at the same time since when the first one is
+ // executing, there may be new data from the second update request.
+ // Ignore the third update request because the second task is still waiting for the first
+ // task to complete in a serial thread, which will get the latest data.
+ if (mCategoriesUpdateTaskCount < 2) {
+ new CategoriesUpdateTask().execute(fromBroadcast);
+ }
+ }
+
+ /**
+ * A handler implementing a {@link CategoryMixin}
+ */
+ public interface CategoryHandler {
+ /** returns a {@link CategoryMixin} */
+ CategoryMixin getCategoryMixin();
+ }
+
+ /**
+ * A listener receiving category change events.
+ */
+ public interface CategoryListener {
+ /**
+ * @param categories the changed categories that have to be refreshed, or null to force
+ * refreshing all.
+ */
+ void onCategoriesChanged(@Nullable Set<String> categories);
+ }
+
+ private class CategoriesUpdateTask extends AsyncTask<Boolean, Void, Set<String>> {
+
+ private final CategoryManager mCategoryManager;
+ private Map<ComponentName, Tile> mPreviousTileMap;
+
+ CategoriesUpdateTask() {
+ mCategoriesUpdateTaskCount++;
+ mCategoryManager = CategoryManager.get(mContext);
+ }
+
+ @Override
+ protected Set<String> doInBackground(Boolean... params) {
+ mPreviousTileMap = mCategoryManager.getTileByComponentMap();
+ mCategoryManager.reloadAllCategories(mContext);
+ mCategoryManager.updateCategoryFromDenylist(sTileDenylist);
+ return getChangedCategories(params[0]);
+ }
+
+ @Override
+ protected void onPostExecute(Set<String> categories) {
+ if (categories == null || !categories.isEmpty()) {
+ onCategoriesChanged(categories);
+ }
+ mCategoriesUpdateTaskCount--;
+ }
+
+ // Return the changed categories that have to be refreshed, or null to force refreshing all.
+ private Set<String> getChangedCategories(boolean fromBroadcast) {
+ if (!fromBroadcast) {
+ // Always refresh for non-broadcast case.
+ return null;
+ }
+
+ final Set<String> changedCategories = new ArraySet<>();
+ final Map<ComponentName, Tile> currentTileMap =
+ mCategoryManager.getTileByComponentMap();
+ currentTileMap.forEach((component, currentTile) -> {
+ final Tile previousTile = mPreviousTileMap.get(component);
+ // Check if the tile is newly added.
+ if (previousTile == null) {
+ Log.i(TAG, "Tile added: " + component.flattenToShortString());
+ changedCategories.add(currentTile.getCategory());
+ return;
+ }
+
+ // Check if the title or summary has changed.
+ if (!TextUtils.equals(currentTile.getTitle(mContext),
+ previousTile.getTitle(mContext))
+ || !TextUtils.equals(currentTile.getSummary(mContext),
+ previousTile.getSummary(mContext))) {
+ Log.i(TAG, "Tile changed: " + component.flattenToShortString());
+ changedCategories.add(currentTile.getCategory());
+ }
+ });
+
+ // Check if any previous tile is removed.
+ final Set<ComponentName> removal = new ArraySet(mPreviousTileMap.keySet());
+ removal.removeAll(currentTileMap.keySet());
+ removal.forEach(component -> {
+ Log.i(TAG, "Tile removed: " + component.flattenToShortString());
+ changedCategories.add(mPreviousTileMap.get(component).getCategory());
+ });
+
+ return changedCategories;
+ }
+ }
+
+ private class PackageReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ updateCategories(true /* fromBroadcast */);
+ }
+ }
+}
diff --git a/src/com/android/settings/core/SettingsBaseActivity.java b/src/com/android/settings/core/SettingsBaseActivity.java
index 6dba83b8af..fb6b49f966 100644
--- a/src/com/android/settings/core/SettingsBaseActivity.java
+++ b/src/com/android/settings/core/SettingsBaseActivity.java
@@ -16,52 +16,39 @@
package com.android.settings.core;
import android.annotation.LayoutRes;
-import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.ActivityOptions;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.TypedArray;
-import android.os.AsyncTask;
import android.os.Bundle;
-import android.os.UserHandle;
import android.text.TextUtils;
-import android.util.ArraySet;
import android.util.Log;
import android.view.LayoutInflater;
-import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.Toolbar;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.fragment.app.FragmentActivity;
import com.android.settings.R;
import com.android.settings.SubSettings;
-import com.android.settings.Utils;
-import com.android.settings.dashboard.CategoryManager;
+import com.android.settings.core.CategoryMixin.CategoryHandler;
import com.android.settingslib.core.lifecycle.HideNonSystemOverlayMixin;
-import com.android.settingslib.drawer.Tile;
-import com.android.settingslib.transition.SettingsTransitionHelper;
import com.android.settingslib.transition.SettingsTransitionHelper.TransitionType;
+import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout;
import com.google.android.material.resources.TextAppearanceConfig;
import com.google.android.setupcompat.util.WizardManagerHelper;
import com.google.android.setupdesign.util.ThemeHelper;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-public class SettingsBaseActivity extends FragmentActivity {
+/** Base activity for Settings pages */
+public class SettingsBaseActivity extends FragmentActivity implements CategoryHandler {
/**
* What type of page transition should be apply.
@@ -70,29 +57,20 @@ public class SettingsBaseActivity extends FragmentActivity {
protected static final boolean DEBUG_TIMING = false;
private static final String TAG = "SettingsBaseActivity";
- private static final String DATA_SCHEME_PKG = "package";
private static final int DEFAULT_REQUEST = -1;
- // Serves as a temporary list of tiles to ignore until we heard back from the PM that they
- // are disabled.
- private static ArraySet<ComponentName> sTileDenylist = new ArraySet<>();
-
- private final PackageReceiver mPackageReceiver = new PackageReceiver();
- private final List<CategoryListener> mCategoryListeners = new ArrayList<>();
-
+ protected CategoryMixin mCategoryMixin;
protected CollapsingToolbarLayout mCollapsingToolbarLayout;
- private int mCategoriesUpdateTaskCount;
+ protected AppBarLayout mAppBarLayout;
private Toolbar mToolbar;
@Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- if (Utils.isPageTransitionEnabled(this)) {
- // Enable Activity transitions
- getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
- SettingsTransitionHelper.applyForwardTransition(this);
- SettingsTransitionHelper.applyBackwardTransition(this);
- }
+ public CategoryMixin getCategoryMixin() {
+ return mCategoryMixin;
+ }
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (isLockTaskModePinned() && !isSettingsRunOnTop()) {
Log.w(TAG, "Devices lock task mode pinned.");
@@ -102,6 +80,9 @@ public class SettingsBaseActivity extends FragmentActivity {
getLifecycle().addObserver(new HideNonSystemOverlayMixin(this));
TextAppearanceConfig.setShouldLoadFontSynchronously(true);
+ mCategoryMixin = new CategoryMixin(this);
+ getLifecycle().addObserver(mCategoryMixin);
+
final TypedArray theme = getTheme().obtainStyledAttributes(android.R.styleable.Theme);
if (!theme.getBoolean(android.R.styleable.Theme_windowNoTitle, false)) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
@@ -118,6 +99,8 @@ public class SettingsBaseActivity extends FragmentActivity {
if (isToolbarEnabled() && !isAnySetupWizard) {
super.setContentView(R.layout.collapsing_toolbar_base_layout);
mCollapsingToolbarLayout = findViewById(R.id.collapsing_toolbar);
+ mAppBarLayout = findViewById(R.id.app_bar);
+ disableCollapsingToolbarLayoutScrollingBehavior();
} else {
super.setContentView(R.layout.settings_base_layout);
}
@@ -151,27 +134,9 @@ public class SettingsBaseActivity extends FragmentActivity {
}
@Override
- public boolean onOptionsItemSelected(@NonNull MenuItem item) {
- final int id = item.getItemId();
- if (id == android.R.id.home) {
- // Make the up button behave the same as the back button.
- finishAfterTransition();
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
-
- @Override
public void startActivityForResult(Intent intent, int requestCode,
@androidx.annotation.Nullable Bundle options) {
final int transitionType = getTransitionType(intent);
- if (Utils.isPageTransitionEnabled(this) &&
- transitionType == TransitionType.TRANSITION_SHARED_AXIS) {
- super.startActivityForResult(intent, requestCode,
- createActivityOptionsBundleForTransition(options));
- return;
- }
-
super.startActivityForResult(intent, requestCode, options);
if (transitionType == TransitionType.TRANSITION_SLIDE) {
overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out);
@@ -181,48 +146,14 @@ public class SettingsBaseActivity extends FragmentActivity {
}
@Override
- public void startActivityForResultAsUser(Intent intent, int requestCode,
- UserHandle userHandle) {
- if (!Utils.isPageTransitionEnabled(this) || requestCode == DEFAULT_REQUEST) {
- super.startActivityForResultAsUser(intent, requestCode, userHandle);
- return;
- }
- super.startActivityForResultAsUser(intent, requestCode,
- createActivityOptionsBundleForTransition(null),
- userHandle);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
- filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
- filter.addDataScheme(DATA_SCHEME_PKG);
- registerReceiver(mPackageReceiver, filter);
-
- updateCategories();
- }
-
- @Override
protected void onPause() {
// For accessibility activities launched from setup wizard.
if (getTransitionType(getIntent()) == TransitionType.TRANSITION_FADE) {
overridePendingTransition(R.anim.sud_stay, android.R.anim.fade_out);
}
- unregisterReceiver(mPackageReceiver);
super.onPause();
}
- public void addCategoryListener(CategoryListener listener) {
- mCategoryListeners.add(listener);
- }
-
- public void remCategoryListener(CategoryListener listener) {
- mCategoryListeners.remove(listener);
- }
-
@Override
public void setContentView(@LayoutRes int layoutResID) {
final ViewGroup parent = findViewById(R.id.content_frame);
@@ -270,13 +201,6 @@ public class SettingsBaseActivity extends FragmentActivity {
return true;
}
- private void onCategoriesChanged(Set<String> categories) {
- final int N = mCategoryListeners.size();
- for (int i = 0; i < N; i++) {
- mCategoryListeners.get(i).onCategoriesChanged(categories);
- }
- }
-
private boolean isLockTaskModePinned() {
final ActivityManager activityManager =
getApplicationContext().getSystemService(ActivityManager.class);
@@ -300,9 +224,9 @@ public class SettingsBaseActivity extends FragmentActivity {
boolean isEnabled = state == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
if (isEnabled != enabled || state == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
if (enabled) {
- sTileDenylist.remove(component);
+ mCategoryMixin.removeFromDenylist(component);
} else {
- sTileDenylist.add(component);
+ mCategoryMixin.addToDenylist(component);
}
pm.setComponentEnabledSetting(component, enabled
? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
@@ -313,126 +237,21 @@ public class SettingsBaseActivity extends FragmentActivity {
return false;
}
- /**
- * Updates dashboard categories. Only necessary to call this after setTileEnabled
- */
- public void updateCategories() {
- updateCategories(false /* fromBroadcast */);
- }
-
- private void updateCategories(boolean fromBroadcast) {
- // Only allow at most 2 tasks existing at the same time since when the first one is
- // executing, there may be new data from the second update request.
- // Ignore the third update request because the second task is still waiting for the first
- // task to complete in a serial thread, which will get the latest data.
- if (mCategoriesUpdateTaskCount < 2) {
- new CategoriesUpdateTask().execute(fromBroadcast);
- }
+ private void disableCollapsingToolbarLayoutScrollingBehavior() {
+ final CoordinatorLayout.LayoutParams params =
+ (CoordinatorLayout.LayoutParams) mAppBarLayout.getLayoutParams();
+ final AppBarLayout.Behavior behavior = new AppBarLayout.Behavior();
+ behavior.setDragCallback(
+ new AppBarLayout.Behavior.DragCallback() {
+ @Override
+ public boolean canDrag(@NonNull AppBarLayout appBarLayout) {
+ return false;
+ }
+ });
+ params.setBehavior(behavior);
}
private int getTransitionType(Intent intent) {
- return intent.getIntExtra(EXTRA_PAGE_TRANSITION_TYPE,
- SettingsTransitionHelper.TransitionType.TRANSITION_SHARED_AXIS);
- }
-
- @androidx.annotation.Nullable
- private Bundle createActivityOptionsBundleForTransition(
- @androidx.annotation.Nullable Bundle options) {
- if (mToolbar == null) {
- Log.w(TAG, "setActionBar(Toolbar) is not called. Cannot apply settings transition!");
- return options;
- }
- final Bundle transitionOptions = ActivityOptions.makeSceneTransitionAnimation(this,
- mToolbar, "shared_element_view").toBundle();
- if (options == null) {
- return transitionOptions;
- }
- final Bundle mergedOptions = new Bundle(options);
- mergedOptions.putAll(transitionOptions);
- return mergedOptions;
- }
-
- public interface CategoryListener {
- /**
- * @param categories the changed categories that have to be refreshed, or null to force
- * refreshing all.
- */
- void onCategoriesChanged(@Nullable Set<String> categories);
- }
-
- private class CategoriesUpdateTask extends AsyncTask<Boolean, Void, Set<String>> {
-
- private final Context mContext;
- private final CategoryManager mCategoryManager;
- private Map<ComponentName, Tile> mPreviousTileMap;
-
- public CategoriesUpdateTask() {
- mCategoriesUpdateTaskCount++;
- mContext = SettingsBaseActivity.this;
- mCategoryManager = CategoryManager.get(mContext);
- }
-
- @Override
- protected Set<String> doInBackground(Boolean... params) {
- mPreviousTileMap = mCategoryManager.getTileByComponentMap();
- mCategoryManager.reloadAllCategories(mContext);
- mCategoryManager.updateCategoryFromDenylist(sTileDenylist);
- return getChangedCategories(params[0]);
- }
-
- @Override
- protected void onPostExecute(Set<String> categories) {
- if (categories == null || !categories.isEmpty()) {
- onCategoriesChanged(categories);
- }
- mCategoriesUpdateTaskCount--;
- }
-
- // Return the changed categories that have to be refreshed, or null to force refreshing all.
- private Set<String> getChangedCategories(boolean fromBroadcast) {
- if (!fromBroadcast) {
- // Always refresh for non-broadcast case.
- return null;
- }
-
- final Set<String> changedCategories = new ArraySet<>();
- final Map<ComponentName, Tile> currentTileMap =
- mCategoryManager.getTileByComponentMap();
- currentTileMap.forEach((component, currentTile) -> {
- final Tile previousTile = mPreviousTileMap.get(component);
- // Check if the tile is newly added.
- if (previousTile == null) {
- Log.i(TAG, "Tile added: " + component.flattenToShortString());
- changedCategories.add(currentTile.getCategory());
- return;
- }
-
- // Check if the title or summary has changed.
- if (!TextUtils.equals(currentTile.getTitle(mContext),
- previousTile.getTitle(mContext))
- || !TextUtils.equals(currentTile.getSummary(mContext),
- previousTile.getSummary(mContext))) {
- Log.i(TAG, "Tile changed: " + component.flattenToShortString());
- changedCategories.add(currentTile.getCategory());
- }
- });
-
- // Check if any previous tile is removed.
- final Set<ComponentName> removal = new ArraySet(mPreviousTileMap.keySet());
- removal.removeAll(currentTileMap.keySet());
- removal.forEach(component -> {
- Log.i(TAG, "Tile removed: " + component.flattenToShortString());
- changedCategories.add(mPreviousTileMap.get(component).getCategory());
- });
-
- return changedCategories;
- }
- }
-
- private class PackageReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- updateCategories(true /* fromBroadcast */);
- }
+ return intent.getIntExtra(EXTRA_PAGE_TRANSITION_TYPE, TransitionType.TRANSITION_NONE);
}
}
diff --git a/src/com/android/settings/core/SubSettingLauncher.java b/src/com/android/settings/core/SubSettingLauncher.java
index 0a6966fe17..616d72f3c2 100644
--- a/src/com/android/settings/core/SubSettingLauncher.java
+++ b/src/com/android/settings/core/SubSettingLauncher.java
@@ -28,7 +28,6 @@ import androidx.fragment.app.Fragment;
import com.android.settings.SettingsActivity;
import com.android.settings.SubSettings;
-import com.android.settings.Utils;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.transition.SettingsTransitionHelper.TransitionType;
@@ -182,10 +181,6 @@ public class SubSettingLauncher {
@VisibleForTesting
void launchAsUser(Intent intent, UserHandle userHandle) {
- if (!Utils.isPageTransitionEnabled(mContext)) {
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
- }
mContext.startActivityAsUser(intent, userHandle);
}
diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java
index e0c9820042..dfd931db7e 100644
--- a/src/com/android/settings/dashboard/DashboardFragment.java
+++ b/src/com/android/settings/dashboard/DashboardFragment.java
@@ -22,7 +22,6 @@ import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.ArrayMap;
-import android.util.FeatureFlagUtils;
import android.util.Log;
import androidx.annotation.CallSuper;
@@ -36,9 +35,9 @@ import androidx.preference.SwitchPreference;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.core.BasePreferenceController;
-import com.android.settings.core.FeatureFlags;
+import com.android.settings.core.CategoryMixin.CategoryHandler;
+import com.android.settings.core.CategoryMixin.CategoryListener;
import com.android.settings.core.PreferenceControllerListHelper;
-import com.android.settings.core.SettingsBaseActivity;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.widget.PrimarySwitchPreference;
import com.android.settingslib.core.AbstractPreferenceController;
@@ -63,8 +62,7 @@ import java.util.concurrent.ExecutionException;
* Base fragment for dashboard style UI containing a list of static and dynamic setting items.
*/
public abstract class DashboardFragment extends SettingsPreferenceFragment
- implements SettingsBaseActivity.CategoryListener, Indexable,
- PreferenceGroup.OnExpandButtonClickListener,
+ implements CategoryListener, Indexable, PreferenceGroup.OnExpandButtonClickListener,
BasePreferenceController.UiBlockListener {
public static final String CATEGORY = "category";
private static final String TAG = "DashboardFragment";
@@ -200,9 +198,9 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
return;
}
final Activity activity = getActivity();
- if (activity instanceof SettingsBaseActivity) {
+ if (activity instanceof CategoryHandler) {
mListeningToCategoryChange = true;
- ((SettingsBaseActivity) activity).addCategoryListener(this);
+ ((CategoryHandler) activity).getCategoryMixin().addCategoryListener(this);
}
final ContentResolver resolver = getContentResolver();
mDashboardTilePrefKeys.values().stream()
@@ -245,8 +243,8 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
unregisterDynamicDataObservers(new ArrayList<>(mRegisteredObservers));
if (mListeningToCategoryChange) {
final Activity activity = getActivity();
- if (activity instanceof SettingsBaseActivity) {
- ((SettingsBaseActivity) activity).remCategoryListener(this);
+ if (activity instanceof CategoryHandler) {
+ ((CategoryHandler) activity).getCategoryMixin().removeCategoryListener(this);
}
mListeningToCategoryChange = false;
}
@@ -360,11 +358,6 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
* Update state of each preference managed by PreferenceController.
*/
protected void updatePreferenceStates() {
- if (isParalleledControllers() && FeatureFlagUtils.isEnabled(getContext(),
- FeatureFlags.CONTROLLER_ENHANCEMENT)) {
- updatePreferenceStatesInParallel();
- return;
- }
final PreferenceScreen screen = getPreferenceScreen();
Collection<List<AbstractPreferenceController>> controllerLists =
mPreferenceControllers.values();
@@ -396,6 +389,8 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment
* Use parallel method to update state of each preference managed by PreferenceController.
*/
@VisibleForTesting
+ // To use this parallel approach will cause the side effect of the UI flicker. Such as
+ // the thumb sliding of the toggle button.
void updatePreferenceStatesInParallel() {
final PreferenceScreen screen = getPreferenceScreen();
final Collection<List<AbstractPreferenceController>> controllerLists =
diff --git a/src/com/android/settings/datausage/AppDataUsage.java b/src/com/android/settings/datausage/AppDataUsage.java
index 5c5900e8f3..460f3909f7 100644
--- a/src/com/android/settings/datausage/AppDataUsage.java
+++ b/src/com/android/settings/datausage/AppDataUsage.java
@@ -39,10 +39,8 @@ import androidx.loader.content.Loader;
import androidx.preference.Preference;
import androidx.preference.Preference.OnPreferenceChangeListener;
import androidx.preference.PreferenceCategory;
-import androidx.recyclerview.widget.RecyclerView;
import com.android.settings.R;
-import com.android.settings.Utils;
import com.android.settings.applications.AppInfoBase;
import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.AppItem;
@@ -223,14 +221,6 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC
}
LoaderManager.getInstance(this).restartLoader(LOADER_APP_USAGE_DATA, null /* args */,
mUidDataCallbacks);
-
- if (Utils.isPageTransitionEnabled(mContext)) {
- final RecyclerView recyclerView = getListView();
- if (recyclerView != null) {
- recyclerView.setItemAnimator(null);
- }
- }
-
updatePrefs();
}
diff --git a/src/com/android/settings/datetime/timezone/BaseTimeZonePicker.java b/src/com/android/settings/datetime/timezone/BaseTimeZonePicker.java
index 6032abd9b1..6ce9f6f9ae 100644
--- a/src/com/android/settings/datetime/timezone/BaseTimeZonePicker.java
+++ b/src/com/android/settings/datetime/timezone/BaseTimeZonePicker.java
@@ -27,6 +27,8 @@ import android.widget.LinearLayout;
import android.widget.SearchView;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -95,6 +97,7 @@ public abstract class BaseTimeZonePicker extends InstrumentedFragment
LinearLayoutManager.VERTICAL, /* reverseLayout */ false));
mRecyclerView.setAdapter(mAdapter);
mAppBarLayout = getActivity().findViewById(R.id.app_bar);
+ disableToolBarScrollableBehavior();
// Initialize TimeZoneDataLoader only when mRecyclerView is ready to avoid race
// during onDateLoaderReady callback.
@@ -160,13 +163,15 @@ public abstract class BaseTimeZonePicker extends InstrumentedFragment
public boolean onMenuItemActionExpand(MenuItem item) {
// To prevent a large space on tool bar.
mAppBarLayout.setExpanded(false /*expanded*/, false /*animate*/);
- // To prevent user can expand the collpasing tool bar view.
+ // To prevent user can expand the collapsing tool bar view.
ViewCompat.setNestedScrollingEnabled(mRecyclerView, false);
return true;
}
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
+ // We keep the collapsed status after user cancel the search function.
+ mAppBarLayout.setExpanded(false /*expanded*/, false /*animate*/);
ViewCompat.setNestedScrollingEnabled(mRecyclerView, true);
return true;
}
@@ -188,4 +193,17 @@ public abstract class BaseTimeZonePicker extends InstrumentedFragment
void onListItemClick(T item);
}
+ private void disableToolBarScrollableBehavior() {
+ CoordinatorLayout.LayoutParams params =
+ (CoordinatorLayout.LayoutParams) mAppBarLayout.getLayoutParams();
+ AppBarLayout.Behavior behavior = new AppBarLayout.Behavior();
+ behavior.setDragCallback(
+ new AppBarLayout.Behavior.DragCallback() {
+ @Override
+ public boolean canDrag(@NonNull AppBarLayout appBarLayout) {
+ return false;
+ }
+ });
+ params.setBehavior(behavior);
+ }
}
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index 7afce83a7d..fbab1fd124 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -402,11 +402,6 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
return mPreferenceControllers;
}
- @Override
- protected boolean isParalleledControllers() {
- return true;
- }
-
private void registerReceivers() {
LocalBroadcastManager.getInstance(getContext())
.registerReceiver(mEnableAdbReceiver, new IntentFilter(
diff --git a/src/com/android/settings/development/transcode/TranscodeNotificationPreferenceController.java b/src/com/android/settings/development/transcode/TranscodeNotificationPreferenceController.java
index e51f8ad5b6..fd5ec5aed3 100644
--- a/src/com/android/settings/development/transcode/TranscodeNotificationPreferenceController.java
+++ b/src/com/android/settings/development/transcode/TranscodeNotificationPreferenceController.java
@@ -37,7 +37,7 @@ public class TranscodeNotificationPreferenceController extends TogglePreferenceC
@Override
public boolean isChecked() {
- return SystemProperties.getBoolean(TRANSCODE_NOTIFICATION_SYS_PROP_KEY, true);
+ return SystemProperties.getBoolean(TRANSCODE_NOTIFICATION_SYS_PROP_KEY, false);
}
@Override
diff --git a/src/com/android/settings/deviceinfo/storage/StorageAsyncLoader.java b/src/com/android/settings/deviceinfo/storage/StorageAsyncLoader.java
index 0a67ef29a2..611ee24baa 100644
--- a/src/com/android/settings/deviceinfo/storage/StorageAsyncLoader.java
+++ b/src/com/android/settings/deviceinfo/storage/StorageAsyncLoader.java
@@ -122,7 +122,7 @@ public class StorageAsyncLoader
UserHandle.of(userId));
} catch (NameNotFoundException e) {
Log.e(TAG, "Not able to get Context for user ID " + userId);
- return 0;
+ return 0L;
}
try (Cursor cursor = perUserContext.getContentResolver().query(
@@ -131,9 +131,9 @@ public class StorageAsyncLoader
queryArgs,
null /* cancellationSignal */)) {
if (cursor == null) {
- return 0;
+ return 0L;
}
- return cursor.moveToFirst() ? cursor.getInt(0) : 0;
+ return cursor.moveToFirst() ? cursor.getLong(0) : 0L;
}
}
diff --git a/src/com/android/settings/display/AdaptiveSleepPreferenceController.java b/src/com/android/settings/display/AdaptiveSleepPreferenceController.java
index 356c76f47e..b21c6ac32e 100644
--- a/src/com/android/settings/display/AdaptiveSleepPreferenceController.java
+++ b/src/com/android/settings/display/AdaptiveSleepPreferenceController.java
@@ -90,6 +90,7 @@ public class AdaptiveSleepPreferenceController {
if (enforcedAdmin != null) {
mPreference.setDisabledByAdmin(enforcedAdmin);
} else {
+ mPreference.setChecked(isChecked());
mPreference.setEnabled(hasSufficientPermission(mPackageManager) && !isCameraLocked()
&& !isPowerSaveMode());
}
diff --git a/src/com/android/settings/display/ScreenTimeoutSettings.java b/src/com/android/settings/display/ScreenTimeoutSettings.java
index f79dc07ee0..18d10491e9 100644
--- a/src/com/android/settings/display/ScreenTimeoutSettings.java
+++ b/src/com/android/settings/display/ScreenTimeoutSettings.java
@@ -81,6 +81,7 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment implements
@Override
public void onReceive(Context context, Intent intent) {
mAdaptiveSleepBatterySaverPreferenceController.updateVisibility();
+ mAdaptiveSleepController.updatePreference();
}
};
diff --git a/src/com/android/settings/emergency/MoreSettingsPreferenceController.java b/src/com/android/settings/emergency/MoreSettingsPreferenceController.java
index 3c9ae6ed10..cbb95a6357 100644
--- a/src/com/android/settings/emergency/MoreSettingsPreferenceController.java
+++ b/src/com/android/settings/emergency/MoreSettingsPreferenceController.java
@@ -43,6 +43,7 @@ import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.BasePreferenceController;
+import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.widget.LayoutPreference;
import java.util.List;
@@ -57,6 +58,7 @@ public class MoreSettingsPreferenceController extends BasePreferenceController i
private static final String TAG = "MoreSettingsPrefCtrl";
@VisibleForTesting
Intent mIntent;
+ private LayoutPreference mPreference;
public MoreSettingsPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
@@ -82,8 +84,8 @@ public class MoreSettingsPreferenceController extends BasePreferenceController i
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
- final LayoutPreference pref = screen.findPreference(getPreferenceKey());
- final Button button = pref.findViewById(R.id.button);
+ mPreference = screen.findPreference(getPreferenceKey());
+ final Button button = mPreference.findViewById(R.id.button);
final Drawable icon = getIcon();
button.setText(getButtonText());
if (icon != null) {
@@ -109,6 +111,8 @@ public class MoreSettingsPreferenceController extends BasePreferenceController i
@Override
public void onClick(View v) {
+ FeatureFactory.getFactory(mContext).getMetricsFeatureProvider()
+ .logClickedPreference(mPreference, getMetricsCategory());
final Intent intent = new Intent(mIntent)
.addCategory(Intent.CATEGORY_LAUNCHER)
.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
@@ -170,5 +174,4 @@ public class MoreSettingsPreferenceController extends BasePreferenceController i
icon.draw(canvas);
return bitmap;
}
-
}
diff --git a/src/com/android/settings/enterprise/ActionDisabledByAdminDialog.java b/src/com/android/settings/enterprise/ActionDisabledByAdminDialog.java
index 717c5bcd75..ce7ad24205 100644
--- a/src/com/android/settings/enterprise/ActionDisabledByAdminDialog.java
+++ b/src/com/android/settings/enterprise/ActionDisabledByAdminDialog.java
@@ -37,7 +37,7 @@ public class ActionDisabledByAdminDialog extends Activity
final RestrictedLockUtils.EnforcedAdmin enforcedAdmin =
getAdminDetailsFromIntent(getIntent());
final String restriction = getRestrictionFromIntent(getIntent());
- mDialogHelper = new ActionDisabledByAdminDialogHelper(this);
+ mDialogHelper = new ActionDisabledByAdminDialogHelper(this, restriction);
mDialogHelper.prepareDialogBuilder(restriction, enforcedAdmin)
.setOnDismissListener(this)
.show();
diff --git a/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelper.java b/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelper.java
index 5da076736e..ccc74367cd 100644
--- a/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelper.java
+++ b/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelper.java
@@ -56,11 +56,16 @@ public final class ActionDisabledByAdminDialogHelper {
private final Activity mActivity;
public ActionDisabledByAdminDialogHelper(Activity activity) {
+ this(activity, null /* restriction */);
+ }
+
+ public ActionDisabledByAdminDialogHelper(Activity activity, String restriction) {
mActivity = activity;
mDialogView = (ViewGroup) LayoutInflater.from(mActivity).inflate(
R.layout.admin_support_details_dialog, null);
mActionDisabledByAdminController = ActionDisabledByAdminControllerFactory
- .createInstance(mActivity, new DeviceAdminStringProviderImpl(mActivity));
+ .createInstance(mActivity, restriction,
+ new DeviceAdminStringProviderImpl(mActivity));
}
private @UserIdInt int getEnforcementAdminUserId(@NonNull EnforcedAdmin admin) {
@@ -74,7 +79,9 @@ public final class ActionDisabledByAdminDialogHelper {
public AlertDialog.Builder prepareDialogBuilder(String restriction,
EnforcedAdmin enforcedAdmin) {
AlertDialog.Builder builder = new AlertDialog.Builder(mActivity)
- .setPositiveButton(R.string.okay, null)
+ .setPositiveButton(R.string.okay,
+ mActionDisabledByAdminController
+ .getPositiveButtonListener(mActivity, enforcedAdmin))
.setView(mDialogView);
prepareDialogBuilder(builder, restriction, enforcedAdmin);
return builder;
diff --git a/src/com/android/settings/enterprise/DeviceAdminStringProviderImpl.java b/src/com/android/settings/enterprise/DeviceAdminStringProviderImpl.java
index 68b202108b..5d11d4ab3a 100644
--- a/src/com/android/settings/enterprise/DeviceAdminStringProviderImpl.java
+++ b/src/com/android/settings/enterprise/DeviceAdminStringProviderImpl.java
@@ -79,4 +79,14 @@ class DeviceAdminStringProviderImpl implements DeviceAdminStringProvider {
public String getDisabledByPolicyTitleForFinancedDevice() {
return mContext.getString(R.string.disabled_by_policy_title_financed_device);
}
+
+ @Override
+ public String getDisabledBiometricsParentConsentTitle() {
+ return mContext.getString(R.string.disabled_by_policy_title_biometric_parental_consent);
+ }
+
+ @Override
+ public String getDisabledBiometricsParentConsentContent() {
+ return mContext.getString(R.string.disabled_by_policy_content_biometric_parental_consent);
+ }
}
diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
index ca228e6821..3319e1b149 100644
--- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
+++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
@@ -157,7 +157,8 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
/** Launches battery details page for an individual battery consumer. */
public static void startBatteryDetailPage(Activity caller,
- InstrumentedPreferenceFragment fragment, BatteryEntry entry, String usagePercent) {
+ InstrumentedPreferenceFragment fragment, BatteryEntry entry, String usagePercent,
+ boolean isValidToShowSummary) {
final LaunchBatteryDetailPageArgs launchArgs = new LaunchBatteryDetailPageArgs();
// configure the launch argument.
launchArgs.mUsagePercent = usagePercent;
@@ -166,8 +167,8 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
launchArgs.mUid = entry.getUid();
launchArgs.mIconId = entry.iconId;
launchArgs.mConsumedPower = (int) entry.getConsumedPower();
- launchArgs.mForegroundTimeMs = entry.getTimeInForegroundMs();
- launchArgs.mBackgroundTimeMs = entry.getTimeInBackgroundMs();
+ launchArgs.mForegroundTimeMs = isValidToShowSummary ? entry.getTimeInForegroundMs() : 0;
+ launchArgs.mBackgroundTimeMs = isValidToShowSummary ? entry.getTimeInBackgroundMs() : 0;
launchArgs.mIsUserEntry = entry.isUserEntry();
startBatteryDetailPage(caller, fragment, launchArgs);
}
diff --git a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
index 1dc572d91a..29872ac4d4 100644
--- a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
@@ -188,7 +188,7 @@ public class BatteryAppListPreferenceController extends AbstractPreferenceContro
PowerGaugePreference pgp = (PowerGaugePreference) preference;
BatteryEntry entry = pgp.getInfo();
AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity,
- mFragment, entry, pgp.getPercent());
+ mFragment, entry, pgp.getPercent(), /*isValidToShowSummary=*/ true);
return true;
}
return false;
@@ -359,9 +359,7 @@ public class BatteryAppListPreferenceController extends AbstractPreferenceContro
componentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
+ deviceConsumer.getCustomPowerComponentCount();
componentId++) {
- if (!showAllApps
- && mBatteryUtils.shouldHideCustomDevicePowerComponent(deviceConsumer,
- componentId)) {
+ if (!showAllApps) {
continue;
}
diff --git a/src/com/android/settings/fuelgauge/BatteryUtils.java b/src/com/android/settings/fuelgauge/BatteryUtils.java
index 52a4629a7e..d521ed166a 100644
--- a/src/com/android/settings/fuelgauge/BatteryUtils.java
+++ b/src/com/android/settings/fuelgauge/BatteryUtils.java
@@ -81,8 +81,6 @@ public class BatteryUtils {
private static final String TAG = "BatteryUtils";
- private static final double MIN_POWER_THRESHOLD_MILLI_AMP_HOURS = 0.002;
-
private static BatteryUtils sInstance;
private PackageManager mPackageManager;
@@ -180,8 +178,7 @@ public class BatteryUtils {
* battery consumption list.
*/
public boolean shouldHideUidBatteryConsumer(UidBatteryConsumer consumer, String[] packages) {
- return consumer.getConsumedPower() < MIN_POWER_THRESHOLD_MILLI_AMP_HOURS
- || mPowerUsageFeatureProvider.isTypeSystem(consumer.getUid(), packages)
+ return mPowerUsageFeatureProvider.isTypeSystem(consumer.getUid(), packages)
|| shouldHideUidBatteryConsumerUnconditionally(consumer, packages);
}
@@ -208,22 +205,11 @@ public class BatteryUtils {
case BatteryConsumer.POWER_COMPONENT_WIFI:
return true;
default:
- return consumer.getConsumedPower(powerComponentId)
- < MIN_POWER_THRESHOLD_MILLI_AMP_HOURS;
+ return false;
}
}
/**
- * Returns true if the specified device custom power component should be excluded from the
- * summary battery consumption list.
- */
- public boolean shouldHideCustomDevicePowerComponent(BatteryConsumer consumer,
- int customPowerComponentId) {
- return consumer.getConsumedPowerForCustomComponent(customPowerComponentId)
- < MIN_POWER_THRESHOLD_MILLI_AMP_HOURS;
- }
-
- /**
* Returns true if one the specified packages belongs to a hidden system module.
*/
public boolean isHiddenSystemModule(String[] packages) {
diff --git a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarController.java b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarController.java
index 3e62ea1450..173d946761 100644
--- a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarController.java
+++ b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarController.java
@@ -55,7 +55,8 @@ public class BatterySaverScheduleSeekBarController implements
public BatterySaverScheduleSeekBarController(Context context) {
mContext = context;
mSeekBarPreference = new SeekBarPreference(context);
- mSeekBarPreference.setLayoutResource(R.layout.battery_saver_schedule_percentage_seekbar);
+ mSeekBarPreference.setLayoutResource(R.layout.preference_widget_seekbar_settings);
+ mSeekBarPreference.setIconSpaceReserved(false);
mSeekBarPreference.setOnPreferenceChangeListener(this);
mSeekBarPreference.setContinuousUpdates(true);
mSeekBarPreference.setMax(MAX_SEEKBAR_VALUE);
@@ -71,8 +72,9 @@ public class BatterySaverScheduleSeekBarController implements
final int percentage = ((Integer) newValue) * 5;
Settings.Global.putInt(mContext.getContentResolver(), Global.LOW_POWER_MODE_TRIGGER_LEVEL,
percentage);
- preference.setTitle(mContext.getString(
- R.string.battery_saver_seekbar_title, Utils.formatPercentage(percentage)));
+ final CharSequence stateDescription = formatStateDescription(percentage);
+ preference.setTitle(stateDescription);
+ mSeekBarPreference.overrideSeekBarStateDescription(stateDescription);
return true;
}
@@ -92,9 +94,10 @@ public class BatterySaverScheduleSeekBarController implements
final int currentSeekbarValue = Math.max(threshold / 5, MIN_SEEKBAR_VALUE);
mSeekBarPreference.setVisible(true);
mSeekBarPreference.setProgress(currentSeekbarValue);
- mSeekBarPreference.setTitle(mContext.getString(
- R.string.battery_saver_seekbar_title,
- Utils.formatPercentage(currentSeekbarValue * 5)));
+ final CharSequence stateDescription = formatStateDescription(
+ currentSeekbarValue * 5);
+ mSeekBarPreference.setTitle(stateDescription);
+ mSeekBarPreference.overrideSeekBarStateDescription(stateDescription);
}
} else {
mSeekBarPreference.setVisible(false);
@@ -112,4 +115,9 @@ public class BatterySaverScheduleSeekBarController implements
mSeekBarPreference.setOrder(100);
screen.addPreference(mSeekBarPreference);
}
+
+ private CharSequence formatStateDescription(int percentage) {
+ return mContext.getString(R.string.battery_saver_seekbar_title,
+ Utils.formatPercentage(percentage));
+ }
}
diff --git a/src/com/android/settings/gestures/GesturePreferenceController.java b/src/com/android/settings/gestures/GesturePreferenceController.java
index 4fcf276375..c771abdce2 100644
--- a/src/com/android/settings/gestures/GesturePreferenceController.java
+++ b/src/com/android/settings/gestures/GesturePreferenceController.java
@@ -42,7 +42,10 @@ public abstract class GesturePreferenceController extends TogglePreferenceContro
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
if (isAvailable()) {
- mVideoPreference = screen.findPreference(getVideoPrefKey());
+ final Preference pref = screen.findPreference(getVideoPrefKey());
+ if (pref instanceof VideoPreference) {
+ mVideoPreference = screen.findPreference(getVideoPrefKey());
+ }
}
}
diff --git a/src/com/android/settings/gestures/OneHandedActionPullDownPrefController.java b/src/com/android/settings/gestures/OneHandedActionPullDownPrefController.java
new file mode 100644
index 0000000000..84ea8b6c45
--- /dev/null
+++ b/src/com/android/settings/gestures/OneHandedActionPullDownPrefController.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2021 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.gestures;
+
+import android.content.Context;
+import android.net.Uri;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+import com.android.settingslib.widget.RadioButtonPreference;
+
+/**
+ * The controller to handle one-handed mode pull screen into reach preference.
+ **/
+public class OneHandedActionPullDownPrefController extends BasePreferenceController
+ implements OneHandedSettingsUtils.TogglesCallback, LifecycleObserver, OnStart, OnStop {
+
+ private final OneHandedSettingsUtils mUtils;
+
+ private Preference mPreference;
+
+ public OneHandedActionPullDownPrefController(Context context, String key) {
+ super(context, key);
+ mUtils = new OneHandedSettingsUtils(context);
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ if (preference instanceof RadioButtonPreference) {
+ ((RadioButtonPreference) preference).setChecked(
+ !OneHandedSettingsUtils.isSwipeDownNotificationEnabled(mContext));
+ }
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return (OneHandedSettingsUtils.isSupportOneHandedMode()
+ && OneHandedSettingsUtils.canEnableController(mContext))
+ ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
+ }
+
+ @Override
+ public boolean handlePreferenceTreeClick(Preference preference) {
+ if (!getPreferenceKey().equals(preference.getKey())) {
+ return false;
+ }
+ OneHandedSettingsUtils.setSwipeDownNotificationEnabled(mContext, false);
+ if (preference instanceof RadioButtonPreference) {
+ ((RadioButtonPreference) preference).setChecked(true);
+ }
+ return true;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreference = screen.findPreference(getPreferenceKey());
+ }
+
+ @Override
+ public void onStart() {
+ mUtils.registerToggleAwareObserver(this);
+ }
+
+ @Override
+ public void onStop() {
+ mUtils.unregisterToggleAwareObserver();
+ }
+
+ @Override
+ public void onChange(Uri uri) {
+ if (mPreference == null) {
+ return;
+ }
+ if (uri.equals(OneHandedSettingsUtils.ONE_HANDED_MODE_ENABLED_URI)) {
+ mPreference.setEnabled(OneHandedSettingsUtils.canEnableController(mContext));
+ } else if (uri.equals(OneHandedSettingsUtils.SHOW_NOTIFICATION_ENABLED_URI)) {
+ updateState(mPreference);
+ }
+ }
+}
diff --git a/src/com/android/settings/gestures/OneHandedActionShowNotificationPrefController.java b/src/com/android/settings/gestures/OneHandedActionShowNotificationPrefController.java
new file mode 100644
index 0000000000..564429986e
--- /dev/null
+++ b/src/com/android/settings/gestures/OneHandedActionShowNotificationPrefController.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2021 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.gestures;
+
+import android.content.Context;
+import android.net.Uri;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+import com.android.settingslib.widget.RadioButtonPreference;
+
+/**
+ * The controller to handle one-handed mode show notification preference.
+ **/
+public class OneHandedActionShowNotificationPrefController extends BasePreferenceController
+ implements OneHandedSettingsUtils.TogglesCallback, LifecycleObserver, OnStart, OnStop {
+
+ private final OneHandedSettingsUtils mUtils;
+
+ private Preference mPreference;
+
+ public OneHandedActionShowNotificationPrefController(Context context, String key) {
+ super(context, key);
+ mUtils = new OneHandedSettingsUtils(context);
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ if (preference instanceof RadioButtonPreference) {
+ ((RadioButtonPreference) preference).setChecked(
+ OneHandedSettingsUtils.isSwipeDownNotificationEnabled(mContext));
+ }
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return (OneHandedSettingsUtils.isSupportOneHandedMode()
+ && OneHandedSettingsUtils.canEnableController(mContext))
+ ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
+ }
+
+ @Override
+ public boolean handlePreferenceTreeClick(Preference preference) {
+ if (!getPreferenceKey().equals(preference.getKey())) {
+ return false;
+ }
+ OneHandedSettingsUtils.setSwipeDownNotificationEnabled(mContext, true);
+ if (preference instanceof RadioButtonPreference) {
+ ((RadioButtonPreference) preference).setChecked(true);
+ }
+ return true;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreference = screen.findPreference(getPreferenceKey());
+ }
+
+ @Override
+ public void onStart() {
+ mUtils.registerToggleAwareObserver(this);
+ }
+
+ @Override
+ public void onStop() {
+ mUtils.unregisterToggleAwareObserver();
+ }
+
+ @Override
+ public void onChange(Uri uri) {
+ if (mPreference == null) {
+ return;
+ }
+ if (uri.equals(OneHandedSettingsUtils.ONE_HANDED_MODE_ENABLED_URI)) {
+ mPreference.setEnabled(OneHandedSettingsUtils.canEnableController(mContext));
+ } else if (uri.equals(OneHandedSettingsUtils.SHOW_NOTIFICATION_ENABLED_URI)) {
+ updateState(mPreference);
+ }
+ }
+}
diff --git a/src/com/android/settings/gestures/OneHandedEnablePreferenceController.java b/src/com/android/settings/gestures/OneHandedEnablePreferenceController.java
index 1e9c240115..bd8de9fdad 100644
--- a/src/com/android/settings/gestures/OneHandedEnablePreferenceController.java
+++ b/src/com/android/settings/gestures/OneHandedEnablePreferenceController.java
@@ -17,40 +17,32 @@
package com.android.settings.gestures;
import android.content.Context;
+import android.provider.Settings;
import com.android.settings.R;
-import com.android.settings.widget.SettingsMainSwitchPreferenceController;
+import com.android.settings.core.BasePreferenceController;
/**
- * The controller to handle one-handed mode enable or disable state.
- **/
-public class OneHandedEnablePreferenceController extends SettingsMainSwitchPreferenceController {
+ * Preference controller for One-handed mode shortcut settings
+ */
+public class OneHandedEnablePreferenceController extends BasePreferenceController {
- public OneHandedEnablePreferenceController(Context context, String key) {
- super(context, key);
- }
+ private static final String ONE_HANDED_ENABLED = Settings.Secure.ONE_HANDED_MODE_ENABLED;
- @Override
- public int getAvailabilityStatus() {
- return OneHandedSettingsUtils.isFeatureAvailable(mContext)
- ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ public OneHandedEnablePreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
}
@Override
- public boolean setChecked(boolean isChecked) {
- OneHandedSettingsUtils.setOneHandedModeEnabled(mContext, isChecked);
- OneHandedSettingsUtils.setSwipeDownNotificationEnabled(mContext, !isChecked);
- return true;
- }
-
- @Override
- public boolean isChecked() {
- return OneHandedSettingsUtils.isOneHandedModeEnabled(mContext);
+ public int getAvailabilityStatus() {
+ return OneHandedSettingsUtils.isSupportOneHandedMode() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
@Override
public CharSequence getSummary() {
return mContext.getText(
- isChecked() ? R.string.gesture_setting_on : R.string.gesture_setting_off);
+ OneHandedSettingsUtils.isOneHandedModeEnabled(mContext)
+ ? R.string.gesture_setting_on : R.string.gesture_setting_off);
}
+
}
diff --git a/src/com/android/settings/gestures/OneHandedAppTapsExitPreferenceController.java b/src/com/android/settings/gestures/OneHandedMainSwitchPreferenceController.java
index 1cc7911c02..2b7d7a0f30 100644
--- a/src/com/android/settings/gestures/OneHandedAppTapsExitPreferenceController.java
+++ b/src/com/android/settings/gestures/OneHandedMainSwitchPreferenceController.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -19,62 +19,58 @@ package com.android.settings.gestures;
import android.content.Context;
import android.net.Uri;
-import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.settings.core.TogglePreferenceController;
+import com.android.settings.widget.SettingsMainSwitchPreferenceController;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
+import com.android.settingslib.widget.MainSwitchPreference;
/**
- * The Controller to handle app taps to exit of one-handed mode
- */
-public class OneHandedAppTapsExitPreferenceController extends TogglePreferenceController implements
- LifecycleObserver, OnStart, OnStop, OneHandedSettingsUtils.TogglesCallback {
+ * The controller to handle one-handed mode main switch enable or disable state.
+ **/
+public class OneHandedMainSwitchPreferenceController extends
+ SettingsMainSwitchPreferenceController implements OneHandedSettingsUtils.TogglesCallback,
+ LifecycleObserver, OnStart, OnStop {
private final OneHandedSettingsUtils mUtils;
- private Preference mPreference;
-
- public OneHandedAppTapsExitPreferenceController(Context context, String key) {
- super(context, key);
+ private MainSwitchPreference mPreference;
+ public OneHandedMainSwitchPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
mUtils = new OneHandedSettingsUtils(context);
-
- // By default, app taps to stop one-handed is enabled, this will get default value once.
- OneHandedSettingsUtils.setTapsAppToExitEnabled(mContext, isChecked());
}
@Override
public int getAvailabilityStatus() {
- return OneHandedSettingsUtils.isOneHandedModeEnabled(mContext)
+ return (OneHandedSettingsUtils.isSupportOneHandedMode()
+ && OneHandedSettingsUtils.getNavigationBarMode(mContext) != 0 /* 3-button mode */)
? AVAILABLE : DISABLED_DEPENDENT_SETTING;
}
@Override
- public void displayPreference(PreferenceScreen screen) {
- super.displayPreference(screen);
- mPreference = screen.findPreference(getPreferenceKey());
- }
-
- @Override
- public void updateState(Preference preference) {
- super.updateState(preference);
-
- final int availabilityStatus = getAvailabilityStatus();
- preference.setEnabled(
- availabilityStatus == AVAILABLE || availabilityStatus == AVAILABLE_UNSEARCHABLE);
+ public boolean isChecked() {
+ return OneHandedSettingsUtils.isOneHandedModeEnabled(mContext);
}
@Override
public boolean setChecked(boolean isChecked) {
- return OneHandedSettingsUtils.setTapsAppToExitEnabled(mContext, isChecked);
+ if (isChecked) {
+ // Set default value for TapsAppToExit and Timeout
+ OneHandedSettingsUtils.setTapsAppToExitEnabled(mContext, true);
+ OneHandedSettingsUtils.setTimeoutValue(mContext,
+ OneHandedSettingsUtils.OneHandedTimeout.MEDIUM.getValue());
+ }
+ OneHandedSettingsUtils.setOneHandedModeEnabled(mContext, isChecked);
+ return true;
}
@Override
- public boolean isChecked() {
- return OneHandedSettingsUtils.isTapsAppToExitEnabled(mContext);
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreference = screen.findPreference(getPreferenceKey());
}
@Override
@@ -89,6 +85,11 @@ public class OneHandedAppTapsExitPreferenceController extends TogglePreferenceCo
@Override
public void onChange(Uri uri) {
- updateState(mPreference);
+ if (mPreference == null) {
+ return;
+ }
+ if (uri.equals(OneHandedSettingsUtils.ONE_HANDED_MODE_ENABLED_URI)) {
+ mPreference.setChecked(isChecked());
+ }
}
}
diff --git a/src/com/android/settings/gestures/OneHandedSettings.java b/src/com/android/settings/gestures/OneHandedSettings.java
index 11fc774cd3..6d1cbfd35d 100644
--- a/src/com/android/settings/gestures/OneHandedSettings.java
+++ b/src/com/android/settings/gestures/OneHandedSettings.java
@@ -17,21 +17,41 @@
package com.android.settings.gestures;
import android.app.settings.SettingsEnums;
+import android.content.ComponentName;
import android.content.Context;
+import android.os.Bundle;
import android.os.UserHandle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import com.android.internal.accessibility.AccessibilityShortcutController;
import com.android.settings.R;
-import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.accessibility.AccessibilityShortcutPreferenceFragment;
import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settingslib.search.SearchIndexable;
/**
- * The Fragment for one-handed mode settings.
+ * Fragment for One-handed mode settings
+ *
+ * <p>The child {@link AccessibilityShortcutPreferenceFragment} shows the actual UI for
+ * providing basic accessibility shortcut service setup.
*/
-@SearchIndexable
-public class OneHandedSettings extends DashboardFragment {
+public class OneHandedSettings extends AccessibilityShortcutPreferenceFragment {
+ private static final String ONE_HANDED_SHORTCUT_KEY = "one_handed_shortcuts_preference";
+ private String mFeatureName;
- private static final String TAG = "OneHandedSettings";
+ @Override
+ protected void updatePreferenceStates() {
+ OneHandedSettingsUtils.setUserId(UserHandle.myUserId());
+ super.updatePreferenceStates();
+ }
+
+ @Override
+ public int getDialogMetricsCategory(int dialogId) {
+ final int dialogMetrics = super.getDialogMetricsCategory(dialogId);
+ return dialogMetrics == SettingsEnums.ACTION_UNKNOWN ? SettingsEnums.SETTINGS_ONE_HANDED
+ : dialogMetrics;
+ }
@Override
public int getMetricsCategory() {
@@ -39,14 +59,29 @@ public class OneHandedSettings extends DashboardFragment {
}
@Override
- protected String getLogTag() {
- return TAG;
+ protected String getShortcutPreferenceKey() {
+ return ONE_HANDED_SHORTCUT_KEY;
}
@Override
- protected void updatePreferenceStates() {
- OneHandedSettingsUtils.setUserId(UserHandle.myUserId());
- super.updatePreferenceStates();
+ protected boolean showGeneralCategory() {
+ return true;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return super.onCreateView(inflater, container, savedInstanceState);
+ }
+
+ @Override
+ protected ComponentName getComponentName() {
+ return AccessibilityShortcutController.ONE_HANDED_COMPONENT_NAME;
+ }
+
+ @Override
+ protected CharSequence getLabelName() {
+ return mFeatureName;
}
@Override
@@ -54,6 +89,17 @@ public class OneHandedSettings extends DashboardFragment {
return R.xml.one_handed_settings;
}
+ @Override
+ protected String getLogTag() {
+ return null;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ mFeatureName = getContext().getString(R.string.one_handed_title);
+ super.onCreate(savedInstanceState);
+ }
+
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.one_handed_settings) {
@Override
diff --git a/src/com/android/settings/gestures/OneHandedSettingsUtils.java b/src/com/android/settings/gestures/OneHandedSettingsUtils.java
index 21998a6e21..a9311296b4 100644
--- a/src/com/android/settings/gestures/OneHandedSettingsUtils.java
+++ b/src/com/android/settings/gestures/OneHandedSettingsUtils.java
@@ -26,6 +26,8 @@ import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
+import androidx.annotation.VisibleForTesting;
+
/**
* The Util to query one-handed mode settings config
*/
@@ -34,6 +36,10 @@ public class OneHandedSettingsUtils {
static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
static final int OFF = 0;
static final int ON = 1;
+ static final Uri ONE_HANDED_MODE_ENABLED_URI =
+ Settings.Secure.getUriFor(Settings.Secure.ONE_HANDED_MODE_ENABLED);
+ static final Uri SHOW_NOTIFICATION_ENABLED_URI =
+ Settings.Secure.getUriFor(Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED);
public enum OneHandedTimeout {
NEVER(0), SHORT(4), MEDIUM(8), LONG(12);
@@ -177,6 +183,21 @@ public class OneHandedSettingsUtils {
}
/**
+ * Set NavigationBar mode flag to Settings provider.
+ * @param context App context
+ * @param value Navigation bar mode:
+ * 0 = 3 button
+ * 1 = 2 button
+ * 2 = fully gestural
+ * @return true if the value was set, false on database errors.
+ */
+ @VisibleForTesting
+ public boolean setNavigationBarMode(Context context, String value) {
+ return Settings.Secure.putStringForUser(context.getContentResolver(),
+ Settings.Secure.NAVIGATION_MODE, value, UserHandle.myUserId());
+ }
+
+ /**
* Get NavigationBar mode flag from Settings provider.
* @param context App context
* @return Navigation bar mode:
@@ -190,12 +211,17 @@ public class OneHandedSettingsUtils {
}
/**
- *
+ * Check if One-handed mode settings controllers can enabled or disabled.
* @param context App context
- * @return Support One-Handed mode feature or not.
+ * @return true if controllers are able to enabled, false otherwise.
+ *
+ * Note: For better UX experience, just disabled controls that let users know to use
+ * this feature, they need to make sure gesture navigation is turned on in system
+ * navigation settings.
*/
- public static boolean isFeatureAvailable(Context context) {
- return isSupportOneHandedMode() && getNavigationBarMode(context) != 0;
+ public static boolean canEnableController(Context context) {
+ return (OneHandedSettingsUtils.isOneHandedModeEnabled(context)
+ && OneHandedSettingsUtils.getNavigationBarMode(context) != 0 /* 3-button mode */);
}
/**
@@ -218,9 +244,6 @@ public class OneHandedSettingsUtils {
private final class SettingsObserver extends ContentObserver {
private TogglesCallback mCallback;
- private final Uri mOneHandedEnabledAware = Settings.Secure.getUriFor(
- Settings.Secure.ONE_HANDED_MODE_ENABLED);
-
SettingsObserver(Handler handler) {
super(handler);
}
@@ -231,7 +254,8 @@ public class OneHandedSettingsUtils {
public void observe() {
final ContentResolver resolver = mContext.getContentResolver();
- resolver.registerContentObserver(mOneHandedEnabledAware, true, this);
+ resolver.registerContentObserver(ONE_HANDED_MODE_ENABLED_URI, true, this);
+ resolver.registerContentObserver(SHOW_NOTIFICATION_ENABLED_URI, true, this);
}
@Override
diff --git a/src/com/android/settings/gestures/OneHandedTimeoutPreferenceController.java b/src/com/android/settings/gestures/OneHandedTimeoutPreferenceController.java
deleted file mode 100644
index 8ce0e86ffd..0000000000
--- a/src/com/android/settings/gestures/OneHandedTimeoutPreferenceController.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2020 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.gestures;
-
-import android.content.Context;
-import android.net.Uri;
-
-import androidx.preference.ListPreference;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
-
-import com.android.settings.R;
-import com.android.settings.core.BasePreferenceController;
-import com.android.settingslib.core.lifecycle.LifecycleObserver;
-import com.android.settingslib.core.lifecycle.events.OnStart;
-import com.android.settingslib.core.lifecycle.events.OnStop;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * The Controller to handle one-handed mode timeout state.
- **/
-public class OneHandedTimeoutPreferenceController extends BasePreferenceController implements
- Preference.OnPreferenceChangeListener, LifecycleObserver, OnStart, OnStop,
- OneHandedSettingsUtils.TogglesCallback {
-
- private final Map<String, String> mTimeoutMap;
- private final OneHandedSettingsUtils mUtils;
-
- private Preference mTimeoutPreference;
-
- public OneHandedTimeoutPreferenceController(Context context, String preferenceKey) {
- super(context, preferenceKey);
-
- mTimeoutMap = new HashMap<>();
- initTimeoutMap();
- mUtils = new OneHandedSettingsUtils(context);
- }
-
- @Override
- public int getAvailabilityStatus() {
- return OneHandedSettingsUtils.isOneHandedModeEnabled(mContext)
- ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
- }
-
- @Override
- public boolean onPreferenceChange(Preference preference, Object object) {
- if (!(preference instanceof ListPreference)) {
- return false;
- }
- final int newValue = Integer.parseInt((String) object);
- OneHandedSettingsUtils.setTimeoutValue(mContext, newValue);
- updateState(preference);
- return true;
- }
-
- @Override
- public void updateState(Preference preference) {
- super.updateState(preference);
- if (!(preference instanceof ListPreference)) {
- return;
- }
- final ListPreference listPreference = (ListPreference) preference;
- listPreference.setValue(getTimeoutValue());
-
- final int availabilityStatus = getAvailabilityStatus();
- preference.setEnabled(
- availabilityStatus == AVAILABLE || availabilityStatus == AVAILABLE_UNSEARCHABLE);
- }
-
- @Override
- public CharSequence getSummary() {
- if (OneHandedSettingsUtils.getTimeoutValue(mContext) == 0) {
- return mContext.getResources().getString(R.string.screensaver_settings_summary_never);
- }
- return String.format(mContext.getResources().getString(
- R.string.screen_timeout_summary), mTimeoutMap.get(getTimeoutValue()));
- }
-
- @Override
- public void displayPreference(PreferenceScreen screen) {
- super.displayPreference(screen);
- mTimeoutPreference = screen.findPreference(mPreferenceKey);
- }
-
- @Override
- public void onStart() {
- mUtils.registerToggleAwareObserver(this);
- }
-
- @Override
- public void onStop() {
- mUtils.unregisterToggleAwareObserver();
- }
-
- @Override
- public void onChange(Uri uri) {
- updateState(mTimeoutPreference);
- }
-
- private String getTimeoutValue() {
- return String.valueOf(OneHandedSettingsUtils.getTimeoutValue(mContext));
- }
-
- private void initTimeoutMap() {
- if (mTimeoutMap.size() != 0) {
- return;
- }
-
- final String[] timeoutValues = mContext.getResources().getStringArray(
- R.array.one_handed_timeout_values);
- final String[] timeoutTitles = mContext.getResources().getStringArray(
- R.array.one_handed_timeout_title);
-
- if (timeoutValues.length != timeoutTitles.length) {
- return;
- }
-
- for (int i = 0; i < timeoutValues.length; i++) {
- mTimeoutMap.put(timeoutValues[i], timeoutTitles[i]);
- }
- }
-}
diff --git a/src/com/android/settings/gestures/PreventRingingGesturePreferenceController.java b/src/com/android/settings/gestures/PreventRingingGesturePreferenceController.java
index 541bfe4576..2f89c16131 100644
--- a/src/com/android/settings/gestures/PreventRingingGesturePreferenceController.java
+++ b/src/com/android/settings/gestures/PreventRingingGesturePreferenceController.java
@@ -30,7 +30,6 @@ import androidx.preference.PreferenceScreen;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin;
-import com.android.settings.widget.VideoPreference;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
@@ -52,8 +51,6 @@ public class PreventRingingGesturePreferenceController extends AbstractPreferenc
private final String KEY = "gesture_prevent_ringing_category";
private final Context mContext;
- private VideoPreference mVideoPreference;
-
@VisibleForTesting
PreferenceCategory mPreferenceCategory;
@VisibleForTesting
@@ -85,8 +82,6 @@ public class PreventRingingGesturePreferenceController extends AbstractPreferenc
if (mPreferenceCategory != null) {
mSettingObserver = new SettingObserver(mPreferenceCategory);
}
-
- mVideoPreference = screen.findPreference(getVideoPrefKey());
}
@Override
@@ -142,10 +137,6 @@ public class PreventRingingGesturePreferenceController extends AbstractPreferenc
mSettingObserver.register(mContext.getContentResolver());
mSettingObserver.onChange(false, null);
}
-
- if (mVideoPreference != null) {
- mVideoPreference.onViewVisible();
- }
}
@Override
@@ -153,10 +144,6 @@ public class PreventRingingGesturePreferenceController extends AbstractPreferenc
if (mSettingObserver != null) {
mSettingObserver.unregister(mContext.getContentResolver());
}
-
- if (mVideoPreference != null) {
- mVideoPreference.onViewInvisible();
- }
}
private int keyToSetting(String key) {
diff --git a/src/com/android/settings/gestures/SwipeBottomToNotificationPreferenceController.java b/src/com/android/settings/gestures/SwipeBottomToNotificationPreferenceController.java
deleted file mode 100644
index ec81482dd6..0000000000
--- a/src/com/android/settings/gestures/SwipeBottomToNotificationPreferenceController.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2020 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.gestures;
-
-
-import android.content.Context;
-
-import com.android.settings.R;
-import com.android.settings.core.TogglePreferenceController;
-
-/**
- * Handles swipe bottom to expand notification panel gesture.
- **/
-public class SwipeBottomToNotificationPreferenceController extends TogglePreferenceController {
-
- public SwipeBottomToNotificationPreferenceController(Context context, String key) {
- super(context, key);
- }
-
- @Override
- public int getAvailabilityStatus() {
- return OneHandedSettingsUtils.isFeatureAvailable(mContext)
- ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
- }
-
- @Override
- public boolean isSliceable() {
- return true;
- }
-
- @Override
- public boolean isPublicSlice() {
- return true;
- }
-
- @Override
- public boolean setChecked(boolean isChecked) {
- if (isChecked) {
- OneHandedSettingsUtils.setOneHandedModeEnabled(mContext, false);
- }
- OneHandedSettingsUtils.setSwipeDownNotificationEnabled(mContext, isChecked);
- return true;
- }
-
- @Override
- public boolean isChecked() {
- return OneHandedSettingsUtils.isSwipeDownNotificationEnabled(mContext);
- }
-
- @Override
- public CharSequence getSummary() {
- // This toggle preference summary will be updated in gesture preference page since we bound
- // it with entry preference in gesture.xml
- return mContext.getText(
- isChecked() ? R.string.gesture_setting_on : R.string.gesture_setting_off);
- }
-}
diff --git a/src/com/android/settings/gestures/SwipeBottomToNotificationSettings.java b/src/com/android/settings/gestures/SwipeBottomToNotificationSettings.java
deleted file mode 100644
index 9d85f1123a..0000000000
--- a/src/com/android/settings/gestures/SwipeBottomToNotificationSettings.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2020 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.gestures;
-
-import android.app.settings.SettingsEnums;
-import android.content.Context;
-
-import com.android.settings.R;
-import com.android.settings.dashboard.DashboardFragment;
-import com.android.settings.search.BaseSearchIndexProvider;
-
-/**
- * The Fragment for swipe bottom to notification gesture settings.
- */
-public class SwipeBottomToNotificationSettings extends DashboardFragment {
-
- private static final String TAG = "SwipeBottomToNotificationSettings";
-
- @Override
- public int getMetricsCategory() {
- return SettingsEnums.SETTINGS_SWIPE_BOTTOM_TO_NOTIFICATION;
- }
-
- @Override
- protected String getLogTag() {
- return TAG;
- }
-
- @Override
- protected int getPreferenceScreenResId() {
- return R.xml.swipe_bottom_to_notification_settings;
- }
-
- public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
- new BaseSearchIndexProvider(R.xml.swipe_bottom_to_notification_settings) {
-
- @Override
- protected boolean isPageSearchEnabled(Context context) {
- if (!OneHandedSettingsUtils.isSupportOneHandedMode()) {
- return false;
- }
- return !OneHandedSettingsUtils.isOneHandedModeEnabled(context);
- }
- };
-}
diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java
index 5950e4b8ba..1d7b5dc14b 100644
--- a/src/com/android/settings/homepage/SettingsHomepageActivity.java
+++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java
@@ -18,14 +18,11 @@ package com.android.settings.homepage;
import android.animation.LayoutTransition;
import android.app.ActivityManager;
-import android.app.ActivityOptions;
import android.app.settings.SettingsEnums;
-import android.content.Intent;
import android.os.Bundle;
import android.util.FeatureFlagUtils;
import android.util.Log;
import android.view.View;
-import android.view.Window;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.Toolbar;
@@ -36,15 +33,16 @@ import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import com.android.settings.R;
-import com.android.settings.Utils;
import com.android.settings.accounts.AvatarViewMixin;
+import com.android.settings.core.CategoryMixin;
import com.android.settings.core.FeatureFlags;
import com.android.settings.homepage.contextualcards.ContextualCardsFragment;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.lifecycle.HideNonSystemOverlayMixin;
-import com.android.settingslib.transition.SettingsTransitionHelper;
-public class SettingsHomepageActivity extends FragmentActivity {
+/** Settings homepage activity */
+public class SettingsHomepageActivity extends FragmentActivity implements
+ CategoryMixin.CategoryHandler {
private static final String TAG = "SettingsHomepageActivity";
@@ -52,6 +50,12 @@ public class SettingsHomepageActivity extends FragmentActivity {
private View mHomepageView;
private View mSuggestionView;
+ private CategoryMixin mCategoryMixin;
+
+ @Override
+ public CategoryMixin getCategoryMixin() {
+ return mCategoryMixin;
+ }
/**
* Shows the homepage and shows/hides the suggestion together. Only allows to be executed once
@@ -69,12 +73,6 @@ public class SettingsHomepageActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
- if (Utils.isPageTransitionEnabled(this)) {
- // Enable Activity transitions
- getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
- SettingsTransitionHelper.applyForwardTransition(this);
- SettingsTransitionHelper.applyBackwardTransition(this);
- }
super.onCreate(savedInstanceState);
setContentView(R.layout.settings_homepage_container);
@@ -87,6 +85,8 @@ public class SettingsHomepageActivity extends FragmentActivity {
.initSearchToolbar(this /* activity */, toolbar, SettingsEnums.SETTINGS_HOMEPAGE);
getLifecycle().addObserver(new HideNonSystemOverlayMixin(this));
+ mCategoryMixin = new CategoryMixin(this);
+ getLifecycle().addObserver(mCategoryMixin);
if (!getSystemService(ActivityManager.class).isLowRamDevice()) {
// Only allow features on high ram devices.
@@ -107,16 +107,6 @@ public class SettingsHomepageActivity extends FragmentActivity {
.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
}
- @Override
- public void startActivity(Intent intent) {
- if (Utils.isPageTransitionEnabled(this)) {
- final Bundle bundle = ActivityOptions.makeSceneTransitionAnimation(this).toBundle();
- super.startActivity(intent, bundle);
- return;
- }
- super.startActivity(intent);
- }
-
private void showSuggestionFragment() {
final Class<? extends Fragment> fragment = FeatureFactory.getFactory(this)
.getSuggestionFeatureProvider(this).getContextualSuggestionFragment();
diff --git a/src/com/android/settings/media/MediaDeviceUpdateWorker.java b/src/com/android/settings/media/MediaDeviceUpdateWorker.java
index 14e82674c3..dd27073d49 100644
--- a/src/com/android/settings/media/MediaDeviceUpdateWorker.java
+++ b/src/com/android/settings/media/MediaDeviceUpdateWorker.java
@@ -259,6 +259,10 @@ public class MediaDeviceUpdateWorker extends SliceBackgroundWorker
return mLocalMediaManager.shouldDisableMediaOutput(packageName);
}
+ boolean shouldEnableVolumeSeekBar(RoutingSessionInfo sessionInfo) {
+ return mLocalMediaManager.shouldEnableVolumeSeekBar(sessionInfo);
+ }
+
private class DevicesChangedBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
diff --git a/src/com/android/settings/media/RemoteMediaSlice.java b/src/com/android/settings/media/RemoteMediaSlice.java
index e69c005014..3d81c44b33 100644
--- a/src/com/android/settings/media/RemoteMediaSlice.java
+++ b/src/com/android/settings/media/RemoteMediaSlice.java
@@ -126,6 +126,12 @@ public class RemoteMediaSlice implements CustomSliceable {
+ maxVolume);
continue;
}
+ if (!getWorker().shouldEnableVolumeSeekBar(info)) {
+ // There is no disable state. We hide it directly.
+ Log.d(TAG, "Unable to add Slice. " + info.getName() + ": This is a group session");
+ continue;
+ }
+
final CharSequence appName = Utils.getApplicationLabel(
mContext, info.getClientPackageName());
final CharSequence outputTitle = mContext.getString(R.string.media_output_label_title,
diff --git a/src/com/android/settings/network/CarrierWifiTogglePreferenceController.java b/src/com/android/settings/network/CarrierWifiTogglePreferenceController.java
index cf65034d9c..c5d8b97309 100644
--- a/src/com/android/settings/network/CarrierWifiTogglePreferenceController.java
+++ b/src/com/android/settings/network/CarrierWifiTogglePreferenceController.java
@@ -78,7 +78,6 @@ public class CarrierWifiTogglePreferenceController extends TogglePreferenceContr
return false;
}
mWifiPickerTrackerHelper.setCarrierNetworkEnabled(isChecked);
- updateCarrierNetworkPreference(isChecked);
return true;
}
@@ -86,21 +85,17 @@ public class CarrierWifiTogglePreferenceController extends TogglePreferenceContr
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mCarrierNetworkPreference = screen.findPreference(CARRIER_WIFI_NETWORK_PREF_KEY);
- updateCarrierNetworkPreference(isChecked());
+ updateCarrierNetworkPreference();
}
@Override
public void onWifiStateChanged() {
- if (mCarrierNetworkPreference != null && mCarrierNetworkPreference.isVisible()) {
- mCarrierNetworkPreference.setSummary(getCarrierNetworkSsid());
- }
+ updateCarrierNetworkPreference();
}
@Override
public void onWifiEntriesChanged() {
- if (mCarrierNetworkPreference != null && mCarrierNetworkPreference.isVisible()) {
- mCarrierNetworkPreference.setSummary(getCarrierNetworkSsid());
- }
+ updateCarrierNetworkPreference();
}
@Override
@@ -113,11 +108,11 @@ public class CarrierWifiTogglePreferenceController extends TogglePreferenceContr
// Do nothing
}
- protected void updateCarrierNetworkPreference(boolean isCarrierNetworkEnabled) {
+ protected void updateCarrierNetworkPreference() {
if (mCarrierNetworkPreference == null) {
return;
}
- if (!isCarrierNetworkEnabled || getAvailabilityStatus() != AVAILABLE) {
+ if (getAvailabilityStatus() != AVAILABLE || !isCarrierNetworkActive()) {
mCarrierNetworkPreference.setVisible(false);
return;
}
@@ -125,6 +120,13 @@ public class CarrierWifiTogglePreferenceController extends TogglePreferenceContr
mCarrierNetworkPreference.setSummary(getCarrierNetworkSsid());
}
+ protected boolean isCarrierNetworkActive() {
+ if (mWifiPickerTrackerHelper == null) {
+ return false;
+ }
+ return mWifiPickerTrackerHelper.isCarrierNetworkActive();
+ }
+
protected String getCarrierNetworkSsid() {
if (mWifiPickerTrackerHelper == null) {
return null;
diff --git a/src/com/android/settings/network/NetworkDashboardFragment.java b/src/com/android/settings/network/NetworkDashboardFragment.java
index f023ced6ef..c53b4f652a 100644
--- a/src/com/android/settings/network/NetworkDashboardFragment.java
+++ b/src/com/android/settings/network/NetworkDashboardFragment.java
@@ -95,11 +95,6 @@ public class NetworkDashboardFragment extends DashboardFragment implements
this /* fragment */, this /* mobilePlanHost */);
}
- @Override
- protected boolean isParalleledControllers() {
- return true;
- }
-
private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
Lifecycle lifecycle, MetricsFeatureProvider metricsFeatureProvider, Fragment fragment,
MobilePlanPreferenceHost mobilePlanHost) {
diff --git a/src/com/android/settings/network/NetworkProviderSettings.java b/src/com/android/settings/network/NetworkProviderSettings.java
index 22b9bccc52..25cf4ce7da 100644
--- a/src/com/android/settings/network/NetworkProviderSettings.java
+++ b/src/com/android/settings/network/NetworkProviderSettings.java
@@ -169,9 +169,18 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment
return WifiPickerTracker.isVerboseLoggingEnabled();
}
+ private boolean mIsViewLoading;
+ private final Runnable mRemoveLoadingRunnable = () -> {
+ if (mIsViewLoading) {
+ setLoading(false, false);
+ mIsViewLoading = false;
+ }
+ };
+
private boolean mIsWifiEntryListStale = true;
private final Runnable mUpdateWifiEntryPreferencesRunnable = () -> {
updateWifiEntryPreferences();
+ getView().postDelayed(mRemoveLoadingRunnable, 10);
};
private final Runnable mHideProgressBarRunnable = () -> {
setProgressBarVisible(false);
@@ -245,11 +254,21 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
- final Activity activity = getActivity();
- if (activity != null) {
- mProgressHeader = setPinnedHeaderView(R.layout.progress_header)
- .findViewById(R.id.progress_bar_animation);
- setProgressBarVisible(false);
+ Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+
+ mProgressHeader = setPinnedHeaderView(R.layout.progress_header)
+ .findViewById(R.id.progress_bar_animation);
+ setProgressBarVisible(false);
+
+ mWifiManager = activity.getSystemService(WifiManager.class);
+ if (mWifiManager != null) {
+ setLoading(true, false);
+ mIsViewLoading = true;
+ getView().postDelayed(mRemoveLoadingRunnable,
+ mWifiManager.isWifiEnabled() ? 1000 : 100);
}
}
@@ -332,12 +351,6 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment
mWifiPickerTracker = mWifiPickerTrackerHelper.getWifiPickerTracker();
mInternetUpdater = new InternetUpdater(getContext(), getSettingsLifecycle(), this);
- final Activity activity = getActivity();
-
- if (activity != null) {
- mWifiManager = getActivity().getSystemService(WifiManager.class);
- }
-
mConnectListener = new WifiConnectListener(getActivity());
mSaveListener = new WifiManager.ActionListener() {
diff --git a/src/com/android/settings/network/ProviderModelSlice.java b/src/com/android/settings/network/ProviderModelSlice.java
index cfaef53ac6..aafe715c4a 100644
--- a/src/com/android/settings/network/ProviderModelSlice.java
+++ b/src/com/android/settings/network/ProviderModelSlice.java
@@ -22,16 +22,20 @@ import static android.app.slice.Slice.EXTRA_TOGGLE_STATE;
import static com.android.settings.slices.CustomSliceRegistry.PROVIDER_MODEL_SLICE_URI;
import android.annotation.ColorInt;
+import android.app.AlertDialog;
+import android.app.AlertDialog.Builder;
import android.app.PendingIntent;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.telephony.SubscriptionManager;
import android.util.Log;
+import android.view.WindowManager.LayoutParams;
import androidx.annotation.VisibleForTesting;
import androidx.core.graphics.drawable.IconCompat;
@@ -64,13 +68,16 @@ import java.util.stream.Collectors;
public class ProviderModelSlice extends WifiSlice {
private static final String TAG = "ProviderModelSlice";
- protected static final String ACTION_TITLE_CONNECT_TO_CARRIER = "Connect_To_Carrier";
+ protected static final String PREF_NAME = "ProviderModelSlice";
+ protected static final String PREF_HAS_TURNED_OFF_MOBILE_DATA = "PrefHasTurnedOffMobileData";
private final ProviderModelSliceHelper mHelper;
+ private final SharedPreferences mSharedPref;
public ProviderModelSlice(Context context) {
super(context);
mHelper = getHelper();
+ mSharedPref = getSharedPreference();
}
@Override
@@ -195,10 +202,21 @@ public class ProviderModelSlice extends WifiSlice {
boolean isToggleAction = intent.hasExtra(EXTRA_TOGGLE_STATE);
boolean newState = intent.getBooleanExtra(EXTRA_TOGGLE_STATE,
mHelper.isMobileDataEnabled());
+
if (isToggleAction) {
// The ToggleAction is used to set mobile data enabled.
- MobileNetworkUtils.setMobileDataEnabled(mContext, defaultSubId, newState,
- false /* disableOtherSubscriptions */);
+ if (!newState && mSharedPref != null
+ && mSharedPref.getBoolean(PREF_HAS_TURNED_OFF_MOBILE_DATA, true)) {
+ String carrierName = mHelper.getMobileTitle();
+ if (carrierName.equals(mContext.getString(R.string.mobile_data_settings_title))) {
+ carrierName = mContext.getString(
+ R.string.mobile_data_disable_message_default_carrier);
+ }
+ showMobileDataDisableDialog(getMobileDataDisableDialog(defaultSubId, carrierName));
+ } else {
+ MobileNetworkUtils.setMobileDataEnabled(mContext, defaultSubId, newState,
+ false /* disableOtherSubscriptions */);
+ }
}
final boolean isDataEnabled =
@@ -207,6 +225,38 @@ public class ProviderModelSlice extends WifiSlice {
}
@VisibleForTesting
+ AlertDialog getMobileDataDisableDialog(int defaultSubId, String carrierName) {
+ return new Builder(mContext)
+ .setTitle(R.string.mobile_data_disable_title)
+ .setMessage(mContext.getString(R.string.mobile_data_disable_message,
+ carrierName))
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(
+ com.android.internal.R.string.alert_windows_notification_turn_off_action,
+ (dialog, which) -> {
+ MobileNetworkUtils.setMobileDataEnabled(mContext, defaultSubId,
+ false /* enabled */,
+ false /* disableOtherSubscriptions */);
+ if (mSharedPref != null) {
+ SharedPreferences.Editor editor = mSharedPref.edit();
+ editor.putBoolean(PREF_HAS_TURNED_OFF_MOBILE_DATA, false);
+ editor.apply();
+ }
+ })
+ .create();
+ }
+
+ private void showMobileDataDisableDialog(AlertDialog dialog) {
+ if (dialog == null) {
+ log("AlertDialog is null");
+ return;
+ }
+
+ dialog.getWindow().setType(LayoutParams.TYPE_KEYGUARD_DIALOG);
+ dialog.show();
+ }
+
+ @VisibleForTesting
void doCarrierNetworkAction(boolean isToggleAction, boolean isDataEnabled, int subId) {
final NetworkProviderWorker worker = getWorker();
if (worker == null) {
@@ -248,6 +298,11 @@ public class ProviderModelSlice extends WifiSlice {
return SliceBackgroundWorker.getInstance(getUri());
}
+ @VisibleForTesting
+ SharedPreferences getSharedPreference() {
+ return mContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
+ }
+
private @InternetUpdater.InternetType int getInternetType() {
final NetworkProviderWorker worker = getWorker();
if (worker == null) {
@@ -295,31 +350,6 @@ public class ProviderModelSlice extends WifiSlice {
}
@Override
- protected ListBuilder.RowBuilder getWifiSliceItemRow(WifiSliceItem wifiSliceItem) {
- final CharSequence title = wifiSliceItem.getTitle();
- final IconCompat levelIcon = getWifiSliceItemLevelIcon(wifiSliceItem);
- final ListBuilder.RowBuilder rowBuilder = new ListBuilder.RowBuilder()
- .setTitleItem(levelIcon, ListBuilder.ICON_IMAGE)
- .setTitle(title)
- .setSubtitle(wifiSliceItem.getSummary())
- .setContentDescription(wifiSliceItem.getContentDescription());
-
- final IconCompat endIcon;
- if (wifiSliceItem.hasInternetAccess()) {
- rowBuilder.setPrimaryAction(SliceAction.create(getBroadcastIntent(mContext),
- levelIcon, ListBuilder.ICON_IMAGE, ACTION_TITLE_CONNECT_TO_CARRIER));
- endIcon = IconCompat.createWithResource(mContext, R.drawable.ic_settings_close);
- } else {
- rowBuilder.setPrimaryAction(getWifiEntryAction(wifiSliceItem, levelIcon, title));
- endIcon = getEndIcon(wifiSliceItem);
- }
- if (endIcon != null) {
- rowBuilder.addEndItem(endIcon, ListBuilder.ICON_IMAGE);
- }
- return rowBuilder;
- }
-
- @Override
protected IconCompat getWifiSliceItemLevelIcon(WifiSliceItem wifiSliceItem) {
if (wifiSliceItem.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED
&& getInternetType() != InternetUpdater.INTERNET_WIFI) {
diff --git a/src/com/android/settings/network/ProviderModelSliceHelper.java b/src/com/android/settings/network/ProviderModelSliceHelper.java
index 28f857464a..16d5c92455 100644
--- a/src/com/android/settings/network/ProviderModelSliceHelper.java
+++ b/src/com/android/settings/network/ProviderModelSliceHelper.java
@@ -264,7 +264,7 @@ public class ProviderModelSliceHelper {
return summary;
}
- private String getMobileTitle() {
+ protected String getMobileTitle() {
String title = mContext.getText(R.string.mobile_data_settings_title).toString();
if (mSubscriptionManager == null) {
return title;
diff --git a/src/com/android/settings/network/SubscriptionUtil.java b/src/com/android/settings/network/SubscriptionUtil.java
index e7dd5e914c..48ff591a43 100644
--- a/src/com/android/settings/network/SubscriptionUtil.java
+++ b/src/com/android/settings/network/SubscriptionUtil.java
@@ -54,6 +54,7 @@ import java.util.stream.Stream;
public class SubscriptionUtil {
private static final String TAG = "SubscriptionUtil";
+ private static final String PROFILE_GENERIC_DISPLAY_NAME = "CARD";
private static List<SubscriptionInfo> sAvailableResultsForTesting;
private static List<SubscriptionInfo> sActiveResultsForTesting;
@@ -257,7 +258,10 @@ public class SubscriptionUtil {
.map(i -> {
DisplayInfo info = new DisplayInfo();
info.subscriptionInfo = i;
- info.originalName = i.getDisplayName().toString().trim();
+ String displayName = i.getDisplayName().toString();
+ info.originalName = TextUtils.equals(displayName, PROFILE_GENERIC_DISPLAY_NAME)
+ ? context.getResources().getString(R.string.sim_card)
+ : displayName.trim();
return info;
});
diff --git a/src/com/android/settings/network/SubscriptionsPreferenceController.java b/src/com/android/settings/network/SubscriptionsPreferenceController.java
index 8c7347bfb8..e88cded175 100644
--- a/src/com/android/settings/network/SubscriptionsPreferenceController.java
+++ b/src/com/android/settings/network/SubscriptionsPreferenceController.java
@@ -277,12 +277,12 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl
final boolean isDataInService = tmForSubId.getDataState()
== TelephonyManager.DATA_CONNECTED;
- final boolean isActiveCarrierNetwork =
+ final boolean isCarrierNetworkActive =
(mWifiPickerTrackerHelper != null)
- && mWifiPickerTrackerHelper.isActiveCarrierNetwork();
+ && mWifiPickerTrackerHelper.isCarrierNetworkActive();
String result = mSubsPrefCtrlInjector.getNetworkType(
- mContext, mConfig, mTelephonyDisplayInfo, subId, isActiveCarrierNetwork);
- if (mSubsPrefCtrlInjector.isActiveCellularNetwork(mContext) || isActiveCarrierNetwork) {
+ mContext, mConfig, mTelephonyDisplayInfo, subId, isCarrierNetworkActive);
+ if (mSubsPrefCtrlInjector.isActiveCellularNetwork(mContext) || isCarrierNetworkActive) {
Log.i(TAG, "Active cellular network or active carrier network.");
result = mContext.getString(R.string.preference_summary_default_combination,
mContext.getString(R.string.mobile_data_connection_active), result);
@@ -306,7 +306,7 @@ public class SubscriptionsPreferenceController extends AbstractPreferenceControl
final boolean isActiveCellularNetwork =
mSubsPrefCtrlInjector.isActiveCellularNetwork(mContext);
if (isActiveCellularNetwork || (mWifiPickerTrackerHelper != null)
- && mWifiPickerTrackerHelper.isActiveCarrierNetwork()) {
+ && mWifiPickerTrackerHelper.isCarrierNetworkActive()) {
icon.setTint(Utils.getColorAccentDefaultColor(mContext));
return icon;
}
diff --git a/src/com/android/settings/network/apn/ApnSettings.java b/src/com/android/settings/network/apn/ApnSettings.java
index 02d9b3d1f3..4df2e5ee38 100755
--- a/src/com/android/settings/network/apn/ApnSettings.java
+++ b/src/com/android/settings/network/apn/ApnSettings.java
@@ -88,6 +88,13 @@ public class ApnSettings extends RestrictedSettingsFragment
Telephony.Carriers.EDITED_STATUS,
};
+ /** Copied from {@code com.android.internal.telephony.TelephonyIntents} */
+ private static final String ACTION_SIM_STATE_CHANGED =
+ "android.intent.action.SIM_STATE_CHANGED";
+ /** Copied from {@code com.android.internal.telephony.IccCardConstants} */
+ public static final String INTENT_KEY_ICC_STATE = "ss";
+ public static final String INTENT_VALUE_ICC_ABSENT = "ABSENT";
+
private static final int ID_INDEX = 0;
private static final int NAME_INDEX = 1;
private static final int APN_INDEX = 2;
@@ -151,7 +158,16 @@ public class ApnSettings extends RestrictedSettingsFragment
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(
+ String action = intent.getAction();
+ if (ACTION_SIM_STATE_CHANGED.equals(action)
+ && intent.getStringExtra(INTENT_KEY_ICC_STATE)
+ .equals(INTENT_VALUE_ICC_ABSENT)) {
+ final SubscriptionManager sm = context.getSystemService(SubscriptionManager.class);
+ if (sm != null && !sm.isActiveSubscriptionId(mSubId)) {
+ Log.d(TAG, "Due to SIM absent, closes APN settings page");
+ finish();
+ }
+ } else if (intent.getAction().equals(
TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED)) {
if (mRestoreDefaultApnMode) {
return;
@@ -201,8 +217,9 @@ public class ApnSettings extends RestrictedSettingsFragment
mSubId = activity.getIntent().getIntExtra(SUB_ID,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
mPhoneId = SubscriptionUtil.getPhoneId(activity, mSubId);
- mIntentFilter = new IntentFilter(
- TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED);
+ mIntentFilter = new IntentFilter();
+ mIntentFilter.addAction(TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED);
+ mIntentFilter.addAction(ACTION_SIM_STATE_CHANGED);
setIfOnlyAvailableForAdmins(true);
diff --git a/src/com/android/settings/network/telephony/NetworkProviderWorker.java b/src/com/android/settings/network/telephony/NetworkProviderWorker.java
index 369218b86a..675d60fb94 100644
--- a/src/com/android/settings/network/telephony/NetworkProviderWorker.java
+++ b/src/com/android/settings/network/telephony/NetworkProviderWorker.java
@@ -261,7 +261,7 @@ public class NetworkProviderWorker extends WifiScanWorker implements
String iconKey = getIconKey(telephonyDisplayInfo);
int resId = mapIconSets(config).get(iconKey).dataContentDescription;
if (mWifiPickerTrackerHelper != null
- && mWifiPickerTrackerHelper.isActiveCarrierNetwork()) {
+ && mWifiPickerTrackerHelper.isCarrierNetworkActive()) {
MobileIconGroup carrierMergedWifiIconGroup = TelephonyIcons.CARRIER_MERGED_WIFI;
resId = carrierMergedWifiIconGroup.dataContentDescription;
return resId != 0
diff --git a/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java b/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java
index 146b575442..0064e6ccfd 100644
--- a/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java
+++ b/src/com/android/settings/network/telephony/ToggleSubscriptionDialogActivity.java
@@ -30,14 +30,12 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.SidecarFragment;
-import com.android.settings.core.SettingsBaseActivity;
import com.android.settings.network.EnableMultiSimSidecar;
import com.android.settings.network.SubscriptionUtil;
import com.android.settings.network.SwitchToEuiccSubscriptionSidecar;
import com.android.settings.network.SwitchToRemovableSlotSidecar;
import com.android.settings.network.UiccSlotUtil;
import com.android.settings.sim.SimActivationNotifier;
-import com.android.settingslib.transition.SettingsTransitionHelper;
import com.google.common.collect.ImmutableList;
@@ -70,9 +68,6 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
Intent intent = new Intent(context, ToggleSubscriptionDialogActivity.class);
intent.putExtra(ARG_SUB_ID, subId);
intent.putExtra(ARG_enable, enable);
- // suppress page transition as this is a dialog
- intent.putExtra(SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE,
- SettingsTransitionHelper.TransitionType.TRANSITION_NONE);
return intent;
}
diff --git a/src/com/android/settings/notification/ConfigureNotificationSettings.java b/src/com/android/settings/notification/ConfigureNotificationSettings.java
index 644d5cbc53..5f78acc1cc 100644
--- a/src/com/android/settings/notification/ConfigureNotificationSettings.java
+++ b/src/com/android/settings/notification/ConfigureNotificationSettings.java
@@ -96,11 +96,6 @@ public class ConfigureNotificationSettings extends DashboardFragment implements
mNotificationAssistantPreferenceController.setBackend(new NotificationBackend());
}
- @Override
- protected boolean isParalleledControllers() {
- return true;
- }
-
private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
Application app, Fragment host) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
diff --git a/src/com/android/settings/notification/RemoteVolumeGroupController.java b/src/com/android/settings/notification/RemoteVolumeGroupController.java
index dd6f516012..3d8d261aa0 100644
--- a/src/com/android/settings/notification/RemoteVolumeGroupController.java
+++ b/src/com/android/settings/notification/RemoteVolumeGroupController.java
@@ -129,6 +129,7 @@ public class RemoteVolumeGroupController extends BasePreferenceController implem
seekBarPreference.setMin(0);
seekBarPreference.setOnPreferenceChangeListener(this);
seekBarPreference.setIcon(R.drawable.ic_volume_remote);
+ seekBarPreference.setEnabled(mLocalMediaManager.shouldEnableVolumeSeekBar(info));
mPreferenceCategory.addPreference(seekBarPreference);
}
diff --git a/src/com/android/settings/notification/app/AppConversationListPreferenceController.java b/src/com/android/settings/notification/app/AppConversationListPreferenceController.java
index 0e89be3ddd..dd44a13f7c 100644
--- a/src/com/android/settings/notification/app/AppConversationListPreferenceController.java
+++ b/src/com/android/settings/notification/app/AppConversationListPreferenceController.java
@@ -33,6 +33,7 @@ import com.android.settings.R;
import com.android.settings.applications.AppInfoBase;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.notification.NotificationBackend;
+import com.android.settingslib.widget.AppPreference;
import java.util.ArrayList;
import java.util.Collections;
@@ -144,7 +145,7 @@ public class AppConversationListPreferenceController extends NotificationPrefere
}
protected Preference createConversationPref(final ConversationChannelWrapper conversation) {
- Preference pref = new Preference(mContext);
+ AppPreference pref = new AppPreference(mContext);
populateConversationPreference(conversation, pref);
return pref;
}
diff --git a/src/com/android/settings/notification/app/AppNotificationSettings.java b/src/com/android/settings/notification/app/AppNotificationSettings.java
index 8d0819cd97..ebcd261df2 100644
--- a/src/com/android/settings/notification/app/AppNotificationSettings.java
+++ b/src/com/android/settings/notification/app/AppNotificationSettings.java
@@ -40,35 +40,11 @@ public class AppNotificationSettings extends NotificationSettings {
private static final String TAG = "AppNotificationSettings";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static String KEY_ADVANCED_CATEGORY = "app_advanced";
- private static String KEY_BADGE = "badge";
- private static String KEY_APP_LINK = "app_link";
- private static String[] LEGACY_NON_ADVANCED_KEYS = {KEY_BADGE, KEY_APP_LINK};
-
@Override
public int getMetricsCategory() {
return SettingsEnums.NOTIFICATION_APP_NOTIFICATION;
}
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- final PreferenceScreen screen = getPreferenceScreen();
- if (mShowLegacyChannelConfig && screen != null) {
- // if showing legacy settings, pull advanced settings out of the advanced category
- PreferenceGroup advanced = (PreferenceGroup) findPreference(KEY_ADVANCED_CATEGORY);
- removePreference(KEY_ADVANCED_CATEGORY);
- if (advanced != null) {
- for (String key : LEGACY_NON_ADVANCED_KEYS) {
- Preference pref = advanced.findPreference(key);
- advanced.removePreference(pref);
- if (pref != null) {
- screen.addPreference(pref);
- }
- }
- }
- }
- }
@Override
public void onResume() {
@@ -80,12 +56,7 @@ public class AppNotificationSettings extends NotificationSettings {
return;
}
- if (Utils.isPageTransitionEnabled(mContext)) {
- final RecyclerView recyclerView = getListView();
- if (recyclerView != null) {
- recyclerView.setItemAnimator(null);
- }
- }
+ getActivity().setTitle(mAppRow.label);
for (NotificationPreferenceController controller : mControllers) {
controller.onResume(mAppRow, mChannel, mChannelGroup, null, null, mSuspendedAppsAdmin,
diff --git a/src/com/android/settings/notification/app/ChannelListPreferenceController.java b/src/com/android/settings/notification/app/ChannelListPreferenceController.java
index b1109ecbad..12c15c28ee 100644
--- a/src/com/android/settings/notification/app/ChannelListPreferenceController.java
+++ b/src/com/android/settings/notification/app/ChannelListPreferenceController.java
@@ -322,7 +322,7 @@ public class ChannelListPreferenceController extends NotificationPreferenceContr
if (channel.getImportance() > IMPORTANCE_LOW) {
channelPref.setIcon(getAlertingIcon());
} else {
- channelPref.setIcon(null);
+ channelPref.setIcon(R.drawable.empty_icon);
}
channelPref.setIconSize(PrimarySwitchPreference.ICON_SIZE_SMALL);
channelPref.setTitle(channel.getName());
@@ -350,7 +350,7 @@ public class ChannelListPreferenceController extends NotificationPreferenceContr
channel.setImportance(importance);
channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
PrimarySwitchPreference channelPref1 = (PrimarySwitchPreference) preference;
- channelPref1.setIcon(null);
+ channelPref1.setIcon(R.drawable.empty_icon);
if (channel.getImportance() > IMPORTANCE_LOW) {
channelPref1.setIcon(getAlertingIcon());
}
diff --git a/src/com/android/settings/notification/app/ChannelNotificationSettings.java b/src/com/android/settings/notification/app/ChannelNotificationSettings.java
index 296863cc0b..a7aba5284d 100644
--- a/src/com/android/settings/notification/app/ChannelNotificationSettings.java
+++ b/src/com/android/settings/notification/app/ChannelNotificationSettings.java
@@ -66,8 +66,9 @@ public class ChannelNotificationSettings extends NotificationSettings {
return;
}
- if (mChannel != null && !TextUtils.isEmpty(mChannel.getConversationId())
- && !mChannel.isDemoted()) {
+ getActivity().setTitle(mChannel.getName());
+
+ if (!TextUtils.isEmpty(mChannel.getConversationId()) && !mChannel.isDemoted()) {
Intent intent = new SubSettingLauncher(mContext)
.setDestination(ConversationNotificationSettings.class.getName())
.setArguments(getArguments())
@@ -128,7 +129,6 @@ public class ChannelNotificationSettings extends NotificationSettings {
mDependentFieldListener, mBackend));
mControllers.add(new VibrationPreferenceController(context, mBackend));
mControllers.add(new AppLinkPreferenceController(context));
- mControllers.add(new DescriptionPreferenceController(context));
mControllers.add(new VisibilityPreferenceController(context, new LockPatternUtils(context),
mBackend));
mControllers.add(new LightsPreferenceController(context, mBackend));
diff --git a/src/com/android/settings/notification/app/ConversationListPreferenceController.java b/src/com/android/settings/notification/app/ConversationListPreferenceController.java
index 2500a22c18..b609a9a4d5 100644
--- a/src/com/android/settings/notification/app/ConversationListPreferenceController.java
+++ b/src/com/android/settings/notification/app/ConversationListPreferenceController.java
@@ -18,7 +18,6 @@ package com.android.settings.notification.app;
import android.app.settings.SettingsEnums;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.os.Bundle;
import android.os.UserHandle;
@@ -34,6 +33,7 @@ import com.android.settings.applications.AppInfoBase;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.notification.NotificationBackend;
import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.widget.AppPreference;
import java.text.Collator;
import java.util.Comparator;
@@ -96,7 +96,7 @@ public abstract class ConversationListPreferenceController extends AbstractPrefe
protected Preference createConversationPref(final ConversationChannelWrapper conversation,
int order) {
- Preference pref = new Preference(mContext);
+ AppPreference pref = new AppPreference(mContext);
pref.setOrder(order);
pref.setTitle(getTitle(conversation));
diff --git a/src/com/android/settings/notification/app/ConversationNotificationSettings.java b/src/com/android/settings/notification/app/ConversationNotificationSettings.java
index d712c84ca6..d659c545cd 100644
--- a/src/com/android/settings/notification/app/ConversationNotificationSettings.java
+++ b/src/com/android/settings/notification/app/ConversationNotificationSettings.java
@@ -41,11 +41,13 @@ public class ConversationNotificationSettings extends NotificationSettings {
@Override
public void onResume() {
super.onResume();
- if (mUid < 0 || TextUtils.isEmpty(mPkg) || mPkgInfo == null || mChannel == null) {
+ if (mUid < 0 || TextUtils.isEmpty(mPkg) || mPkgInfo == null || mChannel == null
+ || mConversationInfo == null) {
Log.w(TAG, "Missing package or uid or packageinfo or channel");
finish();
return;
}
+ getActivity().setTitle(mConversationInfo.getLabel());
for (NotificationPreferenceController controller : mControllers) {
controller.onResume(mAppRow, mChannel, mChannelGroup, mConversationDrawable,
diff --git a/src/com/android/settings/notification/app/HeaderPreferenceController.java b/src/com/android/settings/notification/app/HeaderPreferenceController.java
index 974ac79be0..7379d55395 100644
--- a/src/com/android/settings/notification/app/HeaderPreferenceController.java
+++ b/src/com/android/settings/notification/app/HeaderPreferenceController.java
@@ -84,6 +84,7 @@ public class HeaderPreferenceController extends NotificationPreferenceController
pref = mHeaderController.setIcon(mAppRow.icon)
.setLabel(getLabel())
.setSummary(getSummary())
+ .setSecondSummary(getSecondSummary())
.setPackageName(mAppRow.pkg)
.setUid(mAppRow.uid)
.setButtonActions(EntityHeaderController.ActionType.ACTION_NOTIF_PREFERENCE,
@@ -96,14 +97,27 @@ public class HeaderPreferenceController extends NotificationPreferenceController
}
}
+ public CharSequence getLabel() {
+ if (mChannel != null && !isDefaultChannel()) {
+ return mChannel.getName();
+ } else {
+ return mAppRow.label;
+ }
+ }
+
+ @OnLifecycleEvent(Lifecycle.Event.ON_START)
+ public void onStart() {
+ mStarted = true;
+ }
+
@Override
public CharSequence getSummary() {
- if (mChannel != null && !isDefaultChannel()) {
+ if (mChannel != null) {
if (mChannelGroup != null
&& !TextUtils.isEmpty(mChannelGroup.getName())) {
final SpannableStringBuilder summary = new SpannableStringBuilder();
BidiFormatter bidi = BidiFormatter.getInstance();
- summary.append(bidi.unicodeWrap(mAppRow.label.toString()));
+ summary.append(bidi.unicodeWrap(mAppRow.label));
summary.append(bidi.unicodeWrap(mContext.getText(
R.string.notification_header_divider_symbol_with_spaces)));
summary.append(bidi.unicodeWrap(mChannelGroup.getName().toString()));
@@ -111,23 +125,11 @@ public class HeaderPreferenceController extends NotificationPreferenceController
} else {
return mAppRow.label.toString();
}
- } else if (mChannelGroup != null) {
- return mAppRow.label.toString();
- } else {
- return "";
}
+ return "";
}
- @OnLifecycleEvent(Lifecycle.Event.ON_START)
- public void onStart() {
- mStarted = true;
- }
-
- @VisibleForTesting
- CharSequence getLabel() {
- return (mChannel != null && !isDefaultChannel()) ? mChannel.getName()
- : mChannelGroup != null
- ? mChannelGroup.getName()
- : mAppRow.label;
+ public CharSequence getSecondSummary() {
+ return mChannel == null ? null : mChannel.getDescription();
}
}
diff --git a/src/com/android/settings/notification/app/RecentConversationsPreferenceController.java b/src/com/android/settings/notification/app/RecentConversationsPreferenceController.java
index d715ce457a..4984fad940 100644
--- a/src/com/android/settings/notification/app/RecentConversationsPreferenceController.java
+++ b/src/com/android/settings/notification/app/RecentConversationsPreferenceController.java
@@ -167,6 +167,8 @@ public class RecentConversationsPreferenceController extends AbstractPreferenceC
pref.setOnClearClickListener(() -> {
try {
mPs.removeRecentConversation(pkg, UserHandle.getUserId(uid), conversationId);
+ pref.getClearView().announceForAccessibility(
+ mContext.getString(R.string.recent_convo_removed));
parent.removePreference(pref);
} catch (RemoteException e) {
Slog.w(TAG, "Could not clear recent", e);
diff --git a/src/com/android/settings/notification/zen/AbstractZenModeAutomaticRulePreferenceController.java b/src/com/android/settings/notification/zen/AbstractZenModeAutomaticRulePreferenceController.java
index f6f183995e..701abbb0b6 100644
--- a/src/com/android/settings/notification/zen/AbstractZenModeAutomaticRulePreferenceController.java
+++ b/src/com/android/settings/notification/zen/AbstractZenModeAutomaticRulePreferenceController.java
@@ -26,8 +26,11 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
+import android.os.Binder;
import android.provider.Settings;
import android.service.notification.ConditionProviderService;
+import android.util.Log;
+import android.util.Slog;
import androidx.fragment.app.Fragment;
import androidx.preference.Preference;
@@ -36,6 +39,7 @@ import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.core.lifecycle.Lifecycle;
import java.util.Map;
+import java.util.Objects;
abstract public class AbstractZenModeAutomaticRulePreferenceController extends
AbstractZenModePreferenceController implements PreferenceControllerMixin {
@@ -92,7 +96,7 @@ abstract public class AbstractZenModeAutomaticRulePreferenceController extends
? ci.metaData.getString(ConditionProviderService.META_DATA_RULE_TYPE)
: ci.metaData.getString(NotificationManager.META_DATA_AUTOMATIC_RULE_TYPE);
- final ComponentName configurationActivity = getSettingsActivity(null, ci);
+ final ComponentName configurationActivity = getSettingsActivity(pm, null, ci);
if (ruleType != null && !ruleType.trim().isEmpty() && configurationActivity != null) {
final ZenRuleInfo ri = new ZenRuleInfo();
ri.serviceComponent =
@@ -110,28 +114,44 @@ abstract public class AbstractZenModeAutomaticRulePreferenceController extends
return null;
}
- protected static ComponentName getSettingsActivity(AutomaticZenRule rule, ComponentInfo ci) {
+ protected static ComponentName getSettingsActivity(PackageManager pm, AutomaticZenRule rule,
+ ComponentInfo ci) {
+ String owner = rule != null ? rule.getPackageName() : ci.packageName;
+ ComponentName settingsActivity = null;
// prefer config activity on the rule itself; fallback to manifest definition
if (rule != null && rule.getConfigurationActivity() != null) {
- return rule.getConfigurationActivity();
- }
- if (ci == null) {
- return null;
+ settingsActivity = rule.getConfigurationActivity();
+ } else {
+ if (ci == null) {
+ settingsActivity = null;
+ } else if (ci instanceof ActivityInfo) {
+ // new activity backed rule
+ settingsActivity = new ComponentName(ci.packageName, ci.name);
+ } else if (ci.metaData != null) {
+ // old service backed rule
+ final String configurationActivity = ci.metaData.getString(
+ ConditionProviderService.META_DATA_CONFIGURATION_ACTIVITY);
+ if (configurationActivity != null) {
+ settingsActivity = ComponentName.unflattenFromString(configurationActivity);
+ }
+ }
}
- // new activity backed rule
- if (ci instanceof ActivityInfo) {
- return new ComponentName(ci.packageName, ci.name);
+ if (settingsActivity == null || owner == null) {
+ return settingsActivity;
}
- // old service backed rule
- if (ci.metaData != null) {
- final String configurationActivity = ci.metaData.getString(
- ConditionProviderService.META_DATA_CONFIGURATION_ACTIVITY);
- if (configurationActivity != null) {
- return ComponentName.unflattenFromString(configurationActivity);
+ try {
+ int ownerUid = pm.getPackageUid(owner, 0);
+ int configActivityOwnerUid = pm.getPackageUid(settingsActivity.getPackageName(), 0);
+ if (ownerUid == configActivityOwnerUid) {
+ return settingsActivity;
+ } else {
+ Log.w(TAG, "Config activity not in owner package for " + rule.getName());
+ return null;
}
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Failed to find config activity");
+ return null;
}
-
- return null;
}
public class RuleNameChangeListener implements ZenRuleNameDialog.PositiveClickListener {
diff --git a/src/com/android/settings/notification/zen/ZenRulePreference.java b/src/com/android/settings/notification/zen/ZenRulePreference.java
index b8c8354eb3..a265a0776d 100644
--- a/src/com/android/settings/notification/zen/ZenRulePreference.java
+++ b/src/com/android/settings/notification/zen/ZenRulePreference.java
@@ -168,7 +168,7 @@ public class ZenRulePreference extends TwoTargetPreference {
: isEvent ? ZenModeEventRuleSettings.ACTION : "";
ComponentInfo si = mServiceListing.findService(rule.getOwner());
ComponentName settingsActivity = AbstractZenModeAutomaticRulePreferenceController.
- getSettingsActivity(rule, si);
+ getSettingsActivity(mPm, rule, si);
mIntent = AbstractZenModeAutomaticRulePreferenceController.getRuleIntent(action,
settingsActivity, mId);
if (mIntent.resolveActivity(mPm) == null) {
diff --git a/src/com/android/settings/overlay/FeatureFactory.java b/src/com/android/settings/overlay/FeatureFactory.java
index 58f80fce22..cfdc9eb5b6 100644
--- a/src/com/android/settings/overlay/FeatureFactory.java
+++ b/src/com/android/settings/overlay/FeatureFactory.java
@@ -23,6 +23,7 @@ import android.util.Log;
import androidx.annotation.Nullable;
import com.android.settings.R;
+import com.android.settings.accessibility.AccessibilitySearchFeatureProvider;
import com.android.settings.accounts.AccountFeatureProvider;
import com.android.settings.applications.ApplicationFeatureProvider;
import com.android.settings.applications.GameSettingsFeatureProvider;
@@ -174,6 +175,11 @@ public abstract class FeatureFactory {
*/
public abstract GameSettingsFeatureProvider getGameSettingsFeatureProvider();
+ /**
+ * Retrieve implementation for Accessibility search index feature.
+ */
+ public abstract AccessibilitySearchFeatureProvider getAccessibilitySearchFeatureProvider();
+
public static final class FactoryNotFoundException extends RuntimeException {
public FactoryNotFoundException(Throwable throwable) {
super("Unable to create factory. Did you misconfigure Proguard?", throwable);
diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.java b/src/com/android/settings/overlay/FeatureFactoryImpl.java
index 13b7b604de..9890a10964 100644
--- a/src/com/android/settings/overlay/FeatureFactoryImpl.java
+++ b/src/com/android/settings/overlay/FeatureFactoryImpl.java
@@ -25,6 +25,8 @@ import android.os.UserManager;
import androidx.annotation.Keep;
+import com.android.settings.accessibility.AccessibilitySearchFeatureProvider;
+import com.android.settings.accessibility.AccessibilitySearchFeatureProviderImpl;
import com.android.settings.accounts.AccountFeatureProvider;
import com.android.settings.accounts.AccountFeatureProviderImpl;
import com.android.settings.applications.ApplicationFeatureProvider;
@@ -106,6 +108,7 @@ public class FeatureFactoryImpl extends FeatureFactory {
private ExtraAppInfoFeatureProvider mExtraAppInfoFeatureProvider;
private SecuritySettingsFeatureProvider mSecuritySettingsFeatureProvider;
private GameSettingsFeatureProvider mGameSettingsFeatureProvider;
+ private AccessibilitySearchFeatureProvider mAccessibilitySearchFeatureProvider;
@Override
public SupportFeatureProvider getSupportFeatureProvider(Context context) {
@@ -335,4 +338,12 @@ public class FeatureFactoryImpl extends FeatureFactory {
}
return mGameSettingsFeatureProvider;
}
+
+ @Override
+ public AccessibilitySearchFeatureProvider getAccessibilitySearchFeatureProvider() {
+ if (mAccessibilitySearchFeatureProvider == null) {
+ mAccessibilitySearchFeatureProvider = new AccessibilitySearchFeatureProviderImpl();
+ }
+ return mAccessibilitySearchFeatureProvider;
+ }
}
diff --git a/src/com/android/settings/panel/InternetConnectivityPanel.java b/src/com/android/settings/panel/InternetConnectivityPanel.java
index e6344d806c..53c0f20ad1 100644
--- a/src/com/android/settings/panel/InternetConnectivityPanel.java
+++ b/src/com/android/settings/panel/InternetConnectivityPanel.java
@@ -30,6 +30,7 @@ import android.net.wifi.WifiManager;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Looper;
+import android.provider.Settings;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyCallback;
@@ -84,12 +85,13 @@ public class InternetConnectivityPanel implements PanelContent, LifecycleObserve
}
if (TextUtils.equals(intent.getAction(), WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
- showProgressBar();
+ updateProgressBar();
updatePanelTitle();
return;
}
if (TextUtils.equals(intent.getAction(), WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+ updateProgressBar();
updatePanelTitle();
}
}
@@ -110,13 +112,40 @@ public class InternetConnectivityPanel implements PanelContent, LifecycleObserve
private int mDefaultDataSubid = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
// Wi-Fi scanning progress bar
+ protected HandlerInjector mHandlerInjector;
protected boolean mIsProgressBarVisible;
- protected final Runnable mHideProgressBarRunnable = () -> {
+ protected boolean mIsScanningSubTitleShownOnce;
+ protected Runnable mHideProgressBarRunnable = () -> {
setProgressBarVisible(false);
};
+ protected Runnable mHideScanningSubTitleRunnable = () -> {
+ mIsScanningSubTitleShownOnce = true;
+ updatePanelTitle();
+ };
+
+ /**
+ * Wrapper for testing compatibility.
+ */
+ @VisibleForTesting
+ static class HandlerInjector {
+ protected final Handler mHandler;
+
+ HandlerInjector(Context context) {
+ mHandler = context.getMainThreadHandler();
+ }
+
+ public void postDelay(Runnable runnable) {
+ mHandler.postDelayed(runnable, 2000 /* delay millis */);
+ }
+
+ public void removeCallbacks(Runnable runnable) {
+ mHandler.removeCallbacks(runnable);
+ }
+ }
private InternetConnectivityPanel(Context context) {
mContext = context.getApplicationContext();
+ mHandlerInjector = new HandlerInjector(context);
mIsProviderModelEnabled = Utils.isProviderModelEnabled(mContext);
mInternetUpdater = new InternetUpdater(context, null /* Lifecycle */, this);
@@ -150,7 +179,7 @@ public class InternetConnectivityPanel implements PanelContent, LifecycleObserve
mTelephonyManager.registerTelephonyCallback(
new HandlerExecutor(new Handler(Looper.getMainLooper())), mTelephonyCallback);
mContext.registerReceiver(mWifiStateReceiver, mWifiStateFilter);
- showProgressBar();
+ updateProgressBar();
updatePanelTitle();
}
@@ -165,7 +194,8 @@ public class InternetConnectivityPanel implements PanelContent, LifecycleObserve
mConnectivityListener.stop();
mTelephonyManager.unregisterTelephonyCallback(mTelephonyCallback);
mContext.unregisterReceiver(mWifiStateReceiver);
- mContext.getMainThreadHandler().removeCallbacks(mHideProgressBarRunnable);
+ mHandlerInjector.removeCallbacks(mHideProgressBarRunnable);
+ mHandlerInjector.removeCallbacks(mHideScanningSubTitleRunnable);
}
/**
@@ -206,7 +236,10 @@ public class InternetConnectivityPanel implements PanelContent, LifecycleObserve
@Override
public Intent getSeeMoreIntent() {
- return null;
+ // Don't remove the see more intent for non-provider model design. This intent will be
+ // used when isCustomizedButtonUsed() returns false.
+ return new Intent(Settings.ACTION_WIRELESS_SETTINGS)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
@Override
@@ -246,6 +279,7 @@ public class InternetConnectivityPanel implements PanelContent, LifecycleObserve
*/
@Override
public void onAirplaneModeChanged(boolean isAirplaneModeOn) {
+ log("onAirplaneModeChanged: isAirplaneModeOn:" + isAirplaneModeOn);
updatePanelTitle();
}
@@ -254,6 +288,7 @@ public class InternetConnectivityPanel implements PanelContent, LifecycleObserve
*/
@Override
public void onWifiEnabledChanged(boolean enabled) {
+ log("onWifiEnabledChanged: enabled:" + enabled);
updatePanelTitle();
}
@@ -305,13 +340,6 @@ public class InternetConnectivityPanel implements PanelContent, LifecycleObserve
return;
}
- if (mIsProgressBarVisible) {
- // When the Wi-Fi scan result callback is received
- // Sub-Title: Searching for networks...
- mSubtitle = SUBTITLE_TEXT_SEARCHING_FOR_NETWORKS;
- return;
- }
-
if (mInternetUpdater.isAirplaneModeOn()) {
return;
}
@@ -319,11 +347,18 @@ public class InternetConnectivityPanel implements PanelContent, LifecycleObserve
final List<ScanResult> wifiList = mWifiManager.getScanResults();
if (wifiList != null && wifiList.size() != 0) {
// When the Wi-Fi scan result is not empty
- // Sub-Title: Select the network you want to use for data
+ // Sub-Title: Tap a network to connect
mSubtitle = SUBTITLE_TEXT_TAP_A_NETWORK_TO_CONNECT;
return;
}
+ if (!mIsScanningSubTitleShownOnce && mIsProgressBarVisible) {
+ // When the Wi-Fi scan result callback is received
+ // Sub-Title: Searching for networks...
+ mSubtitle = SUBTITLE_TEXT_SEARCHING_FOR_NETWORKS;
+ return;
+ }
+
// Sub-Title:
// show non_carrier_network_unavailable
// - while Wi-Fi on + no Wi-Fi item
@@ -353,7 +388,7 @@ public class InternetConnectivityPanel implements PanelContent, LifecycleObserve
mSubtitle = SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE;
}
- protected void showProgressBar() {
+ protected void updateProgressBar() {
if (mWifiManager == null || !mInternetUpdater.isWifiEnabled()) {
setProgressBarVisible(false);
return;
@@ -362,8 +397,9 @@ public class InternetConnectivityPanel implements PanelContent, LifecycleObserve
setProgressBarVisible(true);
List<ScanResult> wifiScanResults = mWifiManager.getScanResults();
if (wifiScanResults != null && wifiScanResults.size() > 0) {
- mContext.getMainThreadHandler().postDelayed(mHideProgressBarRunnable,
- 2000 /* delay millis */);
+ mHandlerInjector.postDelay(mHideProgressBarRunnable);
+ } else if (!mIsScanningSubTitleShownOnce) {
+ mHandlerInjector.postDelay(mHideScanningSubTitleRunnable);
}
}
diff --git a/src/com/android/settings/password/BiometricFragment.java b/src/com/android/settings/password/BiometricFragment.java
index 7666a3609c..e4c08e73ff 100644
--- a/src/com/android/settings/password/BiometricFragment.java
+++ b/src/com/android/settings/password/BiometricFragment.java
@@ -47,7 +47,6 @@ public class BiometricFragment extends InstrumentedFragment {
private int mUserId;
// Created/Initialized once and retained
- private PromptInfo mPromptInfo;
private BiometricPrompt mBiometricPrompt;
private CancellationSignal mCancellationSignal;
@@ -127,7 +126,7 @@ public class BiometricFragment extends InstrumentedFragment {
final Bundle bundle = getArguments();
final PromptInfo promptInfo = bundle.getParcelable(KEY_PROMPT_INFO);
- final BiometricPrompt.Builder builder = new BiometricPrompt.Builder(getContext())
+ mBiometricPrompt = new BiometricPrompt.Builder(getContext())
.setTitle(promptInfo.getTitle())
.setUseDefaultTitle() // use default title if title is null/empty
.setDeviceCredentialAllowed(true)
@@ -140,13 +139,19 @@ public class BiometricFragment extends InstrumentedFragment {
.setConfirmationRequired(promptInfo.isConfirmationRequested())
.setDisallowBiometricsIfPolicyExists(
promptInfo.isDisallowBiometricsIfPolicyExists())
- .setReceiveSystemEvents(true);
+ .setReceiveSystemEvents(true)
+ .build();
+ }
- mBiometricPrompt = builder.build();
- mCancellationSignal = new CancellationSignal();
+ @Override
+ public void onResume() {
+ super.onResume();
- mBiometricPrompt.authenticateUser(mCancellationSignal, mClientExecutor,
- mAuthenticationCallback, mUserId);
+ if (mCancellationSignal == null) {
+ mCancellationSignal = new CancellationSignal();
+ mBiometricPrompt.authenticateUser(mCancellationSignal, mClientExecutor,
+ mAuthenticationCallback, mUserId);
+ }
}
@Override
@@ -154,4 +159,3 @@ public class BiometricFragment extends InstrumentedFragment {
return SettingsEnums.BIOMETRIC_FRAGMENT;
}
}
-
diff --git a/src/com/android/settings/password/ChooseLockPattern.java b/src/com/android/settings/password/ChooseLockPattern.java
index 53898a6eb9..b60b427360 100644
--- a/src/com/android/settings/password/ChooseLockPattern.java
+++ b/src/com/android/settings/password/ChooseLockPattern.java
@@ -16,6 +16,8 @@
package com.android.settings.password;
+import static android.view.View.ACCESSIBILITY_LIVE_REGION_POLITE;
+
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_UNIFICATION_PROFILE_CREDENTIAL;
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_UNIFICATION_PROFILE_ID;
@@ -496,6 +498,7 @@ public class ChooseLockPattern extends SettingsActivity {
R.layout.choose_lock_pattern, container, false);
updateActivityTitle();
layout.setHeaderText(getActivity().getTitle());
+ layout.getHeaderTextView().setAccessibilityLiveRegion(ACCESSIBILITY_LIVE_REGION_POLITE);
if (getResources().getBoolean(R.bool.config_lock_pattern_minimal_ui)) {
View iconView = layout.findViewById(R.id.sud_layout_icon);
if (iconView != null) {
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
index 6241cc91cf..03e83a4f61 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
@@ -150,17 +150,6 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
}
};
- private String getStringForError(int errorCode) {
- switch (errorCode) {
- case BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED:
- return getString(com.android.internal.R.string.biometric_error_user_canceled);
- case BiometricConstants.BIOMETRIC_ERROR_CANCELED:
- return getString(com.android.internal.R.string.biometric_error_canceled);
- default:
- return null;
- }
- }
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
diff --git a/src/com/android/settings/search/SearchFeatureProvider.java b/src/com/android/settings/search/SearchFeatureProvider.java
index 18c802c219..4c39b9c0c1 100644
--- a/src/com/android/settings/search/SearchFeatureProvider.java
+++ b/src/com/android/settings/search/SearchFeatureProvider.java
@@ -21,7 +21,6 @@ import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO;
import android.annotation.NonNull;
import android.app.Activity;
import android.app.ActivityOptions;
-import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -43,6 +42,7 @@ import com.google.android.setupcompat.util.WizardManagerHelper;
*/
public interface SearchFeatureProvider {
+ String KEY_HOMEPAGE_SEARCH_BAR = "homepage_search_bar";
int REQUEST_CODE = 501;
/**
@@ -100,8 +100,9 @@ public interface SearchFeatureProvider {
FeatureFactory.getFactory(context).getSlicesFeatureProvider()
.indexSliceDataAsync(context);
+
FeatureFactory.getFactory(context).getMetricsFeatureProvider()
- .action(context, SettingsEnums.ACTION_SEARCH_RESULTS);
+ .logSettingsTileClick(KEY_HOMEPAGE_SEARCH_BAR, pageId);
final Bundle bundle = ActivityOptions.makeSceneTransitionAnimation(activity).toBundle();
activity.startActivityForResult(intent, REQUEST_CODE, bundle);
});
diff --git a/src/com/android/settings/sim/ChooseSimActivity.java b/src/com/android/settings/sim/ChooseSimActivity.java
index f8bdc3011b..d0ccc4c9d0 100644
--- a/src/com/android/settings/sim/ChooseSimActivity.java
+++ b/src/com/android/settings/sim/ChooseSimActivity.java
@@ -19,6 +19,7 @@ package com.android.settings.sim;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
+import android.icu.text.MessageFormat;
import android.os.Bundle;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
@@ -41,10 +42,12 @@ import com.google.android.setupdesign.items.IItem;
import com.google.android.setupdesign.items.Item;
import com.google.android.setupdesign.items.ItemGroup;
import com.google.android.setupdesign.items.RecyclerItemAdapter;
-import com.google.android.setupdesign.view.HeaderRecyclerView;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
+import java.util.Map;
/** Activity to show a list of profiles for user to choose. */
public class ChooseSimActivity extends Activity
@@ -104,13 +107,17 @@ public class ChooseSimActivity extends Activity
}
GlifLayout layout = findViewById(R.id.glif_layout);
- TextView textView = findViewById(R.id.subtitle);
int subscriptionCount = mEmbeddedSubscriptions.size();
if (mHasPsim) { // Choose a number to use
subscriptionCount++;
}
layout.setHeaderText(getString(R.string.choose_sim_title));
- textView.setText(getString(R.string.choose_sim_text, subscriptionCount));
+ MessageFormat msgFormat = new MessageFormat(
+ getString(R.string.choose_sim_text),
+ Locale.getDefault());
+ Map<String, Object> arguments = new HashMap<>();
+ arguments.put("count", subscriptionCount);
+ layout.setDescriptionText(msgFormat.format(arguments));
displaySubscriptions();
@@ -217,7 +224,7 @@ public class ChooseSimActivity extends Activity
private void displaySubscriptions() {
View rootView = findViewById(android.R.id.content);
- GlifRecyclerLayout layout = rootView.findViewById(R.id.recycler_list);
+ GlifRecyclerLayout layout = rootView.findViewById(R.id.glif_layout);
RecyclerItemAdapter adapter = (RecyclerItemAdapter) layout.getAdapter();
adapter.setOnItemSelectedListener(this);
mItemGroup = (ItemGroup) adapter.getRootItemHierarchy();
@@ -265,10 +272,6 @@ public class ChooseSimActivity extends Activity
item.setId(index++);
mItemGroup.addChild(item);
}
-
- // This removes the unused header artifact from GlifRecyclerLayout.
- HeaderRecyclerView rv = (HeaderRecyclerView) layout.getRecyclerView();
- rv.getHeader().setVisibility(View.GONE);
}
private void updateSubscriptions() {
diff --git a/src/com/android/settings/widget/LoadingViewController.java b/src/com/android/settings/widget/LoadingViewController.java
index 294e55e7ea..66eebf387b 100644
--- a/src/com/android/settings/widget/LoadingViewController.java
+++ b/src/com/android/settings/widget/LoadingViewController.java
@@ -22,34 +22,66 @@ import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
+import androidx.annotation.Nullable;
+
/**
- * A helper class that manages show/hide loading spinner.
+ * A helper class that manages show/hide loading spinner, content view and empty view (optional).
*/
public class LoadingViewController {
private static final long DELAY_SHOW_LOADING_CONTAINER_THRESHOLD_MS = 100L;
- public final Handler mFgHandler;
- public final View mLoadingView;
- public final View mContentView;
+ private final Handler mFgHandler;
+ private final View mLoadingView;
+ private final View mContentView;
+ private final View mEmptyView;
public LoadingViewController(View loadingView, View contentView) {
+ this(loadingView, contentView, null /* emptyView*/);
+ }
+
+ public LoadingViewController(View loadingView, View contentView, @Nullable View emptyView) {
mLoadingView = loadingView;
mContentView = contentView;
+ mEmptyView = emptyView;
mFgHandler = new Handler(Looper.getMainLooper());
}
private Runnable mShowLoadingContainerRunnable = new Runnable() {
public void run() {
- handleLoadingContainer(false /* done */, false /* animate */);
+ showLoadingView();
}
};
+ /**
+ * Shows content view and hides loading view & empty view.
+ */
public void showContent(boolean animate) {
// Cancel any pending task to show the loading animation and show the list of
// apps directly.
mFgHandler.removeCallbacks(mShowLoadingContainerRunnable);
- handleLoadingContainer(true /* show */, animate);
+ handleLoadingContainer(true /* showContent */, false /* showEmpty*/, animate);
+ }
+
+ /**
+ * Shows empty view and hides loading view & content view.
+ */
+ public void showEmpty(boolean animate) {
+ if (mEmptyView == null) {
+ return;
+ }
+
+ // Cancel any pending task to show the loading animation and show the list of
+ // apps directly.
+ mFgHandler.removeCallbacks(mShowLoadingContainerRunnable);
+ handleLoadingContainer(false /* showContent */, true /* showEmpty */, animate);
+ }
+
+ /**
+ * Shows loading view and hides content view & empty view.
+ */
+ public void showLoadingView() {
+ handleLoadingContainer(false /* showContent */, false /* showEmpty */, false /* animate */);
}
public void showLoadingViewDelayed() {
@@ -57,8 +89,9 @@ public class LoadingViewController {
mShowLoadingContainerRunnable, DELAY_SHOW_LOADING_CONTAINER_THRESHOLD_MS);
}
- public void handleLoadingContainer(boolean done, boolean animate) {
- handleLoadingContainer(mLoadingView, mContentView, done, animate);
+ private void handleLoadingContainer(boolean showContent, boolean showEmpty, boolean animate) {
+ handleLoadingContainer(mLoadingView, mContentView, mEmptyView,
+ showContent, showEmpty, animate);
}
/**
@@ -75,6 +108,25 @@ public class LoadingViewController {
setViewShown(content, done, animate);
}
+ /**
+ * Show/hide loading view and content view and empty view.
+ *
+ * @param loading The loading spinner view
+ * @param content The content view
+ * @param empty The empty view shows no item summary to users.
+ * @param showContent If true, content is set visible and loading is set invisible.
+ * @param showEmpty If true, empty is set visible and loading is set invisible.
+ * @param animate Whether or not content/loading views should animate in/out.
+ */
+ public static void handleLoadingContainer(View loading, View content, View empty,
+ boolean showContent, boolean showEmpty, boolean animate) {
+ if (empty != null) {
+ setViewShown(empty, showEmpty, animate);
+ }
+ setViewShown(content, showContent, animate);
+ setViewShown(loading, !showContent && !showEmpty, animate);
+ }
+
private static void setViewShown(final View view, boolean shown, boolean animate) {
if (animate) {
Animation animation = AnimationUtils.loadAnimation(view.getContext(),
diff --git a/src/com/android/settings/widget/SeekBarPreference.java b/src/com/android/settings/widget/SeekBarPreference.java
index 47bb28608d..62a19b945e 100644
--- a/src/com/android/settings/widget/SeekBarPreference.java
+++ b/src/com/android/settings/widget/SeekBarPreference.java
@@ -57,6 +57,7 @@ public class SeekBarPreference extends RestrictedPreference
private SeekBar mSeekBar;
private boolean mShouldBlink;
private int mAccessibilityRangeInfoType = AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_INT;
+ private CharSequence mOverrideSeekBarStateDescription;
private CharSequence mSeekBarContentDescription;
private CharSequence mSeekBarStateDescription;
@@ -162,6 +163,9 @@ public class SeekBarPreference extends RestrictedPreference
mAccessibilityRangeInfoType, rangeInfo.getMin(),
rangeInfo.getMax(), rangeInfo.getCurrent()));
}
+ if (mOverrideSeekBarStateDescription != null) {
+ info.setStateDescription(mOverrideSeekBarStateDescription);
+ }
}
});
}
@@ -348,6 +352,13 @@ public class SeekBarPreference extends RestrictedPreference
}
}
+ /**
+ * Overrides the state description of {@link SeekBar} with given content.
+ */
+ public void overrideSeekBarStateDescription(CharSequence stateDescription) {
+ mOverrideSeekBarStateDescription = stateDescription;
+ }
+
@Override
protected Parcelable onSaveInstanceState() {
/*
diff --git a/src/com/android/settings/widget/SettingsMainSwitchPreference.java b/src/com/android/settings/widget/SettingsMainSwitchPreference.java
index de7d6926d0..09e1ca4cdc 100644
--- a/src/com/android/settings/widget/SettingsMainSwitchPreference.java
+++ b/src/com/android/settings/widget/SettingsMainSwitchPreference.java
@@ -82,15 +82,15 @@ public class SettingsMainSwitchPreference extends TwoStatePreference implements
holder.setDividerAllowedAbove(false);
holder.setDividerAllowedBelow(false);
- mMainSwitchBar = (SettingsMainSwitchBar) holder.findViewById(R.id.main_switch_bar);
- mMainSwitchBar.show();
if (mRestrictedHelper != null) {
mEnforcedAdmin = mRestrictedHelper.checkRestrictionEnforced();
}
- updateStatus(isChecked());
- registerListenerToSwitchBar();
-
- if (!mIsVisible) {
+ mMainSwitchBar = (SettingsMainSwitchBar) holder.findViewById(R.id.main_switch_bar);
+ if (mIsVisible) {
+ mMainSwitchBar.show();
+ updateStatus(isChecked());
+ registerListenerToSwitchBar();
+ } else {
mMainSwitchBar.hide();
}
}
diff --git a/src/com/android/settings/wifi/WifiPickerTrackerHelper.java b/src/com/android/settings/wifi/WifiPickerTrackerHelper.java
index aaab3685f7..424c818cb6 100644
--- a/src/com/android/settings/wifi/WifiPickerTrackerHelper.java
+++ b/src/com/android/settings/wifi/WifiPickerTrackerHelper.java
@@ -77,7 +77,7 @@ public class WifiPickerTrackerHelper implements LifecycleObserver {
Process.THREAD_PRIORITY_BACKGROUND);
mWorkerThread.start();
- mWifiPickerTracker = FeatureFactory.getFactory(context)
+ mWifiPickerTracker = FeatureFactory.getFactory(context)
.getWifiTrackerLibProvider()
.createWifiPickerTracker(lifecycle, context,
new Handler(Looper.getMainLooper()),
@@ -139,8 +139,8 @@ public class WifiPickerTrackerHelper implements LifecycleObserver {
return true;
}
- /** Confirms connection of the carrier network */
- public boolean isActiveCarrierNetwork() {
+ /** Confirms connection of the carrier network connected with the internet access */
+ public boolean isCarrierNetworkActive() {
final MergedCarrierEntry mergedCarrierEntry = mWifiPickerTracker.getMergedCarrierEntry();
if (mergedCarrierEntry != null) {
return mergedCarrierEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED