1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.credentialmanager.createflow
18 
19 import android.app.PendingIntent
20 import android.content.Intent
21 import android.graphics.drawable.Drawable
22 import com.android.credentialmanager.common.BaseEntry
23 import com.android.credentialmanager.common.CredentialType
24 import java.time.Instant
25 
26 data class CreateCredentialUiState(
27   val enabledProviders: List<EnabledProviderInfo>,
28   val disabledProviders: List<DisabledProviderInfo>? = null,
29   val currentScreenState: CreateScreenState,
30   val requestDisplayInfo: RequestDisplayInfo,
31   val sortedCreateOptionsPairs: List<Pair<CreateOptionInfo, EnabledProviderInfo>>,
32   val activeEntry: ActiveEntry? = null,
33   val remoteEntry: RemoteInfo? = null,
34   val foundCandidateFromUserDefaultProvider: Boolean,
35 )
36 
37 internal fun isFlowAutoSelectable(
38     uiState: CreateCredentialUiState
39 ): Boolean {
40   return uiState.requestDisplayInfo.isAutoSelectRequest &&
41       // Even if the flow is auto selectable, still allow passkey intro screen to show once if
42       // applicable.
43       uiState.currentScreenState != CreateScreenState.PASSKEY_INTRO &&
44       uiState.currentScreenState != CreateScreenState.MORE_ABOUT_PASSKEYS_INTRO &&
45       uiState.sortedCreateOptionsPairs.size == 1 &&
46       uiState.activeEntry?.activeEntryInfo?.let {
47         it is CreateOptionInfo && it.allowAutoSelect
48       } ?: false
49 }
50 
51 internal fun hasContentToDisplay(state: CreateCredentialUiState): Boolean {
52     return state.sortedCreateOptionsPairs.isNotEmpty() ||
53         (!state.requestDisplayInfo.preferImmediatelyAvailableCredentials &&
54             state.remoteEntry != null)
55 }
56 
57 open class ProviderInfo(
58   val icon: Drawable,
59   val id: String,
60   val displayName: String,
61 )
62 
63 class EnabledProviderInfo(
64     icon: Drawable,
65     id: String,
66     displayName: String,
67     // Sorted by last used time
68     var sortedCreateOptions: List<CreateOptionInfo>,
69     var remoteEntry: RemoteInfo?,
70 ) : ProviderInfo(icon, id, displayName)
71 
72 class DisabledProviderInfo(
73   icon: Drawable,
74   id: String,
75   displayName: String,
76 ) : ProviderInfo(icon, id, displayName)
77 
78 class CreateOptionInfo(
79     providerId: String,
80     entryKey: String,
81     entrySubkey: String,
82     pendingIntent: PendingIntent?,
83     fillInIntent: Intent?,
84     val userProviderDisplayName: String,
85     val profileIcon: Drawable?,
86     val passwordCount: Int?,
87     val passkeyCount: Int?,
88     val totalCredentialCount: Int?,
89     val lastUsedTime: Instant,
90     val footerDescription: String?,
91     val allowAutoSelect: Boolean,
92 ) : BaseEntry(
93     providerId,
94     entryKey,
95     entrySubkey,
96     pendingIntent,
97     fillInIntent,
98     shouldTerminateUiUponSuccessfulProviderResult = true,
99 )
100 
101 class RemoteInfo(
102   providerId: String,
103   entryKey: String,
104   entrySubkey: String,
105   pendingIntent: PendingIntent?,
106   fillInIntent: Intent?,
107 ) : BaseEntry(
108     providerId,
109     entryKey,
110     entrySubkey,
111     pendingIntent,
112     fillInIntent,
113     shouldTerminateUiUponSuccessfulProviderResult = true,
114 )
115 
116 data class RequestDisplayInfo(
117   val title: String,
118   val subtitle: String?,
119   val type: CredentialType,
120   val appName: String,
121   val typeIcon: Drawable,
122   val preferImmediatelyAvailableCredentials: Boolean,
123   val appPreferredDefaultProviderId: String?,
124   val userSetDefaultProviderIds: Set<String>,
125   // Whether the given CreateCredentialRequest allows auto select.
126   val isAutoSelectRequest: Boolean,
127 )
128 
129 /**
130  * This is initialized to be the most recent used. Can then be changed if
131  * user selects a different entry on the more option page.
132  */
133 data class ActiveEntry (
134   val activeProvider: EnabledProviderInfo,
135   val activeEntryInfo: BaseEntry,
136 )
137 
138 /** The name of the current screen. */
139 enum class CreateScreenState {
140   PASSKEY_INTRO,
141   MORE_ABOUT_PASSKEYS_INTRO,
142   CREATION_OPTION_SELECTION,
143   MORE_OPTIONS_SELECTION,
144   DEFAULT_PROVIDER_CONFIRMATION,
145   EXTERNAL_ONLY_SELECTION,
146 }
147