Merge "Fixes ContactDiscoveryDialogFragmentTest" into rvc-dev
diff --git a/src/com/android/settings/biometrics/BiometricEnrollActivity.java b/src/com/android/settings/biometrics/BiometricEnrollActivity.java
index 61c417c..fa01977 100644
--- a/src/com/android/settings/biometrics/BiometricEnrollActivity.java
+++ b/src/com/android/settings/biometrics/BiometricEnrollActivity.java
@@ -16,9 +16,12 @@
 
 package com.android.settings.biometrics;
 
+import android.app.admin.DevicePolicyManager;
 import android.app.settings.SettingsEnums;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.BiometricManager.Authenticators;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -30,6 +33,7 @@
 import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroduction;
 import com.android.settings.biometrics.fingerprint.SetupFingerprintEnrollIntroduction;
 import com.android.settings.core.InstrumentedActivity;
+import com.android.settings.password.ChooseLockGeneric;
 import com.android.settings.password.ChooseLockSettingsHelper;
 
 import com.google.android.setupcompat.util.WizardManagerHelper;
@@ -43,6 +47,7 @@
 public class BiometricEnrollActivity extends InstrumentedActivity {
 
     private static final String TAG = "BiometricEnrollActivity";
+    private static final int REQUEST_ENROLL = 1;
 
     public static final String EXTRA_SKIP_INTRO = "skip_intro";
 
@@ -52,14 +57,33 @@
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        Log.d(TAG, "Min strength: " + getIntent()
-                .getIntExtra(Settings.EXTRA_BIOMETRIC_MINIMUM_STRENGTH_REQUIRED, 0));
+        // Default behavior is to enroll BIOMETRIC_WEAK or above. See ACTION_BIOMETRIC_ENROLL.
+        final int authenticators = getIntent().getIntExtra(
+                Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, Authenticators.BIOMETRIC_WEAK);
 
+        Log.d(TAG, "Authenticators: " + authenticators);
+
+        final BiometricManager bm = getSystemService(BiometricManager.class);
         final PackageManager pm = getApplicationContext().getPackageManager();
         Intent intent = null;
 
-        // This logic may have to be modified on devices with multiple biometrics.
-        if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+        final int result = bm.canAuthenticate(authenticators);
+
+        if (result == BiometricManager.BIOMETRIC_SUCCESS
+                || result == BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE) {
+            Log.e(TAG, "Unexpected result: " + result);
+            setResult(RESULT_CANCELED);
+            finish();
+            return;
+        }
+
+        if (authenticators == BiometricManager.Authenticators.DEVICE_CREDENTIAL) {
+            // If only device credential was specified, ask the user to only set that up.
+            intent = new Intent(this, ChooseLockGeneric.class);
+            intent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY,
+                    DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
+        } else if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+            // This logic may have to be modified on devices with multiple biometrics.
             // ChooseLockGeneric can request to start fingerprint enroll bypassing the intro screen.
             if (getIntent().getBooleanExtra(EXTRA_SKIP_INTRO, false)
                     && this instanceof InternalActivity) {
@@ -72,8 +96,6 @@
         }
 
         if (intent != null) {
-            intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
-
             if (this instanceof InternalActivity) {
                 // Propagate challenge and user Id from ChooseLockGeneric.
                 final byte[] token = getIntent()
@@ -85,9 +107,20 @@
                 intent.putExtra(Intent.EXTRA_USER_ID, userId);
             }
 
-            startActivity(intent);
+            startActivityForResult(intent, REQUEST_ENROLL);
+        } else {
+            Log.e(TAG, "Intent was null, finishing with RESULT_CANCELED");
+            setResult(RESULT_CANCELED);
+            finish();
         }
-        finish();
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == REQUEST_ENROLL) {
+            setResult(RESULT_OK);
+            finish();
+        }
     }
 
     private Intent getFingerprintFindSensorIntent() {
diff --git a/src/com/android/settings/notification/history/HistoryLoader.java b/src/com/android/settings/notification/history/HistoryLoader.java
index 046b5ef..461bbc1 100644
--- a/src/com/android/settings/notification/history/HistoryLoader.java
+++ b/src/com/android/settings/notification/history/HistoryLoader.java
@@ -22,6 +22,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.os.UserHandle;
+import android.util.Slog;
 
 import com.android.settings.notification.NotificationBackend;
 import com.android.settingslib.utils.ThreadUtils;
@@ -33,6 +34,7 @@
 import java.util.Map;
 
 public class HistoryLoader {
+    private static final String TAG = "HistoryLoader";
     private final Context mContext;
     private final NotificationBackend mBackend;
     private final PackageManager mPm;
@@ -45,45 +47,48 @@
 
     public void load(OnHistoryLoaderListener listener) {
         ThreadUtils.postOnBackgroundThread(() -> {
-            Map<String, NotificationHistoryPackage> historicalNotifications = new HashMap<>();
-            NotificationHistory history =
-                    mBackend.getNotificationHistory(mContext.getPackageName());
+            try {
+                Map<String, NotificationHistoryPackage> historicalNotifications = new HashMap<>();
+                NotificationHistory history =
+                        mBackend.getNotificationHistory(mContext.getPackageName());
+                while (history.hasNextNotification()) {
+                    HistoricalNotification hn = history.getNextNotification();
 
-            while (history.hasNextNotification()) {
-                HistoricalNotification hn = history.getNextNotification();
-
-                String key = hn.getPackage() + "|" + hn.getUid();
-                NotificationHistoryPackage hnsForPackage = historicalNotifications.getOrDefault(
-                        key,
-                        new NotificationHistoryPackage(hn.getPackage(), hn.getUid()));
-                hnsForPackage.notifications.add(hn);
-                historicalNotifications.put(key, hnsForPackage);
-            }
-            List<NotificationHistoryPackage> packages =
-                    new ArrayList<>(historicalNotifications.values());
-            Collections.sort(packages,
-                    (o1, o2) -> -1 * Long.compare(o1.getMostRecent(), o2.getMostRecent()));
-            for (NotificationHistoryPackage nhp : packages) {
-                ApplicationInfo info;
-                try {
-                    info = mPm.getApplicationInfoAsUser(
-                            nhp.pkgName,
-                            PackageManager.MATCH_UNINSTALLED_PACKAGES
-                                    | PackageManager.MATCH_DISABLED_COMPONENTS
-                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
-                                    | PackageManager.MATCH_DIRECT_BOOT_AWARE,
-                            UserHandle.getUserId(nhp.uid));
-                    if (info != null) {
-                        nhp.label = String.valueOf(mPm.getApplicationLabel(info));
-                        nhp.icon = mPm.getUserBadgedIcon(mPm.getApplicationIcon(info),
-                                UserHandle.of(UserHandle.getUserId(nhp.uid)));
-                    }
-                } catch (PackageManager.NameNotFoundException e) {
-                    // app is gone, just show package name and generic icon
-                    nhp.icon = mPm.getDefaultActivityIcon();
+                    String key = hn.getPackage() + "|" + hn.getUid();
+                    NotificationHistoryPackage hnsForPackage = historicalNotifications.getOrDefault(
+                            key,
+                            new NotificationHistoryPackage(hn.getPackage(), hn.getUid()));
+                    hnsForPackage.notifications.add(hn);
+                    historicalNotifications.put(key, hnsForPackage);
                 }
+                List<NotificationHistoryPackage> packages =
+                        new ArrayList<>(historicalNotifications.values());
+                Collections.sort(packages,
+                        (o1, o2) -> -1 * Long.compare(o1.getMostRecent(), o2.getMostRecent()));
+                for (NotificationHistoryPackage nhp : packages) {
+                    ApplicationInfo info;
+                    try {
+                        info = mPm.getApplicationInfoAsUser(
+                                nhp.pkgName,
+                                PackageManager.MATCH_UNINSTALLED_PACKAGES
+                                        | PackageManager.MATCH_DISABLED_COMPONENTS
+                                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                                        | PackageManager.MATCH_DIRECT_BOOT_AWARE,
+                                UserHandle.getUserId(nhp.uid));
+                        if (info != null) {
+                            nhp.label = String.valueOf(mPm.getApplicationLabel(info));
+                            nhp.icon = mPm.getUserBadgedIcon(mPm.getApplicationIcon(info),
+                                    UserHandle.of(UserHandle.getUserId(nhp.uid)));
+                        }
+                    } catch (PackageManager.NameNotFoundException e) {
+                        // app is gone, just show package name and generic icon
+                        nhp.icon = mPm.getDefaultActivityIcon();
+                    }
+                }
+                ThreadUtils.postOnMainThread(() -> listener.onHistoryLoaded(packages));
+            } catch (Exception e) {
+                Slog.e(TAG, "Error loading history", e);
             }
-            ThreadUtils.postOnMainThread(() -> listener.onHistoryLoaded(packages));
         });
     }
 
diff --git a/src/com/android/settings/wifi/ConnectedWifiEntryPreference.java b/src/com/android/settings/wifi/ConnectedWifiEntryPreference.java
new file mode 100644
index 0000000..f79499a
--- /dev/null
+++ b/src/com/android/settings/wifi/ConnectedWifiEntryPreference.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.wifi;
+
+import android.content.Context;
+import android.view.View;
+
+import androidx.fragment.app.Fragment;
+import androidx.preference.PreferenceViewHolder;
+
+import com.android.settingslib.R;
+import com.android.settingslib.wifi.LongPressWifiEntryPreference;
+import com.android.wifitrackerlib.WifiEntry;
+
+/**
+ * An AP preference for the currently connected AP.
+ */
+public class ConnectedWifiEntryPreference extends LongPressWifiEntryPreference implements
+        View.OnClickListener {
+
+    private OnGearClickListener mOnGearClickListener;
+
+    public ConnectedWifiEntryPreference(Context context, WifiEntry wifiEntry, Fragment fragment) {
+        super(context, wifiEntry, fragment);
+        setWidgetLayoutResource(R.layout.preference_widget_gear_optional_background);
+    }
+
+    @Override
+    public void refresh() {
+        super.refresh();
+
+        if (getWifiEntry().canSignIn()) {
+            setSummary(R.string.wifi_tap_to_sign_in);
+        }
+    }
+
+    /**
+     * Set gear icon click callback listener.
+     */
+    public void setOnGearClickListener(OnGearClickListener l) {
+        mOnGearClickListener = l;
+        notifyChanged();
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+
+        final View gear = holder.findViewById(R.id.settings_button);
+        gear.setOnClickListener(this);
+
+        final boolean canSignIn = getWifiEntry().canSignIn();
+        holder.findViewById(R.id.settings_button_no_background).setVisibility(
+                canSignIn ? View.INVISIBLE : View.VISIBLE);
+        gear.setVisibility(canSignIn ? View.VISIBLE : View.INVISIBLE);
+        holder.findViewById(R.id.two_target_divider).setVisibility(
+                canSignIn ? View.VISIBLE : View.INVISIBLE);
+    }
+
+    @Override
+    public void onClick(View v) {
+        if (v.getId() == R.id.settings_button) {
+            if (mOnGearClickListener != null) {
+                mOnGearClickListener.onGearClick(this);
+            }
+        }
+    }
+
+    /**
+     * Gear Icon click callback interface.
+     */
+    public interface OnGearClickListener {
+        /**
+         * The callback triggered when gear icon is clicked.
+         */
+        void onGearClick(ConnectedWifiEntryPreference p);
+    }
+
+}
diff --git a/src/com/android/settings/wifi/WifiSettings2.java b/src/com/android/settings/wifi/WifiSettings2.java
index f88a4df..5152569 100644
--- a/src/com/android/settings/wifi/WifiSettings2.java
+++ b/src/com/android/settings/wifi/WifiSettings2.java
@@ -711,8 +711,8 @@
                     mConnectedWifiEntryPreferenceCategory.findPreference(connectedEntry.getKey());
             if (connectedPref == null || connectedPref.getWifiEntry() != connectedEntry) {
                 mConnectedWifiEntryPreferenceCategory.removeAll();
-                final LongPressWifiEntryPreference pref =
-                        createLongPressWifiEntryPreference(connectedEntry);
+                final ConnectedWifiEntryPreference pref =
+                        new ConnectedWifiEntryPreference(getPrefContext(), connectedEntry, this);
                 pref.setKey(connectedEntry.getKey());
                 pref.refresh();
                 mConnectedWifiEntryPreferenceCategory.addPreference(pref);
@@ -724,6 +724,9 @@
                     }
                     return true;
                 });
+                pref.setOnGearClickListener(preference -> {
+                    launchNetworkDetailsFragment(pref);
+                });
             }
         } else {
             mConnectedWifiEntryPreferenceCategory.removeAll();
diff --git a/tests/robotests/src/com/android/settings/wifi/ConnectedWifiEntryPreferenceTest.java b/tests/robotests/src/com/android/settings/wifi/ConnectedWifiEntryPreferenceTest.java
new file mode 100644
index 0000000..401fdcf
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wifi/ConnectedWifiEntryPreferenceTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.view.View;
+
+import com.android.settings.R;
+import com.android.wifitrackerlib.WifiEntry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class ConnectedWifiEntryPreferenceTest {
+
+    @Mock
+    private WifiEntry mWifiEntry;
+    @Mock
+    private View mView;
+    @Mock
+    private ConnectedWifiEntryPreference.OnGearClickListener mOnGearClickListener;
+    private Context mContext;
+    private ConnectedWifiEntryPreference mConnectedWifiEntryPreference;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = RuntimeEnvironment.application;
+        mConnectedWifiEntryPreference = new ConnectedWifiEntryPreference(mContext, mWifiEntry,
+                null /* fragment */);
+        mConnectedWifiEntryPreference.setOnGearClickListener(mOnGearClickListener);
+    }
+
+    @Test
+    public void testOnClick_gearClicked_listenerInvoked() {
+        when(mView.getId()).thenReturn(R.id.settings_button);
+
+        mConnectedWifiEntryPreference.onClick(mView);
+
+        verify(mOnGearClickListener).onGearClick(mConnectedWifiEntryPreference);
+    }
+
+    @Test
+    public void testOnClick_gearNotClicked_listenerNotInvoked() {
+        mConnectedWifiEntryPreference.onClick(mView);
+
+        verify(mOnGearClickListener, never()).onGearClick(mConnectedWifiEntryPreference);
+    }
+
+    @Test
+    public void testWidgetLayoutPreference() {
+        assertThat(mConnectedWifiEntryPreference.getWidgetLayoutResource())
+            .isEqualTo(R.layout.preference_widget_gear_optional_background);
+    }
+}