Merge "Password settings: Fix work profile" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
e6cff5497d
@@ -28,6 +28,7 @@
|
|||||||
android:persistent="false"
|
android:persistent="false"
|
||||||
android:title="@string/autofill_passwords"
|
android:title="@string/autofill_passwords"
|
||||||
settings:controller="com.android.settings.applications.autofill.PasswordsPreferenceController"
|
settings:controller="com.android.settings.applications.autofill.PasswordsPreferenceController"
|
||||||
|
settings:forWork="true"
|
||||||
settings:keywords="@string/autofill_keywords" />
|
settings:keywords="@string/autofill_keywords" />
|
||||||
|
|
||||||
<com.android.settings.widget.WorkOnlyCategory
|
<com.android.settings.widget.WorkOnlyCategory
|
||||||
|
@@ -55,6 +55,7 @@ import com.android.settings.core.BasePreferenceController;
|
|||||||
import com.android.settingslib.widget.AppPreference;
|
import com.android.settingslib.widget.AppPreference;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
@@ -76,28 +77,30 @@ public class PasswordsPreferenceController extends BasePreferenceController
|
|||||||
private LifecycleOwner mLifecycleOwner;
|
private LifecycleOwner mLifecycleOwner;
|
||||||
|
|
||||||
public PasswordsPreferenceController(Context context, String preferenceKey) {
|
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);
|
super(context, preferenceKey);
|
||||||
mPm = context.getPackageManager();
|
mPm = context.getPackageManager();
|
||||||
mIconFactory = IconDrawableFactory.newInstance(mContext);
|
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--) {
|
for (int i = availableServices.size() - 1; i >= 0; i--) {
|
||||||
final String passwordsActivity = availableServices.get(i).getPasswordsActivity();
|
final String passwordsActivity = availableServices.get(i).getPasswordsActivity();
|
||||||
if (TextUtils.isEmpty(passwordsActivity)) {
|
if (TextUtils.isEmpty(passwordsActivity)) {
|
||||||
availableServices.remove(i);
|
availableServices.remove(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mServices = availableServices;
|
// TODO: Reverse the loop above and add to mServices directly.
|
||||||
}
|
mServices.clear();
|
||||||
|
mServices.addAll(availableServices);
|
||||||
@OnLifecycleEvent(ON_CREATE)
|
|
||||||
void onCreate(LifecycleOwner lifecycleOwner) {
|
|
||||||
mLifecycleOwner = lifecycleOwner;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -109,8 +112,7 @@ public class PasswordsPreferenceController extends BasePreferenceController
|
|||||||
public void displayPreference(PreferenceScreen screen) {
|
public void displayPreference(PreferenceScreen screen) {
|
||||||
super.displayPreference(screen);
|
super.displayPreference(screen);
|
||||||
final PreferenceGroup group = screen.findPreference(getPreferenceKey());
|
final PreferenceGroup group = screen.findPreference(getPreferenceKey());
|
||||||
// TODO(b/169455298): Show work profile passwords too.
|
addPasswordPreferences(screen.getContext(), getUser(), group);
|
||||||
addPasswordPreferences(screen.getContext(), UserHandle.myUserId(), group);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addPasswordPreferences(
|
private void addPasswordPreferences(
|
||||||
@@ -126,9 +128,15 @@ public class PasswordsPreferenceController extends BasePreferenceController
|
|||||||
serviceInfo.applicationInfo,
|
serviceInfo.applicationInfo,
|
||||||
user);
|
user);
|
||||||
pref.setIcon(Utils.getSafeIcon(icon));
|
pref.setIcon(Utils.getSafeIcon(icon));
|
||||||
pref.setIntent(
|
pref.setOnPreferenceClickListener(p -> {
|
||||||
new Intent(Intent.ACTION_MAIN)
|
final Intent intent =
|
||||||
.setClassName(serviceInfo.packageName, service.getPasswordsActivity()));
|
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.
|
// Set an empty summary to avoid a UI flicker when the value loads.
|
||||||
pref.setSummary(R.string.summary_placeholder);
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -21,11 +21,19 @@ import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_U
|
|||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
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.mock;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
import android.os.UserHandle;
|
||||||
import android.service.autofill.AutofillServiceInfo;
|
import android.service.autofill.AutofillServiceInfo;
|
||||||
|
|
||||||
import androidx.lifecycle.Lifecycle;
|
import androidx.lifecycle.Lifecycle;
|
||||||
@@ -40,9 +48,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
|
|||||||
import com.google.android.collect.Lists;
|
import com.google.android.collect.Lists;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -56,7 +64,7 @@ public class PasswordsPreferenceControllerTest {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
mContext = ApplicationProvider.getApplicationContext();
|
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||||
if (Looper.myLooper() == null) {
|
if (Looper.myLooper() == null) {
|
||||||
Looper.prepare(); // needed to create the preference screen
|
Looper.prepare(); // needed to create the preference screen
|
||||||
}
|
}
|
||||||
@@ -66,6 +74,15 @@ public class PasswordsPreferenceControllerTest {
|
|||||||
mScreen.addPreference(mPasswordsPreferenceCategory);
|
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
|
@Test
|
||||||
public void getAvailabilityStatus_noServices_returnsUnavailable() {
|
public void getAvailabilityStatus_noServices_returnsUnavailable() {
|
||||||
PasswordsPreferenceController controller =
|
PasswordsPreferenceController controller =
|
||||||
@@ -105,21 +122,26 @@ public class PasswordsPreferenceControllerTest {
|
|||||||
assertThat(mPasswordsPreferenceCategory.getPreferenceCount()).isEqualTo(0);
|
assertThat(mPasswordsPreferenceCategory.getPreferenceCount()).isEqualTo(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore("TODO: Fix the test to handle the service binding.")
|
|
||||||
@Test
|
@Test
|
||||||
@UiThreadTest
|
@UiThreadTest
|
||||||
public void displayPreference_withPasswords_addsPreference() {
|
public void displayPreference_withPasswords_addsPreference() {
|
||||||
AutofillServiceInfo service = createServiceWithPasswords();
|
AutofillServiceInfo service = createServiceWithPasswords();
|
||||||
|
service.getServiceInfo().packageName = "";
|
||||||
|
service.getServiceInfo().name = "";
|
||||||
PasswordsPreferenceController controller =
|
PasswordsPreferenceController controller =
|
||||||
createControllerWithServices(Lists.newArrayList(service));
|
createControllerWithServices(Lists.newArrayList(service));
|
||||||
controller.onCreate(() -> mock(Lifecycle.class));
|
doReturn(false).when(mContext).bindServiceAsUser(any(), any(), anyInt(), any());
|
||||||
|
|
||||||
controller.displayPreference(mScreen);
|
controller.displayPreference(mScreen);
|
||||||
|
|
||||||
assertThat(mPasswordsPreferenceCategory.getPreferenceCount()).isEqualTo(1);
|
assertThat(mPasswordsPreferenceCategory.getPreferenceCount()).isEqualTo(1);
|
||||||
Preference pref = mPasswordsPreferenceCategory.getPreference(0);
|
Preference pref = mPasswordsPreferenceCategory.getPreference(0);
|
||||||
assertThat(pref.getIcon()).isNotNull();
|
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(
|
.isEqualTo(
|
||||||
new ComponentName(
|
new ComponentName(
|
||||||
service.getServiceInfo().packageName,
|
service.getServiceInfo().packageName,
|
||||||
@@ -128,8 +150,10 @@ public class PasswordsPreferenceControllerTest {
|
|||||||
|
|
||||||
private PasswordsPreferenceController createControllerWithServices(
|
private PasswordsPreferenceController createControllerWithServices(
|
||||||
List<AutofillServiceInfo> availableServices) {
|
List<AutofillServiceInfo> availableServices) {
|
||||||
return new PasswordsPreferenceController(
|
PasswordsPreferenceController controller =
|
||||||
mContext, mPasswordsPreferenceCategory.getKey(), availableServices);
|
new PasswordsPreferenceController(mContext, mPasswordsPreferenceCategory.getKey());
|
||||||
|
controller.init(() -> mock(Lifecycle.class), availableServices);
|
||||||
|
return controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
private AutofillServiceInfo createServiceWithPasswords() {
|
private AutofillServiceInfo createServiceWithPasswords() {
|
||||||
|
Reference in New Issue
Block a user