Merge "Apply Credman Spec styles to Inline Presentation" into main
diff --git a/packages/CredentialManager/res/values/colors.xml b/packages/CredentialManager/res/values/colors.xml
index 7cb1d01..b4d2eeb 100644
--- a/packages/CredentialManager/res/values/colors.xml
+++ b/packages/CredentialManager/res/values/colors.xml
@@ -22,4 +22,7 @@
     <color name="dropdown_container">#F3F3FA</color>
     <color name="sign_in_options_container">#DADADA</color>
     <color name="sign_in_options_icon_color">#1B1B1B</color>
+
+    <!-- These colors are used for Inline Suggestions. -->
+    <color name="inline_background">#FFFFFF</color>
 </resources>
\ No newline at end of file
diff --git a/packages/CredentialManager/res/values/dimens.xml b/packages/CredentialManager/res/values/dimens.xml
index b47a4dc..350920b2 100644
--- a/packages/CredentialManager/res/values/dimens.xml
+++ b/packages/CredentialManager/res/values/dimens.xml
@@ -28,4 +28,6 @@
     <dimen name="dropdown_layout_horizontal_margin">24dp</dimen>
     <integer name="autofill_max_visible_datasets">5</integer>
     <dimen name="dropdown_touch_target_min_height">48dp</dimen>
+    <dimen name="horizontal_chip_padding">8dp</dimen>
+    <dimen name="vertical_chip_padding">6dp</dimen>
 </resources>
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index eef75c7..0f17217 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -57,6 +57,7 @@
 import androidx.credentials.provider.PasswordCredentialEntry
 import androidx.credentials.provider.PublicKeyCredentialEntry
 import com.android.credentialmanager.GetFlowUtils
+import com.android.credentialmanager.common.ui.InlinePresentationsFactory
 import com.android.credentialmanager.common.ui.RemoteViewsFactory
 import com.android.credentialmanager.getflow.ProviderDisplayInfo
 import com.android.credentialmanager.getflow.toProviderDisplayInfo
@@ -294,8 +295,12 @@
                 } else {
                     inlinePresentationSpecs[inlinePresentationSpecsCount - 1]
                 }
