Add buttons for new settings design

This adds a new class called PrimaryProviderPreference
which is responsible for laying out the old cog view if
the feature is disabled and the new button view if it
is enabled.

Screenshots:
https://hsv.googleplex.com/5109836204212224
https://hsv.googleplex.com/5574754636398592
https://hsv.googleplex.com/6737135727017984

Change-Id: I1ad0c59a4afc5be3694b499f66bbd7306dfbee69
Test: Manual test with flag on + off & unit tests
Bug: 300979487
diff --git a/res/layout/preference_credential_manager_with_buttons.xml b/res/layout/preference_credential_manager_with_buttons.xml
new file mode 100644
index 0000000..1889cea
--- /dev/null
+++ b/res/layout/preference_credential_manager_with_buttons.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+
+<!-- Based off preference_single_target.xml with buttons added below text. -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:gravity="center_vertical"
+    android:orientation="vertical"
+    android:background="?android:attr/selectableItemBackground"
+    android:clipToPadding="false">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="horizontal"
+        android:gravity="start|center_vertical"
+        android:clipToPadding="false"
+        android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+        <LinearLayout
+            android:id="@+id/icon_frame"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="start|center_vertical"
+            android:minWidth="56dp"
+            android:orientation="horizontal"
+            android:clipToPadding="false"
+            android:paddingTop="4dp"
+            android:paddingBottom="4dp">
+            <androidx.preference.internal.PreferenceImageView
+                android:id="@android:id/icon"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                settings:maxWidth="48dp"
+                settings:maxHeight="48dp" />
+        </LinearLayout>
+
+        <RelativeLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:paddingTop="16dp"
+            android:paddingBottom="16dp">
+
+            <TextView
+                android:id="@android:id/title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:singleLine="true"
+                android:textAppearance="?android:attr/textAppearanceListItem"
+                android:ellipsize="marquee" />
+
+            <TextView
+                android:id="@android:id/summary"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_below="@android:id/title"
+                android:layout_alignStart="@android:id/title"
+                android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+                android:textColor="?android:attr/textColorSecondary"
+                android:hyphenationFrequency="normalFast"
+                android:lineBreakWordStyle="phrase"
+                android:maxLines="10" />
+
+        </RelativeLayout>
+
+    </LinearLayout>
+
+    <!-- Preference should place its actual preference widget here. -->
+    <LinearLayout
+        android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:minWidth="@dimen/two_target_min_width"
+        android:gravity="center"
+        android:orientation="vertical" />
+
+    <LinearLayout
+        android:id="@+id/credman_button_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:gravity="start|center_vertical"
+        android:minWidth="56dp"
+        android:orientation="horizontal"
+        android:clipToPadding="false"
+        android:paddingTop="4dp"
+        android:paddingLeft="80dp"
+        android:paddingBottom="4dp">
+
+            <Button
+                android:id="@+id/change_button"
+                android:layout_width="match_parent"
+                style="@style/CredentialManagerChangeButton"
+                android:layout_height="wrap_content"
+                android:text="@string/credman_button_change"/>
+
+            <Button
+                android:id="@+id/open_button"
+                style="@style/CredentialManagerOpenButton"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:visibility="gone"
+                android:text="@string/credman_button_open"/>
+
+    </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 402f526..9db5d8020 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -10739,7 +10739,7 @@
     <!-- Preference category for showing auto-fill services with saved passwords. [CHAR LIMIT=60] -->
     <string name="autofill_passwords">Passwords</string>
     <!-- Preference category for showing autofill and credman services with saved credentials. [CHAR LIMIT=60] -->
-    <string name="credman_chosen_app_title">Passwords, passkeys, and data services</string>
+    <string name="credman_chosen_app_title">Preferred service</string>
     <!-- Preference category for showing additional credential providers. [CHAR LIMIT=60] -->
     <string name="credman_credentials">Additional providers</string>
     <!-- Summary for passwords settings that shows how many passwords are saved for each autofill
@@ -10757,6 +10757,10 @@
     <string name="credman_keywords">data, passkey, password</string>
     <!-- Keywords for the credman feature. [CHAR LIMIT=NONE] -->
     <string name="credman_autofill_keywords">auto, fill, autofill, data, passkey, password</string>
+    <!-- Button for choosing credman service. [CHAR LIMIT=40] -->
+    <string name="credman_button_change">Change</string>
+    <!-- Button for opening credman service settings. [CHAR LIMIT=40] -->
+    <string name="credman_button_open">Open</string>
 
     <!-- Message of the warning dialog for setting the auto-fill app. [CHAR_LIMIT=NONE] -->
     <string name="autofill_confirmation_message">
@@ -12706,4 +12710,4 @@
 
     <!-- Authority of the content provider that support methods restartPhoneProcess and restartRild. Will be overlaid by OEM.-->
     <string name="reset_telephony_stack_content_provider_authority" translatable="false"></string>
-</resources>
+</resources>
\ No newline at end of file
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 8df990b..fbc6d7f 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -989,4 +989,22 @@
         <item name="android:layout_alignParentTop">true</item>
     </style>
 
