Merge "Remove LooperMode.LEGACY usage from settings/development" into main
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 194d017..4b38062 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -147,12 +147,13 @@
<uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.GET_BACKGROUND_INSTALLED_PACKAGES" />
+ <uses-permission android:name="android.permission.SATELLITE_COMMUNICATION" />
<application
android:name=".SettingsApplication"
android:label="@string/settings_label"
android:icon="@drawable/ic_launcher_settings"
- android:theme="@style/Theme.Settings.NoActionBar"
+ android:theme="@style/Theme.Settings"
android:hardwareAccelerated="true"
android:requiredForAllUsers="true"
android:supportsRtl="true"
diff --git a/res/drawable/accessibility_shortcut_type_quick_settings.xml b/res/drawable/accessibility_shortcut_type_quick_settings.xml
index a801347..112250b 100644
--- a/res/drawable/accessibility_shortcut_type_quick_settings.xml
+++ b/res/drawable/accessibility_shortcut_type_quick_settings.xml
@@ -28,7 +28,7 @@
android:pathData="M24,0L288,0A24,24 0,0 1,312 24L312,106A24,24 0,0 1,288 130L24,130A24,24 0,0 1,0 106L0,24A24,24 0,0 1,24 0z"/>
<path
android:pathData="M24,0L288,0A24,24 0,0 1,312 24L312,106A24,24 0,0 1,288 130L24,130A24,24 0,0 1,0 106L0,24A24,24 0,0 1,24 0z"
- android:fillColor="#ffffff"/>
+ android:fillColor="@color/settingslib_protection_color"/>
<path
android:pathData="M209,139.62H210V138.62V32C210,24.82 204.18,19 197,19H115C107.82,19 102,24.82 102,32V138.62V139.62H103H209Z"
android:strokeWidth="2"
diff --git a/res/layout/fingerprint_v2_udfps_enroll_enrolling.xml b/res/layout/fingerprint_v2_udfps_enroll_enrolling.xml
index 32df665..ddd2c30 100644
--- a/res/layout/fingerprint_v2_udfps_enroll_enrolling.xml
+++ b/res/layout/fingerprint_v2_udfps_enroll_enrolling.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2023 The Android Open Source Project
+ ~ Copyright (C) 2024 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.
@@ -15,56 +15,77 @@
~ limitations under the License.
-->
-<com.google.android.setupdesign.GlifLayout
+<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
-
android:id="@+id/setup_wizard_layout"
style="?attr/fingerprint_layout_theme"
android:layout_width="match_parent"
android:layout_height="match_parent"
- >
+ android:orientation="vertical">
- <LinearLayout
- style="@style/SudContentFrame"
+ <!-- This is used to grab style attributes and apply them
+ to this layout -->
+ <com.google.android.setupdesign.GlifLayout
+ android:id="@+id/dummy_glif_layout"
+ style="?attr/fingerprint_layout_theme"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:visibility="gone"
+ />
+
+ <ImageView
+ android:id="@+id/sud_layout_icon"
+ style="@style/SudGlifIcon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:scaleType="fitStart"
+ android:src="@drawable/ic_lock" />
+
+ <TextView
+ android:id="@+id/title"
+ style="@style/SudGlifHeaderTitle"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="80dp"
+ android:ellipsize="end"
+ android:lines="2"
+ />
+
+ <TextView
+ android:id="@+id/description"
+ style="@style/SudDescription.Glif"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:lines="3"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ />
+
+
+ <com.airbnb.lottie.LottieAnimationView
+ android:id="@+id/illustration_lottie"
+ android:layout_width="match_parent"
+ android:layout_height="200dp"
android:clipChildren="false"
android:clipToPadding="false"
- android:orientation="vertical">
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ android:scaleType="centerInside"
+ app:lottie_autoPlay="true"
+ app:lottie_loop="true"
+ app:lottie_speed=".85" />
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:gravity="center|bottom"
- android:orientation="vertical">
+ <FrameLayout
+ android:id="@+id/layout_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_horizontal|bottom"
+ android:clipToPadding="false"
+ >
- <FrameLayout
- android:id="@+id/layout_container"
- android:layout_width="200dp"
- android:layout_height="200dp"
- android:layout_gravity="center_horizontal|bottom"
- android:clipChildren="false"
- android:clipToPadding="false"
- tools:ignore="Suspicious0dp">
-
- <!-- Animation res MUST be set in code -->
- <com.airbnb.lottie.LottieAnimationView
- android:id="@+id/illustration_lottie"
- android:layout_width="200dp"
- android:layout_height="200dp"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:scaleType="centerInside"
- app:lottie_autoPlay="true"
- app:lottie_loop="true"
- app:lottie_speed=".85" />
-
- </FrameLayout>
- </LinearLayout>
- </LinearLayout>
+ <include layout="@layout/fingerprint_v2_udfps_enroll_view" />
+ </FrameLayout>
-</com.google.android.setupdesign.GlifLayout>
+</LinearLayout>
diff --git a/res/layout/fingerprint_v2_udfps_enroll_view.xml b/res/layout/fingerprint_v2_udfps_enroll_view.xml
new file mode 100644
index 0000000..20df6e1
--- /dev/null
+++ b/res/layout/fingerprint_v2_udfps_enroll_view.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 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.
+ -->
+
+
+<com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.widget.UdfpsEnrollViewV2
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/udfps_animation_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@+id/udfps_enroll_animation_fp_progress_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ />
+
+ <ImageView
+ android:id="@+id/udfps_enroll_animation_fp_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ />
+
+</com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.widget.UdfpsEnrollViewV2>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 0673a10..408ba8c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -10245,10 +10245,10 @@
<!-- Switch label to enable the Data Saver feature [CHAR LIMIT=NONE] -->
<string name="data_saver_switch_title">Use Data Saver</string>
- <!-- Title for switch to allow app unrestricted data usage [CHAR LIMIT=30] -->
+ <!-- Title for switch to allow app unrestricted data usage [CHAR LIMIT=NONE] -->
<string name="unrestricted_app_title">Unrestricted data usage</string>
- <!-- Title for switch to allow app unrestricted data usage [CHAR LIMIT=75] -->
+ <!-- Title for switch to allow app unrestricted data usage [CHAR LIMIT=NONE] -->
<string name="unrestricted_app_summary">Allow unrestricted data access when Data Saver is on</string>
<!-- Button to switch the default home app [CHAR LIMIT=60] -->
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index f09175f..d44d727 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -1387,20 +1387,19 @@
* @param activity the Activity need to setup the edge to edge feature.
*/
public static void setupEdgeToEdge(@NonNull FragmentActivity activity) {
- if (com.android.window.flags.Flags.enforceEdgeToEdge()) {
- ViewCompat.setOnApplyWindowInsetsListener(activity.findViewById(android.R.id.content),
- (v, windowInsets) -> {
- Insets insets = windowInsets.getInsets(
- WindowInsetsCompat.Type.systemBars()
- | WindowInsetsCompat.Type.ime());
- // Apply the insets paddings to the view.
- v.setPadding(insets.left, insets.top, insets.right, insets.bottom);
+ ViewCompat.setOnApplyWindowInsetsListener(activity.findViewById(android.R.id.content),
+ (v, windowInsets) -> {
+ Insets insets = windowInsets.getInsets(
+ WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.ime());
+ int statusBarHeight = activity.getWindow().getDecorView().getRootWindowInsets()
+ .getInsets(WindowInsetsCompat.Type.statusBars()).top;
+ // Apply the insets paddings to the view.
+ v.setPadding(insets.left, statusBarHeight, insets.right, insets.bottom);
- // Return CONSUMED if you don't want the window insets to keep being
- // passed down to descendant views.
- return WindowInsetsCompat.CONSUMED;
- });
- }
+ // Return CONSUMED if you don't want the window insets to keep being
+ // passed down to descendant views.
+ return WindowInsetsCompat.CONSUMED;
+ });
}
private static FaceManager.RemovalCallback faceManagerRemovalCallback(int userId) {
diff --git a/src/com/android/settings/accessibility/AccessibilityShortcutsTutorial.java b/src/com/android/settings/accessibility/AccessibilityShortcutsTutorial.java
index fd11ab0..306bba7 100644
--- a/src/com/android/settings/accessibility/AccessibilityShortcutsTutorial.java
+++ b/src/com/android/settings/accessibility/AccessibilityShortcutsTutorial.java
@@ -460,6 +460,8 @@
final View image =
createIllustrationView(context,
R.drawable.a11y_shortcut_type_quick_settings);
+ // Remove the unneeded background, since the main image already includes a background
+ image.findViewById(R.id.image_background).setVisibility(GONE);
final int numFingers = AccessibilityUtil.isTouchExploreEnabled(context) ? 2 : 1;
Map<String, Object> arguments = new ArrayMap<>();
arguments.put("count", numFingers);
diff --git a/src/com/android/settings/accessibility/ContrastPreferenceController.java b/src/com/android/settings/accessibility/ContrastPreferenceController.java
index 4e9a9ec..33d3087 100644
--- a/src/com/android/settings/accessibility/ContrastPreferenceController.java
+++ b/src/com/android/settings/accessibility/ContrastPreferenceController.java
@@ -33,6 +33,7 @@
@Override
public int getAvailabilityStatus() {
- return Flags.enableColorContrastControl() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ // Hide color contrast entry point inside Accessibility settings.
+ return CONDITIONALLY_UNAVAILABLE;
}
}
diff --git a/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java b/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java
index f268a40..7f10af7 100644
--- a/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java
+++ b/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java
@@ -210,8 +210,7 @@
return;
}
- if (Flags.removeQsTooltipInSuw()
- && mContext instanceof Activity
+ if (mContext instanceof Activity
&& WizardManagerHelper.isAnySetupWizard(((Activity) mContext).getIntent())) {
// Don't show QuickSettingsTooltip in Setup Wizard
return;
diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
index 137be21..e62e473 100644
--- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
@@ -931,9 +931,7 @@
}
Activity activity = getActivity();
- if (com.android.settings.accessibility.Flags.removeQsTooltipInSuw()
- && activity != null
- && WizardManagerHelper.isAnySetupWizard(activity.getIntent())) {
+ if (activity != null && WizardManagerHelper.isAnySetupWizard(activity.getIntent())) {
// Don't show QuickSettingsTooltip in Setup Wizard
return;
}
diff --git a/src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragment.java b/src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragment.java
index 036190b..a4eaf37 100644
--- a/src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/shortcuts/EditShortcutsPreferenceFragment.java
@@ -54,6 +54,7 @@
import androidx.preference.Preference;
import androidx.recyclerview.widget.RecyclerView;
+import com.android.internal.accessibility.common.ShortcutConstants;
import com.android.internal.accessibility.dialog.AccessibilityTarget;
import com.android.internal.accessibility.dialog.AccessibilityTargetHelper;
import com.android.settings.R;
@@ -204,7 +205,7 @@
// TODO(b/325664350): Implement shortcut type for "all shortcuts"
List<AccessibilityTarget> accessibilityTargets =
AccessibilityTargetHelper.getInstalledTargets(
- activity.getBaseContext(), AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY);
+ activity.getBaseContext(), ShortcutConstants.UserShortcutType.HARDWARE);
Pair<String, String> titles = getTitlesFromAccessibilityTargetList(
mShortcutTargets,
diff --git a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtils.java b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtils.java
index 2eee9df..4ff2e90 100644
--- a/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtils.java
+++ b/src/com/android/settings/biometrics/activeunlock/ActiveUnlockStatusUtils.java
@@ -22,7 +22,6 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ComponentInfo;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.provider.DeviceConfig;
@@ -38,8 +37,6 @@
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.BasePreferenceController.AvailabilityStatus;
-import java.util.List;
-
/** Utilities for active unlock details shared between Security Settings and Safety Center. */
public class ActiveUnlockStatusUtils {
@@ -98,18 +95,14 @@
Log.i(TAG, "authority not set");
return null;
}
- final List<PackageInfo> packageInfos =
- mContext.getPackageManager().getInstalledPackages(
- PackageManager.PackageInfoFlags.of(PackageManager.GET_PROVIDERS));
- for (PackageInfo packageInfo : packageInfos) {
- final ProviderInfo[] providers = packageInfo.providers;
- if (providers != null) {
- for (ProviderInfo provider : providers) {
- if (authority.equals(provider.authority) && isSystemApp(provider)) {
- return authority;
- }
- }
- }
+ final ProviderInfo provider = mContext.getPackageManager().resolveContentProvider(
+ authority, PackageManager.ComponentInfoFlags.of(PackageManager.MATCH_SYSTEM_ONLY));
+ if (provider == null) {
+ Log.i(TAG, "could not find provider");
+ return null;
+ }
+ if (authority.equals(provider.authority) && isSystemApp(provider)) {
+ return authority;
}
Log.e(TAG, "authority not valid");
return null;
diff --git a/src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintSensorRepository.kt b/src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintSensorRepository.kt
index 000a477..b7616e4 100644
--- a/src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintSensorRepository.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/data/repository/FingerprintSensorRepository.kt
@@ -32,6 +32,7 @@
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.transform
import kotlinx.coroutines.withContext
/**
@@ -45,12 +46,12 @@
}
class FingerprintSensorRepositoryImpl(
- fingerprintManager: FingerprintManager,
- backgroundDispatcher: CoroutineDispatcher,
- activityScope: CoroutineScope,
+ fingerprintManager: FingerprintManager,
+ backgroundDispatcher: CoroutineDispatcher,
+ activityScope: CoroutineScope,
) : FingerprintSensorRepository {
- override val fingerprintSensor: Flow<FingerprintSensor> =
+ private val fingerprintPropsInternal: Flow<FingerprintSensorPropertiesInternal> =
callbackFlow {
val callback =
object : IFingerprintAuthenticatorsRegisteredCallback.Stub() {
@@ -60,7 +61,7 @@
if (sensors.isEmpty()) {
trySend(DEFAULT_PROPS)
} else {
- trySend(sensors[0].toFingerprintSensor())
+ trySend(sensors[0])
}
}
}
@@ -71,20 +72,24 @@
}
.stateIn(activityScope, started = SharingStarted.Eagerly, initialValue = DEFAULT_PROPS)
- companion object {
- private const val TAG = "FingerprintSensorRepoImpl"
-
- private val DEFAULT_PROPS =
- FingerprintSensorPropertiesInternal(
- -1 /* sensorId */,
- SensorProperties.STRENGTH_CONVENIENCE,
- 0 /* maxEnrollmentsPerUser */,
- listOf<ComponentInfoInternal>(),
- FingerprintSensorProperties.TYPE_UNKNOWN,
- false /* halControlsIllumination */,
- true /* resetLockoutRequiresHardwareAuthToken */,
- listOf<SensorLocationInternal>(SensorLocationInternal.DEFAULT),
- )
- .toFingerprintSensor()
+ override val fingerprintSensor: Flow<FingerprintSensor> =
+ fingerprintPropsInternal.transform {
+ emit(it.toFingerprintSensor())
}
+
+ companion object {
+ private const val TAG = "FingerprintSensorRepoImpl"
+
+ private val DEFAULT_PROPS =
+ FingerprintSensorPropertiesInternal(
+ -1 /* sensorId */,
+ SensorProperties.STRENGTH_CONVENIENCE,
+ 0 /* maxEnrollmentsPerUser */,
+ listOf<ComponentInfoInternal>(),
+ FingerprintSensorProperties.TYPE_UNKNOWN,
+ false /* halControlsIllumination */,
+ true /* resetLockoutRequiresHardwareAuthToken */,
+ listOf<SensorLocationInternal>(SensorLocationInternal.DEFAULT),
+ )
+ }
}
diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintManagerInteractorImpl.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintManagerInteractorImpl.kt
index 900f7cf..652bc0c 100644
--- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintManagerInteractorImpl.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/FingerprintManagerInteractorImpl.kt
@@ -20,10 +20,12 @@
import android.content.Intent
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricFingerprintConstants
+import android.hardware.biometrics.SensorLocationInternal
import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.FingerprintManager.GenerateChallengeCallback
import android.hardware.fingerprint.FingerprintManager.RemovalCallback
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.os.CancellationSignal
import android.util.Log
import com.android.settings.biometrics.GatekeeperPasswordProvider
diff --git a/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/FingerprintManagerInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/FingerprintManagerInteractor.kt
index c0e1b4a..105929d 100644
--- a/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/FingerprintManagerInteractor.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/lib/domain/interactor/FingerprintManagerInteractor.kt
@@ -16,6 +16,7 @@
package com.android.settings.biometrics.fingerprint2.lib.domain.interactor
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import com.android.settings.biometrics.fingerprint2.lib.model.EnrollReason
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.lib.model.FingerprintAuthAttemptModel
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/fragment/RFPSEnrollFragment.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/fragment/RFPSEnrollFragment.kt
index a9cd16f..f6917f3 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/fragment/RFPSEnrollFragment.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/fragment/RFPSEnrollFragment.kt
@@ -33,7 +33,6 @@
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.settings.R
-import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.FingerEnrollState
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSIconTouchViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSViewModel
@@ -42,7 +41,6 @@
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.widget.RFPSProgressBar
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.BackgroundViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
-import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationViewModel
import com.android.settings.core.instrumentation.InstrumentedDialogFragment
import com.google.android.setupcompat.template.FooterBarMixin
import com.google.android.setupcompat.template.FooterButton
@@ -86,6 +84,7 @@
private val backgroundViewModel: BackgroundViewModel by lazy {
viewModelProvider[BackgroundViewModel::class.java]
}
+
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/widget/RFPSProgressBar.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/widget/RFPSProgressBar.kt
index 5a6fc14..dd7d9f5 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/widget/RFPSProgressBar.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/rfps/ui/widget/RFPSProgressBar.kt
@@ -24,7 +24,6 @@
import android.graphics.drawable.Drawable
import android.graphics.drawable.LayerDrawable
import android.util.AttributeSet
-import android.util.Log
import android.view.animation.AnimationUtils
import android.view.animation.Interpolator
import com.android.settings.R
@@ -82,7 +81,6 @@
}
shouldAnimateInternal = shouldAnimate
-
}
/** This function should only be called when actual progress has been made. */
fun updateProgress(percentComplete: Float) {
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/fragment/UdfpsEnrollFragment.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/fragment/UdfpsEnrollFragment.kt
index 6145128..9dfc222 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/fragment/UdfpsEnrollFragment.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/fragment/UdfpsEnrollFragment.kt
@@ -17,8 +17,9 @@
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.fragment
import android.os.Bundle
-import android.util.Log
import android.view.View
+import android.view.WindowManager
+import android.widget.TextView
import androidx.annotation.VisibleForTesting
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
@@ -28,19 +29,31 @@
import com.airbnb.lottie.LottieAnimationView
import com.airbnb.lottie.LottieCompositionFactory
import com.android.settings.R
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.DescriptionText
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.HeaderText
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.EducationAnimationModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.StageViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.widget.UdfpsEnrollViewV2
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
import com.google.android.setupdesign.GlifLayout
-import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
+/** This fragment is responsible for showing the udfps Enrollment UI. */
class UdfpsEnrollFragment() : Fragment(R.layout.fingerprint_v2_udfps_enroll_enrolling) {
/** Used for testing purposes */
private var factory: ViewModelProvider.Factory? = null
private val viewModel: UdfpsViewModel by lazy { viewModelProvider[UdfpsViewModel::class.java] }
+ private lateinit var udfpsEnrollView: UdfpsEnrollViewV2
+
+ private val viewModelProvider: ViewModelProvider by lazy {
+ if (factory != null) {
+ ViewModelProvider(requireActivity(), factory!!)
+ } else {
+ ViewModelProvider(requireActivity())
+ }
+ }
@VisibleForTesting
constructor(theFactory: ViewModelProvider.Factory) : this() {
@@ -49,38 +62,73 @@
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- val layout = view as GlifLayout
- val illustrationLottie: LottieAnimationView = layout.findViewById(R.id.illustration_lottie)!!
+ val illustrationLottie: LottieAnimationView = view.findViewById(R.id.illustration_lottie)!!
+ udfpsEnrollView = view.findViewById(R.id.udfps_animation_view)!!
+ val titleTextView = view.findViewById<TextView>(R.id.title)!!
+ val descriptionTextView = view.findViewById<TextView>(R.id.description)!!
+
+ val glifLayout = view.findViewById<GlifLayout>(R.id.dummy_glif_layout)!!
+ val backgroundColor = glifLayout.backgroundBaseColor
+ val window = requireActivity().window
+ window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
+ window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
+ val color = backgroundColor?.defaultColor ?: glifLayout.primaryColor.defaultColor
+ window.statusBarColor = color
+ view.setBackgroundColor(color)
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
viewLifecycleOwner.lifecycleScope.launch {
- viewModel.stageFlow.collect {
- layout.setHeaderText(getHeaderText(it))
- getDescriptionText(it)?.let { descriptionText ->
- layout.setDescriptionText(descriptionText)
- }
- getLottie(it)?.let { lottie ->
- layout.descriptionText = ""
- LottieCompositionFactory.fromRawRes(requireContext().applicationContext, lottie)
- .addListener { comp ->
- comp?.let { composition ->
- viewLifecycleOwner.lifecycleScope.launch {
- illustrationLottie.setComposition(composition)
- illustrationLottie.visibility = View.VISIBLE
- illustrationLottie.playAnimation()
- }
- }
- }
+ viewModel.headerText.collect { titleTextView.setText(it.toResource()) }
+ }
+
+ viewLifecycleOwner.lifecycleScope.launch {
+ viewModel.descriptionText.collect {
+ if (it != null) {
+ it.toResource()?.let { text -> descriptionTextView.setText(text) }
+ } else {
+ descriptionTextView.text = ""
}
}
}
+
+ viewLifecycleOwner.lifecycleScope.launch {
+ viewModel.sensorLocation.collect { rect -> udfpsEnrollView.setSensorRect(rect) }
+ }
+
+ viewLifecycleOwner.lifecycleScope.launch {
+ viewModel.accessibilityEnabled.collect { isEnabled -> udfpsEnrollView.setAccessibilityEnabled(isEnabled) }
+ }
+
+ viewLifecycleOwner.lifecycleScope.launch {
+ viewModel.lottie.collect { lottieModel ->
+ val resource = lottieModel.toResource()
+ if (resource != null) {
+ LottieCompositionFactory.fromRawRes(requireContext(), resource).addListener { comp ->
+ comp?.let { composition ->
+ illustrationLottie.setComposition(composition)
+ illustrationLottie.visibility = View.VISIBLE
+ illustrationLottie.playAnimation()
+ }
+ }
+ } else {
+ illustrationLottie.visibility = View.INVISIBLE
+ }
+ }
+ }
+ viewLifecycleOwner.lifecycleScope.launch {
+ viewModel.udfpsEvent.collect { udfpsEnrollView.onUdfpsEvent(it) }
+ }
+
+ viewLifecycleOwner.lifecycleScope.launch {
+ viewModel.enrollStage.collect { udfpsEnrollView.updateStage(it) }
+ }
}
}
}
- private fun getHeaderText(stageViewModel: StageViewModel): Int {
- return when (stageViewModel) {
+ private fun HeaderText.toResource(): Int {
+ return when (this.stageViewModel) {
StageViewModel.Center,
StageViewModel.Guided,
StageViewModel.Fingertip,
@@ -90,8 +138,8 @@
}
}
- private fun getDescriptionText(stageViewModel: StageViewModel): Int? {
- return when (stageViewModel) {
+ private fun DescriptionText.toResource(): Int? {
+ return when (this.stageViewModel) {
StageViewModel.Center,
StageViewModel.Guided,
StageViewModel.Fingertip,
@@ -101,8 +149,8 @@
}
}
- private fun getLottie(stageViewModel: StageViewModel): Int? {
- return when (stageViewModel) {
+ private fun EducationAnimationModel.toResource(): Int? {
+ return when (this.stageViewModel) {
StageViewModel.Center,
StageViewModel.Guided -> R.raw.udfps_center_hint_lottie
StageViewModel.Fingertip -> R.raw.udfps_tip_hint_lottie
@@ -112,14 +160,6 @@
}
}
- private val viewModelProvider: ViewModelProvider by lazy {
- if (factory != null) {
- ViewModelProvider(requireActivity(), factory!!)
- } else {
- ViewModelProvider(requireActivity())
- }
- }
-
companion object {
private const val TAG = "UDFPSEnrollFragment"
private val navStep = FingerprintNavigationStep.Enrollment::class
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/DescriptionText.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/DescriptionText.kt
new file mode 100644
index 0000000..192a787
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/DescriptionText.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 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.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel
+
+/** Represents the description text for UDFPS enrollment */
+data class DescriptionText(
+ val isSuw: Boolean,
+ val isAccessibility: Boolean,
+ val stageViewModel: StageViewModel,
+)
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/EducationAnimationModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/EducationAnimationModel.kt
new file mode 100644
index 0000000..cf125c3
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/EducationAnimationModel.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 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.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel
+
+/** Represents the lottie for UDFPS enrollment */
+data class EducationAnimationModel(
+ val isSuw: Boolean,
+ val isAccessibility: Boolean,
+ val stageViewModel: StageViewModel,
+)
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/HeaderText.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/HeaderText.kt
new file mode 100644
index 0000000..9cfcddc
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/HeaderText.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 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.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel
+
+/** Represents the header text for UDFPS enrollment */
+data class HeaderText(
+ val isSuw: Boolean,
+ val isAccessibility: Boolean,
+ val stageViewModel: StageViewModel,
+)
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/StageViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/StageViewModel.kt
index b879ce1..75eaec7 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/StageViewModel.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/StageViewModel.kt
@@ -22,15 +22,30 @@
* of the where the user should press their fingerprint
*/
sealed class StageViewModel {
+ /** Unknown stage */
data object Unknown : StageViewModel()
+ /** This is the stage that moves the fingerprint icon around during enrollment. */
data object Guided : StageViewModel()
+ /** The center stage is the initial stage of enrollment. */
data object Center : StageViewModel()
+ /**
+ * Fingerprint stage of enrollment. Typically there is some sort of indication that a user should
+ * be using their finger tip to enroll.
+ */
data object Fingertip : StageViewModel()
+ /**
+ * Left edge stage of enrollment. Typically there is an indication that a user should be using the
+ * left edge of their fingerprint.
+ */
data object LeftEdge : StageViewModel()
+ /**
+ * Right edge stage of enrollment. Typically there is an indication that a user should be using
+ * the right edge of their fingerprint.
+ */
data object RightEdge : StageViewModel()
}
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsEnrollEvent.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsEnrollEvent.kt
new file mode 100644
index 0000000..e349ceb
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsEnrollEvent.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 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.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel
+
+/** A class indicating a udfps enroll event occurred. */
+sealed class UdfpsEnrollEvent
+
+/** Describes how many [remainingSteps] and how many [totalSteps] are left in udfps enrollment. */
+data class UdfpsProgress(val remainingSteps: Int, val totalSteps: Int) : UdfpsEnrollEvent()
+
+/** Indicates a help event has been sent by enrollment */
+data class UdfpsHelp(val helpMsgId: Int, val helpString: String) : UdfpsEnrollEvent()
+
+/** Indicates a error event has been sent by enrollment */
+data class UdfpsError(val errMsgId: Int, val errString: String) : UdfpsEnrollEvent()
+
+/** Indicates an acquired event has occurred */
+data class Acquired(val acquiredGood: Boolean) : UdfpsEnrollEvent()
+
+/** Indicates a pointer down event has occurred */
+data object PointerDown : UdfpsEnrollEvent()
+
+/** Indicates a pointer up event has occurred */
+data object PointerUp : UdfpsEnrollEvent()
+
+/** Indicates the overlay has shown */
+data object OverlayShown : UdfpsEnrollEvent()
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsViewModel.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsViewModel.kt
index 4fc3d1c..a5fdf1c 100644
--- a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsViewModel.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/viewmodel/UdfpsViewModel.kt
@@ -16,16 +16,93 @@
package com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel
+import android.graphics.Rect
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintNavigationStep
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
/** ViewModel used to drive UDFPS Enrollment through [UdfpsEnrollFragment] */
class UdfpsViewModel() : ViewModel() {
- /** Indicates what stage UDFPS enrollment is in. */
- val stageFlow = flowOf(StageViewModel.Center)
+ private val isSetupWizard = flowOf(false)
+
+ /** Indicates which Enrollment stage we are currently in. */
+ private val sensorLocationInternal = Pair(540, 1713)
+ private val sensorRadius = 100
+ private val sensorRect =
+ Rect(
+ this.sensorLocationInternal.first - sensorRadius,
+ this.sensorLocationInternal.second - sensorRadius,
+ this.sensorLocationInternal.first + sensorRadius,
+ this.sensorLocationInternal.second + sensorRadius,
+ )
+
+ private val stageThresholds = flowOf(listOf(.25, .5, .75, .875))
+
+ /** Indicates if accessibility is enabled */
+ val accessibilityEnabled = flowOf(false)
+
+ /** Indicates the locates of the fingerprint sensor. */
+ val sensorLocation: Flow<Rect> = flowOf(sensorRect)
+
+ /** This is currently not hooked up to fingerprint manager, and is being fed mock events. */
+ val udfpsEvent: Flow<UdfpsEnrollEvent> =
+ flow {
+ enrollEvents.forEach { events ->
+ events.forEach { event -> emit(event) }
+ delay(1000)
+ }
+ }
+ .flowOn(Dispatchers.IO)
+
+ /** Determines the current [StageViewModel] enrollment is in */
+ val enrollStage: Flow<StageViewModel> =
+ combine(stageThresholds, udfpsEvent) { thresholds, event ->
+ if (event is UdfpsProgress) {
+ thresholdToStageMap(thresholds, event.totalSteps - event.remainingSteps, event.totalSteps)
+ } else {
+ null
+ }
+ }
+ .filterNotNull()
+
+ /** The header text for UDFPS enrollment */
+ val headerText: Flow<HeaderText> =
+ combine(isSetupWizard, accessibilityEnabled, enrollStage) { isSuw, isAccessibility, stage ->
+ return@combine HeaderText(isSuw, isAccessibility, stage)
+ }
+
+ private val shouldClearDescriptionText = enrollStage.map { it is StageViewModel.Unknown }
+
+ /** The description text for UDFPS enrollment */
+ val descriptionText: Flow<DescriptionText?> =
+ combine(isSetupWizard, accessibilityEnabled, enrollStage, shouldClearDescriptionText) {
+ isSuw,
+ isAccessibility,
+ stage,
+ shouldClearText ->
+ if (shouldClearText) {
+ return@combine null
+ } else {
+ return@combine DescriptionText(isSuw, isAccessibility, stage)
+ }
+ }
+
+ /** The lottie that should be shown for UDFPS Enrollment */
+ val lottie: Flow<EducationAnimationModel> =
+ combine(isSetupWizard, accessibilityEnabled, enrollStage) { isSuw, isAccessibility, stage ->
+ return@combine EducationAnimationModel(isSuw, isAccessibility, stage)
+ }.distinctUntilChanged()
class UdfpsEnrollmentFactory() : ViewModelProvider.Factory {
@@ -38,5 +115,58 @@
companion object {
private val navStep = FingerprintNavigationStep.Enrollment::class
private const val TAG = "UDFPSViewModel"
+ private val ENROLLMENT_STAGES_ORDERED =
+ listOf(
+ StageViewModel.Center,
+ StageViewModel.Guided,
+ StageViewModel.Fingertip,
+ StageViewModel.LeftEdge,
+ StageViewModel.RightEdge,
+ )
+
+ /**
+ * [thresholds] is a list of 4 numbers from [0,1] that separate enrollment into 5 stages. The
+ * stage is determined by mapping [thresholds] * [maxSteps] and finding where the [currentStep]
+ * is.
+ *
+ * Each number in the array should be strictly increasing such as [0.2, 0.5, 0.6, 0.8]
+ */
+ private fun thresholdToStageMap(
+ thresholds: List<Double>,
+ currentStep: Int,
+ maxSteps: Int,
+ ): StageViewModel {
+ val stageIterator = ENROLLMENT_STAGES_ORDERED.iterator()
+ thresholds.forEach {
+ val thresholdLimit = it * maxSteps
+ val curr = stageIterator.next()
+ if (currentStep < thresholdLimit) {
+ return curr
+ }
+ }
+ return stageIterator.next()
+ }
+
+ /** This will be removed */
+ private val enrollEvents: List<List<UdfpsEnrollEvent>> =
+ listOf(
+ listOf(OverlayShown),
+ CreateProgress(10, 10),
+ CreateProgress(9, 10),
+ CreateProgress(8, 10),
+ CreateProgress(7, 10),
+ CreateProgress(6, 10),
+ CreateProgress(5, 10),
+ CreateProgress(4, 10),
+ CreateProgress(3, 10),
+ CreateProgress(2, 10),
+ CreateProgress(1, 10),
+ CreateProgress(0, 10),
+ )
+
+ /** This will be removed */
+ private fun CreateProgress(remaining: Int, total: Int): List<UdfpsEnrollEvent> {
+ return listOf(PointerDown, Acquired(true), UdfpsProgress(remaining, total), PointerUp)
+ }
}
}
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollHelperV2.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollHelperV2.kt
new file mode 100644
index 0000000..5d4607c4
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollHelperV2.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2024 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.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.widget
+
+import android.content.Context
+import android.graphics.PointF
+import android.util.TypedValue
+import android.view.accessibility.AccessibilityManager
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.StageViewModel
+
+/** Keeps track of which guided enrollment point we should be using */
+class UdfpsEnrollHelperV2(private val mContext: Context) {
+
+ private var isGuidedEnrollment: Boolean = false
+ private val accessibilityEnabled: Boolean
+ private val guidedEnrollmentPoints: MutableList<PointF>
+ private var index = 0
+
+ init {
+ val am = mContext.getSystemService(AccessibilityManager::class.java)
+ accessibilityEnabled = am!!.isEnabled
+ guidedEnrollmentPoints = ArrayList()
+
+ // Number of pixels per mm
+ val px =
+ TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 1f, mContext.resources.displayMetrics)
+ guidedEnrollmentPoints.add(PointF(2.00f * px, 0.00f * px))
+ guidedEnrollmentPoints.add(PointF(0.87f * px, -2.70f * px))
+ guidedEnrollmentPoints.add(PointF(-1.80f * px, -1.31f * px))
+ guidedEnrollmentPoints.add(PointF(-1.80f * px, 1.31f * px))
+ guidedEnrollmentPoints.add(PointF(0.88f * px, 2.70f * px))
+ guidedEnrollmentPoints.add(PointF(3.94f * px, -1.06f * px))
+ guidedEnrollmentPoints.add(PointF(2.90f * px, -4.14f * px))
+ guidedEnrollmentPoints.add(PointF(-0.52f * px, -5.95f * px))
+ guidedEnrollmentPoints.add(PointF(-3.33f * px, -3.33f * px))
+ guidedEnrollmentPoints.add(PointF(-3.99f * px, -0.35f * px))
+ guidedEnrollmentPoints.add(PointF(-3.62f * px, 2.54f * px))
+ guidedEnrollmentPoints.add(PointF(-1.49f * px, 5.57f * px))
+ guidedEnrollmentPoints.add(PointF(2.29f * px, 4.92f * px))
+ guidedEnrollmentPoints.add(PointF(3.82f * px, 1.78f * px))
+ }
+
+ /**
+ * This indicates whether we should be offsetting the enrollment icon based on
+ * [guidedEnrollmentPoints]
+ */
+ fun onUpdateStage(stage: StageViewModel) {
+ this.isGuidedEnrollment = stage is StageViewModel.Guided
+ }
+
+ /** Updates [index] to be used by [guidedEnrollmentPoints] */
+ fun onEnrollmentProgress(remaining: Int, totalSteps: Int) {
+ index = totalSteps - remaining
+ }
+
+ /**
+ * Returns the current guided enrollment point, or (0,0) if we are not in guided enrollment or are
+ * in accessibility.
+ */
+ val guidedEnrollmentLocation: PointF?
+ get() {
+ if (accessibilityEnabled || !isGuidedEnrollment) {
+ return null
+ }
+ var scale = SCALE
+ val originalPoint = guidedEnrollmentPoints[index % guidedEnrollmentPoints.size]
+ return PointF(originalPoint.x * scale, originalPoint.y * scale)
+ }
+
+ companion object {
+ private const val TAG = "UdfpsEnrollHelperV2"
+ private const val SCALE = 0.5f
+ }
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollIconV2.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollIconV2.kt
new file mode 100644
index 0000000..3b48140
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollIconV2.kt
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2024 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.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.widget
+
+import android.animation.AnimatorSet
+import android.animation.ValueAnimator
+import android.annotation.ColorInt
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.ColorFilter
+import android.graphics.Paint
+import android.graphics.PixelFormat
+import android.graphics.Rect
+import android.graphics.RectF
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.ShapeDrawable
+import android.graphics.drawable.shapes.PathShape
+import android.util.AttributeSet
+import android.util.Log
+import android.util.PathParser
+import android.view.animation.AccelerateDecelerateInterpolator
+import androidx.core.animation.addListener
+import androidx.core.graphics.toRect
+import androidx.core.graphics.toRectF
+import com.android.settings.R
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.StageViewModel
+import kotlin.math.sin
+
+/**
+ * This class is responsible for drawing the udfps icon, and to update its movement based on the
+ * various stages of enrollment
+ */
+class UdfpsEnrollIconV2 internal constructor(context: Context, attrs: AttributeSet?) : Drawable() {
+ private var targetAnimatorSet: AnimatorSet? = null
+ private val movingTargetFpIcon: Drawable
+ private val fingerprintDrawable: ShapeDrawable
+ private val sensorOutlinePaint: Paint
+ private val blueFill: Paint
+ private val helper = UdfpsEnrollHelperV2(context)
+ @ColorInt private var enrollIconColor = 0
+ @ColorInt private var movingTargetFill = 0
+ private var currentScale = 1.0f
+ private var alpha = 0
+
+ /**
+ * This is the physical location of the sensor. This rect will be updated by [drawSensorRectAt]
+ */
+ private val sensorRectBounds: Rect = Rect()
+
+ /**
+ * The following values are used to describe where the icon should be drawn. [currX] and [currY]
+ * are changed based on the current guided enrollment step which is given by the
+ * [UdfpsEnrollHelperV2]
+ */
+ private var currX = 0f
+ private var currY = 0f
+
+ private var sensorWidth = 0f
+ private var sensorHeight = 0f
+
+ init {
+ fingerprintDrawable = createUdfpsIcon(context)
+ context
+ .obtainStyledAttributes(
+ attrs,
+ R.styleable.BiometricsEnrollView,
+ R.attr.biometricsEnrollStyle,
+ R.style.BiometricsEnrollStyle,
+ )
+ .let {
+ enrollIconColor = it.getColor(R.styleable.BiometricsEnrollView_biometricsEnrollIcon, 0)
+ movingTargetFill =
+ it.getColor(R.styleable.BiometricsEnrollView_biometricsMovingTargetFill, 0)
+ it.recycle()
+ }
+
+ sensorOutlinePaint = Paint(0 /* flags */).apply {
+ isAntiAlias = true
+ setColor(movingTargetFill)
+ style = Paint.Style.FILL
+ }
+
+ blueFill = Paint(0 /* flags */).apply {
+ isAntiAlias = true
+ setColor(movingTargetFill)
+ style = Paint.Style.FILL
+ }
+
+ movingTargetFpIcon = context.resources.getDrawable(R.drawable.ic_enrollment_fingerprint, null).apply {
+ setTint(enrollIconColor)
+ mutate()
+ }
+
+ fingerprintDrawable.setTint(enrollIconColor)
+ setAlpha(255)
+ }
+
+ override fun getAlpha(): Int {
+ return alpha
+ }
+
+ override fun setColorFilter(colorFilter: ColorFilter?) {}
+
+ override fun getOpacity(): Int {
+ return PixelFormat.UNKNOWN
+ }
+
+ override fun setAlpha(alpha: Int) {
+ this.alpha = alpha
+ }
+
+ /**
+ * The [sensorRect] coordinates for the sensor area. The [sensorRect] should be the coordinates
+ * with respect to its root frameview
+ */
+ fun drawSensorRectAt(sensorRect: Rect) {
+ Log.e(TAG, "UdfpsEnrollIcon#drawSensorRect($sensorRect)")
+ sensorRectBounds.set(sensorRect)
+ fingerprintDrawable.bounds = sensorRect
+ movingTargetFpIcon.bounds = sensorRect
+ currX = sensorRect.left.toFloat()
+ currY = sensorRect.top.toFloat()
+ sensorWidth = (sensorRect.right - sensorRect.left).toFloat()
+ sensorHeight = (sensorRect.bottom - sensorRect.top).toFloat()
+ invalidateSelf()
+ }
+
+ /** Update the progress of the icon */
+ fun onEnrollmentProgress(remaining: Int, totalSteps: Int) {
+ helper.onEnrollmentProgress(remaining, totalSteps)
+ val offset = helper.guidedEnrollmentLocation
+ val currentBounds = getCurrLocation().toRect()
+ if (offset != null) {
+ // This is the desired location of the sensor rect, the [EnrollHelper]
+ // offsets the initial sensor rect by a bit to get the user to move their finger a bit more.
+ val targetRect = Rect(sensorRectBounds).toRectF()
+ targetRect.offset(offset.x, offset.y)
+ var shouldAnimateMovement =
+ !currentBounds.equals(targetRect) && offset.x != 0f && offset.y != 0f
+ if (shouldAnimateMovement) {
+ targetAnimatorSet?.let { it.cancel() }
+ animateMovement(currentBounds, targetRect, true)
+ }
+ } else {
+ // If we are not offsetting the sensor, move it back to its original place
+ animateMovement(currentBounds, sensorRectBounds.toRectF(), false)
+ }
+
+ invalidateSelf()
+ }
+
+ /** Update the stage of the icon */
+ fun updateStage(it: StageViewModel) {
+ helper.onUpdateStage(it)
+ invalidateSelf()
+ }
+
+ override fun draw(canvas: Canvas) {
+ val currLocation = getCurrLocation()
+ canvas.scale(currentScale, currentScale, currLocation.centerX(), currLocation.centerY())
+
+ sensorRectBounds?.let { canvas.drawOval(currLocation, sensorOutlinePaint) }
+ fingerprintDrawable.bounds = currLocation.toRect()
+ fingerprintDrawable.draw(canvas)
+ fingerprintDrawable.setAlpha(alpha)
+ sensorOutlinePaint.setAlpha(alpha)
+ }
+
+ private fun getCurrLocation(): RectF =
+ RectF(currX, currY, currX + sensorWidth, currY + sensorHeight)
+
+ private fun animateMovement(currentBounds: Rect, offsetRect: RectF, scaleMovement: Boolean) {
+ if (currentBounds.equals(offsetRect)) {
+ return
+ }
+ val xAnimator = ValueAnimator.ofFloat(currentBounds.left.toFloat(), offsetRect.left)
+ xAnimator.addUpdateListener {
+ currX = it.animatedValue as Float
+ invalidateSelf()
+ }
+
+ val yAnimator = ValueAnimator.ofFloat(currentBounds.top.toFloat(), offsetRect.top)
+ yAnimator.addUpdateListener {
+ currY = it.animatedValue as Float
+ invalidateSelf()
+ }
+ val animators = mutableListOf(xAnimator, yAnimator)
+ val duration = TARGET_ANIM_DURATION_LONG
+ if (scaleMovement) {
+ val scaleAnimator = ValueAnimator.ofFloat(0f, Math.PI.toFloat())
+ scaleAnimator.setDuration(duration)
+ scaleAnimator.addUpdateListener { animation: ValueAnimator ->
+ // Grow then shrink
+ currentScale =
+ (1 + SCALE_MAX * sin((animation.getAnimatedValue() as Float).toDouble()).toFloat())
+ invalidateSelf()
+ }
+ scaleAnimator.addListener(onEnd = { currentScale = 1f })
+ animators.add(scaleAnimator)
+ }
+
+ targetAnimatorSet = AnimatorSet()
+
+ targetAnimatorSet?.let {
+ it.interpolator = AccelerateDecelerateInterpolator()
+ it.setDuration(duration)
+ it.playTogether(animators.toList())
+ it.start()
+ }
+ }
+
+ companion object {
+ private const val TAG = "UdfpsEnrollDrawableV2"
+ private const val DEFAULT_STROKE_WIDTH = 3f
+ private const val TARGET_ANIM_DURATION_LONG = 800L
+ private const val SCALE_MAX = 0.25f
+
+ private fun createUdfpsIcon(context: Context): ShapeDrawable {
+ val fpPath = context.resources.getString(R.string.config_udfpsIcon)
+ val drawable = ShapeDrawable(PathShape(PathParser.createPathFromPathData(fpPath), 72f, 72f)).apply {
+ mutate()
+ paint.style = Paint.Style.STROKE
+ paint.strokeCap = Paint.Cap.ROUND
+ paint.strokeWidth = DEFAULT_STROKE_WIDTH
+ }
+ return drawable
+ }
+ }
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollProgressBarDrawableV2.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollProgressBarDrawableV2.kt
new file mode 100644
index 0000000..e5f89cb
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollProgressBarDrawableV2.kt
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2024 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.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.widget
+
+import android.animation.ValueAnimator
+import android.animation.ValueAnimator.AnimatorUpdateListener
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.ColorFilter
+import android.graphics.Paint
+import android.graphics.PixelFormat
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
+import android.os.Process
+import android.os.VibrationAttributes
+import android.os.VibrationEffect
+import android.os.Vibrator
+import android.util.AttributeSet
+import android.util.DisplayMetrics
+import android.view.animation.DecelerateInterpolator
+import android.view.animation.Interpolator
+import androidx.annotation.ColorInt
+import androidx.core.graphics.toRectF
+import com.android.internal.annotations.VisibleForTesting
+import com.android.settings.R
+import kotlin.math.max
+import kotlin.math.min
+
+/**
+ * UDFPS enrollment progress bar. This view is responsible for drawing the progress ring and its
+ * fill around the center of the UDFPS sensor.
+ */
+class UdfpsEnrollProgressBarDrawableV2(private val mContext: Context, attrs: AttributeSet?) :
+ Drawable() {
+ private val sensorRect: Rect = Rect()
+ private val strokeWidthPx: Float
+
+ @ColorInt private val progressColor: Int
+ @ColorInt private var helpColor: Int = 0
+ @ColorInt private val onFirstBucketFailedColor: Int
+
+ private val backgroundPaint: Paint
+
+ @VisibleForTesting val fillPaint: Paint
+ private val vibrator: Vibrator
+ private var isAccessibilityEnabled: Boolean = false
+ private var afterFirstTouch = false
+ private var remainingSteps = 0
+ private var totalSteps = 0
+ private var progress = 0f
+ private var progressAnimator: ValueAnimator? = null
+ private val progressUpdateListener: AnimatorUpdateListener
+ private var showingHelp = false
+ private var fillColorAnimator: ValueAnimator? = null
+ private val fillColorUpdateListener: AnimatorUpdateListener
+ private var backgroundColorAnimator: ValueAnimator? = null
+ private val backgroundColorUpdateListener: AnimatorUpdateListener
+ private var complete = false
+ private var movingTargetFill = 0
+ private var movingTargetFillError = 0
+ private var enrollProgressColor = 0
+ private var enrollProgressHelp = 0
+ private var enrollProgressHelpWithTalkback = 0
+ private val progressBarRadius: Int
+
+ init {
+ val ta =
+ mContext.obtainStyledAttributes(
+ attrs,
+ R.styleable.BiometricsEnrollView,
+ R.attr.biometricsEnrollStyle,
+ R.style.BiometricsEnrollStyle,
+ )
+ movingTargetFill = ta.getColor(R.styleable.BiometricsEnrollView_biometricsMovingTargetFill, 0)
+ movingTargetFillError =
+ ta.getColor(R.styleable.BiometricsEnrollView_biometricsMovingTargetFillError, 0)
+ enrollProgressColor = ta.getColor(R.styleable.BiometricsEnrollView_biometricsEnrollProgress, 0)
+ enrollProgressHelp =
+ ta.getColor(R.styleable.BiometricsEnrollView_biometricsEnrollProgressHelp, 0)
+ enrollProgressHelpWithTalkback =
+ ta.getColor(R.styleable.BiometricsEnrollView_biometricsEnrollProgressHelpWithTalkback, 0)
+ ta.recycle()
+ val density = mContext.resources.displayMetrics.densityDpi.toFloat()
+ strokeWidthPx = STROKE_WIDTH_DP * (density / DisplayMetrics.DENSITY_DEFAULT)
+ progressColor = enrollProgressColor
+ onFirstBucketFailedColor = movingTargetFillError
+ updateHelpColor()
+ backgroundPaint = Paint().apply {
+ strokeWidth = strokeWidthPx
+ setColor(movingTargetFill)
+ isAntiAlias = true
+ style = Paint.Style.STROKE
+ strokeCap = Paint.Cap.ROUND
+ }
+
+ // Progress fill should *not* use the extracted system color.
+ fillPaint = Paint().apply {
+ strokeWidth = strokeWidthPx
+ setColor(progressColor)
+ isAntiAlias = true
+ style = Paint.Style.STROKE
+ strokeCap = Paint.Cap.ROUND
+ }
+ vibrator = mContext.getSystemService(Vibrator::class.java)!!
+
+ progressBarRadius = mContext.resources.getInteger(R.integer.config_udfpsEnrollProgressBar)
+
+ progressUpdateListener = AnimatorUpdateListener { animation: ValueAnimator ->
+ progress = animation.getAnimatedValue() as Float
+ invalidateSelf()
+ }
+ fillColorUpdateListener = AnimatorUpdateListener { animation: ValueAnimator ->
+ fillPaint.setColor(animation.getAnimatedValue() as Int)
+ invalidateSelf()
+ }
+ backgroundColorUpdateListener = AnimatorUpdateListener { animation: ValueAnimator ->
+ backgroundPaint.setColor(animation.getAnimatedValue() as Int)
+ invalidateSelf()
+ }
+ }
+
+ /** Indicates enrollment progress has occurred. */
+ fun onEnrollmentProgress(remaining: Int, totalSteps: Int) {
+ afterFirstTouch = true
+ updateState(remaining, totalSteps, false /* showingHelp */)
+ }
+
+ /** Indicates enrollment help has occurred. */
+ fun onEnrollmentHelp(remaining: Int, totalSteps: Int) {
+ updateState(remaining, totalSteps, true /* showingHelp */)
+ }
+
+ /** Indicates the last step was acquired. */
+ fun onLastStepAcquired() {
+ updateState(0, totalSteps, false /* showingHelp */)
+ }
+
+ override fun draw(canvas: Canvas) {
+
+ canvas.save()
+ // This takes the sensors bounding box and expands it by [progressBarRadius] in all directions
+ val sensorProgressRect = Rect(sensorRect)
+ sensorProgressRect.inset(
+ -progressBarRadius,
+ -progressBarRadius,
+ -progressBarRadius,
+ -progressBarRadius,
+ )
+
+ // Rotate -90 degrees to make the progress start from the top right and not the bottom
+ // right
+ canvas.rotate(
+ -90f,
+ sensorProgressRect.centerX().toFloat(),
+ sensorProgressRect.centerY().toFloat(),
+ )
+ if (progress < 1f) {
+ // Draw the background color of the progress circle.
+ canvas.drawArc(
+ sensorProgressRect.toRectF(),
+ 0f /* startAngle */,
+ 360f /* sweepAngle */,
+ false /* useCenter */,
+ backgroundPaint,
+ )
+ }
+ if (progress > 0f) {
+ // Draw the filled portion of the progress circle.
+ canvas.drawArc(
+ sensorProgressRect.toRectF(),
+ 0f /* startAngle */,
+ 360f * progress /* sweepAngle */,
+ false /* useCenter */,
+ fillPaint,
+ )
+ }
+ canvas.restore()
+ }
+
+ /** Do nothing here, we will control the alpha internally. */
+ override fun setAlpha(alpha: Int) {}
+
+ /** Do not apply color filters here for enrollment. */
+ override fun setColorFilter(colorFilter: ColorFilter?) {}
+
+ /** Legacy code, returning PixelFormat.UNKNOWN */
+ override fun getOpacity(): Int {
+ return PixelFormat.UNKNOWN
+ }
+ /**
+ * Draws the progress with locations [sensorLocationX] [sensorLocationY], note these must be with
+ * respect to the parent framelayout.
+ */
+ fun drawProgressAt(sensorRect: Rect) {
+ this.sensorRect.set(sensorRect)
+ }
+
+ /** Indicates if accessibility is enabled or not. */
+ fun setAccessibilityEnabled(enabled: Boolean) {
+ isAccessibilityEnabled = enabled
+ updateHelpColor()
+ }
+
+ private fun updateHelpColor() {
+ helpColor =
+ if (!isAccessibilityEnabled) {
+ enrollProgressHelp
+ } else {
+ enrollProgressHelpWithTalkback
+ }
+ }
+
+ private fun updateState(remainingSteps: Int, totalSteps: Int, showingHelp: Boolean) {
+ updateProgress(remainingSteps, totalSteps, showingHelp)
+ updateFillColor(showingHelp)
+ }
+
+ private fun updateProgress(remainingSteps: Int, totalSteps: Int, showingHelp: Boolean) {
+ if (this.remainingSteps == remainingSteps && this.totalSteps == totalSteps) {
+ return
+ }
+ if (this.showingHelp) {
+ if (vibrator != null && isAccessibilityEnabled) {
+ vibrator.vibrate(
+ Process.myUid(),
+ mContext.opPackageName,
+ VIBRATE_EFFECT_ERROR,
+ javaClass.getSimpleName() + "::onEnrollmentHelp",
+ FINGERPRINT_ENROLLING_SONFICATION_ATTRIBUTES,
+ )
+ }
+ } else {
+ // If the first touch is an error, remainingSteps will be -1 and the callback
+ // doesn't come from onEnrollmentHelp. If we are in the accessibility flow,
+ // we still would like to vibrate.
+ if (vibrator != null) {
+ if (remainingSteps == -1 && isAccessibilityEnabled) {
+ vibrator.vibrate(
+ Process.myUid(),
+ mContext.opPackageName,
+ VIBRATE_EFFECT_ERROR,
+ javaClass.getSimpleName() + "::onFirstTouchError",
+ FINGERPRINT_ENROLLING_SONFICATION_ATTRIBUTES,
+ )
+ } else if (remainingSteps != -1 && !isAccessibilityEnabled) {
+ vibrator.vibrate(
+ Process.myUid(),
+ mContext.opPackageName,
+ SUCCESS_VIBRATION_EFFECT,
+ javaClass.getSimpleName() + "::OnEnrollmentProgress",
+ HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES,
+ )
+ }
+ }
+ }
+ this.showingHelp = showingHelp
+ this.remainingSteps = remainingSteps
+ this.totalSteps = totalSteps
+ val progressSteps = max(0.0, (totalSteps - remainingSteps).toDouble()).toInt()
+
+ // If needed, add 1 to progress and total steps to account for initial touch.
+ val adjustedSteps = if (afterFirstTouch) progressSteps + 1 else progressSteps
+ val adjustedTotal = if (afterFirstTouch) this.totalSteps + 1 else this.totalSteps
+ val targetProgress =
+ min(1.0, (adjustedSteps.toFloat() / adjustedTotal.toFloat()).toDouble()).toFloat()
+ if (progressAnimator != null && progressAnimator!!.isRunning) {
+ progressAnimator!!.cancel()
+ }
+ progressAnimator =
+ ValueAnimator.ofFloat(progress, targetProgress).also {
+ it.setDuration(PROGRESS_ANIMATION_DURATION_MS)
+ it.addUpdateListener(progressUpdateListener)
+ it.start()
+ }
+ if (remainingSteps == 0) {
+ startCompletionAnimation()
+ } else if (remainingSteps > 0) {
+ rollBackCompletionAnimation()
+ }
+ }
+
+ private fun animateBackgroundColor() {
+ if (backgroundColorAnimator != null && backgroundColorAnimator!!.isRunning) {
+ backgroundColorAnimator!!.end()
+ }
+ backgroundColorAnimator =
+ ValueAnimator.ofArgb(backgroundPaint.color, onFirstBucketFailedColor).also {
+ it.setDuration(FILL_COLOR_ANIMATION_DURATION_MS)
+ it.repeatCount = 1
+ it.repeatMode = ValueAnimator.REVERSE
+ it.interpolator = DEACCEL
+ it.addUpdateListener(backgroundColorUpdateListener)
+ it.start()
+ }
+ }
+
+ private fun updateFillColor(showingHelp: Boolean) {
+ if (!afterFirstTouch && showingHelp) {
+ // If we are on the first touch, animate the background color
+ // instead of the progress color.
+ animateBackgroundColor()
+ return
+ }
+ if (fillColorAnimator != null && fillColorAnimator!!.isRunning) {
+ fillColorAnimator!!.end()
+ }
+ @ColorInt val targetColor = if (showingHelp) helpColor else progressColor
+ fillColorAnimator =
+ ValueAnimator.ofArgb(fillPaint.color, targetColor).also {
+ it.setDuration(FILL_COLOR_ANIMATION_DURATION_MS)
+ it.repeatCount = 1
+ it.repeatMode = ValueAnimator.REVERSE
+ it.interpolator = DEACCEL
+ it.addUpdateListener(fillColorUpdateListener)
+ it.start()
+ }
+ }
+
+ private fun startCompletionAnimation() {
+ if (complete) {
+ return
+ }
+ complete = true
+ }
+
+ private fun rollBackCompletionAnimation() {
+ if (!complete) {
+ return
+ }
+ complete = false
+ }
+
+ private fun loadResources(context: Context, attrs: AttributeSet?) {}
+
+ companion object {
+ private const val TAG = "UdfpsProgressBar"
+ private const val FILL_COLOR_ANIMATION_DURATION_MS = 350L
+ private const val PROGRESS_ANIMATION_DURATION_MS = 400L
+ private const val STROKE_WIDTH_DP = 12f
+ private val DEACCEL: Interpolator = DecelerateInterpolator()
+ private val VIBRATE_EFFECT_ERROR = VibrationEffect.createWaveform(longArrayOf(0, 5, 55, 60), -1)
+ private val FINGERPRINT_ENROLLING_SONFICATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_ACCESSIBILITY)
+ private val HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK)
+ private val SUCCESS_VIBRATION_EFFECT = VibrationEffect.get(VibrationEffect.EFFECT_CLICK)
+ }
+}
diff --git a/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollViewV2.kt b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollViewV2.kt
new file mode 100644
index 0000000..38bb024
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/ui/enrollment/modules/enrolling/udfps/ui/widget/UdfpsEnrollViewV2.kt
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2024 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.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.widget
+
+import android.content.Context
+import android.graphics.Rect
+import android.util.AttributeSet
+import android.util.Log
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.ImageView
+import com.android.settings.R
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.Acquired
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.OverlayShown
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.PointerDown
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.PointerUp
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.StageViewModel
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsEnrollEvent
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsError
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsHelp
+import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.udfps.ui.viewmodel.UdfpsProgress
+
+/**
+ * View corresponding with fingerprint_v2_udfps_enroll_view.xml. This view is responsible for
+ * drawing the [UdfpsEnrollIconV2] and the [UdfpsEnrollProgressBarDrawableV2].
+ */
+class UdfpsEnrollViewV2(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs) {
+ private var isAccessibilityEnabled: Boolean = false
+ private lateinit var sensorRect: Rect
+ private val fingerprintIcon: UdfpsEnrollIconV2 = UdfpsEnrollIconV2(mContext, attrs)
+ private val fingerprintProgressDrawable: UdfpsEnrollProgressBarDrawableV2 =
+ UdfpsEnrollProgressBarDrawableV2(mContext, attrs)
+ private var mTotalSteps = -1
+ private var mRemainingSteps = -1
+
+ /**
+ * This function computes the center (x,y) location with respect to the parent [FrameLayout] for
+ * the [UdfpsEnrollProgressBarDrawableV2]. It also computes the [Rect] with respect to the parent
+ * [FrameLayout] for the [UdfpsEnrollIconV2].
+ */
+ fun setSensorRect(rect: Rect) {
+ this.sensorRect = rect
+ post {
+ findViewById<ImageView?>(R.id.udfps_enroll_animation_fp_progress_view)?.also {
+ it.setImageDrawable(fingerprintProgressDrawable)
+ }
+ findViewById<ImageView>(R.id.udfps_enroll_animation_fp_view)?.also {
+ it.setImageDrawable(fingerprintIcon)
+ }
+ val parentView = parent as ViewGroup
+ val coords = parentView.getLocationOnScreen()
+ val parentLeft = coords[0]
+ val parentTop = coords[1]
+ val sensorRectOffset = Rect(sensorRect)
+ sensorRectOffset.offset(-parentLeft, -parentTop)
+
+ fingerprintIcon.drawSensorRectAt(sensorRectOffset)
+ fingerprintProgressDrawable.drawProgressAt(sensorRectOffset)
+ }
+ }
+
+ /** Updates the current enrollment stage. */
+ fun updateStage(it: StageViewModel) {
+ fingerprintIcon.updateStage(it)
+ }
+
+ /** Receive enroll progress event */
+ fun onUdfpsEvent(event: UdfpsEnrollEvent) {
+ when (event) {
+ is UdfpsProgress -> onEnrollmentProgress(event.remainingSteps, event.totalSteps)
+ is Acquired -> onAcquired(event.acquiredGood)
+ is UdfpsHelp -> onEnrollmentHelp()
+ is PointerDown -> onPointerDown()
+ is PointerUp -> onPointerUp()
+ OverlayShown -> overlayShown()
+ is UdfpsError -> udfpsError(event.errMsgId, event.errString)
+ }
+ }
+
+ /** Indicates if accessibility is enabled. */
+ fun setAccessibilityEnabled(enabled: Boolean) {
+ this.isAccessibilityEnabled = enabled
+ fingerprintProgressDrawable.setAccessibilityEnabled(enabled)
+ }
+
+ override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
+ invalidate()
+ super.onLayout(changed, left, top, right, bottom)
+ invalidate()
+ }
+
+ private fun udfpsError(errMsgId: Int, errString: String) {
+ Log.e(TAG, "Implement udfpsError")
+ }
+
+ private fun overlayShown() {
+ Log.e(TAG, "Implement overlayShown")
+ }
+
+ /** Receive enroll progress event */
+ private fun onEnrollmentProgress(remaining: Int, totalSteps: Int) {
+ post {
+ fingerprintIcon.onEnrollmentProgress(remaining, totalSteps)
+ fingerprintProgressDrawable.onEnrollmentProgress(remaining, totalSteps)
+ }
+ }
+
+ /** Receive enroll help event */
+ private fun onEnrollmentHelp() {
+ post { fingerprintProgressDrawable.onEnrollmentHelp(mRemainingSteps, mTotalSteps) }
+ }
+
+ /** Receive onAcquired event */
+ private fun onAcquired(isAcquiredGood: Boolean) {
+ val animateIfLastStepGood = isAcquiredGood && mRemainingSteps <= 2 && mRemainingSteps >= 0
+ post { if (animateIfLastStepGood) fingerprintProgressDrawable.onLastStepAcquired() }
+ }
+
+ /** Receive onPointerDown event */
+ private fun onPointerDown() {}
+
+ /** Receive onPointerUp event */
+ private fun onPointerUp() {}
+
+ companion object {
+ private const val TAG = "UdfpsEnrollView"
+ }
+}
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsHearingAidsPresetsController.java b/src/com/android/settings/bluetooth/BluetoothDetailsHearingAidsPresetsController.java
index 43721f4..feecd90 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsHearingAidsPresetsController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsHearingAidsPresetsController.java
@@ -41,9 +41,12 @@
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.HapClientProfile;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.events.OnPause;
import com.android.settingslib.core.lifecycle.events.OnResume;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
import com.android.settingslib.utils.ThreadUtils;
import java.util.List;
@@ -53,13 +56,16 @@
*/
public class BluetoothDetailsHearingAidsPresetsController extends
BluetoothDetailsController implements Preference.OnPreferenceChangeListener,
- BluetoothHapClient.Callback, OnResume, OnPause {
+ BluetoothHapClient.Callback, LocalBluetoothProfileManager.ServiceListener,
+ OnStart, OnResume, OnPause, OnStop {
private static final boolean DEBUG = true;
private static final String TAG = "BluetoothDetailsHearingAidsPresetsController";
static final String KEY_HEARING_AIDS_PRESETS = "hearing_aids_presets";
+ private final LocalBluetoothProfileManager mProfileManager;
private final HapClientProfile mHapClientProfile;
+
@Nullable
private ListPreference mPreference;
@@ -69,26 +75,35 @@
@NonNull CachedBluetoothDevice device,
@NonNull Lifecycle lifecycle) {
super(context, fragment, device, lifecycle);
- mHapClientProfile = manager.getProfileManager().getHapClientProfile();
+ mProfileManager = manager.getProfileManager();
+ mHapClientProfile = mProfileManager.getHapClientProfile();
+ }
+
+ @Override
+ public void onStart() {
+ if (mHapClientProfile != null && !mHapClientProfile.isProfileReady()) {
+ mProfileManager.addServiceListener(this);
+ }
}
@Override
public void onResume() {
+ registerHapCallback();
super.onResume();
- if (mHapClientProfile != null) {
- mHapClientProfile.registerCallback(ThreadUtils.getBackgroundExecutor(), this);
- }
}
@Override
public void onPause() {
- if (mHapClientProfile != null) {
- mHapClientProfile.unregisterCallback(this);
- }
+ unregisterHapCallback();
super.onPause();
}
@Override
+ public void onStop() {
+ mProfileManager.removeServiceListener(this);
+ }
+
+ @Override
public boolean onPreferenceChange(@NonNull Preference preference, @Nullable Object newValue) {
if (TextUtils.equals(preference.getKey(), getPreferenceKey())) {
if (newValue instanceof final String value
@@ -203,9 +218,8 @@
public void onPresetSelectionFailed(@NonNull BluetoothDevice device, int reason) {
if (device.equals(mCachedDevice.getDevice())) {
if (DEBUG) {
- Log.d(TAG,
- "onPresetSelectionFailed, device: " + device.getAddress()
- + ", reason: " + reason);
+ Log.d(TAG, "onPresetSelectionFailed, device: " + device.getAddress()
+ + ", reason: " + reason);
}
mContext.getMainExecutor().execute(() -> {
refresh();
@@ -305,4 +319,41 @@
Toast.makeText(mContext, R.string.bluetooth_hearing_aids_presets_error,
Toast.LENGTH_SHORT).show();
}
+
+ private void registerHapCallback() {
+ if (mHapClientProfile != null) {
+ try {
+ mHapClientProfile.registerCallback(ThreadUtils.getBackgroundExecutor(), this);
+ } catch (IllegalArgumentException e) {
+ // The callback was already registered
+ Log.w(TAG, "Cannot register callback: " + e.getMessage());
+ }
+
+ }
+ }
+
+ private void unregisterHapCallback() {
+ if (mHapClientProfile != null) {
+ try {
+ mHapClientProfile.unregisterCallback(this);
+ } catch (IllegalArgumentException e) {
+ // The callback was never registered or was already unregistered
+ Log.w(TAG, "Cannot unregister callback: " + e.getMessage());
+ }
+ }
+ }
+
+ @Override
+ public void onServiceConnected() {
+ if (mHapClientProfile != null && mHapClientProfile.isProfileReady()) {
+ mProfileManager.removeServiceListener(this);
+ registerHapCallback();
+ refresh();
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected() {
+ // Do nothing
+ }
}
diff --git a/src/com/android/settings/bluetooth/BluetoothPairingDialog.java b/src/com/android/settings/bluetooth/BluetoothPairingDialog.java
index d5b4211..347de4a 100644
--- a/src/com/android/settings/bluetooth/BluetoothPairingDialog.java
+++ b/src/com/android/settings/bluetooth/BluetoothPairingDialog.java
@@ -105,6 +105,8 @@
registerReceiver(mReceiver, new IntentFilter(BluetoothDevice.ACTION_PAIRING_CANCEL));
registerReceiver(mReceiver, new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED));
mReceiverRegistered = true;
+
+ closeSystemDialogs();
}
@Override
diff --git a/src/com/android/settings/core/SettingsBaseActivity.java b/src/com/android/settings/core/SettingsBaseActivity.java
index c282498..c9530ab 100644
--- a/src/com/android/settings/core/SettingsBaseActivity.java
+++ b/src/com/android/settings/core/SettingsBaseActivity.java
@@ -46,6 +46,7 @@
import com.android.settings.core.CategoryMixin.CategoryHandler;
import com.android.settingslib.core.lifecycle.HideNonSystemOverlayMixin;
import com.android.settingslib.transition.SettingsTransitionHelper.TransitionType;
+import com.android.window.flags.Flags;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout;
@@ -92,8 +93,11 @@
Log.w(TAG, "Devices lock task mode pinned.");
finish();
}
- Utils.setupEdgeToEdge(this);
final long startTime = System.currentTimeMillis();
+ if (Flags.enforceEdgeToEdge()) {
+ Utils.setupEdgeToEdge(this);
+ hideInternalActionBar();
+ }
getLifecycle().addObserver(new HideNonSystemOverlayMixin(this));
TextAppearanceConfig.setShouldLoadFontSynchronously(true);
@@ -291,4 +295,18 @@
}
return intent.getIntExtra(EXTRA_PAGE_TRANSITION_TYPE, TransitionType.TRANSITION_NONE);
}
+
+ /**
+ * This internal ActionBar will be appeared automatically when the
+ * Utils.setupEdgeToEdge is invoked.
+ *
+ * @see Utils.setupEdgeToEdge
+ */
+ private void hideInternalActionBar() {
+ final View actionBarContainer =
+ findViewById(com.android.internal.R.id.action_bar_container);
+ if (actionBarContainer != null) {
+ actionBarContainer.setVisibility(View.GONE);
+ }
+ }
}
diff --git a/src/com/android/settings/datausage/lib/NetworkStatsRepository.kt b/src/com/android/settings/datausage/lib/NetworkStatsRepository.kt
index 56b1931..484a8a3 100644
--- a/src/com/android/settings/datausage/lib/NetworkStatsRepository.kt
+++ b/src/com/android/settings/datausage/lib/NetworkStatsRepository.kt
@@ -93,13 +93,15 @@
val buckets = mutableListOf<Bucket>()
val bucket = NetworkStats.Bucket()
while (getNextBucket(bucket)) {
- buckets += Bucket(
- uid = bucket.uid,
- bytes = bucket.bytes,
- state = bucket.state,
- startTimeStamp = bucket.startTimeStamp,
- endTimeStamp = bucket.endTimeStamp,
- )
+ if (bucket.bytes > 0) {
+ buckets += Bucket(
+ uid = bucket.uid,
+ bytes = bucket.bytes,
+ state = bucket.state,
+ startTimeStamp = bucket.startTimeStamp,
+ endTimeStamp = bucket.endTimeStamp,
+ )
+ }
}
buckets
}
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index d3bf8d8..bca2377 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -740,7 +740,6 @@
controllers.add(new DefaultLaunchPreferenceController(context, "density"));
controllers.add(new DefaultLaunchPreferenceController(context, "background_check"));
controllers.add(new DefaultLaunchPreferenceController(context, "inactive_apps"));
- controllers.add(new TarePreferenceController(context));
controllers.add(new AutofillCategoryController(context, lifecycle));
controllers.add(new AutofillLoggingLevelPreferenceController(context, lifecycle));
controllers.add(new AutofillResetOptionsPreferenceController(context));
diff --git a/src/com/android/settings/development/TarePreferenceController.java b/src/com/android/settings/development/TarePreferenceController.java
deleted file mode 100644
index 8c70246..0000000
--- a/src/com/android/settings/development/TarePreferenceController.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.development;
-
-import android.app.tare.EconomyManager;
-import android.content.Context;
-import android.content.Intent;
-import android.os.UserManager;
-import android.provider.Settings;
-
-import androidx.preference.Preference;
-
-import com.android.settings.core.PreferenceControllerMixin;
-import com.android.settings.development.tare.TareHomePage;
-import com.android.settingslib.development.DeveloperOptionsPreferenceController;
-
-/** PreferenceController that serves as an entry point to the TARE configuration screen. */
-public class TarePreferenceController extends
- DeveloperOptionsPreferenceController implements PreferenceControllerMixin {
-
- private static final String KEY_TARE = "tare";
-
- private final EconomyManager mEconomyManager;
- private final UserManager mUserManager;
-
- public TarePreferenceController(Context context) {
- super(context);
- mEconomyManager = context.getSystemService(EconomyManager.class);
- mUserManager = context.getSystemService(UserManager.class);
- }
-
- @Override
- public boolean isAvailable() {
- // Enable the UI if the dedicated flag enables it or if TARE itself is on.
- final boolean settingEnabled = Settings.Global.getInt(
- mContext.getContentResolver(), Settings.Global.SHOW_TARE_DEVELOPER_OPTIONS, 0) == 1;
- final boolean isTareUiEnabled = settingEnabled
- || mEconomyManager.getEnabledMode() != EconomyManager.ENABLED_MODE_OFF;
- return isTareUiEnabled
- && !mUserManager.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES);
- }
-
- @Override
- public String getPreferenceKey() {
- return KEY_TARE;
- }
-
- @Override
- public boolean handlePreferenceTreeClick(Preference preference) {
- if (!KEY_TARE.equals(preference.getKey())) {
- return false;
- }
- mContext.startActivity(new Intent(mContext, TareHomePage.class));
- return false;
- }
-}
diff --git a/src/com/android/settings/development/tare/AlarmManagerFragment.java b/src/com/android/settings/development/tare/AlarmManagerFragment.java
deleted file mode 100644
index b55c7cf..0000000
--- a/src/com/android/settings/development/tare/AlarmManagerFragment.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * 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.development.tare;
-
-import android.app.Fragment;
-import android.app.tare.EconomyManager;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ExpandableListView;
-import android.widget.ExpandableListView.OnChildClickListener;
-
-import androidx.annotation.Nullable;
-
-import com.android.settings.R;
-
-/**
- * Creates the AlarmManager fragment to display all the AlarmManager factors
- * when the AlarmManager policy is chosen in the dropdown TARE menu.
- */
-public class AlarmManagerFragment extends Fragment implements
- TareFactorController.DataChangeListener {
-
- private TareFactorController mFactorController;
-
- private TareFactorExpandableListAdapter mExpandableListAdapter;
-
- private String[] mGroups;
- private String[][] mChildren;
- private String[][] mKeys;
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mFactorController = TareFactorController.getInstance(getContext());
- populateArrays();
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- View v = inflater.inflate(R.layout.tare_policy_fragment, null);
- ExpandableListView elv = (ExpandableListView) v.findViewById(R.id.factor_list);
- mExpandableListAdapter = new TareFactorExpandableListAdapter(
- mFactorController, LayoutInflater.from(getActivity()), mGroups, mChildren, mKeys);
- elv.setGroupIndicator(null);
- elv.setAdapter(mExpandableListAdapter);
- elv.setOnChildClickListener(new OnChildClickListener() {
- public boolean onChildClick(ExpandableListView parent, View v,
- int groupPosition, int childPosition, long id) {
- final String key = mExpandableListAdapter.getKey(groupPosition, childPosition);
- mFactorController.createDialog(key).show(getFragmentManager(), key);
- return true;
- }
- });
- return v;
- }
-
- @Override
- public void onStart() {
- super.onStart();
- mFactorController.registerListener(this);
- }
-
- @Override
- public void onStop() {
- mFactorController.unregisterListener(this);
- super.onStop();
- }
-
- @Override
- public void onDataChanged() {
- mExpandableListAdapter.notifyDataSetChanged();
- }
-
- private void populateArrays() {
- final Resources resources = getResources();
-
- mGroups = new String[]{
- resources.getString(R.string.tare_consumption_limits),
- resources.getString(R.string.tare_balances),
- // resources.getString(R.string.tare_modifiers),
- resources.getString(R.string.tare_actions_ctp),
- resources.getString(R.string.tare_actions_base_price),
- resources.getString(R.string.tare_rewards_instantaneous),
- resources.getString(R.string.tare_rewards_ongoing),
- resources.getString(R.string.tare_rewards_max)
- };
-
- mChildren = new String[][]{
- resources.getStringArray(R.array.tare_consumption_limit_subfactors),
- resources.getStringArray(R.array.tare_app_balance_subfactors),
- // TODO: support
- // resources.getStringArray(R.array.tare_modifiers_subfactors),
- resources.getStringArray(R.array.tare_alarm_manager_actions),
- resources.getStringArray(R.array.tare_alarm_manager_actions),
- resources.getStringArray(R.array.tare_rewards_subfactors),
- {resources.getString(R.string.tare_top_activity)},
- resources.getStringArray(R.array.tare_rewards_subfactors)
- };
-
- mKeys = new String[][]{
- {
- EconomyManager.KEY_AM_INITIAL_CONSUMPTION_LIMIT,
- EconomyManager.KEY_AM_MIN_CONSUMPTION_LIMIT,
- EconomyManager.KEY_AM_MAX_CONSUMPTION_LIMIT,
- },
- {
- EconomyManager.KEY_AM_MAX_SATIATED_BALANCE,
- EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED,
- EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP,
- EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP
- },
- // {},
- {
- EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP,
- EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP,
- EconomyManager.KEY_AM_ACTION_ALARM_EXACT_WAKEUP_CTP,
- EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP,
- EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP,
- EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP,
- EconomyManager.KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP,
- EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP,
- EconomyManager.KEY_AM_ACTION_ALARM_ALARMCLOCK_CTP
- },
- {
- EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE,
- EconomyManager
- .KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE,
- EconomyManager.KEY_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE,
- EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE,
- EconomyManager
- .KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_BASE_PRICE,
- EconomyManager
- .KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE,
- EconomyManager.KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE,
- EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE,
- EconomyManager.KEY_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE
- },
- {
- EconomyManager.KEY_AM_REWARD_TOP_ACTIVITY_INSTANT,
- EconomyManager.KEY_AM_REWARD_NOTIFICATION_SEEN_INSTANT,
- EconomyManager.KEY_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT,
- EconomyManager.KEY_AM_REWARD_WIDGET_INTERACTION_INSTANT,
- EconomyManager.KEY_AM_REWARD_OTHER_USER_INTERACTION_INSTANT,
- },
- {EconomyManager.KEY_AM_REWARD_TOP_ACTIVITY_ONGOING},
- {
- EconomyManager.KEY_AM_REWARD_TOP_ACTIVITY_MAX,
- EconomyManager.KEY_AM_REWARD_NOTIFICATION_SEEN_MAX,
- EconomyManager.KEY_AM_REWARD_NOTIFICATION_INTERACTION_MAX,
- EconomyManager.KEY_AM_REWARD_WIDGET_INTERACTION_MAX,
- EconomyManager.KEY_AM_REWARD_OTHER_USER_INTERACTION_MAX,
- }
- };
- }
-}
diff --git a/src/com/android/settings/development/tare/DropdownActivity.java b/src/com/android/settings/development/tare/DropdownActivity.java
deleted file mode 100644
index 66b57dd..0000000
--- a/src/com/android/settings/development/tare/DropdownActivity.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * 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.development.tare;
-
-import android.app.Activity;
-import android.app.Fragment;
-import android.app.FragmentTransaction;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.Spinner;
-
-import com.android.settings.R;
-import com.android.settingslib.widget.SettingsSpinnerAdapter;
-
-/**
- * Dropdown activity to allow for the user to easily switch between the different TARE
- * policies in the developer options of settings. Depending on what is chosen, the fragment
- * containing that specific policies' factors will be generated.
- */
-public class DropdownActivity extends Activity {
-
- private Fragment mAlarmManagerFragment;
- private Fragment mJobSchedulerFragment;
- private Spinner mSpinner;
- static final String EXTRA_POLICY = "policy";
- static final int POLICY_ALARM_MANAGER = 0;
- static final int POLICY_JOB_SCHEDULER = 1;
- private static final int DEFAULT_POLICY = POLICY_ALARM_MANAGER;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.tare_dropdown_page);
-
- // Determines what policy fragment to open up to
- Intent intent = getIntent();
- int policy = intent.getIntExtra(EXTRA_POLICY, DEFAULT_POLICY);
-
- mSpinner = findViewById(R.id.spinner);
- mAlarmManagerFragment = new AlarmManagerFragment();
- mJobSchedulerFragment = new JobSchedulerFragment();
-
- String[] policies = getResources().getStringArray(R.array.tare_policies);
-
- ArrayAdapter<String> arrayAdapter = new SettingsSpinnerAdapter<String>(this);
- arrayAdapter.addAll(policies);
- mSpinner.setAdapter(arrayAdapter);
-
- mSpinner.setSelection(policy);
- selectFragment(policy);
-
- mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView<?> adapterView, View view, int position,
- long id) {
- selectFragment(position);
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> adapterView) {
- }
- });
- }
-
- /** Selects the correct policy fragment to display based on user selection */
- private void selectFragment(int policy) {
- switch (policy) {
- case POLICY_ALARM_MANAGER:
- openFragment(mAlarmManagerFragment);
- break;
- case POLICY_JOB_SCHEDULER:
- openFragment(mJobSchedulerFragment);
- break;
- default:
- openFragment(mAlarmManagerFragment);
- }
- }
-
- /** Opens the correct policy fragment */
- private void openFragment(Fragment fragment) {
- FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
- fragmentTransaction.replace(R.id.frame_layout, fragment);
- fragmentTransaction.commit();
- }
-}
diff --git a/src/com/android/settings/development/tare/JobSchedulerFragment.java b/src/com/android/settings/development/tare/JobSchedulerFragment.java
deleted file mode 100644
index 7f5e663..0000000
--- a/src/com/android/settings/development/tare/JobSchedulerFragment.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * 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.development.tare;
-
-import android.app.Fragment;
-import android.app.tare.EconomyManager;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ExpandableListView;
-import android.widget.ExpandableListView.OnChildClickListener;
-
-import androidx.annotation.Nullable;
-
-import com.android.settings.R;
-
-/**
- * Creates the JobScheduler fragment to display all the JobScheduler factors
- * when the JobScheduler policy is chosen in the dropdown TARE menu.
- */
-public class JobSchedulerFragment extends Fragment implements
- TareFactorController.DataChangeListener {
-
- private TareFactorController mFactorController;
-
- private TareFactorExpandableListAdapter mExpandableListAdapter;
-
- private String[] mGroups;
- private String[][] mChildren;
- private String[][] mKeys;
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mFactorController = TareFactorController.getInstance(getContext());
- populateArrays();
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- View v = inflater.inflate(R.layout.tare_policy_fragment, null);
- ExpandableListView elv = (ExpandableListView) v.findViewById(R.id.factor_list);
- mExpandableListAdapter = new TareFactorExpandableListAdapter(
- mFactorController, LayoutInflater.from(getActivity()), mGroups, mChildren, mKeys);
- elv.setGroupIndicator(null);
- elv.setAdapter(mExpandableListAdapter);
- elv.setOnChildClickListener(new OnChildClickListener() {
- public boolean onChildClick(ExpandableListView parent, View v,
- int groupPosition, int childPosition, long id) {
- final String key = mExpandableListAdapter.getKey(groupPosition, childPosition);
- mFactorController.createDialog(key).show(getFragmentManager(), key);
- return true;
- }
- });
-
- return v;
- }
-
- @Override
- public void onStart() {
- super.onStart();
- mFactorController.registerListener(this);
- }
-
- @Override
- public void onStop() {
- mFactorController.unregisterListener(this);
- super.onStop();
- }
-
- @Override
- public void onDataChanged() {
- mExpandableListAdapter.notifyDataSetChanged();
- }
-
- private void populateArrays() {
- final Resources resources = getResources();
-
- mGroups = new String[]{
- resources.getString(R.string.tare_consumption_limits),
- resources.getString(R.string.tare_balances),
- // mResources.getString(R.string.tare_modifiers),
- resources.getString(R.string.tare_actions_ctp),
- resources.getString(R.string.tare_actions_base_price),
- resources.getString(R.string.tare_rewards_instantaneous),
- resources.getString(R.string.tare_rewards_ongoing),
- resources.getString(R.string.tare_rewards_max)
- };
-
- mChildren = new String[][]{
- resources.getStringArray(R.array.tare_consumption_limit_subfactors),
- resources.getStringArray(R.array.tare_job_scheduler_app_balance_subfactors),
- // TODO: support
- // mResources.getStringArray(R.array.tare_modifiers_subfactors),
- resources.getStringArray(R.array.tare_job_scheduler_actions),
- resources.getStringArray(R.array.tare_job_scheduler_actions),
- resources.getStringArray(R.array.tare_job_scheduler_rewards_subfactors),
- {resources.getString(R.string.tare_top_activity)},
- resources.getStringArray(R.array.tare_job_scheduler_rewards_subfactors)
- };
-
- mKeys = new String[][]{
- {
- EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT,
- EconomyManager.KEY_JS_MIN_CONSUMPTION_LIMIT,
- EconomyManager.KEY_JS_MAX_CONSUMPTION_LIMIT,
- },
- {
- EconomyManager.KEY_JS_MAX_SATIATED_BALANCE,
- EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED,
- EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP,
- EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP,
- EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER,
- },
- // {},
- {
- EconomyManager.KEY_JS_ACTION_JOB_MAX_START_CTP,
- EconomyManager.KEY_JS_ACTION_JOB_MAX_RUNNING_CTP,
- EconomyManager.KEY_JS_ACTION_JOB_HIGH_START_CTP,
- EconomyManager.KEY_JS_ACTION_JOB_HIGH_RUNNING_CTP,
- EconomyManager.KEY_JS_ACTION_JOB_DEFAULT_START_CTP,
- EconomyManager.KEY_JS_ACTION_JOB_DEFAULT_RUNNING_CTP,
- EconomyManager.KEY_JS_ACTION_JOB_LOW_START_CTP,
- EconomyManager.KEY_JS_ACTION_JOB_LOW_RUNNING_CTP,
- EconomyManager.KEY_JS_ACTION_JOB_MIN_START_CTP,
- EconomyManager.KEY_JS_ACTION_JOB_MIN_RUNNING_CTP,
- EconomyManager.KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP
- },
- {
- EconomyManager.KEY_JS_ACTION_JOB_MAX_START_BASE_PRICE,
- EconomyManager.KEY_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE,
- EconomyManager.KEY_JS_ACTION_JOB_HIGH_START_BASE_PRICE,
- EconomyManager.KEY_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE,
- EconomyManager.KEY_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE,
- EconomyManager.KEY_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE,
- EconomyManager.KEY_JS_ACTION_JOB_LOW_START_BASE_PRICE,
- EconomyManager.KEY_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE,
- EconomyManager.KEY_JS_ACTION_JOB_MIN_START_BASE_PRICE,
- EconomyManager.KEY_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE,
- EconomyManager.KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE
- },
- {
- EconomyManager.KEY_JS_REWARD_TOP_ACTIVITY_INSTANT,
- EconomyManager.KEY_JS_REWARD_NOTIFICATION_SEEN_INSTANT,
- EconomyManager.KEY_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT,
- EconomyManager.KEY_JS_REWARD_WIDGET_INTERACTION_INSTANT,
- EconomyManager.KEY_JS_REWARD_OTHER_USER_INTERACTION_INSTANT,
- EconomyManager.KEY_JS_REWARD_APP_INSTALL_INSTANT,
- },
- {EconomyManager.KEY_JS_REWARD_TOP_ACTIVITY_ONGOING},
- {
- EconomyManager.KEY_JS_REWARD_TOP_ACTIVITY_MAX,
- EconomyManager.KEY_JS_REWARD_NOTIFICATION_SEEN_MAX,
- EconomyManager.KEY_JS_REWARD_NOTIFICATION_INTERACTION_MAX,
- EconomyManager.KEY_JS_REWARD_WIDGET_INTERACTION_MAX,
- EconomyManager.KEY_JS_REWARD_OTHER_USER_INTERACTION_MAX,
- EconomyManager.KEY_JS_REWARD_APP_INSTALL_MAX,
- }
- };
- }
-}
diff --git a/src/com/android/settings/development/tare/OWNERS b/src/com/android/settings/development/tare/OWNERS
deleted file mode 100644
index 46d25c8..0000000
--- a/src/com/android/settings/development/tare/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-# Bug component: 330055
-
-include platform/frameworks/base:/apex/jobscheduler/service/java/com/android/server/tare/OWNERS
\ No newline at end of file
diff --git a/src/com/android/settings/development/tare/TareFactorController.java b/src/com/android/settings/development/tare/TareFactorController.java
deleted file mode 100644
index 3407b7b..0000000
--- a/src/com/android/settings/development/tare/TareFactorController.java
+++ /dev/null
@@ -1,715 +0,0 @@
-/*
- * 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.development.tare;
-
-import static android.app.tare.EconomyManager.CAKE_IN_ARC;
-import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE_CAKES;
-import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE_CAKES;
-import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP_CAKES;
-import static android.app.tare.EconomyManager.DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE_CAKES;
-import static android.app.tare.EconomyManager.parseCreditValue;
-import static android.provider.Settings.Global.TARE_ALARM_MANAGER_CONSTANTS;
-import static android.provider.Settings.Global.TARE_JOB_SCHEDULER_CONSTANTS;
-
-import android.app.tare.EconomyManager;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
-import android.provider.Settings;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.KeyValueListParser;
-import android.util.Slog;
-
-import androidx.annotation.NonNull;
-
-import com.android.settings.R;
-
-/**
- * Takes an AlarmManager or JobScheduler csv string and parses it to get key:value pairs.
- * This allows us to populate a dialog with the correct information.
- */
-public class TareFactorController {
- private static final String TAG = "TareFactorController";
-
- private static TareFactorController sInstance;
-
- private static final int POLICY_ALARM_MANAGER = 0;
- private static final int POLICY_JOB_SCHEDULER = 1;
-
- private final ContentResolver mContentResolver;
- private final KeyValueListParser mParser = new KeyValueListParser(',');
- private final Resources mResources;
- private final ArrayMap<String, TareFactorData> mAlarmManagerMap = new ArrayMap<>();
- private final ArrayMap<String, TareFactorData> mJobSchedulerMap = new ArrayMap<>();
- private String mAlarmManagerConstants;
- private String mJobSchedulerConstants;
-
- private final ArraySet<DataChangeListener> mDataChangeListeners = new ArraySet<>();
-
- private TareFactorController(Context context) {
- mContentResolver = context.getContentResolver();
- mResources = context.getResources();
-
- ConfigObserver configObserver = new ConfigObserver(new Handler(Looper.getMainLooper()));
- configObserver.start();
-
- mAlarmManagerConstants =
- Settings.Global.getString(mContentResolver, TARE_ALARM_MANAGER_CONSTANTS);
- mJobSchedulerConstants =
- Settings.Global.getString(mContentResolver, TARE_JOB_SCHEDULER_CONSTANTS);
-
- initAlarmManagerMap();
- parseAlarmManagerGlobalSettings();
-
- initJobSchedulerMap();
- parseJobSchedulerGlobalSettings();
- }
-
- static TareFactorController getInstance(Context context) {
- synchronized (TareFactorController.class) {
- if (sInstance == null) {
- sInstance = new TareFactorController(context.getApplicationContext());
- }
- }
- return sInstance;
- }
-
- /**
- * Initialization for AlarmManager Map that sets a AM factor key to a title, default value, and
- * policy type in a data object.
- */
- private void initAlarmManagerMap() {
- mAlarmManagerMap.put(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED,
- new TareFactorData(mResources.getString(R.string.tare_min_balance_exempted),
- EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP,
- new TareFactorData(mResources.getString(R.string.tare_min_balance_headless_app),
- EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP,
- new TareFactorData(mResources.getString(R.string.tare_min_balance_other_app),
- EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_MAX_SATIATED_BALANCE,
- new TareFactorData(mResources.getString(R.string.tare_max_satiated_balance),
- EconomyManager.DEFAULT_AM_MAX_SATIATED_BALANCE_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_INITIAL_CONSUMPTION_LIMIT,
- new TareFactorData(mResources.getString(R.string.tare_initial_consumption_limit),
- EconomyManager.DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_MIN_CONSUMPTION_LIMIT,
- new TareFactorData(mResources.getString(R.string.tare_min_consumption_limit),
- EconomyManager.DEFAULT_AM_MIN_CONSUMPTION_LIMIT_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_MAX_CONSUMPTION_LIMIT,
- new TareFactorData(mResources.getString(R.string.tare_max_consumption_limit),
- EconomyManager.DEFAULT_AM_MAX_CONSUMPTION_LIMIT_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_REWARD_TOP_ACTIVITY_INSTANT,
- new TareFactorData(mResources.getString(R.string.tare_top_activity),
- EconomyManager.DEFAULT_AM_REWARD_TOP_ACTIVITY_INSTANT_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_REWARD_TOP_ACTIVITY_ONGOING,
- new TareFactorData(mResources.getString(R.string.tare_top_activity),
- EconomyManager.DEFAULT_AM_REWARD_TOP_ACTIVITY_ONGOING_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_REWARD_TOP_ACTIVITY_MAX,
- new TareFactorData(mResources.getString(R.string.tare_top_activity),
- EconomyManager.DEFAULT_AM_REWARD_TOP_ACTIVITY_MAX_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_REWARD_NOTIFICATION_SEEN_INSTANT,
- new TareFactorData(mResources.getString(R.string.tare_notification_seen),
- EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_SEEN_INSTANT_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_REWARD_NOTIFICATION_SEEN_ONGOING,
- new TareFactorData(mResources.getString(R.string.tare_notification_seen),
- EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_SEEN_ONGOING_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_REWARD_NOTIFICATION_SEEN_MAX,
- new TareFactorData(mResources.getString(R.string.tare_notification_seen),
- EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_SEEN_MAX_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_REWARD_NOTIFICATION_SEEN_WITHIN_15_INSTANT,
- new TareFactorData(mResources.getString(R.string.tare_notification_seen_15_min),
- EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_SEEN_WITHIN_15_INSTANT_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_REWARD_NOTIFICATION_SEEN_WITHIN_15_ONGOING,
- new TareFactorData(mResources.getString(R.string.tare_notification_seen_15_min),
- EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_SEEN_WITHIN_15_ONGOING_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_REWARD_NOTIFICATION_SEEN_WITHIN_15_MAX,
- new TareFactorData(mResources.getString(R.string.tare_notification_seen_15_min),
- EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_SEEN_WITHIN_15_MAX_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT,
- new TareFactorData(mResources.getString(R.string.tare_notification_interaction),
- EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_INSTANT_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING,
- new TareFactorData(mResources.getString(R.string.tare_notification_interaction),
- EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_ONGOING_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_REWARD_NOTIFICATION_INTERACTION_MAX,
- new TareFactorData(mResources.getString(R.string.tare_notification_interaction),
- EconomyManager.DEFAULT_AM_REWARD_NOTIFICATION_INTERACTION_MAX_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_REWARD_WIDGET_INTERACTION_INSTANT,
- new TareFactorData(mResources.getString(R.string.tare_widget_interaction),
- EconomyManager.DEFAULT_AM_REWARD_WIDGET_INTERACTION_INSTANT_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_REWARD_WIDGET_INTERACTION_ONGOING,
- new TareFactorData(mResources.getString(R.string.tare_widget_interaction),
- EconomyManager.DEFAULT_AM_REWARD_WIDGET_INTERACTION_ONGOING_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_REWARD_WIDGET_INTERACTION_MAX,
- new TareFactorData(mResources.getString(R.string.tare_widget_interaction),
- EconomyManager.DEFAULT_AM_REWARD_WIDGET_INTERACTION_MAX_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_REWARD_OTHER_USER_INTERACTION_INSTANT,
- new TareFactorData(mResources.getString(R.string.tare_other_interaction),
- EconomyManager.DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_INSTANT_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_REWARD_OTHER_USER_INTERACTION_ONGOING,
- new TareFactorData(mResources.getString(R.string.tare_other_interaction),
- EconomyManager.DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_ONGOING_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_REWARD_OTHER_USER_INTERACTION_MAX,
- new TareFactorData(mResources.getString(R.string.tare_other_interaction),
- EconomyManager.DEFAULT_AM_REWARD_OTHER_USER_INTERACTION_MAX_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP,
- new TareFactorData(mResources.getString(R.string.tare_wakeup_exact_idle),
- EconomyManager
- .DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_CTP_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP,
- new TareFactorData(mResources.getString(R.string.tare_wakeup_inexact_idle),
- EconomyManager
- .DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_CTP_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_ACTION_ALARM_EXACT_WAKEUP_CTP,
- new TareFactorData(mResources.getString(R.string.tare_wakeup_exact),
- EconomyManager.DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_CTP_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP,
- new TareFactorData(mResources.getString(R.string.tare_wakeup_inexact),
- EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_WAKEUP_CTP_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(
- EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP,
- new TareFactorData(mResources.getString(R.string.tare_nonwakeup_exact_idle),
- EconomyManager
- .DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_CTP_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP,
- new TareFactorData(mResources.getString(R.string.tare_nonwakeup_exact),
- EconomyManager.DEFAULT_AM_ACTION_ALARM_EXACT_NONWAKEUP_CTP_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(
- EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP,
- new TareFactorData(mResources.getString(R.string.tare_nonwakeup_inexact_idle),
- DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_CTP_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP,
- new TareFactorData(mResources.getString(R.string.tare_nonwakeup_inexact),
- EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_CTP_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_ACTION_ALARM_ALARMCLOCK_CTP,
- new TareFactorData(mResources.getString(R.string.tare_alarm_clock),
- EconomyManager.DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_CTP_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(
- EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE,
- new TareFactorData(mResources.getString(R.string.tare_alarm_clock),
- DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_WAKEUP_BASE_PRICE_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(
- EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE,
- new TareFactorData(mResources.getString(R.string.tare_alarm_clock),
- DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_WAKEUP_BASE_PRICE_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE,
- new TareFactorData(mResources.getString(R.string.tare_alarm_clock),
- EconomyManager.DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_WAKEUP_BASE_PRICE,
- new TareFactorData(mResources.getString(R.string.tare_alarm_clock),
- EconomyManager.DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(
- EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_EXACT_NONWAKEUP_BASE_PRICE,
- new TareFactorData(mResources.getString(R.string.tare_alarm_clock),
- EconomyManager.DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_ACTION_ALARM_EXACT_NONWAKEUP_BASE_PRICE,
- new TareFactorData(mResources.getString(R.string.tare_alarm_clock),
- EconomyManager.DEFAULT_AM_ACTION_ALARM_EXACT_WAKEUP_BASE_PRICE_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(
- EconomyManager.KEY_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE,
- new TareFactorData(mResources.getString(R.string.tare_alarm_clock),
- DEFAULT_AM_ACTION_ALARM_ALLOW_WHILE_IDLE_INEXACT_NONWAKEUP_BASE_PRICE_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE,
- new TareFactorData(mResources.getString(R.string.tare_alarm_clock),
- EconomyManager.DEFAULT_AM_ACTION_ALARM_INEXACT_NONWAKEUP_BASE_PRICE_CAKES,
- POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE,
- new TareFactorData(mResources.getString(R.string.tare_alarm_clock),
- EconomyManager.DEFAULT_AM_ACTION_ALARM_ALARMCLOCK_BASE_PRICE_CAKES,
- POLICY_ALARM_MANAGER));
- }
-
- /**
- * Initialization for JobScheduler Map that sets a JS factor key to a title, default value, and
- * policy type in a data object.
- */
- private void initJobSchedulerMap() {
- mJobSchedulerMap.put(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED,
- new TareFactorData(mResources.getString(R.string.tare_min_balance_exempted),
- EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP,
- new TareFactorData(mResources.getString(R.string.tare_min_balance_headless_app),
- EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP,
- new TareFactorData(mResources.getString(R.string.tare_min_balance_other_app),
- EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER,
- new TareFactorData(
- mResources.getString(R.string.tare_min_balance_addition_app_updater),
- EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_MAX_SATIATED_BALANCE,
- new TareFactorData(mResources.getString(R.string.tare_max_satiated_balance),
- EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT,
- new TareFactorData(mResources.getString(R.string.tare_initial_consumption_limit),
- EconomyManager.DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_MIN_CONSUMPTION_LIMIT,
- new TareFactorData(mResources.getString(R.string.tare_min_consumption_limit),
- EconomyManager.DEFAULT_JS_MIN_CONSUMPTION_LIMIT_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_MAX_CONSUMPTION_LIMIT,
- new TareFactorData(mResources.getString(R.string.tare_max_consumption_limit),
- EconomyManager.DEFAULT_JS_MAX_CONSUMPTION_LIMIT_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_REWARD_APP_INSTALL_INSTANT,
- new TareFactorData(mResources.getString(R.string.tare_app_install),
- EconomyManager.DEFAULT_JS_REWARD_APP_INSTALL_INSTANT_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_REWARD_APP_INSTALL_ONGOING,
- new TareFactorData(mResources.getString(R.string.tare_app_install),
- EconomyManager.DEFAULT_JS_REWARD_APP_INSTALL_ONGOING_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_REWARD_APP_INSTALL_MAX,
- new TareFactorData(mResources.getString(R.string.tare_app_install),
- EconomyManager.DEFAULT_JS_REWARD_APP_INSTALL_MAX_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_REWARD_TOP_ACTIVITY_INSTANT,
- new TareFactorData(mResources.getString(R.string.tare_top_activity),
- EconomyManager.DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_REWARD_TOP_ACTIVITY_ONGOING,
- new TareFactorData(mResources.getString(R.string.tare_top_activity),
- EconomyManager.DEFAULT_JS_REWARD_TOP_ACTIVITY_ONGOING_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_REWARD_TOP_ACTIVITY_MAX,
- new TareFactorData(mResources.getString(R.string.tare_top_activity),
- EconomyManager.DEFAULT_JS_REWARD_TOP_ACTIVITY_MAX_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_REWARD_NOTIFICATION_SEEN_INSTANT,
- new TareFactorData(mResources.getString(R.string.tare_notification_seen),
- EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_SEEN_INSTANT_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_REWARD_NOTIFICATION_SEEN_ONGOING,
- new TareFactorData(mResources.getString(R.string.tare_notification_seen),
- EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_SEEN_ONGOING_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_REWARD_NOTIFICATION_SEEN_MAX,
- new TareFactorData(mResources.getString(R.string.tare_notification_seen),
- EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_SEEN_MAX_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT,
- new TareFactorData(mResources.getString(R.string.tare_notification_interaction),
- EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING,
- new TareFactorData(mResources.getString(R.string.tare_notification_interaction),
- EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_REWARD_NOTIFICATION_INTERACTION_MAX,
- new TareFactorData(mResources.getString(R.string.tare_notification_interaction),
- EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_MAX_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_REWARD_WIDGET_INTERACTION_INSTANT,
- new TareFactorData(mResources.getString(R.string.tare_widget_interaction),
- EconomyManager.DEFAULT_JS_REWARD_WIDGET_INTERACTION_INSTANT_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_REWARD_WIDGET_INTERACTION_ONGOING,
- new TareFactorData(mResources.getString(R.string.tare_widget_interaction),
- EconomyManager.DEFAULT_JS_REWARD_WIDGET_INTERACTION_ONGOING_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_REWARD_WIDGET_INTERACTION_MAX,
- new TareFactorData(mResources.getString(R.string.tare_widget_interaction),
- EconomyManager.DEFAULT_JS_REWARD_WIDGET_INTERACTION_MAX_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_REWARD_OTHER_USER_INTERACTION_INSTANT,
- new TareFactorData(mResources.getString(R.string.tare_other_interaction),
- EconomyManager.DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_INSTANT_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_REWARD_OTHER_USER_INTERACTION_ONGOING,
- new TareFactorData(mResources.getString(R.string.tare_other_interaction),
- EconomyManager.DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_ONGOING_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_REWARD_OTHER_USER_INTERACTION_MAX,
- new TareFactorData(mResources.getString(R.string.tare_other_interaction),
- EconomyManager.DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_MAX_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_ACTION_JOB_MAX_START_CTP,
- new TareFactorData(mResources.getString(R.string.tare_job_max_start),
- EconomyManager.DEFAULT_JS_ACTION_JOB_MAX_START_CTP_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_ACTION_JOB_MAX_RUNNING_CTP,
- new TareFactorData(mResources.getString(R.string.tare_job_max_running),
- EconomyManager.DEFAULT_JS_ACTION_JOB_MAX_RUNNING_CTP_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_ACTION_JOB_HIGH_START_CTP,
- new TareFactorData(mResources.getString(R.string.tare_job_high_start),
- EconomyManager.DEFAULT_JS_ACTION_JOB_HIGH_START_CTP_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_ACTION_JOB_HIGH_RUNNING_CTP,
- new TareFactorData(mResources.getString(R.string.tare_job_high_running),
- EconomyManager.DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_CTP_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_ACTION_JOB_DEFAULT_START_CTP,
- new TareFactorData(mResources.getString(R.string.tare_job_default_start),
- EconomyManager.DEFAULT_JS_ACTION_JOB_DEFAULT_START_CTP_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_ACTION_JOB_DEFAULT_RUNNING_CTP,
- new TareFactorData(mResources.getString(R.string.tare_job_default_running),
- EconomyManager.DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_CTP_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_ACTION_JOB_LOW_START_CTP,
- new TareFactorData(mResources.getString(R.string.tare_job_low_start),
- EconomyManager.DEFAULT_JS_ACTION_JOB_LOW_START_CTP_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(
- EconomyManager.KEY_JS_ACTION_JOB_LOW_RUNNING_CTP,
- new TareFactorData(mResources.getString(R.string.tare_job_low_running),
- EconomyManager.DEFAULT_JS_ACTION_JOB_LOW_RUNNING_CTP_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_ACTION_JOB_MIN_START_CTP,
- new TareFactorData(mResources.getString(R.string.tare_job_min_start),
- EconomyManager.DEFAULT_JS_ACTION_JOB_MIN_START_CTP_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(
- EconomyManager.KEY_JS_ACTION_JOB_MIN_RUNNING_CTP,
- new TareFactorData(mResources.getString(R.string.tare_job_min_running),
- EconomyManager.DEFAULT_JS_ACTION_JOB_MIN_RUNNING_CTP_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP,
- new TareFactorData(mResources.getString(R.string.tare_job_timeout_penalty),
- EconomyManager.DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_CTP_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_ACTION_JOB_MAX_START_BASE_PRICE,
- new TareFactorData(mResources.getString(R.string.tare_job_max_start),
- EconomyManager.DEFAULT_JS_ACTION_JOB_MAX_START_BASE_PRICE_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(
- EconomyManager.KEY_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE,
- new TareFactorData(mResources.getString(R.string.tare_job_max_running),
- EconomyManager.DEFAULT_JS_ACTION_JOB_MAX_RUNNING_BASE_PRICE_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(
- EconomyManager.KEY_JS_ACTION_JOB_HIGH_START_BASE_PRICE,
- new TareFactorData(mResources.getString(R.string.tare_job_high_start),
- EconomyManager.DEFAULT_JS_ACTION_JOB_HIGH_START_BASE_PRICE_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE,
- new TareFactorData(mResources.getString(R.string.tare_job_high_running),
- EconomyManager.DEFAULT_JS_ACTION_JOB_HIGH_RUNNING_BASE_PRICE_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE,
- new TareFactorData(mResources.getString(R.string.tare_job_default_start),
- EconomyManager.DEFAULT_JS_ACTION_JOB_DEFAULT_START_BASE_PRICE_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(
- EconomyManager.KEY_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE,
- new TareFactorData(mResources.getString(R.string.tare_job_default_running),
- EconomyManager.DEFAULT_JS_ACTION_JOB_DEFAULT_RUNNING_BASE_PRICE_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_ACTION_JOB_LOW_START_BASE_PRICE,
- new TareFactorData(mResources.getString(R.string.tare_job_low_start),
- EconomyManager.DEFAULT_JS_ACTION_JOB_LOW_START_BASE_PRICE_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(
- EconomyManager.KEY_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE,
- new TareFactorData(mResources.getString(R.string.tare_job_low_running),
- EconomyManager.DEFAULT_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_ACTION_JOB_MIN_START_BASE_PRICE,
- new TareFactorData(mResources.getString(R.string.tare_job_min_start),
- EconomyManager.DEFAULT_JS_ACTION_JOB_MIN_START_BASE_PRICE_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE,
- new TareFactorData(mResources.getString(R.string.tare_job_min_running),
- EconomyManager.DEFAULT_JS_ACTION_JOB_MIN_RUNNING_BASE_PRICE_CAKES,
- POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE,
- new TareFactorData(mResources.getString(R.string.tare_job_timeout_penalty),
- EconomyManager.DEFAULT_JS_ACTION_JOB_TIMEOUT_PENALTY_BASE_PRICE_CAKES,
- POLICY_JOB_SCHEDULER));
- }
-
- /**
- * Parses the AM constant from Settings.Global to get to the current value.
- */
- private void parseAlarmManagerGlobalSettings() {
- parseSettingsIntoMap(mAlarmManagerConstants, mAlarmManagerMap);
- }
-
- /**
- * Parses the JS constant from Settings.Global to get to the current value.
- */
- private void parseJobSchedulerGlobalSettings() {
- parseSettingsIntoMap(mJobSchedulerConstants, mJobSchedulerMap);
- }
-
- private void parseSettingsIntoMap(String constants, ArrayMap<String, TareFactorData> map) {
- try {
- mParser.setString(constants);
- } catch (Exception e) {
- Slog.e(TAG, "Bad string constants value", e);
- }
-
- for (int i = map.size() - 1; i >= 0; --i) {
- final String key = map.keyAt(i);
- final TareFactorData data = map.valueAt(i);
- data.currentValue = parseCreditValue(mParser.getString(key, null), data.defaultValue);
- }
- }
-
- @NonNull
- private ArrayMap<String, TareFactorData> getMap(int factorPolicy) {
- switch (factorPolicy) {
- case POLICY_ALARM_MANAGER:
- return mAlarmManagerMap;
- case POLICY_JOB_SCHEDULER:
- return mJobSchedulerMap;
- default:
- throw new IllegalArgumentException("Invalid factor policy given");
- }
- }
-
- /**
- * Takes a key and factor policy as input and grabs the title linked to it.
- *
- * @param key the key of the factor you want to get the title of
- * @param factorPolicy the policy you want the title of
- */
- private String getTitle(String key, int factorPolicy) {
- final ArrayMap<String, TareFactorData> currentMap = getMap(factorPolicy);
- return currentMap.get(key).title;
- }
-
- /**
- * Takes a key and factor policy as input and grabs the current value linked to it.
- *
- * @param key the key of the factor you want to get the default value of
- * @param factorPolicy the policy you want the current value of
- */
- private long getCurrentValue(String key, int factorPolicy) {
- final ArrayMap<String, TareFactorData> currentMap = getMap(factorPolicy);
- return currentMap.get(key).currentValue;
- }
-
- /**
- * Takes a key as input and grabs the factor type linked to it.
- *
- * @param key the key of the factor you want to get the factor type of
- */
- private int getFactorType(String key) {
- ArrayMap<String, TareFactorData> currentMap;
- if (mAlarmManagerMap.containsKey(key)) {
- currentMap = mAlarmManagerMap;
- } else if (mJobSchedulerMap.containsKey(key)) {
- currentMap = mJobSchedulerMap;
- } else {
- throw new IllegalArgumentException("Couldn't link key '" + key + "' to a policy");
- }
- return currentMap.get(key).factorPolicy;
- }
-
- long getValue(String key) {
- final int policy = getFactorType(key);
- return getCurrentValue(key, policy);
- }
-
- /**
- * Takes a key,edited value, and factor policy as input and assigns the new edited value to
- * be the new current value for that factors key.
- *
- * @param key the key of the factor you want to get the default value of
- * @param editedValue the value entered by the user in the dialog
- * @param factorPolicy policy being updated
- */
- public void updateValue(String key, long editedValue, int factorPolicy) {
- final ArrayMap<String, TareFactorData> map = getMap(factorPolicy);
-
- final TareFactorData data = map.get(key);
- if (data.currentValue == editedValue) {
- return;
- }
- data.currentValue = editedValue;
- rebuildPolicyConstants(factorPolicy);
- }
-
- /**
- * Iterates through the factor policy map for keys and current values to
- * rebuild a current string that is then assigned to be the new global settings string.
- *
- * @param factorPolicy policy being updated
- */
- private void rebuildPolicyConstants(int factorPolicy) {
- switch (factorPolicy) {
- case POLICY_ALARM_MANAGER:
- writeConstantsToSettings(mAlarmManagerMap, TARE_ALARM_MANAGER_CONSTANTS);
- break;
- case POLICY_JOB_SCHEDULER:
- writeConstantsToSettings(mJobSchedulerMap, TARE_JOB_SCHEDULER_CONSTANTS);
- break;
- }
- }
-
- private void writeConstantsToSettings(ArrayMap<String, TareFactorData> factorMap,
- String settingsKey) {
- final StringBuilder constantsStringBuilder = new StringBuilder();
-
- for (int i = 0, size = factorMap.size(); i < size; ++i) {
- final TareFactorData factor = factorMap.valueAt(i);
- if (factor.currentValue == factor.defaultValue) {
- continue;
- }
-
- if (constantsStringBuilder.length() > 0) {
- constantsStringBuilder.append(",");
- }
-
- constantsStringBuilder
- .append(factorMap.keyAt(i))
- .append("=");
- if (factor.currentValue % CAKE_IN_ARC == 0) {
- constantsStringBuilder
- .append(factor.currentValue / CAKE_IN_ARC)
- .append("A");
- } else {
- constantsStringBuilder
- .append(factor.currentValue)
- .append("ck");
- }
- }
-
- Settings.Global.putString(mContentResolver, settingsKey, constantsStringBuilder.toString());
- }
-
- /**
- * Creates a dialog with the values linked to the key.
- *
- * @param key the key of the factor you want to get the default value of
- */
- public TareFactorDialogFragment createDialog(String key) {
- int policy = getFactorType(key);
- return new TareFactorDialogFragment(getTitle(key, policy), key,
- getCurrentValue(key, policy), policy, this);
- }
-
- /**
- * Data object that holds a title,default value,and current value for a key.
- */
- private static class TareFactorData {
- public final String title;
- public final long defaultValue;
- public final int factorPolicy;
- public long currentValue;
-
- TareFactorData(String title, long defaultValue, int factorPolicy) {
- this.title = title;
- this.defaultValue = defaultValue;
- this.factorPolicy = factorPolicy;
- this.currentValue = defaultValue;
- }
- }
-
- interface DataChangeListener {
- void onDataChanged();
- }
-
- void registerListener(DataChangeListener listener) {
- mDataChangeListeners.add(listener);
- }
-
- void unregisterListener(DataChangeListener listener) {
- mDataChangeListeners.remove(listener);
- }
-
- void notifyListeners() {
- for (int i = mDataChangeListeners.size() - 1; i >= 0; --i) {
- mDataChangeListeners.valueAt(i).onDataChanged();
- }
- }
-
- private class ConfigObserver extends ContentObserver {
-
- ConfigObserver(Handler handler) {
- super(handler);
- }
-
- public void start() {
- mContentResolver.registerContentObserver(
- Settings.Global.getUriFor(TARE_ALARM_MANAGER_CONSTANTS), false, this);
- mContentResolver.registerContentObserver(
- Settings.Global.getUriFor(TARE_JOB_SCHEDULER_CONSTANTS), false, this);
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- if (uri.equals(Settings.Global.getUriFor(TARE_ALARM_MANAGER_CONSTANTS))) {
- mAlarmManagerConstants =
- Settings.Global.getString(mContentResolver, TARE_ALARM_MANAGER_CONSTANTS);
- parseAlarmManagerGlobalSettings();
- notifyListeners();
- } else if (uri.equals(Settings.Global.getUriFor(TARE_JOB_SCHEDULER_CONSTANTS))) {
- mJobSchedulerConstants =
- Settings.Global.getString(mContentResolver, TARE_JOB_SCHEDULER_CONSTANTS);
- parseJobSchedulerGlobalSettings();
- notifyListeners();
- }
- }
- }
-}
diff --git a/src/com/android/settings/development/tare/TareFactorDialogFragment.java b/src/com/android/settings/development/tare/TareFactorDialogFragment.java
deleted file mode 100644
index 269e209..0000000
--- a/src/com/android/settings/development/tare/TareFactorDialogFragment.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * 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.development.tare;
-
-import static android.app.tare.EconomyManager.CAKE_IN_ARC;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.content.Context;
-import android.os.Bundle;
-import android.text.InputType;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.EditText;
-import android.widget.Spinner;
-
-import androidx.annotation.NonNull;
-
-import com.android.settings.R;
-import com.android.settings.Utils;
-
-/**
- * Dialog Fragment for changing tare factor values
- */
-public class TareFactorDialogFragment extends DialogFragment {
- private static final String TAG = "TareDialogFragment";
-
- // This follows the order in strings.xml:tare_units array.
- private static final int UNIT_IDX_ARC = 0;
- private static final int UNIT_IDX_CAKE = 1;
-
- private final String mFactorKey;
- private final String mFactorTitle;
- private final long mFactorValue;
- private final int mFactorPolicy;
- private final TareFactorController mTareFactorController;
-
- private EditText mFactorValueView;
- private Spinner mUnitSpinner;
-
- /**
- * @param title the title that will show at the top of the Dialog for the Factor
- * @param key the key of the Factor being initialized.
- * @param currentValue the current value set for the Factor
- */
- public TareFactorDialogFragment(@NonNull String title, @NonNull String key, long currentValue,
- int factorPolicy, TareFactorController tareFactorController) {
- mFactorTitle = title;
- mFactorKey = key;
- mFactorValue = currentValue;
- mFactorPolicy = factorPolicy;
- mTareFactorController = tareFactorController;
- }
-
- @NonNull
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- AlertDialog.Builder builder = new AlertDialog.Builder(
- getActivity())
- .setTitle(mFactorTitle)
- .setView(createDialogView())
- .setPositiveButton(R.string.tare_dialog_confirm_button_title, (dialog, which) -> {
-
- final String stringValue = mFactorValueView.getText().toString();
- long newVal = mFactorValue;
- try {
- newVal = Long.parseLong(stringValue);
- if (mUnitSpinner.getSelectedItemPosition() == UNIT_IDX_ARC) {
- // Convert ARC to cake
- newVal *= CAKE_IN_ARC;
- }
- } catch (NumberFormatException e) {
- Log.e(TAG, "Error parsing '" + stringValue + "'. Using "
- + mFactorValue + " instead", e);
- }
- mTareFactorController.updateValue(mFactorKey, newVal, mFactorPolicy);
- })
- .setNegativeButton(android.R.string.cancel, (dialog, which) -> {
- // When the negative button is clicked do nothing
- });
-
- return builder.create();
- }
-
- /**
- * Creates a view for the factor Dialog that currently
- * is linked to the basic dialog_edittext.xml layout.
- */
- private View createDialogView() {
- final LayoutInflater layoutInflater = (LayoutInflater) getActivity()
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- View layout = layoutInflater.inflate(R.layout.dialog_edittext_dropdown, null);
- mFactorValueView = layout.findViewById(R.id.edittext);
- mFactorValueView.setInputType(InputType.TYPE_CLASS_NUMBER);
-
- mUnitSpinner = layout.findViewById(R.id.spinner);
- final String[] units = getResources().getStringArray(R.array.tare_units);
- ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(
- getActivity(), android.R.layout.simple_spinner_item, units);
- mUnitSpinner.setAdapter(spinnerArrayAdapter);
-
- final int unitIdx;
- if (mFactorValue % CAKE_IN_ARC == 0) {
- mFactorValueView.setText(String.format("%d", mFactorValue / CAKE_IN_ARC));
- unitIdx = UNIT_IDX_ARC;
- } else {
- mFactorValueView.setText(String.format("%d", mFactorValue));
- unitIdx = UNIT_IDX_CAKE;
- }
- mUnitSpinner.setSelection(unitIdx);
- mUnitSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
- private int mSelectedPosition = unitIdx;
-
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- if (mSelectedPosition == position) {
- return;
- }
- mSelectedPosition = position;
- final String stringValue = mFactorValueView.getText().toString();
-
- try {
- long newVal = Long.parseLong(stringValue);
- if (mUnitSpinner.getSelectedItemPosition() == UNIT_IDX_ARC) {
- // Convert cake to ARC
- newVal /= CAKE_IN_ARC;
- } else {
- // Convert ARC to cake
- newVal *= CAKE_IN_ARC;
- }
- mFactorValueView.setText(String.format("%d", newVal));
- } catch (NumberFormatException e) {
- Log.e(TAG, "Error parsing '" + stringValue + "'", e);
- }
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
-
- }
- });
-
- Utils.setEditTextCursorPosition(mFactorValueView);
- return layout;
- }
-}
diff --git a/src/com/android/settings/development/tare/TareFactorExpandableListAdapter.java b/src/com/android/settings/development/tare/TareFactorExpandableListAdapter.java
deleted file mode 100644
index 9c78d06..0000000
--- a/src/com/android/settings/development/tare/TareFactorExpandableListAdapter.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.development.tare;
-
-import static android.app.tare.EconomyManager.CAKE_IN_ARC;
-
-import android.annotation.SuppressLint;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseExpandableListAdapter;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-
-import com.android.settings.R;
-
-/**
- * Creates the expandable list that will allow modifying individual factors.
- */
-public class TareFactorExpandableListAdapter extends BaseExpandableListAdapter {
-
- private final LayoutInflater mLayoutInflater;
- private final TareFactorController mFactorController;
-
- private final String[] mGroups;
- private final String[][] mChildren;
- private final String[][] mKeys;
-
- TareFactorExpandableListAdapter(TareFactorController factorController,
- LayoutInflater layoutInflater, String[] groups, String[][] children, String[][] keys) {
- mLayoutInflater = layoutInflater;
- mFactorController = factorController;
-
- mGroups = groups;
- mChildren = children;
- mKeys = keys;
-
- validateMappings();
- }
-
- private void validateMappings() {
- if (mGroups.length != mChildren.length) {
- throw new IllegalStateException("groups and children don't have the same length");
- }
- if (mChildren.length != mKeys.length) {
- throw new IllegalStateException("children and keys don't have the same length");
- }
- for (int i = 0; i < mChildren.length; ++i) {
- if (mChildren[i].length != mKeys[i].length) {
- throw new IllegalStateException(
- "children and keys don't have the same length in row " + i);
- }
- }
- }
-
- @Override
- public int getGroupCount() {
- return mGroups.length;
- }
-
- @Override
- public int getChildrenCount(int groupPosition) {
- return mChildren[groupPosition].length;
- }
-
- @Override
- public Object getGroup(int groupPosition) {
- return mGroups[groupPosition];
- }
-
- @Override
- public Object getChild(int groupPosition, int childPosition) {
- return mChildren[groupPosition][childPosition];
- }
-
- @Override
- public long getGroupId(int groupPosition) {
- return groupPosition;
- }
-
- @Override
- public long getChildId(int groupPosition, int childPosition) {
- return childPosition;
- }
-
- @NonNull
- String getKey(int groupPosition, int childPosition) {
- return mKeys[groupPosition][childPosition];
- }
-
- @Override
- public boolean hasStableIds() {
- return true;
- }
-
- @Override
- public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
- ViewGroup parent) {
- if (convertView == null) {
- convertView = mLayoutInflater.inflate(android.R.layout.simple_list_item_1, parent,
- false);
- }
- TextView factor = convertView.findViewById(android.R.id.text1);
- factor.setText(getGroup(groupPosition).toString());
- return convertView;
- }
-
- @Override
- @SuppressLint("InflateParams") // AdapterView doesn't support addView
- public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
- View convertView, ViewGroup parent) {
- // Here a custom child item is used instead of android.R.simple_list_item_2 because it
- // is more customizable for this specific UI
- if (convertView == null) {
- convertView = mLayoutInflater.inflate(R.layout.tare_child_item, null);
- }
- TextView factor = convertView.findViewById(R.id.factor);
- TextView value = convertView.findViewById(R.id.factor_number);
-
- factor.setText(getChild(groupPosition, childPosition).toString());
- value.setText(cakeToString(
- mFactorController.getValue(getKey(groupPosition, childPosition))));
-
- return convertView;
- }
-
- @NonNull
- private static String cakeToString(long cakes) {
- // Resources.getQuantityString doesn't handle floating point numbers, so doing this manually
- if (cakes == 0) {
- return "0";
- }
- final long sub = cakes % CAKE_IN_ARC;
- final long arcs = (int) (cakes / CAKE_IN_ARC);
- if (arcs == 0) {
- return sub + " c";
- }
- StringBuilder sb = new StringBuilder();
- sb.append(arcs);
- if (sub > 0) {
- sb.append(".").append(String.format("%03d", sub / (CAKE_IN_ARC / 1000)));
- }
- sb.append(" A");
- return sb.toString();
- }
-
- @Override
- public boolean isChildSelectable(int groupPosition, int childPosition) {
- return true;
- }
-}
diff --git a/src/com/android/settings/development/tare/TareHomePage.java b/src/com/android/settings/development/tare/TareHomePage.java
deleted file mode 100644
index 0eb93fc..0000000
--- a/src/com/android/settings/development/tare/TareHomePage.java
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * 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.development.tare;
-
-import static com.android.settings.development.tare.DropdownActivity.EXTRA_POLICY;
-import static com.android.settings.development.tare.DropdownActivity.POLICY_ALARM_MANAGER;
-import static com.android.settings.development.tare.DropdownActivity.POLICY_JOB_SCHEDULER;
-
-import android.app.Activity;
-import android.app.tare.EconomyManager;
-import android.content.Intent;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.provider.DeviceConfig;
-import android.provider.Settings;
-import android.util.Log;
-import android.view.View;
-import android.widget.Button;
-import android.widget.CompoundButton;
-import android.widget.CompoundButton.OnCheckedChangeListener;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.android.settings.R;
-
-/** Class for creating the TARE homepage in settings */
-public class TareHomePage extends Activity {
- private static final String TAG = "TareHomePage";
-
- private CompoundButton mOnSwitch;
- private Button mRevButton;
- private TextView mAlarmManagerView;
- private TextView mJobSchedulerView;
- private ConfigObserver mConfigObserver;
-
- private static final int SETTING_VALUE_DEFAULT = -1;
- private static final int SETTING_VALUE_OFF = 0;
- private static final int SETTING_VALUE_ON = 1;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.tare_homepage);
-
- mOnSwitch = findViewById(R.id.on_switch);
- mRevButton = findViewById(R.id.revert_button);
- mAlarmManagerView = findViewById(R.id.alarmmanager);
- mJobSchedulerView = findViewById(R.id.jobscheduler);
-
- mConfigObserver = new ConfigObserver(new Handler(Looper.getMainLooper()));
-
- mOnSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- if (mConfigObserver.mEnableTareSetting == SETTING_VALUE_DEFAULT
- && isChecked == mConfigObserver.getDefaultEnabledStatus()) {
- // Don't bother writing something that's not new information. It would make
- // it hard to use DeviceConfig if we did.
- return;
- }
- Settings.Global.putInt(getContentResolver(),
- Settings.Global.ENABLE_TARE,
- isChecked ? SETTING_VALUE_ON : SETTING_VALUE_OFF);
- }
- });
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- mConfigObserver.start();
- }
-
- @Override
- protected void onPause() {
- mConfigObserver.stop();
- super.onPause();
- }
-
- /** Reverts the TARE settings to the original default settings */
- public void revertSettings(View v) {
- Toast.makeText(this, R.string.tare_settings_reverted_toast, Toast.LENGTH_LONG).show();
- final boolean wasSettingsDefault =
- mConfigObserver.mEnableTareSetting == SETTING_VALUE_DEFAULT;
- Settings.Global.putString(getApplicationContext().getContentResolver(),
- Settings.Global.ENABLE_TARE, null);
- Settings.Global.putString(getApplicationContext().getContentResolver(),
- Settings.Global.TARE_ALARM_MANAGER_CONSTANTS, null);
- Settings.Global.putString(getApplicationContext().getContentResolver(),
- Settings.Global.TARE_JOB_SCHEDULER_CONSTANTS, null);
- if (wasSettingsDefault) {
- // Only do this manually here to force a DeviceConfig check if the settings value isn't
- // actually changing.
- setEnabled(mConfigObserver.getDefaultEnabledStatus());
- }
- }
-
- /** Opens up the AlarmManager TARE policy page with its factors to view and edit */
- public void launchAlarmManagerPage(View v) {
- Intent i = new Intent(getApplicationContext(), DropdownActivity.class);
- i.putExtra(EXTRA_POLICY, POLICY_ALARM_MANAGER);
- startActivity(i);
- }
-
- /** Opens up the JobScheduler TARE policy page with its factors to view and edit */
- public void launchJobSchedulerPage(View v) {
- Intent i = new Intent(getApplicationContext(), DropdownActivity.class);
- i.putExtra(EXTRA_POLICY, POLICY_JOB_SCHEDULER);
- startActivity(i);
- }
-
- /** Changes the enabled state of the TARE homepage buttons based on global toggle */
- private void setEnabled(boolean tareStatus) {
- mAlarmManagerView.setEnabled(tareStatus);
- mJobSchedulerView.setEnabled(tareStatus);
- mOnSwitch.setChecked(tareStatus);
- }
-
- private class ConfigObserver extends ContentObserver {
- private int mEnableTareSetting;
-
- ConfigObserver(Handler handler) {
- super(handler);
- }
-
- public void start() {
- getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.ENABLE_TARE), false, this);
- processEnableTareChange();
- }
-
- public void stop() {
- getContentResolver().unregisterContentObserver(this);
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- processEnableTareChange();
- }
-
- private void processEnableTareChange() {
- final String setting =
- Settings.Global.getString(getContentResolver(), Settings.Global.ENABLE_TARE);
- if (setting == null) {
- mEnableTareSetting = SETTING_VALUE_DEFAULT;
- } else {
- try {
- mEnableTareSetting = Integer.parseInt(setting);
- } catch (NumberFormatException e) {
- Log.e(TAG, "Invalid setting value", e);
- mEnableTareSetting = EconomyManager.DEFAULT_ENABLE_TARE_MODE;
- }
- }
- final boolean enabled;
- if (mEnableTareSetting == SETTING_VALUE_ON) {
- enabled = true;
- } else if (mEnableTareSetting == SETTING_VALUE_OFF) {
- enabled = false;
- } else {
- enabled = getDefaultEnabledStatus();
- }
- setEnabled(enabled);
- }
-
- private boolean getDefaultEnabledStatus() {
- // Show Shadow Mode as "off" in the UI since it won't be affecting device behavior.
- return DeviceConfig.getInt(DeviceConfig.NAMESPACE_TARE,
- EconomyManager.KEY_ENABLE_TARE_MODE,
- EconomyManager.DEFAULT_ENABLE_TARE_MODE) == EconomyManager.ENABLED_MODE_ON;
- }
- }
-}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryEntry.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryEntry.java
index 4b65cc9..ddb8ecb 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryEntry.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryEntry.java
@@ -40,6 +40,7 @@
import com.android.settings.R;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settingslib.Utils;
+import com.android.settingslib.utils.StringUtil;
import java.util.Comparator;
import java.util.Locale;
@@ -655,4 +656,46 @@
return 0.0d;
}
}
+
+ @Override
+ public String toString() {
+ return new StringBuilder()
+ .append("BatteryEntry{")
+ .append(String.format("\n\tname=%s isHidden=%b", mName, mIsHidden))
+ .append(String.format("\n\tconsume=%.2f%% | %f", mPercent, mConsumedPower))
+ .append(
+ String.format(
+ "\n\tconsume power= foreground:%f foregroundService:%f",
+ mConsumedPowerInForeground, mConsumedPowerInForegroundService))
+ .append(
+ String.format(
+ "\n\tconsume power= background:%f cached:%f",
+ mConsumedPowerInBackground, mConsumedPowerInCached))
+ .append(
+ String.format(
+ "\n\ttime= foreground:%s foregroundService:%s "
+ + "background:%s usageDuration:%s",
+ StringUtil.formatElapsedTime(
+ mContext,
+ (double) mTimeInForegroundMs,
+ /* withSeconds= */ true,
+ /* collapseTimeUnit= */ false),
+ StringUtil.formatElapsedTime(
+ mContext,
+ (double) mTimeInForegroundServiceMs,
+ /* withSeconds= */ true,
+ /* collapseTimeUnit= */ false),
+ StringUtil.formatElapsedTime(
+ mContext,
+ (double) mTimeInBackgroundMs,
+ /* withSeconds= */ true,
+ /* collapseTimeUnit= */ false),
+ StringUtil.formatElapsedTime(
+ mContext,
+ (double) mUsageDurationMs,
+ /* withSeconds= */ true,
+ /* collapseTimeUnit= */ false)))
+ .append(String.format("\n\tpackage:%s uid:%d", mDefaultPackageName, mUid))
+ .toString();
+ }
}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryHistEntry.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryHistEntry.java
index b42d373..dbe98bc 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryHistEntry.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryHistEntry.java
@@ -192,41 +192,44 @@
@Override
public String toString() {
final String recordAtDateTime = ConvertUtils.utcToLocalTimeForLogging(mTimestamp);
- final StringBuilder builder = new StringBuilder();
- builder.append("\nBatteryHistEntry{");
- builder.append(
- String.format(
- "\n\tpackage=%s|label=%s|uid=%d|userId=%d|isHidden=%b",
- mPackageName, mAppLabel, mUid, mUserId, mIsHidden));
- builder.append(
- String.format(
- "\n\ttimestamp=%s|zoneId=%s|bootTimestamp=%d",
- recordAtDateTime, mZoneId, TimestampUtils.getSeconds(mBootTimestamp)));
- builder.append(
- String.format(
- "\n\tusage=%f|total=%f|consume=%f",
- mPercentOfTotal, mTotalPower, mConsumePower));
- builder.append(
- String.format(
- "\n\tforeground=%f|foregroundService=%f",
- mForegroundUsageConsumePower, mForegroundServiceUsageConsumePower));
- builder.append(
- String.format(
- "\n\tbackground=%f|cached=%f",
- mBackgroundUsageConsumePower, mCachedUsageConsumePower));
- builder.append(
- String.format(
- "\n\telapsedTime,fg=%d|fgs=%d|bg=%d",
- TimestampUtils.getSeconds(mBackgroundUsageTimeInMs),
- TimestampUtils.getSeconds(mForegroundServiceUsageTimeInMs),
- TimestampUtils.getSeconds(mBackgroundUsageTimeInMs)));
- builder.append(
- String.format("\n\tdrainType=%d|consumerType=%d", mDrainType, mConsumerType));
- builder.append(
- String.format(
- "\n\tbattery=%d|status=%d|health=%d\n}",
- mBatteryLevel, mBatteryStatus, mBatteryHealth));
- return builder.toString();
+ return new StringBuilder()
+ .append("\nBatteryHistEntry{")
+ .append(
+ String.format(
+ "\n\tpackage=%s|label=%s|uid=%d|userId=%d|isHidden=%b",
+ mPackageName, mAppLabel, mUid, mUserId, mIsHidden))
+ .append(
+ String.format(
+ "\n\ttimestamp=%s|zoneId=%s|bootTimestamp=%d",
+ recordAtDateTime,
+ mZoneId,
+ TimestampUtils.getSeconds(mBootTimestamp)))
+ .append(
+ String.format(
+ "\n\tusage=%f|total=%f|consume=%f",
+ mPercentOfTotal, mTotalPower, mConsumePower))
+ .append(
+ String.format(
+ "\n\tforeground=%f|foregroundService=%f",
+ mForegroundUsageConsumePower, mForegroundServiceUsageConsumePower))
+ .append(
+ String.format(
+ "\n\tbackground=%f|cached=%f",
+ mBackgroundUsageConsumePower, mCachedUsageConsumePower))
+ .append(
+ String.format(
+ "\n\telapsedTime,fg=%d|fgs=%d|bg=%d",
+ TimestampUtils.getSeconds(mForegroundUsageTimeInMs),
+ TimestampUtils.getSeconds(mForegroundServiceUsageTimeInMs),
+ TimestampUtils.getSeconds(mBackgroundUsageTimeInMs)))
+ .append(
+ String.format(
+ "\n\tdrainType=%d|consumerType=%d", mDrainType, mConsumerType))
+ .append(
+ String.format(
+ "\n\tbattery=%d|status=%d|health=%d\n}",
+ mBatteryLevel, mBatteryStatus, mBatteryHealth))
+ .toString();
}
private int getInteger(ContentValues values, String key) {
diff --git a/src/com/android/settings/gestures/SystemNavigationGestureSettings.java b/src/com/android/settings/gestures/SystemNavigationGestureSettings.java
index 4e35460..cfaee00 100644
--- a/src/com/android/settings/gestures/SystemNavigationGestureSettings.java
+++ b/src/com/android/settings/gestures/SystemNavigationGestureSettings.java
@@ -41,6 +41,7 @@
import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceScreen;
+import com.android.internal.accessibility.common.ShortcutConstants;
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityShortcutsTutorial;
import com.android.settings.core.BasePreferenceController;
@@ -366,7 +367,7 @@
private boolean isAnyServiceSupportAccessibilityButton() {
final AccessibilityManager ams = getContext().getSystemService(AccessibilityManager.class);
final List<String> targets = ams.getAccessibilityShortcutTargets(
- AccessibilityManager.ACCESSIBILITY_BUTTON);
+ ShortcutConstants.UserShortcutType.SOFTWARE);
return !targets.isEmpty();
}
diff --git a/src/com/android/settings/network/apn/ApnTypes.kt b/src/com/android/settings/network/apn/ApnTypes.kt
index 2c8fa2a..d384625 100644
--- a/src/com/android/settings/network/apn/ApnTypes.kt
+++ b/src/com/android/settings/network/apn/ApnTypes.kt
@@ -109,11 +109,15 @@
return regularOptions.filter { it.selected.value }.joinToString(",") { it.text }
}
- private val NotPreSelectedTypes = setOf(
- ApnSetting.TYPE_IMS_STRING,
- ApnSetting.TYPE_IA_STRING,
- ApnSetting.TYPE_EMERGENCY_STRING,
- ApnSetting.TYPE_MCX_STRING,
+ private val PreSelectedTypes = setOf(
+ ApnSetting.TYPE_DEFAULT_STRING,
+ ApnSetting.TYPE_MMS_STRING,
+ ApnSetting.TYPE_SUPL_STRING,
+ ApnSetting.TYPE_DUN_STRING,
+ ApnSetting.TYPE_HIPRI_STRING,
+ ApnSetting.TYPE_FOTA_STRING,
+ ApnSetting.TYPE_CBS_STRING,
+ ApnSetting.TYPE_XCAP_STRING,
)
fun getPreSelectedApnType(customizedConfig: CustomizedConfig): String =
@@ -123,5 +127,5 @@
private fun defaultPreSelectedApnTypes(readOnlyApnTypes: List<String>) =
if (ApnSetting.TYPE_ALL_STRING in readOnlyApnTypes) emptyList()
- else APN_TYPES.filter { it !in readOnlyApnTypes + NotPreSelectedTypes }
+ else PreSelectedTypes.filterNot { it in readOnlyApnTypes }
}
diff --git a/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java b/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java
index 7145460..82ef58b 100644
--- a/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java
+++ b/src/com/android/settings/widget/HighlightablePreferenceGroupAdapter.java
@@ -40,6 +40,7 @@
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.accessibility.AccessibilityUtil;
import com.google.android.material.appbar.AppBarLayout;
@@ -50,6 +51,8 @@
static final long DELAY_COLLAPSE_DURATION_MILLIS = 300L;
@VisibleForTesting
static final long DELAY_HIGHLIGHT_DURATION_MILLIS = 600L;
+ @VisibleForTesting
+ static final long DELAY_HIGHLIGHT_DURATION_MILLIS_A11Y = 300L;
private static final long HIGHLIGHT_DURATION = 15000L;
private static final long HIGHLIGHT_FADE_OUT_DURATION = 500L;
private static final long HIGHLIGHT_FADE_IN_DURATION = 200L;
@@ -59,6 +62,7 @@
@VisibleForTesting
boolean mFadeInAnimated;
+ private final Context mContext;
private final int mNormalBackgroundRes;
private final String mHighlightKey;
private boolean mHighlightRequested;
@@ -102,12 +106,12 @@
super(preferenceGroup);
mHighlightKey = key;
mHighlightRequested = highlightRequested;
- final Context context = preferenceGroup.getContext();
+ mContext = preferenceGroup.getContext();
final TypedValue outValue = new TypedValue();
- context.getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
+ mContext.getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
outValue, true /* resolveRefs */);
mNormalBackgroundRes = outValue.resourceId;
- mHighlightColor = context.getColor(R.color.preference_highlight_color);
+ mHighlightColor = mContext.getColor(R.color.preference_highlight_color);
}
@Override
@@ -121,12 +125,11 @@
View v = holder.itemView;
if (position == mHighlightPosition
&& (mHighlightKey != null
- && TextUtils.equals(mHighlightKey, getItem(position).getKey()))) {
+ && TextUtils.equals(mHighlightKey, getItem(position).getKey()))
+ && v.isShown()) {
// This position should be highlighted. If it's highlighted before - skip animation.
+ v.requestAccessibilityFocus();
addHighlightBackground(holder, !mFadeInAnimated);
- if (v != null) {
- v.requestAccessibilityFocus();
- }
} else if (Boolean.TRUE.equals(v.getTag(R.id.preference_highlighted))) {
// View with highlight is reused for a view that should not have highlight
removeHighlightBackground(holder, false /* animate */);
@@ -157,13 +160,14 @@
// Remove the animator as early as possible to avoid a RecyclerView crash.
recyclerView.setItemAnimator(null);
- // Scroll to correct position after 600 milliseconds.
+ // Scroll to correct position after a short delay.
root.postDelayed(() -> {
if (ensureHighlightPosition()) {
recyclerView.smoothScrollToPosition(mHighlightPosition);
highlightAndFocusTargetItem(recyclerView, mHighlightPosition);
}
- }, DELAY_HIGHLIGHT_DURATION_MILLIS);
+ }, AccessibilityUtil.isTouchExploreEnabled(mContext)
+ ? DELAY_HIGHLIGHT_DURATION_MILLIS_A11Y : DELAY_HIGHLIGHT_DURATION_MILLIS);
}
private void highlightAndFocusTargetItem(RecyclerView recyclerView, int highlightPosition) {
diff --git a/tests/robotests/src/com/android/settings/SettingsActivityTest.java b/tests/robotests/src/com/android/settings/SettingsActivityTest.java
index a879695..89f8449 100644
--- a/tests/robotests/src/com/android/settings/SettingsActivityTest.java
+++ b/tests/robotests/src/com/android/settings/SettingsActivityTest.java
@@ -27,7 +27,6 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.app.ActionBar;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
@@ -115,15 +114,6 @@
assertThat(((ListenerFragment) fragments.get(1)).mOnActivityResultCalled).isTrue();
}
- @Test
- public void getActionBar_hasNoActionBar() {
- final SettingsActivity activity = Robolectric.buildActivity(SettingsActivity.class).get();
-
- final ActionBar actionBar = activity.getActionBar();
-
- assertThat(actionBar).isNull();
- }
-
public static class ListenerFragment extends Fragment implements OnActivityResultListener {
private boolean mOnActivityResultCalled;
diff --git a/tests/robotests/src/com/android/settings/UtilsTest.java b/tests/robotests/src/com/android/settings/UtilsTest.java
index a4b0105..0c555da 100644
--- a/tests/robotests/src/com/android/settings/UtilsTest.java
+++ b/tests/robotests/src/com/android/settings/UtilsTest.java
@@ -30,6 +30,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.ActionBar;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyResourcesManager;
import android.content.ComponentName;
@@ -55,9 +56,11 @@
import android.os.storage.VolumeInfo;
import android.util.IconDrawableFactory;
import android.widget.EditText;
+import android.widget.ScrollView;
import android.widget.TextView;
import androidx.core.graphics.drawable.IconCompat;
+import androidx.fragment.app.FragmentActivity;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
@@ -68,6 +71,7 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
@@ -267,6 +271,17 @@
}
@Test
+ public void setActionBarShadowAnimation_shouldSetElevationToZero() {
+ final FragmentActivity activity = Robolectric.setupActivity(FragmentActivity.class);
+ final ActionBar actionBar = activity.getActionBar();
+
+ Utils.setActionBarShadowAnimation(activity, activity.getLifecycle(),
+ new ScrollView(mContext));
+
+ assertThat(actionBar.getElevation()).isEqualTo(0.f);
+ }
+
+ @Test
public void isSettingsIntelligence_IsSI_returnTrue() {
final String siPackageName = mContext.getString(
R.string.config_settingsintelligence_package_name);
diff --git a/tests/robotests/src/com/android/settings/accessibility/ContrastPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/ContrastPreferenceControllerTest.java
index 219f3d9..07c3b54 100644
--- a/tests/robotests/src/com/android/settings/accessibility/ContrastPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/ContrastPreferenceControllerTest.java
@@ -18,17 +18,11 @@
import static com.google.common.truth.Truth.assertThat;
-import android.content.Context;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.platform.test.flag.junit.SetFlagsRule;
-
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.core.BasePreferenceController;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
@@ -37,34 +31,19 @@
@RunWith(RobolectricTestRunner.class)
public class ContrastPreferenceControllerTest {
- @Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
- @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
private static final String PREFERENCE_KEY = "preference_key";
- private Context mContext;
private ContrastPreferenceController mController;
@Before
public void setUp() {
- mContext = ApplicationProvider.getApplicationContext();
- mController = new ContrastPreferenceController(mContext, PREFERENCE_KEY);
+ mController = new ContrastPreferenceController(ApplicationProvider.getApplicationContext(),
+ PREFERENCE_KEY);
}
@Test
- public void getAvailabilityStatus_flagsEnabled_shouldReturnAvailable() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_COLOR_CONTRAST_CONTROL);
-
+ public void getAvailabilityStatus_shouldReturnUnavailable() {
assertThat(mController.getAvailabilityStatus())
- .isEqualTo(BasePreferenceController.AVAILABLE);
- }
-
- @Test
- public void getAvailabilityStatus_flagsDisabled_shouldReturnUnsupported() {
- mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_COLOR_CONTRAST_CONTROL);
-
- assertThat(mController.getAvailabilityStatus())
- .isEqualTo(BasePreferenceController.UNSUPPORTED_ON_DEVICE);
+ .isEqualTo(BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
}
}
diff --git a/tests/robotests/src/com/android/settings/accessibility/PreviewSizeSeekBarControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/PreviewSizeSeekBarControllerTest.java
index af72beb..05273fc 100644
--- a/tests/robotests/src/com/android/settings/accessibility/PreviewSizeSeekBarControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/PreviewSizeSeekBarControllerTest.java
@@ -30,9 +30,6 @@
import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.view.LayoutInflater;
import android.widget.PopupWindow;
import android.widget.SeekBar;
@@ -73,9 +70,6 @@
public class PreviewSizeSeekBarControllerTest {
@Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
- @Rule
public ActivityScenarioRule<EmptyFragmentActivity> rule =
new ActivityScenarioRule<>(EmptyFragmentActivity.class);
private static final String FONT_SIZE_KEY = "font_size";
@@ -213,7 +207,6 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_REMOVE_QS_TOOLTIP_IN_SUW)
public void onProgressChanged_inSuw_toolTipShouldNotShown() {
Intent intent = mContext.getIntent();
intent.putExtra(WizardManagerHelper.EXTRA_IS_SETUP_FLOW, true);
diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java
index 3d0f223..af94723 100644
--- a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java
@@ -339,7 +339,6 @@
}
@Test
- @EnableFlags(com.android.settings.accessibility.Flags.FLAG_REMOVE_QS_TOOLTIP_IN_SUW)
@Config(shadows = ShadowFragment.class)
public void onPreferenceToggledOnEnabledService_inSuw_toolTipViewShouldNotShow() {
Intent suwIntent = new Intent();
diff --git a/tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockContentListenerTest.java b/tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockContentListenerTest.java
index 4da8151..8dd6199 100644
--- a/tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockContentListenerTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/activeunlock/ActiveUnlockContentListenerTest.java
@@ -19,6 +19,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import static org.robolectric.shadows.ShadowLooper.idleMainLooper;
@@ -44,8 +45,6 @@
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
-import java.util.ArrayList;
-
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowDeviceConfig.class})
public class ActiveUnlockContentListenerTest {
@@ -141,8 +140,7 @@
@Test
public void noProvider_subscribeDoesntRegisterObserver() {
- when(mPackageManager.getInstalledPackages(any()))
- .thenReturn(new ArrayList<>());
+ when(mPackageManager.resolveContentProvider(anyString(), any())).thenReturn(null);
OnContentChangedListener listener = new OnContentChangedListener() {
@Override
public void onContentChanged(String newValue) {}
diff --git a/tests/robotests/src/com/android/settings/testutils/ActiveUnlockTestUtils.java b/tests/robotests/src/com/android/settings/testutils/ActiveUnlockTestUtils.java
index 06ca05c..e932280 100644
--- a/tests/robotests/src/com/android/settings/testutils/ActiveUnlockTestUtils.java
+++ b/tests/robotests/src/com/android/settings/testutils/ActiveUnlockTestUtils.java
@@ -18,12 +18,12 @@
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
@@ -32,8 +32,6 @@
import com.android.settings.biometrics.activeunlock.ActiveUnlockStatusUtils;
-import java.util.ArrayList;
-
/** Utilities class to enable or disable the Active Unlock flag in tests. */
public final class ActiveUnlockTestUtils {
@@ -61,15 +59,10 @@
resolveInfo.activityInfo.applicationInfo = applicationInfo;
when(packageManager.resolveActivity(any(), anyInt())).thenReturn(resolveInfo);
- PackageInfo packageInfo = new PackageInfo();
- packageInfo.applicationInfo = applicationInfo;
ProviderInfo providerInfo = new ProviderInfo();
providerInfo.authority = PROVIDER;
providerInfo.applicationInfo = applicationInfo;
- packageInfo.providers = new ProviderInfo[] { providerInfo };
- ArrayList<PackageInfo> packageInfos = new ArrayList<>();
- packageInfos.add(packageInfo);
- when(packageManager.getInstalledPackages(any())).thenReturn(packageInfos);
+ when(packageManager.resolveContentProvider(anyString(), any())).thenReturn(providerInfo);
DeviceConfig.setProperty(
DeviceConfig.NAMESPACE_REMOTE_AUTH,
diff --git a/tests/robotests/src/com/android/settings/widget/HighlightablePreferenceGroupAdapterTest.java b/tests/robotests/src/com/android/settings/widget/HighlightablePreferenceGroupAdapterTest.java
index 29560ab..03684ad 100644
--- a/tests/robotests/src/com/android/settings/widget/HighlightablePreferenceGroupAdapterTest.java
+++ b/tests/robotests/src/com/android/settings/widget/HighlightablePreferenceGroupAdapterTest.java
@@ -33,6 +33,7 @@
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.view.View;
+import android.view.accessibility.AccessibilityManager;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
@@ -54,6 +55,8 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowAccessibilityManager;
import org.robolectric.util.ReflectionHelpers;
@RunWith(RobolectricTestRunner.class)
@@ -67,7 +70,7 @@
@Mock
private View mRoot;
@Mock
- private PreferenceCategory mPreferenceCatetory;
+ private PreferenceCategory mPreferenceCategory;
@Mock
private SettingsPreferenceFragment mFragment;
@@ -82,8 +85,8 @@
mContext = RuntimeEnvironment.application;
mPreference = new Preference(mContext);
mPreference.setKey(TEST_KEY);
- when(mPreferenceCatetory.getContext()).thenReturn(mContext);
- mAdapter = spy(new HighlightablePreferenceGroupAdapter(mPreferenceCatetory, TEST_KEY,
+ when(mPreferenceCategory.getContext()).thenReturn(mContext);
+ mAdapter = spy(new HighlightablePreferenceGroupAdapter(mPreferenceCategory, TEST_KEY,
false /* highlighted*/));
when(mAdapter.getItem(anyInt())).thenReturn(mPreference);
mViewHolder = PreferenceViewHolder.createInstanceForTests(
@@ -102,6 +105,18 @@
}
@Test
+ public void requestHighlight_enableTouchExploration_shouldHaveA11yHighlightDelay() {
+ ShadowAccessibilityManager am = Shadow.extract(AccessibilityManager.getInstance(mContext));
+ am.setTouchExplorationEnabled(true);
+ when(mAdapter.getPreferenceAdapterPosition(anyString())).thenReturn(1);
+ mAdapter.requestHighlight(mRoot, mock(RecyclerView.class), mock(AppBarLayout.class));
+
+ // DELAY_HIGHLIGHT_DURATION_MILLIS_A11Y = DELAY_COLLAPSE_DURATION_MILLIS
+ verify(mRoot, times(2)).postDelayed(any(),
+ eq(HighlightablePreferenceGroupAdapter.DELAY_HIGHLIGHT_DURATION_MILLIS_A11Y));
+ }
+
+ @Test
public void requestHighlight_noKey_highlightedBefore_noRecyclerView_shouldNotRequest() {
ReflectionHelpers.setField(mAdapter, "mHighlightKey", null);
ReflectionHelpers.setField(mAdapter, "mHighlightRequested", false);
@@ -178,12 +193,24 @@
assertThat(mViewHolder.itemView.getTag(R.id.preference_highlighted)).isNull();
}
+ @Test
+ public void updateBackground_itemViewIsInvisible_shouldNotSetHighlightedTag() {
+ ReflectionHelpers.setField(mAdapter, "mHighlightPosition", 10);
+ ReflectionHelpers.setField(mViewHolder, "itemView", spy(mViewHolder.itemView));
+ when(mViewHolder.itemView.isShown()).thenReturn(false);
+
+ mAdapter.updateBackground(mViewHolder, 0);
+
+ assertThat(mViewHolder.itemView.getTag(R.id.preference_highlighted)).isNull();
+ }
+
/**
* When background is being updated, we also request the a11y focus on the preference
*/
@Test
public void updateBackground_shouldRequestAccessibilityFocus() {
View viewItem = mock(View.class);
+ when(viewItem.isShown()).thenReturn(true);
mViewHolder = PreferenceViewHolder.createInstanceForTests(viewItem);
ReflectionHelpers.setField(mAdapter, "mHighlightPosition", 10);
@@ -195,6 +222,8 @@
@Test
public void updateBackground_highlight_shouldAnimateBackgroundAndSetHighlightedTag() {
ReflectionHelpers.setField(mAdapter, "mHighlightPosition", 10);
+ ReflectionHelpers.setField(mViewHolder, "itemView", spy(mViewHolder.itemView));
+ when(mViewHolder.itemView.isShown()).thenReturn(true);
assertThat(mAdapter.mFadeInAnimated).isFalse();
mAdapter.updateBackground(mViewHolder, 10);
@@ -206,9 +235,21 @@
}
@Test
+ public void updateBackground_highlight_itemViewIsInvisible_shouldNotAnimate() {
+ ReflectionHelpers.setField(mAdapter, "mHighlightPosition", 10);
+ ReflectionHelpers.setField(mViewHolder, "itemView", spy(mViewHolder.itemView));
+ when(mViewHolder.itemView.isShown()).thenReturn(false);
+
+ mAdapter.updateBackground(mViewHolder, 10);
+
+ assertThat(mAdapter.mFadeInAnimated).isFalse();
+ }
+
+ @Test
public void updateBackgroundTwice_highlight_shouldAnimateOnce() {
ReflectionHelpers.setField(mAdapter, "mHighlightPosition", 10);
ReflectionHelpers.setField(mViewHolder, "itemView", spy(mViewHolder.itemView));
+ when(mViewHolder.itemView.isShown()).thenReturn(true);
assertThat(mAdapter.mFadeInAnimated).isFalse();
mAdapter.updateBackground(mViewHolder, 10);
// mFadeInAnimated change from false to true - indicating background change is scheduled
diff --git a/tests/spa_unit/src/com/android/settings/datausage/lib/AppDataUsageDetailsRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/datausage/lib/AppDataUsageDetailsRepositoryTest.kt
index fda3dc9..8476a49 100644
--- a/tests/spa_unit/src/com/android/settings/datausage/lib/AppDataUsageDetailsRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/datausage/lib/AppDataUsageDetailsRepositoryTest.kt
@@ -125,6 +125,32 @@
)
}
+ @Test
+ fun queryDetailsForCycles_appWithZeroUsage_filtered(): Unit = runBlocking {
+ networkStatsRepository.stub {
+ on { queryBuckets(CYCLE1_END_TIME, CYCLE2_END_TIME) } doReturn listOf(
+ Bucket(
+ uid = UID,
+ bytes = 0L,
+ startTimeStamp = 0L,
+ endTimeStamp = 0L,
+ ),
+ )
+ }
+ val repository = AppDataUsageDetailsRepository(
+ context = context,
+ cycles = null,
+ template = template,
+ uids = listOf(UID),
+ networkCycleDataRepository = networkCycleDataRepository,
+ networkStatsRepository = networkStatsRepository,
+ )
+
+ val detailsForCycles = repository.queryDetailsForCycles()
+
+ assertThat(detailsForCycles).isEmpty()
+ }
+
private companion object {
const val CYCLE1_START_TIME = 1694444444000L
const val CYCLE1_END_TIME = 1695555555000L
diff --git a/tests/spa_unit/src/com/android/settings/network/apn/ApnTypesTest.kt b/tests/spa_unit/src/com/android/settings/network/apn/ApnTypesTest.kt
new file mode 100644
index 0000000..95471b0
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/apn/ApnTypesTest.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 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.network.apn
+
+import android.telephony.data.ApnSetting
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class ApnTypesTest {
+
+ @Test
+ fun getPreSelectedApnType_regular() {
+ val apnType = ApnTypes.getPreSelectedApnType(CustomizedConfig())
+
+ assertThat(apnType).isEqualTo("default,mms,supl,dun,hipri,fota,cbs,xcap")
+ }
+
+ @Test
+ fun getPreSelectedApnType_readOnlyApnTypes() {
+ val customizedConfig = CustomizedConfig(
+ readOnlyApnTypes = listOf(ApnSetting.TYPE_DUN_STRING),
+ )
+
+ val apnType = ApnTypes.getPreSelectedApnType(customizedConfig)
+
+ assertThat(apnType).isEqualTo("default,mms,supl,hipri,fota,cbs,xcap")
+ }
+}
diff --git a/tests/unit/src/com/android/settings/MainClearTest.kt b/tests/unit/src/com/android/settings/MainClearTest.kt
index 05f06df..bfaafdd 100644
--- a/tests/unit/src/com/android/settings/MainClearTest.kt
+++ b/tests/unit/src/com/android/settings/MainClearTest.kt
@@ -28,11 +28,13 @@
import com.android.settings.Settings.FactoryResetActivity
import com.android.settings.flags.Flags
import com.google.common.truth.Truth.assertThat
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
/** Test [MainClear]. */
+@Ignore
@RunWith(AndroidJUnit4::class)
class MainClearTest {
@get:Rule
diff --git a/tests/unit/src/com/android/settings/testutils/ActiveUnlockTestUtils.java b/tests/unit/src/com/android/settings/testutils/ActiveUnlockTestUtils.java
index 06ca05c..e932280 100644
--- a/tests/unit/src/com/android/settings/testutils/ActiveUnlockTestUtils.java
+++ b/tests/unit/src/com/android/settings/testutils/ActiveUnlockTestUtils.java
@@ -18,12 +18,12 @@
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
@@ -32,8 +32,6 @@
import com.android.settings.biometrics.activeunlock.ActiveUnlockStatusUtils;
-import java.util.ArrayList;
-
/** Utilities class to enable or disable the Active Unlock flag in tests. */
public final class ActiveUnlockTestUtils {
@@ -61,15 +59,10 @@
resolveInfo.activityInfo.applicationInfo = applicationInfo;
when(packageManager.resolveActivity(any(), anyInt())).thenReturn(resolveInfo);
- PackageInfo packageInfo = new PackageInfo();
- packageInfo.applicationInfo = applicationInfo;
ProviderInfo providerInfo = new ProviderInfo();
providerInfo.authority = PROVIDER;
providerInfo.applicationInfo = applicationInfo;
- packageInfo.providers = new ProviderInfo[] { providerInfo };
- ArrayList<PackageInfo> packageInfos = new ArrayList<>();
- packageInfos.add(packageInfo);
- when(packageManager.getInstalledPackages(any())).thenReturn(packageInfos);
+ when(packageManager.resolveContentProvider(anyString(), any())).thenReturn(providerInfo);
DeviceConfig.setProperty(
DeviceConfig.NAMESPACE_REMOTE_AUTH,