Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6a27a601 authored by Helen Qin's avatar Helen Qin Committed by Android (Google) Code Review
Browse files

Merge "Use the launch intent data in the ux."

parents eb5757ca 0bc03e30
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -36,6 +36,12 @@ public class RequestInfo implements Parcelable {
     */
    public static final @NonNull String EXTRA_REQUEST_INFO =
            "android.credentials.ui.extra.REQUEST_INFO";
    /**
     * The intent extra key for the {@code ResultReceiver} object when launching the UX
     * activities.
     */
    public static final @NonNull String EXTRA_RESULT_RECEIVER =
            "android.credentials.ui.extra.RESULT_RECEIVER";

    /** Type value for an executeGetCredential request. */
    public static final @NonNull String TYPE_GET = "android.credentials.ui.TYPE_GET";
+55 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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

import android.app.slice.Slice
import android.credentials.ui.Entry
import android.graphics.drawable.Icon

/**
 * UI representation for a credential entry used during the get credential flow.
 *
 * TODO: move to jetpack.
 */
class CredentialEntryUi(
  val userName: CharSequence,
  val displayName: CharSequence?,
  val icon: Icon?,
  // TODO: add last used.
) {
  companion object {
    fun fromSlice(slice: Slice): CredentialEntryUi {
      val items = slice.items

      var title: String? = null
      var subTitle: String? = null
      var icon: Icon? = null

      items.forEach {
        if (it.hasHint(Entry.HINT_ICON)) {
          icon = it.icon
        } else if (it.hasHint(Entry.HINT_SUBTITLE)) {
          subTitle = it.text.toString()
        } else if (it.hasHint(Entry.HINT_TITLE)) {
          title = it.text.toString()
        }
      }
      // TODO: fail NPE more elegantly.
      return CredentialEntryUi(title!!, subTitle, icon)
    }
  }
}
+49 −142
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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

import android.app.slice.Slice
@@ -9,11 +25,8 @@ import android.credentials.ui.ProviderData
import android.credentials.ui.RequestInfo
import android.graphics.drawable.Icon
import android.os.Binder
import com.android.credentialmanager.createflow.CreateOptionInfo
import com.android.credentialmanager.createflow.CreatePasskeyUiState
import com.android.credentialmanager.createflow.CreateScreenState
import com.android.credentialmanager.createflow.ProviderInfo
import com.android.credentialmanager.getflow.CredentialOptionInfo
import com.android.credentialmanager.getflow.GetCredentialUiState
import com.android.credentialmanager.getflow.GetScreenState

@@ -41,6 +54,39 @@ class CredentialManagerRepo(
    ) ?: testProviderList()
  }

  fun getCredentialInitialUiState(): GetCredentialUiState {
    val providerList = GetFlowUtils.toProviderList(providerList, context)
    return GetCredentialUiState(
      providerList,
      GetScreenState.CREDENTIAL_SELECTION,
      providerList.first()
    )
  }

  fun createPasskeyInitialUiState(): CreatePasskeyUiState {
    val providerList = CreateFlowUtils.toProviderList(providerList, context)
    return CreatePasskeyUiState(
      providers = providerList,
      currentScreenState = CreateScreenState.PASSKEY_INTRO,
    )
  }

  companion object {
    lateinit var repo: CredentialManagerRepo

    fun setup(
      context: Context,
      intent: Intent,
    ) {
      repo = CredentialManagerRepo(context, intent)
    }

    fun getInstance(): CredentialManagerRepo {
      return repo
    }
  }

  // TODO: below are prototype functionalities. To be removed for productionization.
  private fun testProviderList(): List<ProviderData> {
    return listOf(
      ProviderData(
@@ -94,143 +140,4 @@ class CredentialManagerRepo(
      slice
    )
  }

  private fun getCredentialProviderList():
    List<com.android.credentialmanager.getflow.ProviderInfo> {
      return listOf(
        com.android.credentialmanager.getflow.ProviderInfo(
          icon = context.getDrawable(R.drawable.ic_passkey)!!,
          name = "Google Password Manager",
          appDomainName = "tribank.us",
          credentialTypeIcon = context.getDrawable(R.drawable.ic_passkey)!!,
          credentialOptions = listOf(
            CredentialOptionInfo(
              icon = context.getDrawable(R.drawable.ic_passkey)!!,
              title = "Elisa Backett",
              subtitle = "elisa.beckett@gmail.com",
              id = "id-1",
            ),
            CredentialOptionInfo(
              icon = context.getDrawable(R.drawable.ic_passkey)!!,
              title = "Elisa Backett Work",
              subtitle = "elisa.beckett.work@google.com",
              id = "id-2",
            ),
          )
        ),
        com.android.credentialmanager.getflow.ProviderInfo(
          icon = context.getDrawable(R.drawable.ic_passkey)!!,
          name = "Lastpass",
          appDomainName = "tribank.us",
          credentialTypeIcon = context.getDrawable(R.drawable.ic_passkey)!!,
          credentialOptions = listOf(
            CredentialOptionInfo(
              icon = context.getDrawable(R.drawable.ic_passkey)!!,
              title = "Elisa Backett",
              subtitle = "elisa.beckett@lastpass.com",
              id = "id-1",
            ),
          )
        ),
        com.android.credentialmanager.getflow.ProviderInfo(
          icon = context.getDrawable(R.drawable.ic_passkey)!!,
          name = "Dashlane",
          appDomainName = "tribank.us",
          credentialTypeIcon = context.getDrawable(R.drawable.ic_passkey)!!,
          credentialOptions = listOf(
            CredentialOptionInfo(
              icon = context.getDrawable(R.drawable.ic_passkey)!!,
              title = "Elisa Backett",
              subtitle = "elisa.beckett@dashlane.com",
              id = "id-1",
            ),
          )
        ),
      )
  }

  private fun createCredentialProviderList(): List<ProviderInfo> {
    return listOf(
      ProviderInfo(
        icon = context.getDrawable(R.drawable.ic_passkey)!!,
        name = "Google Password Manager",
        appDomainName = "tribank.us",
        credentialTypeIcon = context.getDrawable(R.drawable.ic_passkey)!!,
        createOptions = listOf(
          CreateOptionInfo(
            icon = context.getDrawable(R.drawable.ic_passkey)!!,
            title = "Elisa Backett",
            subtitle = "elisa.beckett@gmail.com",
            id = "id-1",
          ),
          CreateOptionInfo(
            icon = context.getDrawable(R.drawable.ic_passkey)!!,
            title = "Elisa Backett Work",
            subtitle = "elisa.beckett.work@google.com",
            id = "id-2",
          ),
        )
      ),
      ProviderInfo(
        icon = context.getDrawable(R.drawable.ic_passkey)!!,
        name = "Lastpass",
        appDomainName = "tribank.us",
        credentialTypeIcon = context.getDrawable(R.drawable.ic_passkey)!!,
        createOptions = listOf(
          CreateOptionInfo(
            icon = context.getDrawable(R.drawable.ic_passkey)!!,
            title = "Elisa Backett",
            subtitle = "elisa.beckett@lastpass.com",
            id = "id-1",
          ),
        )
      ),
      ProviderInfo(
        icon = context.getDrawable(R.drawable.ic_passkey)!!,
        name = "Dashlane",
        appDomainName = "tribank.us",
        credentialTypeIcon = context.getDrawable(R.drawable.ic_passkey)!!,
        createOptions = listOf(
          CreateOptionInfo(
            icon = context.getDrawable(R.drawable.ic_passkey)!!,
            title = "Elisa Backett",
            subtitle = "elisa.beckett@dashlane.com",
            id = "id-1",
          ),
        )
      ),
    )
  }

  fun getCredentialInitialUiState(): GetCredentialUiState {
    val providerList = getCredentialProviderList()
    return GetCredentialUiState(
      providerList,
      GetScreenState.CREDENTIAL_SELECTION,
      providerList.first()
    )
  }

  fun createPasskeyInitialUiState(): CreatePasskeyUiState {
    val providerList = createCredentialProviderList()
    return CreatePasskeyUiState(
      providers = providerList,
      currentScreenState = CreateScreenState.PASSKEY_INTRO,
    )
  }

  companion object {
    lateinit var repo: CredentialManagerRepo

    fun setup(
      context: Context,
      intent: Intent,
    ) {
      repo = CredentialManagerRepo(context, intent)
    }

    fun getInstance(): CredentialManagerRepo {
      return repo
    }
  }
}
+31 −8
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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

import android.credentials.ui.RequestInfo
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
@@ -16,14 +33,20 @@ class CredentialSelectorActivity : ComponentActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    CredentialManagerRepo.setup(this, intent)
    val startDestination = intent.extras?.getString(
      "start_destination",
      "CREATE_PASSKEY"
    ) ?: "CREATE_PASSKEY"

    val requestInfo = intent.extras?.getParcelable<RequestInfo>(RequestInfo.EXTRA_REQUEST_INFO)
    if (requestInfo != null) {
      val requestType = requestInfo.type
      setContent {
        CredentialSelectorTheme {
        CredentialManagerBottomSheet(startDestination)
          CredentialManagerBottomSheet(requestType)
        }
      }
    } else {
      // TODO: prototype only code to be removed. In production should exit.
      setContent {
        CredentialSelectorTheme {
          CredentialManagerBottomSheet(RequestInfo.TYPE_CREATE)
        }
      }
    }
  }
