Loading res/xml/accounts_work_dashboard_settings.xml +1 −0 Original line number Diff line number Diff line Loading @@ -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" /> <com.android.settings.widget.WorkOnlyCategory Loading src/com/android/settings/applications/autofill/PasswordsPreferenceController.java +31 −18 Original line number Diff line number Diff line Loading @@ -55,6 +55,7 @@ import com.android.settings.core.BasePreferenceController; import com.android.settingslib.widget.AppPreference; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; Loading @@ -76,28 +77,30 @@ public class PasswordsPreferenceController extends BasePreferenceController private LifecycleOwner mLifecycleOwner; public PasswordsPreferenceController(Context context, String preferenceKey) { this(context, preferenceKey, AutofillServiceInfo.getAvailableServices(context, UserHandle.myUserId())); } @VisibleForTesting public PasswordsPreferenceController( Context context, String preferenceKey, List<AutofillServiceInfo> 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<AutofillServiceInfo> 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 Loading @@ -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( Loading @@ -126,9 +128,15 @@ public class PasswordsPreferenceController extends BasePreferenceController serviceInfo.applicationInfo, user); pref.setIcon(Utils.getSafeIcon(icon)); pref.setIntent( pref.setOnPreferenceClickListener(p -> { final Intent intent = new Intent(Intent.ACTION_MAIN) .setClassName(serviceInfo.packageName, service.getPasswordsActivity())); .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); Loading Loading @@ -213,4 +221,9 @@ public class PasswordsPreferenceController extends BasePreferenceController } } } private int getUser() { UserHandle workUser = getWorkProfileUser(); return workUser != null ? workUser.getIdentifier() : UserHandle.myUserId(); } } tests/unit/src/com/android/settings/applications/autofill/PasswordsPreferenceControllerTest.java +31 −7 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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 } Loading @@ -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 = Loading Loading @@ -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<Intent> 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, Loading @@ -128,8 +150,10 @@ public class PasswordsPreferenceControllerTest { private PasswordsPreferenceController createControllerWithServices( List<AutofillServiceInfo> 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() { Loading Loading
res/xml/accounts_work_dashboard_settings.xml +1 −0 Original line number Diff line number Diff line Loading @@ -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" /> <com.android.settings.widget.WorkOnlyCategory Loading
src/com/android/settings/applications/autofill/PasswordsPreferenceController.java +31 −18 Original line number Diff line number Diff line Loading @@ -55,6 +55,7 @@ import com.android.settings.core.BasePreferenceController; import com.android.settingslib.widget.AppPreference; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; Loading @@ -76,28 +77,30 @@ public class PasswordsPreferenceController extends BasePreferenceController private LifecycleOwner mLifecycleOwner; public PasswordsPreferenceController(Context context, String preferenceKey) { this(context, preferenceKey, AutofillServiceInfo.getAvailableServices(context, UserHandle.myUserId())); } @VisibleForTesting public PasswordsPreferenceController( Context context, String preferenceKey, List<AutofillServiceInfo> 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<AutofillServiceInfo> 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 Loading @@ -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( Loading @@ -126,9 +128,15 @@ public class PasswordsPreferenceController extends BasePreferenceController serviceInfo.applicationInfo, user); pref.setIcon(Utils.getSafeIcon(icon)); pref.setIntent( pref.setOnPreferenceClickListener(p -> { final Intent intent = new Intent(Intent.ACTION_MAIN) .setClassName(serviceInfo.packageName, service.getPasswordsActivity())); .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); Loading Loading @@ -213,4 +221,9 @@ public class PasswordsPreferenceController extends BasePreferenceController } } } private int getUser() { UserHandle workUser = getWorkProfileUser(); return workUser != null ? workUser.getIdentifier() : UserHandle.myUserId(); } }
tests/unit/src/com/android/settings/applications/autofill/PasswordsPreferenceControllerTest.java +31 −7 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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 } Loading @@ -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 = Loading Loading @@ -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<Intent> 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, Loading @@ -128,8 +150,10 @@ public class PasswordsPreferenceControllerTest { private PasswordsPreferenceController createControllerWithServices( List<AutofillServiceInfo> 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() { Loading