Merge "Disable the defulat animation of the RecyclerView to avoid UI jank." into rvc-dev
diff --git a/src/com/android/settings/biometrics/face/FaceSettings.java b/src/com/android/settings/biometrics/face/FaceSettings.java
index a493ae0..04df11a 100644
--- a/src/com/android/settings/biometrics/face/FaceSettings.java
+++ b/src/com/android/settings/biometrics/face/FaceSettings.java
@@ -95,9 +95,20 @@
     private final FaceSettingsEnrollButtonPreferenceController.Listener mEnrollListener = intent ->
             startActivityForResult(intent, ENROLL_REQUEST);
 
-    public static boolean isAvailable(Context context) {
+    /**
+     * @param context
+     * @return true if the Face hardware is detected.
+     */
+    public static boolean isFaceHardwareDetected(Context context) {
         FaceManager manager = Utils.getFaceManagerOrNull(context);
-        return manager != null && manager.isHardwareDetected();
+        boolean isHardwareDetected = false;
+        if (manager == null) {
+            Log.d(TAG, "FaceManager is null");
+        } else {
+            isHardwareDetected = manager.isHardwareDetected();
+            Log.d(TAG, "FaceManager is not null. Hardware detected: " + isHardwareDetected);
+        }
+        return manager != null && isHardwareDetected;
     }
 
     @Override
@@ -126,7 +137,7 @@
         super.onCreate(savedInstanceState);
 
         final Context context = getPrefContext();
-        if (!isAvailable(context)) {
+        if (!isFaceHardwareDetected(context)) {
             Log.w(TAG, "no faceManager, finish this");
             finish();
             return;
@@ -273,7 +284,7 @@
 
     @Override
     protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
-        if (!isAvailable(context)) {
+        if (!isFaceHardwareDetected(context)) {
             return null;
         }
         mControllers = buildPreferenceControllers(context, getSettingsLifecycle());
@@ -314,7 +325,7 @@
                 @Override
                 public List<AbstractPreferenceController> createPreferenceControllers(
                         Context context) {
-                    if (isAvailable(context)) {
+                    if (isFaceHardwareDetected(context)) {
                         return buildPreferenceControllers(context, null /* lifecycle */);
                     } else {
                         return null;
@@ -323,7 +334,7 @@
 
                 @Override
                 protected boolean isPageSearchEnabled(Context context) {
-                    if (isAvailable(context)) {
+                    if (isFaceHardwareDetected(context)) {
                         return hasEnrolledBiometrics(context);
                     }
 
@@ -333,7 +344,10 @@
                 @Override
                 public List<String> getNonIndexableKeys(Context context) {
                     final List<String> keys = super.getNonIndexableKeys(context);
-                    if (isAvailable(context)) {
+                    final boolean isFaceHardwareDetected = isFaceHardwareDetected(context);
+                    Log.d(TAG, "Get non indexable keys. isFaceHardwareDetected: "
+                            + isFaceHardwareDetected + ", size:" + keys.size());
+                    if (isFaceHardwareDetected) {
                         final boolean hasEnrolled = hasEnrolledBiometrics(context);
                         keys.add(hasEnrolled ? PREF_KEY_ENROLL_FACE_UNLOCK
                                 : PREF_KEY_DELETE_FACE_DATA);
diff --git a/src/com/android/settings/biometrics/face/FaceSettingsAppPreferenceController.java b/src/com/android/settings/biometrics/face/FaceSettingsAppPreferenceController.java
index a54171e..bde146b 100644
--- a/src/com/android/settings/biometrics/face/FaceSettingsAppPreferenceController.java
+++ b/src/com/android/settings/biometrics/face/FaceSettingsAppPreferenceController.java
@@ -22,10 +22,10 @@
 import android.hardware.face.FaceManager;
 import android.provider.Settings;
 
-import com.android.settings.Utils;
-
 import androidx.preference.Preference;
 
+import com.android.settings.Utils;
+
 /**
  * Preference controller for Face settings page controlling the ability to use
  * Face authentication in apps (through BiometricPrompt).
@@ -51,7 +51,7 @@
 
     @Override
     public boolean isChecked() {
-        if (!FaceSettings.isAvailable(mContext)) {
+        if (!FaceSettings.isFaceHardwareDetected(mContext)) {
             return false;
         }
         return Settings.Secure.getIntForUser(
@@ -67,7 +67,7 @@
     @Override
     public void updateState(Preference preference) {
         super.updateState(preference);
-        if (!FaceSettings.isAvailable(mContext)) {
+        if (!FaceSettings.isFaceHardwareDetected(mContext)) {
             preference.setEnabled(false);
         } else if (!mFaceManager.hasEnrolledTemplates(getUserId())) {
             preference.setEnabled(false);
diff --git a/src/com/android/settings/biometrics/face/FaceSettingsAttentionPreferenceController.java b/src/com/android/settings/biometrics/face/FaceSettingsAttentionPreferenceController.java
index e5fee75..200c0b9 100644
--- a/src/com/android/settings/biometrics/face/FaceSettingsAttentionPreferenceController.java
+++ b/src/com/android/settings/biometrics/face/FaceSettingsAttentionPreferenceController.java
@@ -93,7 +93,7 @@
 
     @Override
     public boolean isChecked() {
-        if (!FaceSettings.isAvailable(mContext)) {
+        if (!FaceSettings.isFaceHardwareDetected(mContext)) {
             return true;
         }
         // Set to disabled until we know the true value.
diff --git a/src/com/android/settings/biometrics/face/FaceSettingsConfirmPreferenceController.java b/src/com/android/settings/biometrics/face/FaceSettingsConfirmPreferenceController.java
index c65cd23..c515742 100644
--- a/src/com/android/settings/biometrics/face/FaceSettingsConfirmPreferenceController.java
+++ b/src/com/android/settings/biometrics/face/FaceSettingsConfirmPreferenceController.java
@@ -25,7 +25,6 @@
 import androidx.preference.Preference;
 
 import com.android.settings.Utils;
-import com.android.settings.core.TogglePreferenceController;
 
 /**
  * Preference controller giving the user an option to always require confirmation.
@@ -65,7 +64,7 @@
     @Override
     public void updateState(Preference preference) {
         super.updateState(preference);
-        if (!FaceSettings.isAvailable(mContext)) {
+        if (!FaceSettings.isFaceHardwareDetected(mContext)) {
             preference.setEnabled(false);
         } else if (!mFaceManager.hasEnrolledTemplates(getUserId())) {
             preference.setEnabled(false);
diff --git a/src/com/android/settings/biometrics/face/FaceSettingsKeyguardPreferenceController.java b/src/com/android/settings/biometrics/face/FaceSettingsKeyguardPreferenceController.java
index 7e7a748..8ee7ffd 100644
--- a/src/com/android/settings/biometrics/face/FaceSettingsKeyguardPreferenceController.java
+++ b/src/com/android/settings/biometrics/face/FaceSettingsKeyguardPreferenceController.java
@@ -53,7 +53,7 @@
 
     @Override
     public boolean isChecked() {
-        if (!FaceSettings.isAvailable(mContext)) {
+        if (!FaceSettings.isFaceHardwareDetected(mContext)) {
             return false;
         } else if (getRestrictingAdmin() != null) {
             return false;
@@ -77,7 +77,7 @@
     public void updateState(Preference preference) {
         EnforcedAdmin admin;
         super.updateState(preference);
-        if (!FaceSettings.isAvailable(mContext)) {
+        if (!FaceSettings.isFaceHardwareDetected(mContext)) {
             preference.setEnabled(false);
         } else if ((admin = getRestrictingAdmin()) != null) {
             ((RestrictedSwitchPreference) preference).setDisabledByAdmin(admin);
diff --git a/src/com/android/settings/biometrics/face/FaceSettingsLockscreenBypassPreferenceController.java b/src/com/android/settings/biometrics/face/FaceSettingsLockscreenBypassPreferenceController.java
index 5c46a3d..0a2757b 100644
--- a/src/com/android/settings/biometrics/face/FaceSettingsLockscreenBypassPreferenceController.java
+++ b/src/com/android/settings/biometrics/face/FaceSettingsLockscreenBypassPreferenceController.java
@@ -47,7 +47,7 @@
 
     @Override
     public boolean isChecked() {
-        if (!FaceSettings.isAvailable(mContext)) {
+        if (!FaceSettings.isFaceHardwareDetected(mContext)) {
             return false;
         } else if (getRestrictingAdmin() != null) {
             return false;
@@ -69,7 +69,7 @@
     public void updateState(Preference preference) {
         EnforcedAdmin admin;
         super.updateState(preference);
-        if (!FaceSettings.isAvailable(mContext)) {
+        if (!FaceSettings.isFaceHardwareDetected(mContext)) {
             preference.setEnabled(false);
         } else if ((admin = getRestrictingAdmin()) != null) {
             ((RestrictedSwitchPreference) preference).setDisabledByAdmin(admin);
diff --git a/src/com/android/settings/biometrics/face/FaceSettingsRemoveButtonPreferenceController.java b/src/com/android/settings/biometrics/face/FaceSettingsRemoveButtonPreferenceController.java
index a4da759..6c98060 100644
--- a/src/com/android/settings/biometrics/face/FaceSettingsRemoveButtonPreferenceController.java
+++ b/src/com/android/settings/biometrics/face/FaceSettingsRemoveButtonPreferenceController.java
@@ -165,7 +165,7 @@
                 .findViewById(R.id.security_settings_face_settings_remove_button);
         mButton.setOnClickListener(this);
 
-        if (!FaceSettings.isAvailable(mContext)) {
+        if (!FaceSettings.isFaceHardwareDetected(mContext)) {
             mButton.setEnabled(false);
         } else {
             mButton.setEnabled(!mRemoving);
diff --git a/src/com/android/settings/biometrics/face/FaceStatusPreferenceController.java b/src/com/android/settings/biometrics/face/FaceStatusPreferenceController.java
index cb82b5e..648da9b 100644
--- a/src/com/android/settings/biometrics/face/FaceStatusPreferenceController.java
+++ b/src/com/android/settings/biometrics/face/FaceStatusPreferenceController.java
@@ -41,7 +41,7 @@
 
     @Override
     protected boolean isDeviceSupported() {
-        return mFaceManager != null && mFaceManager.isHardwareDetected();
+        return FaceSettings.isFaceHardwareDetected(mContext);
     }
 
     @Override
diff --git a/src/com/android/settings/gestures/AssistGestureSettingsPreferenceController.java b/src/com/android/settings/gestures/AssistGestureSettingsPreferenceController.java
index 8e7031d..49a9de6 100644
--- a/src/com/android/settings/gestures/AssistGestureSettingsPreferenceController.java
+++ b/src/com/android/settings/gestures/AssistGestureSettingsPreferenceController.java
@@ -21,6 +21,7 @@
 
 import android.content.Context;
 import android.provider.Settings;
+import android.util.Log;
 
 import androidx.annotation.VisibleForTesting;
 import androidx.preference.Preference;
@@ -31,6 +32,7 @@
 
 public class AssistGestureSettingsPreferenceController extends GesturePreferenceController {
 
+    private static final String TAG = "AssistGesture";
     private static final String PREF_KEY_VIDEO = "gesture_assist_video";
 
     private static final String SECURE_KEY_ASSIST = ASSIST_GESTURE_ENABLED;
@@ -55,8 +57,11 @@
 
     @Override
     public int getAvailabilityStatus() {
-        final boolean isAvailable = mAssistOnly ? mFeatureProvider.isSupported(mContext)
-                : mFeatureProvider.isSensorAvailable(mContext);
+        final boolean isSupported = mFeatureProvider.isSupported(mContext);
+        final boolean isSensorAvailable = mFeatureProvider.isSensorAvailable(mContext);
+        final boolean isAvailable = mAssistOnly ? isSupported : isSensorAvailable;
+        Log.d(TAG, "mAssistOnly:" + mAssistOnly + ", isSupported:" + isSupported
+                + ", isSensorAvailable:" + isSensorAvailable);
         return isAvailable ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java b/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java
index 4d1a1d4..d30fe59 100644
--- a/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardLoader.java
@@ -38,14 +38,14 @@
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.utils.AsyncLoaderCompat;
-import com.android.settingslib.utils.ThreadUtils;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
+import java.util.stream.Collectors;
 
 public class ContextualCardLoader extends AsyncLoaderCompat<List<ContextualCard>> {
 
@@ -56,7 +56,7 @@
     static final int CARD_CONTENT_LOADER_ID = 1;
 
     private static final String TAG = "ContextualCardLoader";
-    private static final long ELIGIBILITY_CHECKER_TIMEOUT_MS = 300;
+    private static final long ELIGIBILITY_CHECKER_TIMEOUT_MS = 400;
 
     private final ContentObserver mObserver = new ContentObserver(
             new Handler(Looper.getMainLooper())) {
@@ -184,23 +184,37 @@
 
     @VisibleForTesting
     List<ContextualCard> filterEligibleCards(List<ContextualCard> candidates) {
+        final ExecutorService executor = Executors.newFixedThreadPool(candidates.size());
         final List<ContextualCard> cards = new ArrayList<>();
-        final List<Future<ContextualCard>> eligibleCards = new ArrayList<>();
+        List<Future<ContextualCard>> eligibleCards = new ArrayList<>();
 
-        for (ContextualCard card : candidates) {
-            final EligibleCardChecker checker = new EligibleCardChecker(mContext, card);
-            eligibleCards.add(ThreadUtils.postOnBackgroundThread(checker));
+        final List<EligibleCardChecker> checkers = candidates.stream()
+                .map(card -> new EligibleCardChecker(mContext, card))
+                .collect(Collectors.toList());
+        try {
+            eligibleCards = executor.invokeAll(checkers, ELIGIBILITY_CHECKER_TIMEOUT_MS,
+                    TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            Log.w(TAG, "Failed to get eligible states for all cards", e);
         }
+        executor.shutdown();
+
         // Collect future and eligible cards
-        for (Future<ContextualCard> cardFuture : eligibleCards) {
+        for (int i = 0; i < eligibleCards.size(); i++) {
+            final Future<ContextualCard> cardFuture = eligibleCards.get(i);
+            if (cardFuture.isCancelled()) {
+                Log.w(TAG, "Timeout getting eligible state for card: "
+                        + candidates.get(i).getSliceUri());
+                continue;
+            }
+
             try {
-                final ContextualCard card = cardFuture.get(ELIGIBILITY_CHECKER_TIMEOUT_MS,
-                        TimeUnit.MILLISECONDS);
+                final ContextualCard card = cardFuture.get();
                 if (card != null) {
                     cards.add(card);
                 }
-            } catch (ExecutionException | InterruptedException | TimeoutException e) {
-                Log.w(TAG, "Failed to get eligible state for card: " + e.toString());
+            } catch (Exception e) {
+                Log.w(TAG, "Failed to get eligible state for card", e);
             }
         }
         return cards;
diff --git a/src/com/android/settings/homepage/contextualcards/EligibleCardChecker.java b/src/com/android/settings/homepage/contextualcards/EligibleCardChecker.java
index 4340330..46b4c86 100644
--- a/src/com/android/settings/homepage/contextualcards/EligibleCardChecker.java
+++ b/src/com/android/settings/homepage/contextualcards/EligibleCardChecker.java
@@ -22,6 +22,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.net.Uri;
+import android.os.AsyncTask;
 import android.util.Log;
 
 import androidx.annotation.VisibleForTesting;
@@ -32,6 +33,7 @@
 
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+import com.android.settingslib.utils.ThreadUtils;
 
 import java.util.List;
 import java.util.concurrent.Callable;
@@ -115,7 +117,12 @@
         // Register a trivial callback to pin the slice
         manager.registerSliceCallback(uri, callback);
         final Slice slice = manager.bindSlice(uri);
-        manager.unregisterSliceCallback(uri, callback);
+
+        // Workaround of unpinning slice in the same SerialExecutor of AsyncTask as SliceCallback's
+        // observer.
+        ThreadUtils.postOnMainThread(() ->
+                AsyncTask.execute(() -> manager.unregisterSliceCallback(uri, callback))
+        );
 
         return slice;
     }
diff --git a/src/com/android/settings/security/trustagent/TrustAgentListPreferenceController.java b/src/com/android/settings/security/trustagent/TrustAgentListPreferenceController.java
index 6067b77..19e25fc 100644
--- a/src/com/android/settings/security/trustagent/TrustAgentListPreferenceController.java
+++ b/src/com/android/settings/security/trustagent/TrustAgentListPreferenceController.java
@@ -23,7 +23,6 @@
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.UserHandle;
-import android.text.TextUtils;
 
 import androidx.annotation.VisibleForTesting;
 import androidx.preference.Preference;
@@ -46,6 +45,7 @@
 import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
 import com.android.settingslib.search.SearchIndexableRaw;
 
+import java.util.ArrayList;
 import java.util.List;
 
 public class TrustAgentListPreferenceController extends AbstractPreferenceController
@@ -66,6 +66,9 @@
     private Intent mTrustAgentClickIntent;
     private PreferenceCategory mSecurityCategory;
 
+    @VisibleForTesting
+    final List<String> mTrustAgentsKeyList;
+
     public TrustAgentListPreferenceController(Context context, SecuritySettings host,
             Lifecycle lifecycle) {
         super(context);
@@ -74,6 +77,7 @@
         mHost = host;
         mLockPatternUtils = provider.getLockPatternUtils(context);
         mTrustAgentManager = provider.getTrustAgentManager();
+        mTrustAgentsKeyList = new ArrayList();
         if (lifecycle != null) {
             lifecycle.addObserver(this);
         }
@@ -113,7 +117,7 @@
 
     @Override
     public boolean handlePreferenceTreeClick(Preference preference) {
-        if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) {
+        if (!mTrustAgentsKeyList.contains(preference.getKey())) {
             return super.handlePreferenceTreeClick(preference);
         }
         final ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper(
@@ -189,6 +193,7 @@
                 mSecurityCategory.removePreference(oldAgent);
             }
         }
+        mTrustAgentsKeyList.clear();
 
         // Then add new ones.
         final boolean hasSecurity = mLockPatternUtils.isSecure(MY_USER_ID);
@@ -196,6 +201,7 @@
             final RestrictedPreference trustAgentPreference =
                     new RestrictedPreference(mSecurityCategory.getContext());
             TrustAgentManager.TrustAgentComponentInfo agent = agents.get(i);
+            mTrustAgentsKeyList.add(PREF_KEY_TRUST_AGENT + i);
             trustAgentPreference.setKey(PREF_KEY_TRUST_AGENT + i);
             trustAgentPreference.setTitle(agent.title);
             trustAgentPreference.setSummary(agent.summary);
diff --git a/src/com/android/settings/slices/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java
index 75061a5..c22d001 100644
--- a/src/com/android/settings/slices/SettingsSliceProvider.java
+++ b/src/com/android/settings/slices/SettingsSliceProvider.java
@@ -143,7 +143,7 @@
     @VisibleForTesting
     final Map<Uri, SliceBackgroundWorker> mPinnedWorkers = new ArrayMap<>();
 
-    private boolean mNightMode;
+    private Boolean mNightMode;
 
     public SettingsSliceProvider() {
         super(READ_SEARCH_INDEXABLES);
@@ -153,8 +153,6 @@
     public boolean onCreateSliceProvider() {
         mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(getContext());
         mSliceWeakDataCache = new WeakHashMap<>();
-        mNightMode = Utils.isNightMode(getContext());
-        getContext().setTheme(R.style.Theme_SettingsBase);
         return true;
     }
 
@@ -207,7 +205,10 @@
             }
 
             final boolean nightMode = Utils.isNightMode(getContext());
-            if (mNightMode != nightMode) {
+            if (mNightMode == null) {
+                mNightMode = nightMode;
+                getContext().setTheme(R.style.Theme_SettingsBase);
+            } else if (mNightMode != nightMode) {
                 Log.d(TAG, "Night mode changed, reload theme");
                 mNightMode = nightMode;
                 getContext().getTheme().rebase();
diff --git a/tests/robotests/src/com/android/settings/security/trustagent/TrustAgentListPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/security/trustagent/TrustAgentListPreferenceControllerTest.java
index c0fbff7..0463e00 100644
--- a/tests/robotests/src/com/android/settings/security/trustagent/TrustAgentListPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/security/trustagent/TrustAgentListPreferenceControllerTest.java
@@ -16,10 +16,8 @@
 
 package com.android.settings.security.trustagent;
 
-import static com.android.settings.security.trustagent.TrustAgentListPreferenceController
-        .PREF_KEY_SECURITY_CATEGORY;
-import static com.android.settings.security.trustagent.TrustAgentListPreferenceController
-        .PREF_KEY_TRUST_AGENT;
+import static com.android.settings.security.trustagent.TrustAgentListPreferenceController.PREF_KEY_SECURITY_CATEGORY;
+import static com.android.settings.security.trustagent.TrustAgentListPreferenceController.PREF_KEY_TRUST_AGENT;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -173,6 +171,26 @@
     }
 
     @Test
+    public void onResume_controllerShouldHasKey() {
+        final List<TrustAgentManager.TrustAgentComponentInfo> agents = new ArrayList<>();
+        final TrustAgentManager.TrustAgentComponentInfo agent =
+                mock(TrustAgentManager.TrustAgentComponentInfo.class);
+        agent.title = "Test_title";
+        agent.summary = "test summary";
+        agent.componentName = new ComponentName("pkg", "agent");
+        agent.admin = null;
+        agents.add(agent);
+        when(mTrustAgentManager.getActiveTrustAgents(mActivity, mLockPatternUtils))
+                .thenReturn(agents);
+        final String key = PREF_KEY_TRUST_AGENT + 0;
+
+        mController.displayPreference(mScreen);
+        mController.onResume();
+
+        assertThat(mController.mTrustAgentsKeyList).containsExactly(key);
+    }
+
+    @Test
     public void updateDynamicRawDataToIndex_shouldIndexAgents() {
         final List<TrustAgentManager.TrustAgentComponentInfo> agents = new ArrayList<>();
         final TrustAgentManager.TrustAgentComponentInfo agent =
@@ -190,5 +208,4 @@
 
         assertThat(indexRaws).hasSize(1);
     }
-
 }