Add combined provider class that manages new settings UI
Example of how to use: https://paste.googleplex.com/6523798525313024 Test: make Bug: 278919696 Change-Id: I934a5f6d02b50f8c97bda6d997902a42ff88e26f
This commit is contained in:
@@ -0,0 +1,262 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.applications.credentials;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.pm.ServiceInfo;
|
||||||
|
import android.credentials.CredentialProviderInfo;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.service.autofill.AutofillServiceInfo;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds combined autofill and credential manager data grouped by package name. Contains backing
|
||||||
|
* logic for each row in settings.
|
||||||
|
*/
|
||||||
|
public final class CombinedProviderInfo {
|
||||||
|
private final List<CredentialProviderInfo> mCredentialProviderInfos;
|
||||||
|
private final @Nullable AutofillServiceInfo mAutofillServiceInfo;
|
||||||
|
private final boolean mIsDefaultAutofillProvider;
|
||||||
|
private final boolean mIsDefaultCredmanProvider;
|
||||||
|
|
||||||
|
/** Constructs an information instance from both autofill and credential provider. */
|
||||||
|
public CombinedProviderInfo(
|
||||||
|
@Nullable List<CredentialProviderInfo> cpis,
|
||||||
|
@Nullable AutofillServiceInfo asi,
|
||||||
|
boolean isDefaultAutofillProvider,
|
||||||
|
boolean isDefaultCredmanProvider) {
|
||||||
|
mCredentialProviderInfos = new ArrayList<>(cpis);
|
||||||
|
mAutofillServiceInfo = asi;
|
||||||
|
mIsDefaultAutofillProvider = isDefaultAutofillProvider;
|
||||||
|
mIsDefaultCredmanProvider = isDefaultCredmanProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the credential provider info. */
|
||||||
|
@Nullable
|
||||||
|
public List<CredentialProviderInfo> getCredentialProviderInfos() {
|
||||||
|
return mCredentialProviderInfos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the autofill provider info. */
|
||||||
|
@Nullable
|
||||||
|
public AutofillServiceInfo getAutofillServiceInfo() {
|
||||||
|
return mAutofillServiceInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the application info. */
|
||||||
|
public @Nullable ApplicationInfo getApplicationInfo() {
|
||||||
|
if (!mCredentialProviderInfos.isEmpty()) {
|
||||||
|
return mCredentialProviderInfos.get(0).getServiceInfo().applicationInfo;
|
||||||
|
}
|
||||||
|
return mAutofillServiceInfo.getServiceInfo().applicationInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the app icon. */
|
||||||
|
@Nullable
|
||||||
|
public Drawable getAppIcon(@NonNull Context context) {
|
||||||
|
Drawable icon = null;
|
||||||
|
ServiceInfo brandingService = getBrandingService();
|
||||||
|
if (brandingService != null) {
|
||||||
|
icon = brandingService.loadIcon(context.getPackageManager());
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the branding service gave us a icon then use that.
|
||||||
|
if (icon != null) {
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise fallback to the app label and then the package name.
|
||||||
|
return getApplicationInfo().loadIcon(context.getPackageManager());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the app name. */
|
||||||
|
@Nullable
|
||||||
|
public CharSequence getAppName(@NonNull Context context) {
|
||||||
|
CharSequence name = "";
|
||||||
|
ServiceInfo brandingService = getBrandingService();
|
||||||
|
if (brandingService != null) {
|
||||||
|
name = brandingService.loadLabel(context.getPackageManager());
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the branding service gave us a name then use that.
|
||||||
|
if (!TextUtils.isEmpty(name)) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise fallback to the app label and then the package name.
|
||||||
|
name = getApplicationInfo().loadLabel(context.getPackageManager());
|
||||||
|
if (TextUtils.isEmpty(name)) {
|
||||||
|
name = getApplicationInfo().packageName;
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets the service to use for branding (name, icons). */
|
||||||
|
public @Nullable ServiceInfo getBrandingService() {
|
||||||
|
// If the app has an autofill service then use that.
|
||||||
|
if (mAutofillServiceInfo != null) {
|
||||||
|
return mAutofillServiceInfo.getServiceInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are no credman providers then stop here.
|
||||||
|
if (mCredentialProviderInfos.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build a list of credential providers and sort them by component names
|
||||||
|
// alphabetically to ensure we are deterministic when picking the provider.
|
||||||
|
Map<String, ServiceInfo> flattenedNamesToServices = new HashMap<>();
|
||||||
|
List<String> flattenedNames = new ArrayList<>();
|
||||||
|
for (CredentialProviderInfo cpi : mCredentialProviderInfos) {
|
||||||
|
final String flattenedName = cpi.getComponentName().flattenToString();
|
||||||
|
flattenedNamesToServices.put(flattenedName, cpi.getServiceInfo());
|
||||||
|
flattenedNames.add(flattenedName);
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.sort(flattenedNames);
|
||||||
|
return flattenedNamesToServices.get(flattenedNames.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns whether the provider is the default autofill provider. */
|
||||||
|
public boolean isDefaultAutofillProvider() {
|
||||||
|
return mIsDefaultAutofillProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns whether the provider is the default credman provider. */
|
||||||
|
public boolean isDefaultCredmanProvider() {
|
||||||
|
return mIsDefaultCredmanProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the settings subtitle. */
|
||||||
|
@Nullable
|
||||||
|
public String getSettingsSubtitle() {
|
||||||
|
List<String> subtitles = new ArrayList<>();
|
||||||
|
for (CredentialProviderInfo cpi : mCredentialProviderInfos) {
|
||||||
|
// Convert from a CharSequence.
|
||||||
|
String subtitle = String.valueOf(cpi.getSettingsSubtitle());
|
||||||
|
if (subtitle != null && !TextUtils.isEmpty(subtitle) && !subtitle.equals("null")) {
|
||||||
|
subtitles.add(subtitle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subtitles.size() == 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return String.join(", ", subtitles);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the autofill component name string. */
|
||||||
|
@Nullable
|
||||||
|
public String getAutofillServiceString() {
|
||||||
|
if (mAutofillServiceInfo != null) {
|
||||||
|
return mAutofillServiceInfo.getServiceInfo().getComponentName().flattenToString();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the provider that gets the top spot. */
|
||||||
|
public static @Nullable CombinedProviderInfo getTopProvider(
|
||||||
|
List<CombinedProviderInfo> providers) {
|
||||||
|
// If there is an autofill provider then it should be the
|
||||||
|
// top app provider.
|
||||||
|
for (CombinedProviderInfo cpi : providers) {
|
||||||
|
if (cpi.isDefaultAutofillProvider()) {
|
||||||
|
return cpi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(280454916): Add logic here.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<CombinedProviderInfo> buildMergedList(
|
||||||
|
List<AutofillServiceInfo> asiList,
|
||||||
|
List<CredentialProviderInfo> cpiList,
|
||||||
|
@Nullable String defaultAutofillProvider) {
|
||||||
|
ComponentName defaultAutofillProviderComponent =
|
||||||
|
(defaultAutofillProvider == null)
|
||||||
|
? null
|
||||||
|
: ComponentName.unflattenFromString(defaultAutofillProvider);
|
||||||
|
|
||||||
|
// Index the autofill providers by package name.
|
||||||
|
Set<String> packageNames = new HashSet<>();
|
||||||
|
Map<String, List<AutofillServiceInfo>> autofillServices = new HashMap<>();
|
||||||
|
for (AutofillServiceInfo asi : asiList) {
|
||||||
|
final String packageName = asi.getServiceInfo().packageName;
|
||||||
|
if (!autofillServices.containsKey(packageName)) {
|
||||||
|
autofillServices.put(packageName, new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
autofillServices.get(packageName).add(asi);
|
||||||
|
packageNames.add(packageName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index the credman providers by package name.
|
||||||
|
Map<String, List<CredentialProviderInfo>> credmanServices = new HashMap<>();
|
||||||
|
for (CredentialProviderInfo cpi : cpiList) {
|
||||||
|
String packageName = cpi.getServiceInfo().packageName;
|
||||||
|
if (!credmanServices.containsKey(packageName)) {
|
||||||
|
credmanServices.put(packageName, new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
credmanServices.get(packageName).add(cpi);
|
||||||
|
packageNames.add(packageName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now go through and build the joint datasets.
|
||||||
|
List<CombinedProviderInfo> cmpi = new ArrayList<>();
|
||||||
|
for (String packageName : packageNames) {
|
||||||
|
List<AutofillServiceInfo> asi = autofillServices.get(packageName);
|
||||||
|
List<CredentialProviderInfo> cpi = credmanServices.get(packageName);
|
||||||
|
|
||||||
|
// If there are multiple autofill services then pick the first one.
|
||||||
|
AutofillServiceInfo selectedAsi = asi.isEmpty() ? null : asi.get(0);
|
||||||
|
|
||||||
|
// Check if we are the default autofill provider.
|
||||||
|
boolean isDefaultAutofillProvider = false;
|
||||||
|
if (defaultAutofillProviderComponent != null
|
||||||
|
&& defaultAutofillProviderComponent.getPackageName().equals(packageName)) {
|
||||||
|
isDefaultAutofillProvider = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have any enabled cred man services.
|
||||||
|
boolean isDefaultCredmanProvider = false;
|
||||||
|
if (!cpi.isEmpty()) {
|
||||||
|
isDefaultCredmanProvider = cpi.get(0).isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
cmpi.add(
|
||||||
|
new CombinedProviderInfo(
|
||||||
|
cpi, selectedAsi, isDefaultAutofillProvider, isDefaultCredmanProvider));
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmpi;
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user