Merge "Add service link when no providers are present"

This commit is contained in:
Becca Hughes
2023-03-28 22:51:10 +00:00
committed by Android (Google) Code Review
2 changed files with 115 additions and 5 deletions

View File

@@ -29,13 +29,16 @@ import android.content.Intent;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo; import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.credentials.CredentialManager; import android.credentials.CredentialManager;
import android.credentials.CredentialProviderInfo; import android.credentials.CredentialProviderInfo;
import android.credentials.SetEnabledProvidersException; import android.credentials.SetEnabledProvidersException;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.OutcomeReceiver; import android.os.OutcomeReceiver;
import android.os.UserHandle; import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.provider.Settings; import android.provider.Settings;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.IconDrawableFactory; import android.util.IconDrawableFactory;
@@ -48,6 +51,7 @@ import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.OnLifecycleEvent; import androidx.lifecycle.OnLifecycleEvent;
import androidx.preference.Preference;
import androidx.preference.PreferenceGroup; import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference; import androidx.preference.SwitchPreference;
@@ -69,6 +73,7 @@ import java.util.concurrent.Executor;
/** Queries available credential manager providers and adds preferences for them. */ /** Queries available credential manager providers and adds preferences for them. */
public class CredentialManagerPreferenceController extends BasePreferenceController public class CredentialManagerPreferenceController extends BasePreferenceController
implements LifecycleObserver { implements LifecycleObserver {
public static final String ADD_SERVICE_DEVICE_CONFIG = "credential_manager_service_search_uri";
private static final String TAG = "CredentialManagerPreferenceController"; private static final String TAG = "CredentialManagerPreferenceController";
private static final String ALTERNATE_INTENT = "android.settings.SYNC_SETTINGS"; private static final String ALTERNATE_INTENT = "android.settings.SYNC_SETTINGS";
private static final int MAX_SELECTABLE_PROVIDERS = 5; private static final int MAX_SELECTABLE_PROVIDERS = 5;
@@ -84,6 +89,7 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
private @Nullable FragmentManager mFragmentManager = null; private @Nullable FragmentManager mFragmentManager = null;
private @Nullable Delegate mDelegate = null; private @Nullable Delegate mDelegate = null;
private @Nullable String mFlagOverrideForTest = null;
public CredentialManagerPreferenceController(Context context, String preferenceKey) { public CredentialManagerPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey); super(context, preferenceKey);
@@ -236,12 +242,16 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
setAvailableServices( setAvailableServices(
lifecycleOwner, lifecycleOwner,
mCredentialManager.getCredentialProviderServices( mCredentialManager.getCredentialProviderServices(
getUser(), CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY)); getUser(), CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY),
null);
} }
@VisibleForTesting @VisibleForTesting
void setAvailableServices( void setAvailableServices(
LifecycleOwner lifecycleOwner, List<CredentialProviderInfo> availableServices) { LifecycleOwner lifecycleOwner,
List<CredentialProviderInfo> availableServices,
String flagOverrideForTest) {
mFlagOverrideForTest = flagOverrideForTest;
mServices.clear(); mServices.clear();
mServices.addAll(availableServices); mServices.addAll(availableServices);
@@ -275,6 +285,65 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
PreferenceGroup group = screen.findPreference(getPreferenceKey()); PreferenceGroup group = screen.findPreference(getPreferenceKey());
Context context = screen.getContext(); Context context = screen.getContext();
mPrefs.putAll(buildPreferenceList(context, group)); mPrefs.putAll(buildPreferenceList(context, group));
// Add the "add service" button only when there are no providers.
if (mPrefs.isEmpty()) {
String searchUri = getAddServiceUri(context);
if (!TextUtils.isEmpty(searchUri)) {
group.addPreference(newAddServicePreference(searchUri, context));
}
}
}
/**
* Returns the "add service" URI to show the play store. It will first try and use the
* credential manager specific search URI and if that is null it will fallback to the autofill
* one.
*/
public @NonNull String getAddServiceUri(@NonNull Context context) {
// Check the credential manager gflag for a link.
String searchUri =
DeviceConfig.getString(
DeviceConfig.NAMESPACE_CREDENTIAL,
ADD_SERVICE_DEVICE_CONFIG,
mFlagOverrideForTest);
if (!TextUtils.isEmpty(searchUri)) {
return searchUri;
}
// If not fall back on autofill.
return Settings.Secure.getStringForUser(
context.getContentResolver(),
Settings.Secure.AUTOFILL_SERVICE_SEARCH_URI,
getUser());
}
/**
* Gets the preference that allows to add a new cred man service.
*
* @return the pref to be added
*/
@VisibleForTesting
public Preference newAddServicePreference(String searchUri, Context context) {
final Intent addNewServiceIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(searchUri));
final Preference preference = new Preference(context);
preference.setOnPreferenceClickListener(
p -> {
context.startActivityAsUser(addNewServiceIntent, UserHandle.of(getUser()));
return true;
});
preference.setTitle(R.string.print_menu_item_add_service);
preference.setOrder(Integer.MAX_VALUE - 1);
preference.setPersistent(false);
// Try to set the icon this should fail in a test environment but work
// in the actual app.
try {
preference.setIcon(R.drawable.ic_add_24dp);
} catch (Resources.NotFoundException e) {
Log.e(TAG, "Failed to find icon for add services link", e);
}
return preference;
} }
/** Aggregates the list of services and builds a list of UI prefs to show. */ /** Aggregates the list of services and builds a list of UI prefs to show. */