-                inlinePresentation = createInlinePresentation(primaryEntry, pendingIntent, icon,
-                        spec!!, duplicateDisplayNamesForPasskeys)
+                if (spec != null) {
+                    inlinePresentation = createInlinePresentation(primaryEntry, pendingIntent, icon,
+                            InlinePresentationsFactory.modifyInlinePresentationSpec
+                            (this@CredentialAutofillService, spec),
+                            duplicateDisplayNamesForPasskeys)
+                }
             }
             var dropdownPresentation: RemoteViews? = null
             if (i < lastDropdownDatasetIndex) {
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/InlinePresentationFactory.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/InlinePresentationFactory.kt
new file mode 100644
index 0000000..3ebdd20
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/InlinePresentationFactory.kt
@@ -0,0 +1,84 @@
+/*
+* Copyright (C) 2024 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.credentialmanager.common.ui
+
+
+import android.content.Context
+import android.util.Size
+import android.widget.inline.InlinePresentationSpec
+import androidx.autofill.inline.common.TextViewStyle
+import androidx.autofill.inline.common.ViewStyle
+import androidx.autofill.inline.UiVersions
+import androidx.autofill.inline.UiVersions.Style
+import androidx.autofill.inline.v1.InlineSuggestionUi
+import androidx.core.content.ContextCompat
+import android.util.TypedValue
+import android.graphics.Typeface
+
+
+class InlinePresentationsFactory {
+    companion object {
+        private const val googleSansMediumFontFamily = "google-sans-medium"
+        private const val googleSansTextFontFamily = "google-sans-text"
+        // There is no min width required for now but this is needed for the spec builder
+        private const val minInlineWidth = 5000
+
+
+        fun modifyInlinePresentationSpec(context: Context,
+                                         originalSpec: InlinePresentationSpec): InlinePresentationSpec {
+            return InlinePresentationSpec.Builder(Size(originalSpec.minSize.width, originalSpec
+                    .minSize.height),
+                    Size(minInlineWidth, originalSpec
+                            .maxSize.height))
+                    .setStyle(UiVersions.newStylesBuilder().addStyle(getStyle(context)).build())
+                    .build()
+        }
+
+
+        fun getStyle(context: Context): Style {
+            val textColorPrimary = ContextCompat.getColor(context,
+                    com.android.credentialmanager.R.color.text_primary)
+            val textColorSecondary = ContextCompat.getColor(context,
+                    com.android.credentialmanager.R.color.text_secondary)
+            val textColorBackground = ContextCompat.getColor(context,
+                    com.android.credentialmanager.R.color.inline_background)
+            val chipHorizontalPadding = context.resources.getDimensionPixelSize(com.android
+                    .credentialmanager.R.dimen.horizontal_chip_padding)
+            val chipVerticalPadding = context.resources.getDimensionPixelSize(com.android
+                    .credentialmanager.R.dimen.vertical_chip_padding)
+            return InlineSuggestionUi.newStyleBuilder()
+                    .setChipStyle(
+                            ViewStyle.Builder().setPadding(chipHorizontalPadding,
+                                    chipVerticalPadding,
+                                    chipHorizontalPadding, chipVerticalPadding).build()
+                    )
+                    .setTitleStyle(
+                            TextViewStyle.Builder().setTextColor(textColorPrimary).setTextSize
+                            (TypedValue.COMPLEX_UNIT_DIP, 14F)
+                                    .setTypeface(googleSansMediumFontFamily,
+                                            Typeface.NORMAL).setBackgroundColor(textColorBackground)
+                                    .build()
+                    )
+                    .setSubtitleStyle(TextViewStyle.Builder().setTextColor(textColorSecondary)
+                            .setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12F).setTypeface
+                            (googleSansTextFontFamily, Typeface.NORMAL).setBackgroundColor
+                            (textColorBackground).build())
+                    .build()
+        }
+    }
+}
\ No newline at end of file
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java b/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
index c734680..ffc80ee 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
@@ -16,6 +16,8 @@
 
 package com.android.server.autofill.ui;
 
+import static android.service.autofill.FillResponse.FLAG_CREDENTIAL_MANAGER_RESPONSE;
+
 import static com.android.server.autofill.Helper.sVerbose;
 
 import android.annotation.NonNull;
@@ -24,6 +26,7 @@
 import android.content.IntentSender;
 import android.service.autofill.Dataset;
 import android.service.autofill.FillResponse;
+import android.service.autofill.Flags;
 import android.service.autofill.InlinePresentation;
 import android.text.TextUtils;
 import android.util.Pair;
@@ -141,10 +144,12 @@
             return new InlineFillUi(inlineFillUiInfo, inlineAuthentication,
                     maxInputLengthForAutofill);
         } else if (response.getDatasets() != null) {
+            boolean ignoreHostSpec = Flags.autofillCredmanIntegration() && (
+                    (response.getFlags() & FLAG_CREDENTIAL_MANAGER_RESPONSE) != 0);
             SparseArray<Pair<Dataset, InlineSuggestion>> inlineSuggestions =
                     InlineSuggestionFactory.createInlineSuggestions(inlineFillUiInfo,
                             InlineSuggestionInfo.SOURCE_AUTOFILL, response.getDatasets(),
-                            uiCallback);
+                            uiCallback, ignoreHostSpec);
             return new InlineFillUi(inlineFillUiInfo, inlineSuggestions,
                     maxInputLengthForAutofill);
         }
@@ -160,7 +165,8 @@
             @NonNull InlineSuggestionUiCallback uiCallback) {
         SparseArray<Pair<Dataset, InlineSuggestion>> inlineSuggestions =
                 InlineSuggestionFactory.createInlineSuggestions(inlineFillUiInfo,
-                        InlineSuggestionInfo.SOURCE_PLATFORM, datasets, uiCallback);
+                        InlineSuggestionInfo.SOURCE_PLATFORM, datasets,
+                        uiCallback, /* ignoreHostSpec= */ false);
         return new InlineFillUi(inlineFillUiInfo, inlineSuggestions);
     }
 
@@ -254,7 +260,7 @@
         if (!TextUtils.isEmpty(mFilterText) && mFilterText.length() > mMaxInputLengthForAutofill) {
             if (sVerbose) {
                 Slog.v(TAG, "Not showing inline suggestion when user entered more than "
-                         + mMaxInputLengthForAutofill + " characters");
+                        + mMaxInputLengthForAutofill + " characters");
             }
             return new InlineSuggestionsResponse(inlineSuggestions);
         }
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
index 52109ba..d3b9501 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -16,6 +16,7 @@
 
 package com.android.server.autofill.ui;
 
+import static android.service.autofill.FillResponse.FLAG_CREDENTIAL_MANAGER_RESPONSE;
 import static android.view.inputmethod.InlineSuggestionInfo.TYPE_SUGGESTION;
 
 import static com.android.server.autofill.Helper.sDebug;