+106 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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

import android.content.Context
import android.credentials.ui.Entry
import android.credentials.ui.ProviderData
import com.android.credentialmanager.createflow.CreateOptionInfo
import com.android.credentialmanager.getflow.CredentialOptionInfo
import com.android.credentialmanager.getflow.ProviderInfo

/** Utility functions for converting CredentialManager data structures to or from UI formats. */
class GetFlowUtils {
  companion object {

    fun toProviderList(
      providerDataList: List<ProviderData>,
      context: Context,
    ): List<ProviderInfo> {
      return providerDataList.map {
        ProviderInfo(
          // TODO: replace to extract from the service data structure when available
          icon = context.getDrawable(R.drawable.ic_passkey)!!,
          name = it.packageName,
          appDomainName = "tribank.us",
          credentialTypeIcon = context.getDrawable(R.drawable.ic_passkey)!!,
          credentialOptions = toCredentialOptionInfoList(it.credentialEntries, context)
        )
      }
    }


    /* From service data structure to UI credential entry list representation. */
    private fun toCredentialOptionInfoList(
      credentialEntries: List<Entry>,
      context: Context,
    ): List<CredentialOptionInfo> {
      return credentialEntries.map {
        val credentialEntryUi = CredentialEntryUi.fromSlice(it.slice)

        // Consider directly move the UI object into the class.
        return@map CredentialOptionInfo(
          // TODO: remove fallbacks
          icon = credentialEntryUi.icon?.loadDrawable(context)
            ?: context.getDrawable(R.drawable.ic_passkey)!!,
          title = credentialEntryUi.userName.toString(),
          subtitle = credentialEntryUi.displayName?.toString() ?: "Unknown display name",
          id = it.entryId,
        )
      }
    }
  }
}

