From d4ce558f3350c04a1a6542337c30502235e891de Mon Sep 17 00:00:00 2001 From: Ahaan Ugale Date: Wed, 30 Jun 2021 00:21:40 -0700 Subject: [PATCH] Password settings: Fix work profile Currently, the work profile password settings just point to the personal profile app. This change fixes this using the forWork metadata tag, that sets the work profile user in BasePreferenceController. Also fixes and re-enables the displayPreference_withPasswords_addsPreference test. Fix: 192417100 Test: manual - work profile link works, password count is accurate Test: atest PasswordsPreferenceControllerTest Change-Id: I6345b69b9c03ff13b8e2784e69dc0188abc436e3 --- res/xml/accounts_work_dashboard_settings.xml | 1 + .../PasswordsPreferenceController.java | 49 ++++++++++++------- .../PasswordsPreferenceControllerTest.java | 38 +++++++++++--- 3 files changed, 63 insertions(+), 25 deletions(-) diff --git a/res/xml/accounts_work_dashboard_settings.xml b/res/xml/accounts_work_dashboard_settings.xml index 1c4c6aa3f3d..32699338434 100644 --- a/res/xml/accounts_work_dashboard_settings.xml +++ b/res/xml/accounts_work_dashboard_settings.xml @@ -28,6 +28,7 @@ android:persistent="false" android:title="@string/autofill_passwords" settings:controller="com.android.settings.applications.autofill.PasswordsPreferenceController" + settings:forWork="true" settings:keywords="@string/autofill_keywords" /> availableServices) { super(context, preferenceKey); mPm = context.getPackageManager(); mIconFactory = IconDrawableFactory.newInstance(mContext); + mServices = new ArrayList<>(); + } + + @OnLifecycleEvent(ON_CREATE) + void onCreate(LifecycleOwner lifecycleOwner) { + init(lifecycleOwner, AutofillServiceInfo.getAvailableServices(mContext, getUser())); + } + + @VisibleForTesting + void init(LifecycleOwner lifecycleOwner, List availableServices) { + mLifecycleOwner = lifecycleOwner; + for (int i = availableServices.size() - 1; i >= 0; i--) { final String passwordsActivity = availableServices.get(i).getPasswordsActivity(); if (TextUtils.isEmpty(passwordsActivity)) { availableServices.remove(i); } } - mServices = availableServices; - } - - @OnLifecycleEvent(ON_CREATE) - void onCreate(LifecycleOwner lifecycleOwner) { - mLifecycleOwner = lifecycleOwner; + // TODO: Reverse the loop above and add to mServices directly. + mServices.clear(); + mServices.addAll(availableServices); } @Override @@ -109,8 +112,7 @@ public class PasswordsPreferenceController extends BasePreferenceController public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); final PreferenceGroup group = screen.findPreference(getPreferenceKey()); - // TODO(b/169455298): Show work profile passwords too. - addPasswordPreferences(screen.getContext(), UserHandle.myUserId(), group); + addPasswordPreferences(screen.getContext(), getUser(), group); } private void addPasswordPreferences( @@ -126,9 +128,15 @@ public class PasswordsPreferenceController extends BasePreferenceController serviceInfo.applicationInfo, user); pref.setIcon(Utils.getSafeIcon(icon)); - pref.setIntent( - new Intent(Intent.ACTION_MAIN) - .setClassName(serviceInfo.packageName, service.getPasswordsActivity())); + pref.setOnPreferenceClickListener(p -> { + final Intent intent = + new Intent(Intent.ACTION_MAIN) + .setClassName( + serviceInfo.packageName, + service.getPasswordsActivity()); + prefContext.startActivityAsUser(intent, UserHandle.of(user)); + return true; + }); // Set an empty summary to avoid a UI flicker when the value loads. pref.setSummary(R.string.summary_placeholder); @@ -213,4 +221,9 @@ public class PasswordsPreferenceController extends BasePreferenceController } } } + + private int getUser() { + UserHandle workUser = getWorkProfileUser(); + return workUser != null ? workUser.getIdentifier() : UserHandle.myUserId(); + } } diff --git a/tests/unit/src/com/android/settings/applications/autofill/PasswordsPreferenceControllerTest.java b/tests/unit/src/com/android/settings/applications/autofill/PasswordsPreferenceControllerTest.java index 25d989385d3..da860ecb539 100644 --- a/tests/unit/src/com/android/settings/applications/autofill/PasswordsPreferenceControllerTest.java +++ b/tests/unit/src/com/android/settings/applications/autofill/PasswordsPreferenceControllerTest.java @@ -21,11 +21,19 @@ import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_U import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.os.Looper; +import android.os.UserHandle; import android.service.autofill.AutofillServiceInfo; import androidx.lifecycle.Lifecycle; @@ -40,9 +48,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.collect.Lists; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import java.util.Collections; import java.util.List; @@ -56,7 +64,7 @@ public class PasswordsPreferenceControllerTest { @Before public void setUp() { - mContext = ApplicationProvider.getApplicationContext(); + mContext = spy(ApplicationProvider.getApplicationContext()); if (Looper.myLooper() == null) { Looper.prepare(); // needed to create the preference screen } @@ -66,6 +74,15 @@ public class PasswordsPreferenceControllerTest { mScreen.addPreference(mPasswordsPreferenceCategory); } + @Test + // Tests that getAvailabilityStatus() does not throw an exception if it's called before the + // Controller is initialized (this can happen during indexing). + public void getAvailabilityStatus_withoutInit_returnsUnavailable() { + PasswordsPreferenceController controller = + new PasswordsPreferenceController(mContext, mPasswordsPreferenceCategory.getKey()); + assertThat(controller.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE); + } + @Test public void getAvailabilityStatus_noServices_returnsUnavailable() { PasswordsPreferenceController controller = @@ -105,21 +122,26 @@ public class PasswordsPreferenceControllerTest { assertThat(mPasswordsPreferenceCategory.getPreferenceCount()).isEqualTo(0); } - @Ignore("TODO: Fix the test to handle the service binding.") @Test @UiThreadTest public void displayPreference_withPasswords_addsPreference() { AutofillServiceInfo service = createServiceWithPasswords(); + service.getServiceInfo().packageName = ""; + service.getServiceInfo().name = ""; PasswordsPreferenceController controller = createControllerWithServices(Lists.newArrayList(service)); - controller.onCreate(() -> mock(Lifecycle.class)); + doReturn(false).when(mContext).bindServiceAsUser(any(), any(), anyInt(), any()); controller.displayPreference(mScreen); assertThat(mPasswordsPreferenceCategory.getPreferenceCount()).isEqualTo(1); Preference pref = mPasswordsPreferenceCategory.getPreference(0); assertThat(pref.getIcon()).isNotNull(); - assertThat(pref.getIntent().getComponent()) + pref.performClick(); + ArgumentCaptor intentCaptor = ArgumentCaptor.forClass(Intent.class); + UserHandle user = mContext.getUser(); + verify(mContext).startActivityAsUser(intentCaptor.capture(), eq(user)); + assertThat(intentCaptor.getValue().getComponent()) .isEqualTo( new ComponentName( service.getServiceInfo().packageName, @@ -128,8 +150,10 @@ public class PasswordsPreferenceControllerTest { private PasswordsPreferenceController createControllerWithServices( List availableServices) { - return new PasswordsPreferenceController( - mContext, mPasswordsPreferenceCategory.getKey(), availableServices); + PasswordsPreferenceController controller = + new PasswordsPreferenceController(mContext, mPasswordsPreferenceCategory.getKey()); + controller.init(() -> mock(Lifecycle.class), availableServices); + return controller; } private AutofillServiceInfo createServiceWithPasswords() {