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

Commit 65f3c064 authored by Helen Qin's avatar Helen Qin
Browse files

Fix the ui test data flow.

Test: local deployment
Fix: 263161336
Change-Id: I6d7c175e052c0d0debae7f3a4230a0d1d8e5d416
parent b9067b1d
Loading
Loading
Loading
Loading
+99 −124
Original line number Diff line number Diff line
@@ -47,6 +47,10 @@ import com.android.credentialmanager.getflow.GetCredentialUiState
import com.android.credentialmanager.jetpack.developer.CreatePasswordRequest.Companion.toBundle
import com.android.credentialmanager.jetpack.developer.CreatePublicKeyCredentialRequest
import com.android.credentialmanager.jetpack.developer.PublicKeyCredential.Companion.TYPE_PUBLIC_KEY_CREDENTIAL
import com.android.credentialmanager.jetpack.provider.Action
import com.android.credentialmanager.jetpack.provider.CreateEntry
import com.android.credentialmanager.jetpack.provider.CredentialCountInformation
import com.android.credentialmanager.jetpack.provider.CredentialEntry

// Consider repo per screen, similar to view model?
class CredentialManagerRepo(
@@ -63,7 +67,7 @@ class CredentialManagerRepo(
    requestInfo = intent.extras?.getParcelable(
      RequestInfo.EXTRA_REQUEST_INFO,
      RequestInfo::class.java
    ) ?: testCreatePasskeyRequestInfo()
    ) ?: testGetRequestInfo()

    providerEnabledList = when (requestInfo.type) {
      RequestInfo.TYPE_CREATE ->
@@ -265,18 +269,12 @@ class CredentialManagerRepo(
            text: String,
            subtext: String? = null,
    ): Entry {
    val slice = Slice.Builder(
      Entry.CREDENTIAL_MANAGER_ENTRY_URI, SliceSpec(credentialType, 1)
    ).addText(
      text, null, listOf(Entry.HINT_ACTION_TITLE)
    )
    if (subtext != null) {
      slice.addText(subtext, null, listOf(Entry.HINT_ACTION_SUBTEXT))
    }
        val action = Action(text, subtext, null)

        return Entry(
                key,
                subkey,
      slice.build()
                Action.toSlice(action)
        )
    }

@@ -312,26 +310,15 @@ class CredentialManagerRepo(
                intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
                or PendingIntent.FLAG_ONE_SHOT))

    val slice = Slice.Builder(
      Entry.CREDENTIAL_MANAGER_ENTRY_URI, SliceSpec(credentialType, 1)
    ).addText(
      credentialTypeDisplayName, null, listOf(Entry.HINT_CREDENTIAL_TYPE_DISPLAY_NAME)
    ).addText(
      userName, null, listOf(Entry.HINT_USER_NAME)
    ).addIcon(
      Icon.createWithResource(context, R.drawable.ic_passkey),
      null,
      listOf(Entry.HINT_PROFILE_ICON))
    if (userDisplayName != null) {
      slice.addText(userDisplayName, null, listOf(Entry.HINT_PASSKEY_USER_DISPLAY_NAME))
    }
    if (lastUsedTimeMillis != null) {
      slice.addLong(lastUsedTimeMillis, null, listOf(Entry.HINT_LAST_USED_TIME_MILLIS))
    }
        val credentialEntry = CredentialEntry(credentialType, credentialTypeDisplayName, userName,
                userDisplayName, pendingIntent, lastUsedTimeMillis
                ?: 0L, Icon.createWithResource(context, R.drawable.ic_passkey),
                false)

        return Entry(
                key,
                subkey,
      slice.build(),
                CredentialEntry.toSlice(credentialEntry),
                pendingIntent,
                null
        )
@@ -358,33 +345,21 @@ class CredentialManagerRepo(
                TYPE_PASSWORD_CREDENTIAL,
                toBundle("beckett-bakert@gmail.com", "password123")
        )
    val fillInIntent = Intent().putExtra(CredentialProviderService.EXTRA_CREATE_CREDENTIAL_REQUEST,
        val fillInIntent = Intent().putExtra(
                CredentialProviderService.EXTRA_CREATE_CREDENTIAL_REQUEST,
                createPasswordRequest)

    val slice = Slice.Builder(
      Entry.CREDENTIAL_MANAGER_ENTRY_URI, SliceSpec(Entry.VERSION, 1)
    ).addText(
        providerDisplayName, null, listOf(Entry.HINT_USER_PROVIDER_ACCOUNT_NAME))
      .addIcon(
        Icon.createWithResource(context, R.drawable.ic_passkey),
        null,
        listOf(Entry.HINT_CREDENTIAL_TYPE_ICON))
      .addIcon(
        Icon.createWithResource(context, R.drawable.ic_profile),
        null,
        listOf(Entry.HINT_PROFILE_ICON))
      .addInt(
        passwordCount, null, listOf(Entry.HINT_PASSWORD_COUNT))
      .addInt(
        passkeyCount, null, listOf(Entry.HINT_PASSKEY_COUNT))
      .addInt(
        totalCredentialCount, null, listOf(Entry.HINT_TOTAL_CREDENTIAL_COUNT))
      .addLong(lastUsedTimeMillis, null, listOf(Entry.HINT_LAST_USED_TIME_MILLIS))
      .build()
        val createEntry = CreateEntry(
                providerDisplayName, pendingIntent,
                Icon.createWithResource(context, R.drawable.ic_profile), lastUsedTimeMillis,
                listOf(
                        CredentialCountInformation.createPasswordCountInformation(passwordCount),
                        CredentialCountInformation.createPublicKeyCountInformation(passkeyCount),
                ))
        return Entry(
                key,
                subkey,
      slice,
                CreateEntry.toSlice(createEntry),
                pendingIntent,
                fillInIntent,
        )
+29 −22
Original line number Diff line number Diff line
@@ -42,9 +42,10 @@ import com.android.credentialmanager.jetpack.developer.CreateCredentialRequest
import com.android.credentialmanager.jetpack.developer.CreatePasswordRequest
import com.android.credentialmanager.jetpack.developer.CreatePublicKeyCredentialRequest
import com.android.credentialmanager.jetpack.developer.PublicKeyCredential.Companion.TYPE_PUBLIC_KEY_CREDENTIAL
import com.android.credentialmanager.jetpack.provider.ActionUi
import com.android.credentialmanager.jetpack.provider.CredentialEntryUi
import com.android.credentialmanager.jetpack.provider.SaveEntryUi
import com.android.credentialmanager.jetpack.provider.Action
import com.android.credentialmanager.jetpack.provider.CredentialCountInformation
import com.android.credentialmanager.jetpack.provider.CredentialEntry
import com.android.credentialmanager.jetpack.provider.CreateEntry
import org.json.JSONObject

/** Utility functions for converting CredentialManager data structures to or from UI formats. */
@@ -107,7 +108,8 @@ class GetFlowUtils {
      context: Context,
    ): List<CredentialEntryInfo> {
      return credentialEntries.map {
        val credentialEntryUi = CredentialEntryUi.fromSlice(it.slice)
        // TODO: handle NPE gracefully
        val credentialEntry = CredentialEntry.fromSlice(it.slice)!!

        // Consider directly move the UI object into the class.
        return@map CredentialEntryInfo(
@@ -116,14 +118,14 @@ class GetFlowUtils {
          entrySubkey = it.subkey,
          pendingIntent = it.pendingIntent,
          fillInIntent = it.frameworkExtrasIntent,
          credentialType = credentialEntryUi.credentialType.toString(),
          credentialTypeDisplayName = credentialEntryUi.credentialTypeDisplayName.toString(),
          userName = credentialEntryUi.userName.toString(),
          displayName = credentialEntryUi.userDisplayName?.toString(),
          credentialType = credentialEntry.type.toString(),
          credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(),
          userName = credentialEntry.username.toString(),
          displayName = credentialEntry.displayName?.toString(),
          // TODO: proper fallback
          icon = credentialEntryUi.entryIcon?.loadDrawable(context)
          icon = credentialEntry.icon?.loadDrawable(context)
                  ?: context.getDrawable(R.drawable.ic_other_sign_in)!!,
          lastUsedTimeMillis = credentialEntryUi.lastUsedTimeMillis,
          lastUsedTimeMillis = credentialEntry.lastUsedTimeMillis,
        )
      }
    }
@@ -170,7 +172,8 @@ class GetFlowUtils {
      providerIcon: Drawable,
    ): List<ActionEntryInfo> {
      return actionEntries.map {
        val actionEntryUi = ActionUi.fromSlice(it.slice)
        // TODO: handle NPE gracefully
        val actionEntryUi = Action.fromSlice(it.slice)!!

        return@map ActionEntryInfo(
          providerId = providerId,
@@ -178,10 +181,10 @@ class GetFlowUtils {
          entrySubkey = it.subkey,
          pendingIntent = it.pendingIntent,
          fillInIntent = it.frameworkExtrasIntent,
          title = actionEntryUi.text.toString(),
          title = actionEntryUi.title.toString(),
          // TODO: gracefully fail
          icon = providerIcon,
          subTitle = actionEntryUi.subtext?.toString(),
          subTitle = actionEntryUi.subTitle?.toString(),
        )
      }
    }
@@ -383,7 +386,8 @@ class CreateFlowUtils {
      context: Context,
    ): List<CreateOptionInfo> {
      return creationEntries.map {
        val saveEntryUi = SaveEntryUi.fromSlice(it.slice)
        // TODO: handle NPE gracefully
        val createEntry = CreateEntry.fromSlice(it.slice)!!

        return@map CreateOptionInfo(
          // TODO: remove fallbacks
@@ -392,13 +396,16 @@ class CreateFlowUtils {
          entrySubkey = it.subkey,
          pendingIntent = it.pendingIntent,
          fillInIntent = it.frameworkExtrasIntent,
          userProviderDisplayName = saveEntryUi.userProviderAccountName as String,
          profileIcon = saveEntryUi.profileIcon?.loadDrawable(context)
          userProviderDisplayName = createEntry.accountName.toString(),
          profileIcon = createEntry.icon?.loadDrawable(context)
                  ?: requestDisplayInfo.typeIcon,
          passwordCount = saveEntryUi.passwordCount ?: 0,
          passkeyCount = saveEntryUi.passkeyCount ?: 0,
          totalCredentialCount = saveEntryUi.totalCredentialCount ?: 0,
          lastUsedTimeMillis = saveEntryUi.lastUsedTimeMillis ?: 0,
          passwordCount = CredentialCountInformation.getPasswordCount(
                  createEntry.credentialCountInformationList) ?: 0,
          passkeyCount = CredentialCountInformation.getPasskeyCount(
                  createEntry.credentialCountInformationList) ?: 0,
          totalCredentialCount = CredentialCountInformation.getTotalCount(
                  createEntry.credentialCountInformationList) ?: 0,
          lastUsedTimeMillis = createEntry.lastUsedTimeMillis ?: 0,
        )
      }
    }
+55 −0
Original line number Diff line number Diff line
@@ -14,35 +14,42 @@
 * limitations under the License.
 */

package com.android.credentialmanager.jetpack.provider
package com.android.credentialmanager.jetpack.developer

import android.app.slice.Slice
import android.credentials.ui.Entry
import android.os.Bundle

/**
 * UI representation for a credential entry used during the get credential flow.
 *
 * TODO: move to jetpack.
 */
class ActionUi(
  val text: CharSequence,
  val subtext: CharSequence?,
) {
class PasswordCredential constructor(
        val id: String,
        val password: String,
) : Credential(android.credentials.Credential.TYPE_PASSWORD_CREDENTIAL, toBundle(id, password)) {

    init {
        require(password.isNotEmpty()) { "password should not be empty" }
    }

    /** @hide */
    companion object {
    fun fromSlice(slice: Slice): ActionUi {
      var text: CharSequence? = null
      var subtext: CharSequence? = null

      val items = slice.items
      items.forEach {
        if (it.hasHint(Entry.HINT_ACTION_TITLE)) {
          text = it.text
        } else if (it.hasHint(Entry.HINT_ACTION_SUBTEXT)) {
          subtext = it.text

        const val BUNDLE_KEY_ID = "androidx.credentials.BUNDLE_KEY_ID"
        const val BUNDLE_KEY_PASSWORD = "androidx.credentials.BUNDLE_KEY_PASSWORD"

        @JvmStatic
        internal fun toBundle(id: String, password: String): Bundle {
            val bundle = Bundle()
            bundle.putString(BUNDLE_KEY_ID, id)
            bundle.putString(BUNDLE_KEY_PASSWORD, password)
            return bundle
        }

        @JvmStatic
        internal fun createFrom(data: Bundle): PasswordCredential {
            try {
                val id = data.getString(BUNDLE_KEY_ID)
                val password = data.getString(BUNDLE_KEY_PASSWORD)
                return PasswordCredential(id!!, password!!)
            } catch (e: Exception) {
                throw FrameworkClassParsingException()
            }
      // TODO: fail NPE more elegantly.
      return ActionUi(text!!, subtext)
        }
    }
}
 No newline at end of file
+100 −0
Original line number Diff line number Diff line
@@ -16,65 +16,85 @@

package com.android.credentialmanager.jetpack.provider

import android.annotation.SuppressLint
import android.app.PendingIntent
import android.app.slice.Slice
import android.graphics.drawable.Icon
import android.app.slice.SliceSpec
import android.net.Uri
import android.util.Log
import java.util.Collections

/**
 * UI representation for a save entry used during the create credential flow.
 * UI representation for a credential entry used during the get credential flow.
 *
 * TODO: move to jetpack.
 */
class SaveEntryUi(
  val userProviderAccountName: CharSequence?,
  val credentialTypeIcon: Icon?,
  val profileIcon: Icon?,
  val passwordCount: Int?,
  val passkeyCount: Int?,
  val totalCredentialCount: Int?,
  val lastUsedTimeMillis: Long?,
class Action constructor(
        val title: CharSequence,
        val subTitle: CharSequence?,
        val pendingIntent: PendingIntent?,
) {

  init {
    require(title.isNotEmpty()) { "title must not be empty" }
  }

  companion object {
    const val SLICE_HINT_ACCOUNT_NAME =
            "androidx.credentials.provider.createEntry.SLICE_HINT_USER_PROVIDER_ACCOUNT_NAME"
    const val SLICE_HINT_ICON =
            "androidx.credentials.provider.createEntry.SLICE_HINT_PROFILE_ICON"
    const val SLICE_HINT_CREDENTIAL_COUNT_INFORMATION =
            "androidx.credentials.provider.createEntry.SLICE_HINT_CREDENTIAL_COUNT_INFORMATION"
    const val SLICE_HINT_LAST_USED_TIME_MILLIS =
            "androidx.credentials.provider.createEntry.SLICE_HINT_LAST_USED_TIME_MILLIS"
    const val SLICE_HINT_PENDING_INTENT =
            "androidx.credentials.provider.createEntry.SLICE_HINT_PENDING_INTENT"
    private const val TAG = "Action"
    internal const val SLICE_HINT_TITLE =
            "androidx.credentials.provider.action.HINT_ACTION_TITLE"
    internal const val SLICE_HINT_SUBTITLE =
            "androidx.credentials.provider.action.HINT_ACTION_SUBTEXT"
    internal const val SLICE_HINT_PENDING_INTENT =
            "androidx.credentials.provider.action.SLICE_HINT_PENDING_INTENT"

    @JvmStatic
    fun toSlice(action: Action): Slice {
      // TODO("Put the right spec and version value")
      val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec("type", 1))
              .addText(action.title, /*subType=*/null,
                      listOf(SLICE_HINT_TITLE))
              .addText(action.subTitle, /*subType=*/null,
                      listOf(SLICE_HINT_SUBTITLE))
      if (action.pendingIntent != null) {
        sliceBuilder.addAction(action.pendingIntent,
                Slice.Builder(sliceBuilder)
                        .addHints(Collections.singletonList(SLICE_HINT_PENDING_INTENT))
                        .build(),
                /*subType=*/null)
      }
      return sliceBuilder.build()
    }

    /**
     * Returns an instance of [SaveEntryUi] derived from a [Slice] object.
     * Returns an instance of [Action] derived from a [Slice] object.
     *
     * @param slice the [Slice] object constructed through the jetpack library
     * @param slice the [Slice] object constructed through [toSlice]
     */
    @SuppressLint("WrongConstant") // custom conversion between jetpack and framework
    @JvmStatic
    fun fromSlice(slice: Slice): SaveEntryUi {
      var accountName: CharSequence? = null
      var icon: Icon? = null
    fun fromSlice(slice: Slice): Action? {
      // TODO("Put the right spec and version value")
      var title: CharSequence = ""
      var subTitle: CharSequence? = null
      var pendingIntent: PendingIntent? = null
      var lastUsedTimeMillis: Long = 0

      slice.items.forEach {
        if (it.hasHint(SLICE_HINT_ACCOUNT_NAME)) {
          accountName = it.text
        } else if (it.hasHint(SLICE_HINT_ICON)) {
          icon = it.icon
        if (it.hasHint(SLICE_HINT_TITLE)) {
          title = it.text
        } else if (it.hasHint(SLICE_HINT_SUBTITLE)) {
          subTitle = it.text
        } else if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {
          pendingIntent = it.action
        } else if (it.hasHint(SLICE_HINT_LAST_USED_TIME_MILLIS)) {
          lastUsedTimeMillis = it.long
        }
      }

      return SaveEntryUi(
              // TODO: Add count parsing
              accountName!!, icon, icon,
              0, 0, 0, lastUsedTimeMillis,
      )
      return try {
        Action(title, subTitle, pendingIntent)
      } catch (e: Exception) {
        Log.i(TAG, "fromSlice failed with: " + e.message)
        null
      }
    }
  }
}
+226 −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.jetpack.provider

import android.annotation.SuppressLint
import android.app.PendingIntent
import android.app.slice.Slice
import android.app.slice.SliceSpec
import android.graphics.drawable.Icon
import android.net.Uri
import android.os.Bundle
import android.util.Log
import java.util.Collections

/**
 * UI representation for a save entry used during the create credential flow.
 *
 * TODO: move to jetpack.
 */
class CreateEntry internal constructor(
        val accountName: CharSequence,
        val pendingIntent: PendingIntent?,
        val icon: Icon?,
        val lastUsedTimeMillis: Long,
        val credentialCountInformationList: List<CredentialCountInformation>
) {

  init {
    require(accountName.isNotEmpty()) { "accountName must not be empty" }
  }

  /**
   * A builder for [CreateEntry]
   *
   * @property accountName the name of the account where the credential will be registered
   * @property pendingIntent the [PendingIntent] that will be fired when the user selects
   * this entry
   *
   * @hide
   */
  class Builder constructor(
          private val accountName: CharSequence,
          private val pendingIntent: PendingIntent? = null
  ) {

    private var credentialCountInformationList: MutableList<CredentialCountInformation> =
            mutableListOf()
    private var icon: Icon? = null
    private var lastUsedTimeMillis: Long = 0

    /** Adds a [CredentialCountInformation] denoting a given credential
     * type and the count of credentials that the provider has stored for that
     * credential type.
     *
     * This information will be displayed on the [CreateEntry] to help the user
     * make a choice.
     */
    @Suppress("MissingGetterMatchingBuilder")
    fun addCredentialCountInformation(info: CredentialCountInformation): Builder {
      credentialCountInformationList.add(info)
      return this
    }

    /** Sets a list of [CredentialCountInformation]. Each item in the list denotes a given
     * credential type and the count of credentials that the provider has stored of that
     * credential type.
     *
     * This information will be displayed on the [CreateEntry] to help the user
     * make a choice.
     */
    fun setCredentialCountInformationList(infoList: List<CredentialCountInformation>): Builder {
      credentialCountInformationList = infoList as MutableList<CredentialCountInformation>
      return this
    }

    /** Sets an icon to be displayed with the entry on the UI */
    fun setIcon(icon: Icon?): Builder {
      this.icon = icon
      return this
    }

    /** Sets the last time this account was used */
    fun setLastUsedTimeMillis(lastUsedTimeMillis: Long): Builder {
      this.lastUsedTimeMillis = lastUsedTimeMillis
      return this
    }

    /**
     * Builds an instance of [CreateEntry]
     *
     * @throws IllegalArgumentException If [accountName] is empty
     */
    fun build(): CreateEntry {
      return CreateEntry(accountName, pendingIntent, icon, lastUsedTimeMillis,
              credentialCountInformationList)
    }
  }

  companion object {
    private const val TAG = "CreateEntry"
    internal const val SLICE_HINT_ACCOUNT_NAME =
            "androidx.credentials.provider.createEntry.SLICE_HINT_USER_PROVIDER_ACCOUNT_NAME"
    internal const val SLICE_HINT_ICON =
            "androidx.credentials.provider.createEntry.SLICE_HINT_PROFILE_ICON"
    internal const val SLICE_HINT_CREDENTIAL_COUNT_INFORMATION =
            "androidx.credentials.provider.createEntry.SLICE_HINT_CREDENTIAL_COUNT_INFORMATION"
    internal const val SLICE_HINT_LAST_USED_TIME_MILLIS =
            "androidx.credentials.provider.createEntry.SLICE_HINT_LAST_USED_TIME_MILLIS"
    internal const val SLICE_HINT_PENDING_INTENT =
            "androidx.credentials.provider.createEntry.SLICE_HINT_PENDING_INTENT"

    @JvmStatic
    fun toSlice(createEntry: CreateEntry): Slice {
      // TODO("Use the right type and revision")
      val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec("type", 1))
      sliceBuilder.addText(createEntry.accountName, /*subType=*/null,
              listOf(SLICE_HINT_ACCOUNT_NAME))
              .addLong(createEntry.lastUsedTimeMillis, /*subType=*/null, listOf(
                      SLICE_HINT_LAST_USED_TIME_MILLIS))
      if (createEntry.icon != null) {
        sliceBuilder.addIcon(createEntry.icon, /*subType=*/null,
                listOf(SLICE_HINT_ICON))
      }

      val credentialCountBundle = convertCredentialCountInfoToBundle(
              createEntry.credentialCountInformationList)
      if (credentialCountBundle != null) {
        sliceBuilder.addBundle(convertCredentialCountInfoToBundle(
                createEntry.credentialCountInformationList), null, listOf(
                SLICE_HINT_CREDENTIAL_COUNT_INFORMATION))
      }
      if (createEntry.pendingIntent != null) {
        sliceBuilder.addAction(createEntry.pendingIntent,
                Slice.Builder(sliceBuilder)
                        .addHints(Collections.singletonList(SLICE_HINT_PENDING_INTENT))
                        .build(),
                /*subType=*/null)
      }
      return sliceBuilder.build()
    }

    /**
     * Returns an instance of [CreateEntry] derived from a [Slice] object.
     *
     * @param slice the [Slice] object constructed through [toSlice]
     */
    @SuppressLint("WrongConstant") // custom conversion between jetpack and framework
    @JvmStatic
    fun fromSlice(slice: Slice): CreateEntry? {
      // TODO("Put the right spec and version value")
      var accountName: CharSequence = ""
      var icon: Icon? = null
      var pendingIntent: PendingIntent? = null
      var credentialCountInfo: List<CredentialCountInformation> = listOf()
      var lastUsedTimeMillis: Long = 0

      slice.items.forEach {
        if (it.hasHint(SLICE_HINT_ACCOUNT_NAME)) {
          accountName = it.text
        } else if (it.hasHint(SLICE_HINT_ICON)) {
          icon = it.icon
        } else if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {
          pendingIntent = it.action
        } else if (it.hasHint(SLICE_HINT_CREDENTIAL_COUNT_INFORMATION)) {
          credentialCountInfo = convertBundleToCredentialCountInfo(it.bundle)
        } else if (it.hasHint(SLICE_HINT_LAST_USED_TIME_MILLIS)) {
          lastUsedTimeMillis = it.long
        }
      }

      return try {
        CreateEntry(accountName, pendingIntent, icon,
                lastUsedTimeMillis, credentialCountInfo)
      } catch (e: Exception) {
        Log.i(TAG, "fromSlice failed with: " + e.message)
        null
      }
    }

    @JvmStatic
    internal fun convertBundleToCredentialCountInfo(bundle: Bundle?):
            List<CredentialCountInformation> {
      val credentialCountList = ArrayList<CredentialCountInformation>()
      if (bundle == null) {
        return credentialCountList
      }
      bundle.keySet().forEach {
        try {
          credentialCountList.add(
                  CredentialCountInformation(it, bundle.getInt(it)))
        } catch (e: Exception) {
          Log.i(TAG, "Issue unpacking credential count info bundle: " + e.message)
        }
      }
      return credentialCountList
    }

    @JvmStatic
    internal fun convertCredentialCountInfoToBundle(
            credentialCountInformationList: List<CredentialCountInformation>
    ): Bundle? {
      if (credentialCountInformationList.isEmpty()) {
        return null
      }
      val bundle = Bundle()
      credentialCountInformationList.forEach {
        bundle.putInt(it.type, it.count)
      }
      return bundle
    }
  }
}
Loading