class CreateFlowUtils {
  companion object {

    fun toProviderList(
      providerDataList: List<ProviderData>,
      context: Context,
    ): List<com.android.credentialmanager.createflow.ProviderInfo> {
      return providerDataList.map {
        com.android.credentialmanager.createflow.ProviderInfo(
          // TODO: replace to extract from the service data structure when available
          icon = context.getDrawable(R.drawable.ic_passkey)!!,
          name = it.packageName,
          appDomainName = "tribank.us",
          credentialTypeIcon = context.getDrawable(R.drawable.ic_passkey)!!,
          createOptions = toCreationOptionInfoList(it.credentialEntries, context),
        )
      }
    }

    private fun toCreationOptionInfoList(
      creationEntries: List<Entry>,
      context: Context,
    ): List<CreateOptionInfo> {
      return creationEntries.map {
        val saveEntryUi = SaveEntryUi.fromSlice(it.slice)

        return@map CreateOptionInfo(
          // TODO: remove fallbacks
          icon = saveEntryUi.icon?.loadDrawable(context)
            ?: context.getDrawable(R.drawable.ic_passkey)!!,
          title = saveEntryUi.title.toString(),
          subtitle = saveEntryUi.subTitle?.toString() ?: "Unknown subtitle",
          id = it.entryId,
        )
      }
    }
  }
}
Loading