+    <style name="CredentialManagerChangeButton" parent="@style/ActionPrimaryButton">
+        <item name="android:fontFamily">google-sans-medium</item>
+        <item name="android:textSize">14sp</item>
+        <item name="android:textAllCaps">false</item>
+        <item name="android:singleLine">true</item>
+        <item name="android:paddingHorizontal">24dp</item>
+    </style>
+
+    <style name="CredentialManagerOpenButton"
+        parent="@style/Widget.AppCompat.Button">
+        <item name="android:theme">@style/RoundedCornerThemeOverlay</item>
+        <item name="android:fontFamily">google-sans-medium</item>
+        <item name="android:textSize">14sp</item>
+        <item name="android:textAllCaps">false</item>
+        <item name="android:singleLine">true</item>
+        <item name="android:background">@null</item>
+    </style>
+
 </resources>
diff --git a/res/xml/accounts_dashboard_settings_credman.xml b/res/xml/accounts_dashboard_settings_credman.xml
index 7bcf62d..7266bda 100644
--- a/res/xml/accounts_dashboard_settings_credman.xml
+++ b/res/xml/accounts_dashboard_settings_credman.xml
@@ -26,15 +26,14 @@
         android:order="10"
         android:title="@string/credman_chosen_app_title">
 
-        <com.android.settings.widget.GearPreference
-            android:fragment="com.android.settings.applications.credentials.DefaultCombinedPicker"
+        <com.android.settings.applications.credentials.PrimaryProviderPreference
             android:key="default_credman_autofill_main"
             android:title="@string/credman_chosen_app_title"
             settings:keywords="@string/credman_autofill_keywords">
             <extra
                 android:name="for_work"
                 android:value="false" />
-        </com.android.settings.widget.GearPreference>
+        </com.android.settings.applications.credentials.PrimaryProviderPreference>
     </PreferenceCategory>
 
     <PreferenceCategory
diff --git a/res/xml/accounts_personal_dashboard_settings_credman.xml b/res/xml/accounts_personal_dashboard_settings_credman.xml
index 835fcb7..9473e17 100644
--- a/res/xml/accounts_personal_dashboard_settings_credman.xml
+++ b/res/xml/accounts_personal_dashboard_settings_credman.xml
@@ -27,15 +27,14 @@
         android:order="10"
         android:title="@string/credman_chosen_app_title">
 
-        <com.android.settings.widget.GearPreference
-            android:fragment="com.android.settings.applications.credentials.DefaultCombinedPicker"
+        <com.android.settings.applications.credentials.PrimaryProviderPreference
             android:key="default_credman_autofill_main"
             android:title="@string/credman_chosen_app_title"
             settings:keywords="@string/credman_autofill_keywords">
             <extra
                 android:name="for_work"
                 android:value="false" />
-        </com.android.settings.widget.GearPreference>
+        </com.android.settings.applications.credentials.PrimaryProviderPreference>
     </PreferenceCategory>
 
     <PreferenceCategory
diff --git a/res/xml/accounts_private_dashboard_settings_credman.xml b/res/xml/accounts_private_dashboard_settings_credman.xml
index e9abcf8..54db839 100644
--- a/res/xml/accounts_private_dashboard_settings_credman.xml
+++ b/res/xml/accounts_private_dashboard_settings_credman.xml
@@ -27,8 +27,7 @@
         android:order="10"
         android:title="@string/credman_chosen_app_title">
 
-        <com.android.settings.widget.GearPreference
-            android:fragment="com.android.settings.applications.credentials.DefaultCombinedPickerPrivate"
+        <com.android.settings.applications.credentials.PrimaryProviderPreference
             android:key="default_credman_autofill_private"
             android:title="@string/credman_chosen_app_title"
             settings:searchable="false">
@@ -36,7 +35,7 @@
             <extra
                 android:name="for_work"
                 android:value="false" />
-        </com.android.settings.widget.GearPreference>
+        </com.android.settings.applications.credentials.PrimaryProviderPreference>
     </PreferenceCategory>
 
     <PreferenceCategory
diff --git a/res/xml/accounts_work_dashboard_settings_credman.xml b/res/xml/accounts_work_dashboard_settings_credman.xml
index 8afbba8..d649940 100644
--- a/res/xml/accounts_work_dashboard_settings_credman.xml
+++ b/res/xml/accounts_work_dashboard_settings_credman.xml
@@ -27,15 +27,14 @@
         android:order="10"
         android:title="@string/credman_chosen_app_title">
 
-        <com.android.settings.widget.GearPreference
-            android:fragment="com.android.settings.applications.credentials.DefaultCombinedPickerWork"
+        <com.android.settings.applications.credentials.PrimaryProviderPreference
             android:key="default_credman_autofill_main_work"
             android:title="@string/credman_chosen_app_title"
             settings:searchable="false">
             <extra
                 android:name="for_work"
                 android:value="true" />
-        </com.android.settings.widget.GearPreference>
+        </com.android.settings.applications.credentials.PrimaryProviderPreference>
     </PreferenceCategory>
 
     <PreferenceCategory