View File

@@ -33,9 +33,11 @@ import android.content.pm.ServiceInfo;
import android.credentials.CredentialProviderInfo; import android.credentials.CredentialProviderInfo;
import android.net.Uri; import android.net.Uri;
import android.os.Looper; import android.os.Looper;
import android.os.UserHandle;
import android.provider.Settings; import android.provider.Settings;
import androidx.lifecycle.Lifecycle; import androidx.lifecycle.Lifecycle;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceScreen;
@@ -120,11 +122,42 @@ public class CredentialManagerPreferenceControllerTest {
} }
@Test @Test
public void displayPreference_noServices_noPreferencesAdded() { public void displayPreference_noServices_noPreferencesAdded_useAutofillUri() {
Settings.Secure.putStringForUser(
mContext.getContentResolver(),
Settings.Secure.AUTOFILL_SERVICE_SEARCH_URI,
"test",
UserHandle.myUserId());
CredentialManagerPreferenceController controller = CredentialManagerPreferenceController controller =
createControllerWithServices(Collections.emptyList()); createControllerWithServices(Collections.emptyList());
controller.displayPreference(mScreen); controller.displayPreference(mScreen);
assertThat(mCredentialsPreferenceCategory.getPreferenceCount()).isEqualTo(0); assertThat(mCredentialsPreferenceCategory.getPreferenceCount()).isEqualTo(1);
Preference pref = mCredentialsPreferenceCategory.getPreference(0);
assertThat(pref.getTitle()).isEqualTo("Add service");
assertThat(controller.getAddServiceUri(mContext)).isEqualTo("test");
}
@Test
public void displayPreference_noServices_noPreferencesAdded_useCredManUri() {
Settings.Secure.putStringForUser(
mContext.getContentResolver(),
Settings.Secure.AUTOFILL_SERVICE_SEARCH_URI,
"test",
UserHandle.myUserId());
CredentialManagerPreferenceController controller =
createControllerWithServicesAndAddServiceOverride(
Collections.emptyList(), "credman");
controller.displayPreference(mScreen);
assertThat(mCredentialsPreferenceCategory.getPreferenceCount()).isEqualTo(1);
Preference pref = mCredentialsPreferenceCategory.getPreference(0);
assertThat(pref.getTitle()).isEqualTo("Add service");
assertThat(controller.getAddServiceUri(mContext)).isEqualTo("credman");
} }
@Test @Test
@@ -134,6 +167,9 @@ public class CredentialManagerPreferenceControllerTest {
controller.displayPreference(mScreen); controller.displayPreference(mScreen);
assertThat(controller.isConnected()).isFalse(); assertThat(controller.isConnected()).isFalse();
assertThat(mCredentialsPreferenceCategory.getPreferenceCount()).isEqualTo(1); assertThat(mCredentialsPreferenceCategory.getPreferenceCount()).isEqualTo(1);
Preference pref = mCredentialsPreferenceCategory.getPreference(0);
assertThat(pref.getTitle()).isNotEqualTo("Add account");
} }
@Test @Test
@@ -400,10 +436,15 @@ public class CredentialManagerPreferenceControllerTest {
private CredentialManagerPreferenceController createControllerWithServices( private CredentialManagerPreferenceController createControllerWithServices(
List<CredentialProviderInfo> availableServices) { List<CredentialProviderInfo> availableServices) {
return createControllerWithServicesAndAddServiceOverride(availableServices, null);
}
private CredentialManagerPreferenceController createControllerWithServicesAndAddServiceOverride(
List<CredentialProviderInfo> availableServices, String addServiceOverride) {
CredentialManagerPreferenceController controller = CredentialManagerPreferenceController controller =
new CredentialManagerPreferenceController( new CredentialManagerPreferenceController(
mContext, mCredentialsPreferenceCategory.getKey()); mContext, mCredentialsPreferenceCategory.getKey());
controller.setAvailableServices(() -> mock(Lifecycle.class), availableServices); controller.setAvailableServices(() -> mock(Lifecycle.class), availableServices, addServiceOverride);
controller.setDelegate(mDelegate); controller.setDelegate(mDelegate);
return controller; return controller;
} }