@@ -25,6 +26,7 @@
 import android.content.IntentSender;
 import android.service.autofill.Dataset;
 import android.service.autofill.FillResponse;
+import android.service.autofill.Flags;
 import android.service.autofill.InlinePresentation;
 import android.util.Pair;
 import android.util.Slog;
@@ -47,11 +49,14 @@
             @NonNull InlineFillUi.InlineSuggestionUiCallback uiCallback) {
         InlinePresentation inlineAuthentication = response.getInlinePresentation();
         final int requestId = response.getRequestId();
+        boolean ignoreHostSpec = Flags.autofillCredmanIntegration() && (
+                (response.getFlags() & FLAG_CREDENTIAL_MANAGER_RESPONSE) != 0);
 
         return createInlineSuggestion(inlineFillUiInfo, InlineSuggestionInfo.SOURCE_AUTOFILL,
                 InlineSuggestionInfo.TYPE_ACTION, () -> uiCallback.authenticate(requestId,
                         AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED),
-                mergedInlinePresentation(inlineFillUiInfo.mInlineRequest, 0, inlineAuthentication),
+                mergedInlinePresentation(inlineFillUiInfo.mInlineRequest, 0, inlineAuthentication,
+                        ignoreHostSpec),
                 createInlineSuggestionTooltip(inlineFillUiInfo.mInlineRequest,
                         inlineFillUiInfo, InlineSuggestionInfo.SOURCE_AUTOFILL,
                         response.getInlineTooltipPresentation()),
@@ -67,7 +72,7 @@
             @NonNull InlineFillUi.InlineFillUiInfo inlineFillUiInfo,
             @NonNull @InlineSuggestionInfo.Source String suggestionSource,
             @NonNull List<Dataset> datasets,
-            @NonNull InlineFillUi.InlineSuggestionUiCallback uiCallback) {
+            @NonNull InlineFillUi.InlineSuggestionUiCallback uiCallback, boolean ignoreHostSpec) {
         if (sDebug) Slog.d(TAG, "createInlineSuggestions(source=" + suggestionSource + ") called");
 
         final InlineSuggestionsRequest request = inlineFillUiInfo.mInlineRequest;
@@ -107,7 +112,8 @@
             InlineSuggestion inlineSuggestion = createInlineSuggestion(
                     inlineFillUiInfo, suggestionSource, suggestionType,
                     () -> uiCallback.autofill(dataset, index),
-                    mergedInlinePresentation(request, datasetIndex, inlinePresentation),
+                    mergedInlinePresentation(request, datasetIndex, inlinePresentation,
+                            ignoreHostSpec),
                     inlineSuggestionTooltip,
                     uiCallback);
             response.append(datasetIndex, Pair.create(dataset, inlineSuggestion));
@@ -141,16 +147,18 @@
      */
     private static InlinePresentation mergedInlinePresentation(
             @NonNull InlineSuggestionsRequest request,
-            int index, @NonNull InlinePresentation inlinePresentation) {
+            int index, @NonNull InlinePresentation inlinePresentation, boolean ignoreHostSpec) {
         final List<InlinePresentationSpec> specs = request.getInlinePresentationSpecs();
         if (specs.isEmpty()) {
             return inlinePresentation;
         }
         InlinePresentationSpec specFromHost = specs.get(Math.min(specs.size() - 1, index));
+        InlinePresentationSpec specToUse =
+                ignoreHostSpec ? inlinePresentation.getInlinePresentationSpec() : specFromHost;
         InlinePresentationSpec mergedInlinePresentation = new InlinePresentationSpec.Builder(
                 inlinePresentation.getInlinePresentationSpec().getMinSize(),
                 inlinePresentation.getInlinePresentationSpec().getMaxSize()).setStyle(
-                specFromHost.getStyle()).build();
+                specToUse.getStyle()).build();
 
         return new InlinePresentation(inlinePresentation.getSlice(), mergedInlinePresentation,
                 inlinePresentation.isPinned());
@@ -211,7 +219,7 @@
                 inlineFillUiInfo, tooltipInline, () -> { /* no operation */ }, uiCallback);
         final InlineSuggestionInfo tooltipInlineSuggestionInfo = new InlineSuggestionInfo(
                 mergedSpec, suggestionSource, /* autofillHints */ null, TYPE_SUGGESTION,
-                        /* pinned */ false, /* tooltip */ null);
+                /* pinned */ false, /* tooltip */ null);
         return new InlineSuggestion(tooltipInlineSuggestionInfo, tooltipContentProvider);
     }