diff --git a/src/com/android/settings/applications/credentials/CombinedProviderInfo.java b/src/com/android/settings/applications/credentials/CombinedProviderInfo.java
index e7a391e..f8a3b0f 100644
--- a/src/com/android/settings/applications/credentials/CombinedProviderInfo.java
+++ b/src/com/android/settings/applications/credentials/CombinedProviderInfo.java
@@ -18,13 +18,17 @@
 
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ServiceInfo;
 import android.credentials.CredentialProviderInfo;
 import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.os.UserManager;
 import android.service.autofill.AutofillServiceInfo;
 import android.text.TextUtils;
 import android.util.IconDrawableFactory;
+import android.util.Log;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -42,6 +46,11 @@
  * logic for each row in settings.
  */
 public final class CombinedProviderInfo {
+    private static final String TAG = "CombinedProviderInfo";
+    private static final String SETTINGS_ACTIVITY_INTENT_ACTION = "android.intent.action.MAIN";
+    private static final String SETTINGS_ACTIVITY_INTENT_CATEGORY =
+            "android.intent.category.LAUNCHER";
+
     private final List<CredentialProviderInfo> mCredentialProviderInfos;
     private final @Nullable AutofillServiceInfo mAutofillServiceInfo;
     private final boolean mIsDefaultAutofillProvider;
@@ -316,4 +325,44 @@
 
         return cmpi;
     }
+
+    public static @Nullable Intent createSettingsActivityIntent(
+            @NonNull Context context,
+            @Nullable CharSequence packageName,
+            @Nullable CharSequence settingsActivity,
+            int currentUserId) {
+        if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(settingsActivity)) {
+            return null;
+        }
+
+        ComponentName cn =
+                new ComponentName(String.valueOf(packageName), String.valueOf(settingsActivity));
+        if (cn == null) {
+            Log.e(
+                    TAG,
+                    "Failed to deserialize settingsActivity attribute, we got: "
+                            + String.valueOf(packageName)
+                            + " and "
+                            + String.valueOf(settingsActivity));
+            return null;
+        }
+
+        Intent intent = new Intent(SETTINGS_ACTIVITY_INTENT_ACTION);
+        intent.addCategory(SETTINGS_ACTIVITY_INTENT_CATEGORY);
+        intent.setComponent(cn);
+
+        int contextUserId = context.getUser().getIdentifier();
+        if (currentUserId != contextUserId && UserManager.isHeadlessSystemUserMode()) {
+            Log.w(
+                    TAG,
+                    "onLeftSideClicked(): using context for current user ("
+                            + currentUserId
+                            + ") instead of user "
+                            + contextUserId
+                            + " on headless system user mode");
+            context = context.createContextAsUser(UserHandle.of(currentUserId), /* flags= */ 0);
+        }
+
+        return intent;
+    }
 }
diff --git a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
index e186206..4e5d7f0 100644
--- a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
+++ b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
@@ -95,9 +95,6 @@
     private static final String ALTERNATE_INTENT = "android.settings.SYNC_SETTINGS";
     private static final String PRIMARY_INTENT = "android.settings.CREDENTIAL_PROVIDER";
     private static final int MAX_SELECTABLE_PROVIDERS = 5;
-    private static final String SETTINGS_ACTIVITY_INTENT_ACTION = "android.intent.action.MAIN";
-    private static final String SETTINGS_ACTIVITY_INTENT_CATEGORY =
-            "android.intent.category.LAUNCHER";
 
     private final PackageManager mPm;
     private final List<CredentialProviderInfo> mServices;
@@ -475,6 +472,7 @@
 
             // If this provider is displayed at the top then we should not show it.
             if (topProvider != null
+                    && topProvider.getApplicationInfo() != null
                     && topProvider.getApplicationInfo().packageName.equals(packageName)) {
                 continue;
             }
@@ -484,10 +482,6 @@
                 continue;
             }
 
-            // Get the settings activity.
-            CharSequence settingsActivity =
-                    combinedInfo.getCredentialProviderInfos().get(0).getSettingsActivity();
-
             Drawable icon = combinedInfo.getAppIcon(context, getUser());
             CharSequence title = combinedInfo.getAppName(context);
 
@@ -499,7 +493,7 @@
                             icon,
                             packageName,
                             combinedInfo.getSettingsSubtitle(),
-                            settingsActivity);
+                            combinedInfo.getSettingsActivity());
             output.put(packageName, pref);
             group.addPreference(pref);
         }
