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.PackageManager;
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.credentials.CredentialManager;
import android.credentials.CredentialProviderInfo;
import android.credentials.SetEnabledProvidersException;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.OutcomeReceiver;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.IconDrawableFactory;
@@ -48,6 +51,7 @@ import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.OnLifecycleEvent;
import androidx.preference.Preference;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
@@ -69,6 +73,7 @@ import java.util.concurrent.Executor;
/** Queries available credential manager providers and adds preferences for them. */
public class CredentialManagerPreferenceController extends BasePreferenceController
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 ALTERNATE_INTENT = "android.settings.SYNC_SETTINGS";
private static final int MAX_SELECTABLE_PROVIDERS = 5;
@@ -84,6 +89,7 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
private @Nullable FragmentManager mFragmentManager = null;
private @Nullable Delegate mDelegate = null;
private @Nullable String mFlagOverrideForTest = null;
public CredentialManagerPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
@@ -236,12 +242,16 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
setAvailableServices(
lifecycleOwner,
mCredentialManager.getCredentialProviderServices(
getUser(), CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY));
getUser(), CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY),
null);
}
@VisibleForTesting
void setAvailableServices(
LifecycleOwner lifecycleOwner, List<CredentialProviderInfo> availableServices) {
LifecycleOwner lifecycleOwner,
List<CredentialProviderInfo> availableServices,
String flagOverrideForTest) {
mFlagOverrideForTest = flagOverrideForTest;
mServices.clear();
mServices.addAll(availableServices);
@@ -275,6 +285,65 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
PreferenceGroup group = screen.findPreference(getPreferenceKey());
Context context = screen.getContext();
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. */

View File

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