@@ -626,43 +620,12 @@
 
                     @Override
                     public void onLeftSideClicked() {
-                        if (settingsActivity == null) {
-                            Log.w(TAG, "settingsActivity was null");
-                            return;
+                        Intent settingsIntent =
+                                CombinedProviderInfo.createSettingsActivityIntent(
+                                        mContext, packageName, settingsActivity, getUser());
+                        if (settingsIntent != null) {
+                            mContext.startActivity(settingsIntent);
                         }
-
-                        String settingsActivityStr = String.valueOf(settingsActivity);
-                        ComponentName cn = ComponentName.unflattenFromString(settingsActivityStr);
-                        if (cn == null) {
-                            Log.w(
-                                    TAG,
-                                    "Failed to deserialize settingsActivity attribute, we got: "
-                                            + settingsActivityStr);
-                            return;
-                        }
-
-                        Intent intent = new Intent(SETTINGS_ACTIVITY_INTENT_ACTION);
-                        intent.addCategory(SETTINGS_ACTIVITY_INTENT_CATEGORY);
-                        intent.setComponent(cn);
-
-                        Context context = mContext;
-                        int currentUserId = getUser();
-                        int contextUserId = context.getUser().getIdentifier();
-
-                        if (currentUserId != contextUserId) {
-                            Log.d(
-                                    TAG,
-                                    "onLeftSideClicked(): using context for current user ("
-                                            + currentUserId
-                                            + ") instead of user "
-                                            + contextUserId
-                                            + " on headless system user mode");
-                            context =
-                                    context.createContextAsUser(
-                                            UserHandle.of(currentUserId), /* flags= */ 0);
-                        }
-
-                        context.startActivity(intent);
                     }
                 });
 
diff --git a/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceController.java b/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceController.java
index 567bc31..d2400bb 100644
--- a/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceController.java
+++ b/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceController.java
@@ -16,31 +16,32 @@
 
 package com.android.settings.applications.credentials;
 
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ServiceInfo;
 import android.credentials.CredentialManager;
 import android.credentials.CredentialProviderInfo;
+import android.graphics.drawable.Drawable;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.autofill.AutofillService;
 import android.service.autofill.AutofillServiceInfo;
-import android.text.TextUtils;
 import android.view.autofill.AutofillManager;
 
 import androidx.annotation.Nullable;
+import androidx.annotation.NonNull;
 import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.R;
+import com.android.settings.Utils;
 import com.android.settings.applications.defaultapps.DefaultAppPreferenceController;
 import com.android.settingslib.applications.DefaultAppInfo;
+import com.android.settingslib.widget.TwoTargetPreference;
 
 import java.util.ArrayList;
 import java.util.List;
 
-public class DefaultCombinedPreferenceController extends DefaultAppPreferenceController
-        implements Preference.OnPreferenceClickListener {
+public class DefaultCombinedPreferenceController extends DefaultAppPreferenceController {
 
     private static final Intent AUTOFILL_PROBE = new Intent(AutofillService.SERVICE_INTERFACE);
     private static final String TAG = "DefaultCombinedPreferenceController";
@@ -78,72 +79,80 @@
         // Despite this method being called getSettingIntent this intent actually
         // opens the primary picker. This is so that we can swap the cog and the left
         // hand side presses to align the UX.
-        return new Intent(mContext, CredentialsPickerActivity.class);
-    }
-
-    @Override
-    public void displayPreference(PreferenceScreen screen) {
-        super.displayPreference(screen);
-
-        final String prefKey = getPreferenceKey();
-        final Preference preference = screen.findPreference(prefKey);
-        if (preference != null) {
-            preference.setOnPreferenceClickListener((Preference.OnPreferenceClickListener) this);
+        if (PrimaryProviderPreference.shouldUseNewSettingsUi()) {
+            // We need to return an empty intent here since the class we inherit
+            // from will throw an NPE if we return null and we don't want it to 
+            // open anything since we added the buttons.
+            return new Intent();
         }
+        return createIntentToOpenPicker();
     }
 
     @Override
-    public boolean onPreferenceClick(Preference preference) {
-        // Get the selected provider.
+    public void updateState(@NonNull Preference preference) {
         final CombinedProviderInfo topProvider = getTopProvider();
-        if (topProvider == null) {
-            return false;
+        if (topProvider != null && mContext != null) {
+            updatePreferenceForProvider(
+                    preference,
+                    topProvider.getAppName(mContext),
+                    topProvider.getSettingsSubtitle(),
+                    topProvider.getAppIcon(mContext, getUser()),
+                    createSettingsActivityIntent(
+                            topProvider.getPackageName(), topProvider.getSettingsActivity()));
+        } else {
+            updatePreferenceForProvider(preference, null, null, null, null);
+        }
+    }
+
+    @VisibleForTesting
+    public void updatePreferenceForProvider(
+            Preference preference,
+            @Nullable CharSequence appName,
+            @Nullable String appSubtitle,
+            @Nullable Drawable appIcon,
+            @Nullable Intent settingsActivityIntent) {
+        if (appName == null) {
+            preference.setTitle(R.string.app_list_preference_none);
+        } else {
+            preference.setTitle(appName);
         }
 
-        // If the top provider has a defined Credential Manager settings
-        // provider then we should open that up.
-        final String settingsActivity = topProvider.getSettingsActivity();
-        if (!TextUtils.isEmpty(settingsActivity)) {
-            final Intent intent =
-                    new Intent(Intent.ACTION_MAIN)
-                            .setComponent(
-                                    new ComponentName(
-                                            topProvider.getPackageName(), settingsActivity));
-            startActivity(intent);
-            return true;
+        if (appIcon == null) {
+            preference.setIcon(null);
+        } else {
+            preference.setIcon(Utils.getSafeIcon(appIcon));
         }
 
-        return false;
+        preference.setSummary(appSubtitle);
+
+        if (preference instanceof PrimaryProviderPreference) {
+            PrimaryProviderPreference primaryPref = (PrimaryProviderPreference) preference;
+            primaryPref.setIconSize(TwoTargetPreference.ICON_SIZE_MEDIUM);
+            primaryPref.setDelegate(
+                    new PrimaryProviderPreference.Delegate() {
+                        public void onOpenButtonClicked() {
+                            if (settingsActivityIntent != null) {
+                                startActivity(settingsActivityIntent);
+                            }
+                        }
+
+                        public void onChangeButtonClicked() {
+                            startActivity(createIntentToOpenPicker());
+                        }
+                    });
+
+            // Hide the open button if there is no defined settings activity.
+            primaryPref.setOpenButtonVisible(settingsActivityIntent != null);
+            primaryPref.setButtonsVisible(appName != null);
+        }
     }
 
     private @Nullable CombinedProviderInfo getTopProvider() {
-        List<CombinedProviderInfo> providers = getAllProviders(getUser());
-        return CombinedProviderInfo.getTopProvider(providers);
+        return CombinedProviderInfo.getTopProvider(getAllProviders(getUser()));
     }
 
     @Override
     protected DefaultAppInfo getDefaultAppInfo() {
-        CombinedProviderInfo topProvider = getTopProvider();
-        if (topProvider != null) {
-            ServiceInfo brandingService = topProvider.getBrandingService();
-            if (brandingService == null) {
-                return new DefaultAppInfo(
-                        mContext,
-                        mPackageManager,
-                        getUser(),
-                        topProvider.getApplicationInfo(),
-                        topProvider.getSettingsSubtitle(),
-                        true);
-            } else {
-                return new DefaultAppInfo(
-                        mContext,
-                        mPackageManager,
-                        getUser(),
-                        brandingService,
-                        topProvider.getSettingsSubtitle(),
-                        true);
-            }
-        }
         return null;
     }
 
@@ -180,4 +189,16 @@
     protected int getUser() {
         return UserHandle.myUserId();
     }
+
+    /** Creates an intent to open the credential picker. */
+    private Intent createIntentToOpenPicker() {
+        return new Intent(mContext, CredentialsPickerActivity.class);
+    }
+
+    /** Creates an intent to open the settings activity of the primary provider (if available). */
+    public @Nullable Intent createSettingsActivityIntent(
+            @Nullable String packageName, @Nullable String settingsActivity) {
+        return CombinedProviderInfo.createSettingsActivityIntent(
+                mContext, packageName, settingsActivity, getUser());
+    }
 }
diff --git a/src/com/android/settings/applications/credentials/PrimaryProviderPreference.java b/src/com/android/settings/applications/credentials/PrimaryProviderPreference.java
new file mode 100644
index 0000000..b8e2529
--- /dev/null
+++ b/src/com/android/settings/applications/credentials/PrimaryProviderPreference.java
@@ -0,0 +1,215 @@
+/*
+ * 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.settings.applications.credentials;
+
+import android.content.Context;
+import android.credentials.flags.Flags;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.Button;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.R;
+import com.android.settings.widget.GearPreference;
+
+/**
+ * This preference is shown at the top of the "passwords & accounts" screen and allows the user to
+ * pick their primary credential manager provider.
+ */
+public class PrimaryProviderPreference extends GearPreference {
+
+    public static boolean shouldUseNewSettingsUi() {
+        return Flags.newSettingsUi();
+    }
+
+    private @Nullable Button mChangeButton = null;
+    private @Nullable Button mOpenButton = null;
+    private @Nullable View mButtonFrameView = null;
+    private @Nullable View mGearView = null;
+    private @Nullable Delegate mDelegate = null;
+    private boolean mButtonsVisible = false;
+    private boolean mOpenButtonVisible = false;
+
+    /** Called to send messages back to the parent controller. */
+    public static interface Delegate {
+        void onOpenButtonClicked();
+
+        void onChangeButtonClicked();
+    }
+
+    public PrimaryProviderPreference(
+            @NonNull Context context,
+            @NonNull AttributeSet attrs,
+            int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        initializeNewSettingsUi();
+    }
+
+    public PrimaryProviderPreference(
+           @NonNull Context context,
+           @NonNull AttributeSet attrs) {
+        super(context, attrs);
+        initializeNewSettingsUi();
+    }
+
+    private void initializeNewSettingsUi() {
+        if (!shouldUseNewSettingsUi()) {
+            return;
+        }
+
+        // Change the layout to the new settings ui.
+        setLayoutResource(R.layout.preference_credential_manager_with_buttons);
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+
+        if (shouldUseNewSettingsUi()) {
+            onBindViewHolderNewSettingsUi(holder);
+        } else {
+            onBindViewHolderOldSettingsUi(holder);
+        }
+    }
+
+    private void onBindViewHolderOldSettingsUi(PreferenceViewHolder holder) {
+        setOnPreferenceClickListener(
+                new Preference.OnPreferenceClickListener() {
+                    public boolean onPreferenceClick(@NonNull Preference preference) {
+                        if (mDelegate != null) {
+                            mDelegate.onOpenButtonClicked();
+                            return true;
+                        }
+
+                        return false;
+                    }
+                });
+
+        // Setup the gear icon to handle opening the change provider scenario.
+        mGearView = holder.findViewById(R.id.settings_button);
+        mGearView.setVisibility(View.VISIBLE);
+        mGearView.setOnClickListener(
+                new View.OnClickListener() {
+                    public void onClick(@NonNull View v) {
+                        if (mDelegate != null) {
+                            mDelegate.onChangeButtonClicked();
+                        }
+                    }
+                });
+    }
+
+    private void onBindViewHolderNewSettingsUi(PreferenceViewHolder holder) {
+        mOpenButton = (Button) holder.findViewById(R.id.open_button);
+        mOpenButton.setOnClickListener(
+                new View.OnClickListener() {
+                    public void onClick(@NonNull View v) {
+                        if (mDelegate != null) {
+                            mDelegate.onOpenButtonClicked();
+                        }
+                    }
+                });
+        setVisibility(mOpenButton, mOpenButtonVisible);
+
+        mChangeButton = (Button) holder.findViewById(R.id.change_button);
+        mChangeButton.setOnClickListener(
+                new View.OnClickListener() {
+                    public void onClick(@NonNull View v) {
+                        if (mDelegate != null) {
+                            mDelegate.onChangeButtonClicked();
+                        }
+                    }
+                });
+
+        mButtonFrameView = holder.findViewById(R.id.credman_button_frame);
+        mButtonFrameView.setVisibility(mButtonsVisible ? View.VISIBLE : View.GONE);
+
+        // There is a special case where if the provider == none then we should
+        // hide the buttons and when the preference is tapped we can open the
+        // provider selection dialog.
+        setOnPreferenceClickListener(
+                new Preference.OnPreferenceClickListener() {
+                    public boolean onPreferenceClick(@NonNull Preference preference) {
+                        return handlePreferenceClickNewSettingsUi();
+                    }
+                });
+    }
+
+    private boolean handlePreferenceClickNewSettingsUi() {
+        if (mDelegate != null && !mButtonsVisible) {
+            mDelegate.onChangeButtonClicked();
+            return true;
+        }
+
+        return false;
+    }
+
+    public void setOpenButtonVisible(boolean isVisible) {
+        if (mOpenButton != null) {
+            mOpenButton.setVisibility(isVisible ? View.VISIBLE : View.GONE);
+            setVisibility(mOpenButton, isVisible);
+        }
+
+        mOpenButtonVisible = isVisible;
+    }
+
+    public void setButtonsVisible(boolean isVisible) {
+        if (mButtonFrameView != null) {
+            setVisibility(mButtonFrameView, isVisible);
+        }
+
+        mButtonsVisible = isVisible;
+    }
+
+    public void setDelegate(@NonNull Delegate delegate) {
+        mDelegate = delegate;
+    }
+
+    @Override
+    protected boolean shouldHideSecondTarget() {
+        return shouldUseNewSettingsUi();
+    }
+
+    @VisibleForTesting
+    public @Nullable Button getOpenButton() {
+        return mOpenButton;
+    }
+
+    @VisibleForTesting
+    public @Nullable Button getChangeButton() {
+        return mChangeButton;
+    }
+
+    @VisibleForTesting
+    public @Nullable View getButtonFrameView() {
+        return mButtonFrameView;
+    }
+
+    @VisibleForTesting
+    public @Nullable View getGearView() {
+        return mGearView;
+    }
+
+    private static void setVisibility(View view, boolean isVisible) {
+        view.setVisibility(isVisible ? View.VISIBLE : View.GONE);
+    }
+}
diff --git a/tests/unit/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceControllerTest.java b/tests/unit/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceControllerTest.java
new file mode 100644
index 0000000..301fcfa
--- /dev/null
+++ b/tests/unit/src/com/android/settings/applications/credentials/DefaultCombinedPreferenceControllerTest.java
@@ -0,0 +1,144 @@
+/*
+ * 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.settings.applications.credentials;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.Looper;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import androidx.preference.PreferenceViewHolder;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settings.R;
+import com.android.settings.testutils.ResourcesUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class DefaultCombinedPreferenceControllerTest {
+
+    private Context mContext;
+    private PrimaryProviderPreference.Delegate mDelegate;
+    private AttributeSet mAttributes;
+
+    @Before
+    public void setUp() {
+        mContext = spy(ApplicationProvider.getApplicationContext());
+        if (Looper.myLooper() == null) {
+            Looper.prepare(); // needed to create the preference screen
+        }
+        mDelegate =
+                new PrimaryProviderPreference.Delegate() {
+                    public void onOpenButtonClicked() {}
+
+                    public void onChangeButtonClicked() {}
+                };
+    }
+
+    @Test
+    public void ensureSettingIntentNullForNewDesign() {
+        if (!PrimaryProviderPreference.shouldUseNewSettingsUi()) {
+            return;
+        }
+
+        // The setting intent should be null for the new design since this
+        // is handled by the delegate for the PrimaryProviderPreference.
+        DefaultCombinedPreferenceController dcpc =
+                new DefaultCombinedPreferenceController(mContext);
+        assertThat(dcpc.getSettingIntent(null).getPackage()).isNull();
+    }
+
+    @Test
+    public void ensureSettingIntentNotNullForOldDesign() {
+        if (PrimaryProviderPreference.shouldUseNewSettingsUi()) {
+            return;
+        }
+
+        // For the old design the setting intent should still be used.
+        DefaultCombinedPreferenceController dcpc =
+                new DefaultCombinedPreferenceController(mContext);
+        assertThat(dcpc.getSettingIntent(null).getPackage()).isNotNull();
+    }
+
+    @Test
+    public void ensureSettingsActivityIntentCreatedSuccessfully() {
+        DefaultCombinedPreferenceController dcpc =
+                new DefaultCombinedPreferenceController(mContext);
+
+        // Ensure that the settings activity is only created if we haved the right combination
+        // of package and class name.
+        assertThat(dcpc.createSettingsActivityIntent(null, null)).isNull();
+        assertThat(dcpc.createSettingsActivityIntent("", null)).isNull();
+        assertThat(dcpc.createSettingsActivityIntent("", "")).isNull();
+        assertThat(dcpc.createSettingsActivityIntent("com.test", "")).isNull();
+        assertThat(dcpc.createSettingsActivityIntent("com.test", "ClassName")).isNotNull();
+    }
+
+    @Test
+    public void ensureUpdatePreferenceForProviderPopulatesInfo() {
+        if (!PrimaryProviderPreference.shouldUseNewSettingsUi()) {
+            return;
+        }
+
+        DefaultCombinedPreferenceController dcpc =
+                new DefaultCombinedPreferenceController(mContext);
+        PrimaryProviderPreference ppp = createTestPreference();
+        Drawable appIcon = mContext.getResources().getDrawable(R.drawable.ic_settings_delete);
+
+        // Update the preference to use the provider and make sure the view
+        // was updated.
+        dcpc.updatePreferenceForProvider(ppp, "App Name", "Subtitle", appIcon, null);
+        assertThat(ppp.getTitle().toString()).isEqualTo("App Name");
+        assertThat(ppp.getSummary().toString()).isEqualTo("Subtitle");
+        assertThat(ppp.getIcon()).isEqualTo(appIcon);
+
+        // Set the preference back to none and make sure the view was updated.
+        dcpc.updatePreferenceForProvider(ppp, null, null, null, null);
+        assertThat(ppp.getTitle().toString()).isEqualTo("None");
+        assertThat(ppp.getSummary()).isNull();
+        assertThat(ppp.getIcon()).isNull();
+    }
+
+    private PrimaryProviderPreference createTestPreference() {
+        int layoutId =
+                ResourcesUtils.getResourcesId(
+                        mContext, "layout", "preference_credential_manager_with_buttons");
+        PreferenceViewHolder holder =
+                PreferenceViewHolder.createInstanceForTests(
+                        LayoutInflater.from(mContext).inflate(layoutId, null));
+        PreferenceViewHolder holderForTest = spy(holder);
+        View gearView = new View(mContext, null);
+        int gearId = ResourcesUtils.getResourcesId(mContext, "id", "settings_button");
+        when(holderForTest.findViewById(gearId)).thenReturn(gearView);
+
+        PrimaryProviderPreference ppp = new PrimaryProviderPreference(mContext, mAttributes);
+        ppp.setDelegate(mDelegate);
+        ppp.onBindViewHolder(holderForTest);
+        return ppp;
+    }
+}
diff --git a/tests/unit/src/com/android/settings/applications/credentials/PrimaryProviderPreferenceTest.java b/tests/unit/src/com/android/settings/applications/credentials/PrimaryProviderPreferenceTest.java
new file mode 100644
index 0000000..51a1fc4
--- /dev/null
+++ b/tests/unit/src/com/android/settings/applications/credentials/PrimaryProviderPreferenceTest.java
@@ -0,0 +1,182 @@
+/*
+ * 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.settings.applications.credentials;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.Looper;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import androidx.preference.PreferenceViewHolder;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settings.testutils.ResourcesUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class PrimaryProviderPreferenceTest {
+
+    private Context mContext;
+    private PrimaryProviderPreference.Delegate mDelegate;
+    private boolean mReceivedOpenButtonClicked = false;
+    private boolean mReceivedChangeButtonClicked = false;
+    private AttributeSet mAttributes;
+
+    @Before
+    public void setUp() {
+        mContext = spy(ApplicationProvider.getApplicationContext());
+        if (Looper.myLooper() == null) {
+            Looper.prepare(); // needed to create the preference screen
+        }
+        mReceivedOpenButtonClicked = false;
+        mReceivedChangeButtonClicked = false;
+        mDelegate =
+                new PrimaryProviderPreference.Delegate() {
+                    public void onOpenButtonClicked() {
+                        mReceivedOpenButtonClicked = true;
+                    }
+
+                    public void onChangeButtonClicked() {
+                        mReceivedChangeButtonClicked = true;
+                    }
+                };
+    }
+
+    @Test
+    public void ensureButtonsClicksCallDelegate_newDesign() {
+        if (!PrimaryProviderPreference.shouldUseNewSettingsUi()) {
+            return;
+        }
+
+        PrimaryProviderPreference ppp = createTestPreferenceWithNewLayout();
+
+        // Test that all the views & buttons are bound correctly.
+        assertThat(ppp.getOpenButton()).isNotNull();
+        assertThat(ppp.getChangeButton()).isNotNull();
+        assertThat(ppp.getButtonFrameView()).isNotNull();
+
+        // Test that clicking the open button results in the delegate being
+        // called.
+        assertThat(mReceivedOpenButtonClicked).isFalse();
+        ppp.getOpenButton().performClick();
+        assertThat(mReceivedOpenButtonClicked).isTrue();
+
+        // Test that clicking the change button results in the delegate being
+        // called.
+        assertThat(mReceivedChangeButtonClicked).isFalse();
+        ppp.getChangeButton().performClick();
+        assertThat(mReceivedChangeButtonClicked).isTrue();
+    }
+
+    @Test
+    public void ensureButtonsClicksCallDelegate_newDesign_openButtonVisibility() {
+        if (!PrimaryProviderPreference.shouldUseNewSettingsUi()) {
+            return;
+        }
+
+        PrimaryProviderPreference ppp = createTestPreferenceWithNewLayout();
+
+        // Test that the open button is visible.
+        assertThat(ppp.getOpenButton()).isNotNull();
+        assertThat(ppp.getOpenButton().getVisibility()).isEqualTo(View.GONE);
+
+        // Show the button and make sure the view was updated.
+        ppp.setOpenButtonVisible(true);
+        assertThat(ppp.getOpenButton().getVisibility()).isEqualTo(View.VISIBLE);
+
+        // Hide the button and make sure the view was updated.
+        ppp.setOpenButtonVisible(false);
+        assertThat(ppp.getOpenButton().getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void ensureButtonsClicksCallDelegate_newDesign_buttonsHidden() {
+        if (!PrimaryProviderPreference.shouldUseNewSettingsUi()) {
+            return;
+        }
+
+        PrimaryProviderPreference ppp = createTestPreferenceWithNewLayout();
+
+        // Test that the buttons are visible.
+        assertThat(ppp.getButtonFrameView()).isNotNull();
+        assertThat(ppp.getButtonFrameView().getVisibility()).isEqualTo(View.GONE);
+        assertThat(mReceivedChangeButtonClicked).isFalse();
+
+        // If we show the buttons the visiblility should be updated.
+        ppp.setButtonsVisible(true);
+        assertThat(ppp.getButtonFrameView().getVisibility()).isEqualTo(View.VISIBLE);
+
+        // If we hide the buttons the visibility should be updated.
+        ppp.setButtonsVisible(false);
+        assertThat(ppp.getButtonFrameView().getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void ensureButtonsClicksCallDelegate_oldDesign() {
+        if (PrimaryProviderPreference.shouldUseNewSettingsUi()) {
+            return;
+        }
+
+        PrimaryProviderPreference ppp = createTestPreference("preference_widget_gear");
+
+        // Test that clicking the preference results in the delegate being
+        // called.
+        assertThat(mReceivedOpenButtonClicked).isFalse();
+        ppp.getOnPreferenceClickListener().onPreferenceClick(ppp);
+        assertThat(mReceivedOpenButtonClicked).isTrue();
+
+        // Test that the gear button is present and visible.
+        assertThat(ppp.getGearView()).isNotNull();
+        assertThat(ppp.getGearView().getVisibility()).isEqualTo(View.VISIBLE);
+
+        // Test that clicking the gear button results in the delegate being
+        // called.
+        assertThat(mReceivedChangeButtonClicked).isFalse();
+        ppp.getGearView().performClick();
+        assertThat(mReceivedChangeButtonClicked).isTrue();
+    }
+
+    private PrimaryProviderPreference createTestPreferenceWithNewLayout() {
+        return createTestPreference("preference_credential_manager_with_buttons");
+    }
+
+    private PrimaryProviderPreference createTestPreference(String layoutName) {
+        int layoutId = ResourcesUtils.getResourcesId(mContext, "layout", layoutName);
+        PreferenceViewHolder holder =
+                PreferenceViewHolder.createInstanceForTests(
+                        LayoutInflater.from(mContext).inflate(layoutId, null));
+        PreferenceViewHolder holderForTest = spy(holder);
+        View gearView = new View(mContext, null);
+        int gearId = ResourcesUtils.getResourcesId(mContext, "id", "settings_button");
+        when(holderForTest.findViewById(gearId)).thenReturn(gearView);
+
+        PrimaryProviderPreference ppp = new PrimaryProviderPreference(mContext, mAttributes);
+        ppp.setDelegate(mDelegate);
+        ppp.onBindViewHolder(holderForTest);
+        return ppp;
+    }
+}