diff --git a/MobileSdk/build.gradle.kts b/MobileSdk/build.gradle.kts index 62fb571..0507d87 100644 --- a/MobileSdk/build.gradle.kts +++ b/MobileSdk/build.gradle.kts @@ -118,7 +118,7 @@ android { } dependencies { - api("com.spruceid.mobile.sdk.rs:mobilesdkrs:0.0.30") + api("com.spruceid.mobile.sdk.rs:mobilesdkrs:0.0.31") //noinspection GradleCompatible implementation("com.android.support:appcompat-v7:28.0.0") /* Begin UI dependencies */ diff --git a/MobileSdk/src/main/java/com/spruceid/mobile/sdk/MDoc.kt b/MobileSdk/src/main/java/com/spruceid/mobile/sdk/MDoc.kt index c7e1c53..7e2529e 100644 --- a/MobileSdk/src/main/java/com/spruceid/mobile/sdk/MDoc.kt +++ b/MobileSdk/src/main/java/com/spruceid/mobile/sdk/MDoc.kt @@ -1,14 +1,14 @@ package com.spruceid.mobile.sdk import android.util.Log -import com.spruceid.mobile.sdk.rs.MDoc as InnerMDoc +import com.spruceid.mobile.sdk.rs.Mdoc as InnerMDoc class MDoc(id: String, issuerAuth: ByteArray, val keyAlias: String) : BaseCredential(id) { val inner: InnerMDoc init { try { - inner = InnerMDoc.fromCbor(issuerAuth) + inner = InnerMDoc.fromCborEncodedDocument(issuerAuth, keyAlias) } catch (e: Throwable) { Log.e("MDoc.init", e.toString()) throw e diff --git a/example/src/main/AndroidManifest.xml b/example/src/main/AndroidManifest.xml index 4ace2ee..497c108 100644 --- a/example/src/main/AndroidManifest.xml +++ b/example/src/main/AndroidManifest.xml @@ -20,6 +20,7 @@ + + + + + + + + + diff --git a/example/src/main/ic_launcher-playstore.png b/example/src/main/ic_launcher-playstore.png new file mode 100644 index 0000000..f1304ef Binary files /dev/null and b/example/src/main/ic_launcher-playstore.png differ diff --git a/example/src/main/java/com/spruceid/mobilesdkexample/HomeView.kt b/example/src/main/java/com/spruceid/mobilesdkexample/HomeView.kt index 7bcaf91..ae6bad2 100644 --- a/example/src/main/java/com/spruceid/mobilesdkexample/HomeView.kt +++ b/example/src/main/java/com/spruceid/mobilesdkexample/HomeView.kt @@ -30,6 +30,8 @@ import com.spruceid.mobilesdkexample.ui.theme.Bg import com.spruceid.mobilesdkexample.ui.theme.Inter import com.spruceid.mobilesdkexample.ui.theme.MobileSdkTheme import com.spruceid.mobilesdkexample.verifier.VerifierHomeView +import com.spruceid.mobilesdkexample.viewmodels.IRawCredentialsViewModel +import com.spruceid.mobilesdkexample.viewmodels.RawCredentialsViewModelPreview import com.spruceid.mobilesdkexample.wallet.WalletHomeView enum class HomeTabs { @@ -39,7 +41,8 @@ enum class HomeTabs { @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") @Composable fun HomeView( - navController: NavController + navController: NavController, + rawCredentialsViewModel: IRawCredentialsViewModel ) { var tab by remember { mutableStateOf(HomeTabs.WALLET) @@ -88,7 +91,10 @@ fun HomeView( ) { Box(modifier = Modifier.padding(bottom = 30.dp)) { if (tab == HomeTabs.WALLET) { - WalletHomeView() + WalletHomeView( + navController, + rawCredentialsViewModel + ) } else { VerifierHomeView(navController = navController) } @@ -101,6 +107,6 @@ fun HomeView( fun HomeViewPreview() { var navController: NavHostController = rememberNavController() MobileSdkTheme { - HomeView(navController) + HomeView(navController, RawCredentialsViewModelPreview()) } } \ No newline at end of file diff --git a/example/src/main/java/com/spruceid/mobilesdkexample/MainActivity.kt b/example/src/main/java/com/spruceid/mobilesdkexample/MainActivity.kt index a964878..92afdbd 100644 --- a/example/src/main/java/com/spruceid/mobilesdkexample/MainActivity.kt +++ b/example/src/main/java/com/spruceid/mobilesdkexample/MainActivity.kt @@ -6,16 +6,19 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge +import androidx.activity.viewModels import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.Surface import androidx.compose.ui.Modifier import androidx.navigation.NavHostController import androidx.navigation.compose.rememberNavController import com.spruceid.mobilesdkexample.db.AppDatabase -import com.spruceid.mobilesdkexample.db.VerificationActivityLogsRepository +import com.spruceid.mobilesdkexample.db.RawCredentialsRepository import com.spruceid.mobilesdkexample.navigation.SetupNavGraph import com.spruceid.mobilesdkexample.ui.theme.Bg import com.spruceid.mobilesdkexample.ui.theme.MobileSdkTheme +import com.spruceid.mobilesdkexample.viewmodels.IRawCredentialsViewModel +import com.spruceid.mobilesdkexample.viewmodels.RawCredentialsViewModelFactory class MainActivity : ComponentActivity() { private lateinit var navController: NavHostController @@ -38,9 +41,17 @@ class MainActivity : ComponentActivity() { ) { navController = rememberNavController() - SetupNavGraph(navController = navController) + val credentialsViewModel: IRawCredentialsViewModel by viewModels { + RawCredentialsViewModelFactory((application as MainApplication).rawCredentialsRepository) + } + SetupNavGraph(navController, credentialsViewModel) } } } } +} + +class MainApplication : Application() { + val db by lazy { AppDatabase.getDatabase(applicationContext) } + val rawCredentialsRepository by lazy { RawCredentialsRepository(db.rawCredentialsDao()) } } \ No newline at end of file diff --git a/example/src/main/java/com/spruceid/mobilesdkexample/db/AppDatabase.kt b/example/src/main/java/com/spruceid/mobilesdkexample/db/AppDatabase.kt index 45f0214..f235e32 100644 --- a/example/src/main/java/com/spruceid/mobilesdkexample/db/AppDatabase.kt +++ b/example/src/main/java/com/spruceid/mobilesdkexample/db/AppDatabase.kt @@ -6,10 +6,17 @@ import androidx.room.Room import androidx.room.RoomDatabase import androidx.room.TypeConverters -@Database(entities = [VerificationActivityLogs::class], version = 1) +@Database( + entities = [ + VerificationActivityLogs::class, + RawCredentials::class, + ], + version = 2 +) @TypeConverters(*[DateConverter::class]) abstract class AppDatabase : RoomDatabase() { abstract fun verificationActivityLogsDao(): VerificationActivityLogsDao + abstract fun rawCredentialsDao(): RawCredentialsDao companion object { @Volatile @@ -21,7 +28,7 @@ abstract class AppDatabase : RoomDatabase() { Room.databaseBuilder( context.applicationContext, AppDatabase::class.java, - "verification_activity_logs", + "referenceAppDb", ) .allowMainThreadQueries() .build() diff --git a/example/src/main/java/com/spruceid/mobilesdkexample/db/Daos.kt b/example/src/main/java/com/spruceid/mobilesdkexample/db/Daos.kt index 7d4b976..1f11174 100644 --- a/example/src/main/java/com/spruceid/mobilesdkexample/db/Daos.kt +++ b/example/src/main/java/com/spruceid/mobilesdkexample/db/Daos.kt @@ -12,3 +12,15 @@ interface VerificationActivityLogsDao { @Query("SELECT * FROM verification_activity_logs") fun getAllVerificationActivityLogs(): List } + +@Dao +interface RawCredentialsDao { + @Insert + suspend fun insertRawCredential(rawCredential: RawCredentials) + + @Query("SELECT * FROM raw_credentials") + fun getAllRawCredentials(): List + + @Query("DELETE FROM raw_credentials") + fun deleteAllRawCredentials(): Int +} diff --git a/example/src/main/java/com/spruceid/mobilesdkexample/db/Entities.kt b/example/src/main/java/com/spruceid/mobilesdkexample/db/Entities.kt index 238dcd9..be9707a 100644 --- a/example/src/main/java/com/spruceid/mobilesdkexample/db/Entities.kt +++ b/example/src/main/java/com/spruceid/mobilesdkexample/db/Entities.kt @@ -13,3 +13,9 @@ data class VerificationActivityLogs( val expirationDate: Date, val status: String, ) + +@Entity(tableName = "raw_credentials") +data class RawCredentials( + @PrimaryKey(autoGenerate = true) val id: Long = 0, + val rawCredential: String, +) \ No newline at end of file diff --git a/example/src/main/java/com/spruceid/mobilesdkexample/db/Repositories.kt b/example/src/main/java/com/spruceid/mobilesdkexample/db/Repositories.kt index cec2bdd..67048f6 100644 --- a/example/src/main/java/com/spruceid/mobilesdkexample/db/Repositories.kt +++ b/example/src/main/java/com/spruceid/mobilesdkexample/db/Repositories.kt @@ -15,3 +15,22 @@ class VerificationActivityLogsRepository(private val verificationActivityLogsDao return verificationActivityLogsDao.getAllVerificationActivityLogs() } } + +class RawCredentialsRepository(private val rawCredentialsDao: RawCredentialsDao) { + val rawCredentials: List = rawCredentialsDao.getAllRawCredentials() + + @WorkerThread + suspend fun insertRawCredential(credential: RawCredentials) { + rawCredentialsDao.insertRawCredential(credential) + } + + @WorkerThread + suspend fun getRawCredentials(): List { + return rawCredentialsDao.getAllRawCredentials() + } + + @WorkerThread + suspend fun deleteAllRawCredentials(): Int { + return rawCredentialsDao.deleteAllRawCredentials() + } +} \ No newline at end of file diff --git a/example/src/main/java/com/spruceid/mobilesdkexample/navigation/Screen.kt b/example/src/main/java/com/spruceid/mobilesdkexample/navigation/Screen.kt index 0c2f11f..790446b 100644 --- a/example/src/main/java/com/spruceid/mobilesdkexample/navigation/Screen.kt +++ b/example/src/main/java/com/spruceid/mobilesdkexample/navigation/Screen.kt @@ -5,6 +5,9 @@ const val VERIFY_DL_PATH = "verify_dl" const val VERIFY_EA_PATH = "verify_ea" const val VERIFY_VC_PATH = "verify_vc" const val VERIFIER_SETTINGS_HOME_PATH = "verifier_settings_home" +const val WALLET_SETTINGS_HOME_PATH = "wallet_settings_home" +const val ADD_TO_WALLET_PATH = "add_to_wallet/{rawCredential}" +const val OID4VP_PATH = "oid4vp/{params}" sealed class Screen(val route: String) { @@ -13,4 +16,7 @@ sealed class Screen(val route: String) { object VerifyEAScreen : Screen(VERIFY_EA_PATH) object VerifyVCScreen : Screen(VERIFY_VC_PATH) object VerifierSettingsHomeScreen : Screen(VERIFIER_SETTINGS_HOME_PATH) + object WalletSettingsHomeScreen : Screen(WALLET_SETTINGS_HOME_PATH) + object AddToWalletScreen : Screen(ADD_TO_WALLET_PATH) + object OID4VPScreen : Screen(OID4VP_PATH) } \ No newline at end of file diff --git a/example/src/main/java/com/spruceid/mobilesdkexample/navigation/SetupNavGraph.kt b/example/src/main/java/com/spruceid/mobilesdkexample/navigation/SetupNavGraph.kt index 3a23e8b..2bc14a2 100644 --- a/example/src/main/java/com/spruceid/mobilesdkexample/navigation/SetupNavGraph.kt +++ b/example/src/main/java/com/spruceid/mobilesdkexample/navigation/SetupNavGraph.kt @@ -1,18 +1,24 @@ package com.spruceid.mobilesdkexample.navigation +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable +import androidx.navigation.navDeepLink import com.spruceid.mobilesdkexample.HomeView import com.spruceid.mobilesdkexample.verifier.VerifyDLView import com.spruceid.mobilesdkexample.verifier.VerifyEAView import com.spruceid.mobilesdkexample.verifier.VerifyVCView import com.spruceid.mobilesdkexample.verifiersettings.VerifierSettingsHomeView +import com.spruceid.mobilesdkexample.viewmodels.IRawCredentialsViewModel +import com.spruceid.mobilesdkexample.wallet.AddToWalletView +import com.spruceid.mobilesdkexample.walletsettings.WalletSettingsHomeView @Composable fun SetupNavGraph( - navController: NavHostController + navController: NavHostController, + rawCredentialsViewModel: IRawCredentialsViewModel ) { NavHost( navController = navController, @@ -21,7 +27,7 @@ fun SetupNavGraph( composable( route = Screen.HomeScreen.route, ) { - HomeView(navController) + HomeView(navController, rawCredentialsViewModel) } composable( route = Screen.VerifyDLScreen.route, @@ -43,6 +49,32 @@ fun SetupNavGraph( ) { VerifierSettingsHomeView(navController) } - + composable( + route = Screen.WalletSettingsHomeScreen.route, + ) { + WalletSettingsHomeView(navController, rawCredentialsViewModel) + } + composable( + route = Screen.AddToWalletScreen.route, + deepLinks = listOf( + navDeepLink { + uriPattern = "spruceid://?sd-jwt={rawCredential}" + } + ) + ) { backStackEntry -> + val rawCredential = backStackEntry.arguments?.getString("rawCredential")!! + AddToWalletView(navController, rawCredential, rawCredentialsViewModel) + } + composable( + route = Screen.OID4VPScreen.route, + deepLinks = listOf( + navDeepLink { + uriPattern = "oid4vp://{params}" + } + ) + ) { + // val params = backStackEntry.arguments?.getString("params")!! + Text(text = "@TODO: OID4VP flow") + } } } diff --git a/example/src/main/java/com/spruceid/mobilesdkexample/ui/theme/Color.kt b/example/src/main/java/com/spruceid/mobilesdkexample/ui/theme/Color.kt index a0d932c..d293b82 100644 --- a/example/src/main/java/com/spruceid/mobilesdkexample/ui/theme/Color.kt +++ b/example/src/main/java/com/spruceid/mobilesdkexample/ui/theme/Color.kt @@ -7,7 +7,9 @@ val Primary = Color(0xFFF7F7F5) val Bg = Color(0xFFFDFDFC) val CredentialBorder = Color(0xFFE6E1D6) val CodeBorder = Color(0xff949494) +val CTAButtonGreen = Color(0xFF087455) val GreenValid = Color(0xFF059669) +val SecondaryButtonRed = Color(0xFFE11D48) val TextBody = Color(0xFF57534E) val TextHeader = Color(0xFF0C0A09) val TextOnPrimary = Color(0xFFA8A29E) diff --git a/example/src/main/java/com/spruceid/mobilesdkexample/verifiersettings/VerifierSettingsHomeView.kt b/example/src/main/java/com/spruceid/mobilesdkexample/verifiersettings/VerifierSettingsHomeView.kt index f140efc..051bd90 100644 --- a/example/src/main/java/com/spruceid/mobilesdkexample/verifiersettings/VerifierSettingsHomeView.kt +++ b/example/src/main/java/com/spruceid/mobilesdkexample/verifiersettings/VerifierSettingsHomeView.kt @@ -1,9 +1,6 @@ package com.spruceid.mobilesdkexample.verifiersettings -import android.graphics.Bitmap import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -11,26 +8,17 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.outlined.List -import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.State import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.rotate import androidx.compose.ui.draw.scale import androidx.compose.ui.res.painterResource @@ -40,18 +28,9 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController import com.spruceid.mobilesdkexample.R -import com.spruceid.mobilesdkexample.navigation.Screen import com.spruceid.mobilesdkexample.ui.theme.Inter -import com.spruceid.mobilesdkexample.ui.theme.Primary import com.spruceid.mobilesdkexample.ui.theme.TextBody import com.spruceid.mobilesdkexample.ui.theme.TextHeader -import com.spruceid.mobilesdkexample.ui.theme.TextOnPrimary -import com.spruceid.mobilesdkexample.ui.theme.VerifierRequestBadgeBinaryBorder -import com.spruceid.mobilesdkexample.ui.theme.VerifierRequestBadgeBinaryFill -import com.spruceid.mobilesdkexample.ui.theme.VerifierRequestBadgeBinaryText -import com.spruceid.mobilesdkexample.ui.theme.VerifierRequestBadgeFieldBorder -import com.spruceid.mobilesdkexample.ui.theme.VerifierRequestBadgeFieldFill -import com.spruceid.mobilesdkexample.ui.theme.VerifierRequestBadgeFieldText enum class VerifierSubSettings { VERIFICATION_ACTIVITY_LOG, @@ -182,89 +161,3 @@ fun VerifierSettingsHomeBody( VerificationActivityLogsScreen() } } - -@Composable -fun VerifierListItem( - title: String, - description: String, - binary: Boolean, - fields: Int, - modifier: Modifier = Modifier -) { - Column( - modifier = modifier.padding(vertical = 12.dp) - ) { - Row( - modifier = Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically - ) { - Text( - text = title, - fontFamily = Inter, - fontWeight = FontWeight.SemiBold, - fontSize = 18.sp, - color = TextHeader, - modifier = Modifier.weight(2f) - ) - VerifierListItemTag(binary = binary, fields = fields) - Spacer(modifier = Modifier.weight(1f)) - Image( - painter = painterResource(id = R.drawable.arrow_right), - contentDescription = stringResource(id = R.string.arrow_right), - modifier = Modifier.width(24.dp) - ) - } - Text( - text = description, - fontFamily = Inter, - fontWeight = FontWeight.Normal, - fontSize = 14.sp, - color = TextBody, - ) - HorizontalDivider() - } -} - -@Composable -fun VerifierListItemTag( - binary: Boolean, - fields: Int -) { - if (binary) { - Text( - text = "Binary", - fontFamily = Inter, - fontWeight = FontWeight.Normal, - fontSize = 12.sp, - color = VerifierRequestBadgeBinaryText, - modifier = Modifier - .border( - width = 1.dp, - color = VerifierRequestBadgeBinaryBorder, - shape = RoundedCornerShape(8.dp) - ) - .clip(shape = RoundedCornerShape(8.dp, 8.dp, 8.dp, 8.dp)) - .background(VerifierRequestBadgeBinaryFill) - .padding(vertical = 2.dp) - .padding(horizontal = 8.dp), - ) - } else { - Text( - text = "$fields Fields", - fontFamily = Inter, - fontWeight = FontWeight.Normal, - fontSize = 12.sp, - color = VerifierRequestBadgeFieldText, - modifier = Modifier - .border( - width = 1.dp, - color = VerifierRequestBadgeFieldBorder, - shape = RoundedCornerShape(8.dp) - ) - .clip(shape = RoundedCornerShape(8.dp, 8.dp, 8.dp, 8.dp)) - .background(VerifierRequestBadgeFieldFill) - .padding(vertical = 2.dp) - .padding(horizontal = 8.dp), - ) - } -} \ No newline at end of file diff --git a/example/src/main/java/com/spruceid/mobilesdkexample/viewmodels/RawCredentialsViewModel.kt b/example/src/main/java/com/spruceid/mobilesdkexample/viewmodels/RawCredentialsViewModel.kt new file mode 100644 index 0000000..e759a44 --- /dev/null +++ b/example/src/main/java/com/spruceid/mobilesdkexample/viewmodels/RawCredentialsViewModel.kt @@ -0,0 +1,64 @@ +package com.spruceid.mobilesdkexample.viewmodels + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewModelScope +import com.spruceid.mobilesdkexample.db.RawCredentials +import com.spruceid.mobilesdkexample.db.RawCredentialsRepository +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch + +abstract class IRawCredentialsViewModel : ViewModel(){ + abstract val rawCredentials: StateFlow> + abstract suspend fun saveRawCredential(rawCredential: RawCredentials) + abstract suspend fun deleteAllRawCredentials() + abstract fun generateRawCredentialsCSV(): String +} + +class RawCredentialsViewModel(private val rawCredentialsRepository: RawCredentialsRepository) : IRawCredentialsViewModel() { + private val _rawCredentials = MutableStateFlow(listOf()) + override val rawCredentials = _rawCredentials.asStateFlow() + + init { + viewModelScope.launch { + _rawCredentials.value = rawCredentialsRepository.rawCredentials + } + } + + override suspend fun saveRawCredential(rawCredential: RawCredentials) { + rawCredentialsRepository.insertRawCredential(rawCredential) + _rawCredentials.value = rawCredentialsRepository.getRawCredentials() + } + + override suspend fun deleteAllRawCredentials() { + rawCredentialsRepository.deleteAllRawCredentials() + _rawCredentials.value = rawCredentialsRepository.getRawCredentials() + } + + override fun generateRawCredentialsCSV(): String { + val heading = "ID, Raw Credential\n" + return heading + + rawCredentials.value.joinToString("\n") { + "${it.id}, ${it.rawCredential}" + } + } +} + +class RawCredentialsViewModelFactory(private val repository: RawCredentialsRepository) : ViewModelProvider.NewInstanceFactory() { + @Suppress("UNCHECKED_CAST") + override fun create(modelClass: Class): T = RawCredentialsViewModel(repository) as T +} + +class RawCredentialsViewModelPreview(override val rawCredentials: StateFlow> = MutableStateFlow( + emptyList() +)) : IRawCredentialsViewModel() { + override suspend fun saveRawCredential(credential: RawCredentials) {} + + override suspend fun deleteAllRawCredentials() {} + + override fun generateRawCredentialsCSV(): String { + return "" + } +} \ No newline at end of file diff --git a/example/src/main/java/com/spruceid/mobilesdkexample/wallet/AchievementCredentialItem.kt b/example/src/main/java/com/spruceid/mobilesdkexample/wallet/AchievementCredentialItem.kt new file mode 100644 index 0000000..41dd83f --- /dev/null +++ b/example/src/main/java/com/spruceid/mobilesdkexample/wallet/AchievementCredentialItem.kt @@ -0,0 +1,233 @@ +package com.spruceid.mobilesdkexample.wallet + +import androidx.compose.foundation.Image +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.Text +import androidx.compose.material3.rememberModalBottomSheetState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.spruceid.mobile.sdk.rs.decodeRevealSdJwt +import com.spruceid.mobilesdkexample.R +import com.spruceid.mobilesdkexample.ui.theme.Bg +import com.spruceid.mobilesdkexample.ui.theme.CredentialBorder +import com.spruceid.mobilesdkexample.ui.theme.GreenValid +import com.spruceid.mobilesdkexample.ui.theme.Inter +import com.spruceid.mobilesdkexample.ui.theme.TextBody +import com.spruceid.mobilesdkexample.ui.theme.TextHeader +import org.json.JSONArray +import org.json.JSONObject +import java.time.OffsetDateTime +import java.time.format.DateTimeFormatter + +class AchievementCredentialItem { + private var credential: JSONObject + + constructor(credential: JSONObject) { + this.credential = credential + } + + constructor(rawCredential: String) { + val decodedSdJwt = decodeRevealSdJwt(rawCredential) + this.credential = JSONObject(decodedSdJwt) + } + + @Composable + fun listComponent() { + val achievementName = keyPathFinder(credential, mutableListOf("achievement", "name")).toString() + val issuerName = keyPathFinder(credential, mutableListOf("issuer", "name")).toString() + + Row( + Modifier.height(intrinsicSize = IntrinsicSize.Max) + ) { + // Leading icon + Column { + // Title + Text( + text = achievementName, + fontFamily = Inter, + fontWeight = FontWeight.Medium, + fontSize = 22.sp, + color = TextHeader, + modifier = Modifier.padding(bottom = 8.dp) + ) + + // Description + Column { + Text( + text = issuerName, + fontFamily = Inter, + fontWeight = FontWeight.Normal, + fontSize = 14.sp, + color = TextBody + ) + Spacer(modifier = Modifier.height(16.dp)) + Row(verticalAlignment = Alignment.CenterVertically) { + Image( + painter = painterResource(id = R.drawable.valid), + contentDescription = stringResource(id = R.string.valid), + modifier = Modifier.width(15.dp) + ) + Text( + text = "Valid", + fontFamily = Inter, + fontWeight = FontWeight.Medium, + fontSize = 10.sp, + color = GreenValid + ) + } + } + } + Spacer(modifier = Modifier.weight(1.0f)) + // Trailing action button + } + } + + @Composable + fun detailsComponent() { + val awardedDate = keyPathFinder(credential, mutableListOf("awardedDate")).toString() + val ISO8601DateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.SSS]Z") + val parsedDate = OffsetDateTime.parse(awardedDate, ISO8601DateFormat) + val dateTimeFormatter = DateTimeFormatter.ofPattern("MMM dd, yyyy 'at' h:mm a") + + val identity = keyPathFinder(credential, mutableListOf("credentialSubject", "identity")) as JSONArray + val details = MutableList(identity.length()) { i -> + val obj = identity.get(i) as JSONObject + Pair(obj["identityType"].toString(), obj["identityHash"].toString()) + } + + details.add(0, Pair("awardedDate", parsedDate.format(dateTimeFormatter))) + + Row( + Modifier.padding(horizontal = 12.dp) + ) { + Column { + details.map { detail -> + Text( + text = splitCamelCase(detail.first), + fontFamily = Inter, + fontWeight = FontWeight.Normal, + fontSize = 14.sp, + color = TextBody, + modifier = Modifier.padding(top = 10.dp) + ) + Text( + text = detail.second, + fontFamily = Inter, + fontSize = 14.sp + ) + } + } + Spacer(modifier = Modifier.weight(1.0f)) + } + } + + @Composable + fun borderedListComponent() { + Box( + Modifier + .fillMaxWidth() + .border( + width = 1.dp, + color = CredentialBorder, + shape = RoundedCornerShape(8.dp) + ) + .padding(12.dp) + ) { + listComponent() + } + } + + @OptIn(ExperimentalMaterial3Api::class) + @Composable + fun component() { + var sheetOpen by remember { + mutableStateOf(false) + } + + Column( + Modifier + .padding(vertical = 10.dp) + .border( + width = 1.dp, + color = CredentialBorder, + shape = RoundedCornerShape(8.dp) + ) + ) { + Box( + Modifier + .padding(all = 12.dp) + .clickable { + sheetOpen = true + } + ) { +// GenericCredentialListItem(credentialPack = credentialPack) + listComponent() + } + } + if (sheetOpen) { + ModalBottomSheet( + onDismissRequest = { + sheetOpen = false + }, + modifier = Modifier + .fillMaxHeight(0.8f), + sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true), + dragHandle = null, + containerColor = Bg, + shape = RoundedCornerShape(8.dp) + ) { + Column( + Modifier + .padding(12.dp) + ) { + Text( + text = "Review Info", + textAlign = TextAlign.Center, + fontFamily = Inter, + fontWeight = FontWeight.Bold, + fontSize = 24.sp, + color = TextHeader, + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 20.dp), + ) + borderedListComponent() + Column( + Modifier + .verticalScroll(rememberScrollState()) + .weight(1f, false) + ) { + detailsComponent() + } + } + } + } + } +} \ No newline at end of file diff --git a/example/src/main/java/com/spruceid/mobilesdkexample/wallet/AddToWalletView.kt b/example/src/main/java/com/spruceid/mobilesdkexample/wallet/AddToWalletView.kt new file mode 100644 index 0000000..63f3dc8 --- /dev/null +++ b/example/src/main/java/com/spruceid/mobilesdkexample/wallet/AddToWalletView.kt @@ -0,0 +1,131 @@ +package com.spruceid.mobilesdkexample.wallet + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.navigation.NavHostController +import androidx.navigation.compose.rememberNavController +import com.spruceid.mobilesdkexample.db.RawCredentials +import com.spruceid.mobilesdkexample.ui.theme.CTAButtonGreen +import com.spruceid.mobilesdkexample.ui.theme.Inter +import com.spruceid.mobilesdkexample.ui.theme.MobileSdkTheme +import com.spruceid.mobilesdkexample.ui.theme.SecondaryButtonRed +import com.spruceid.mobilesdkexample.ui.theme.TextHeader +import com.spruceid.mobilesdkexample.viewmodels.IRawCredentialsViewModel +import com.spruceid.mobilesdkexample.viewmodels.RawCredentialsViewModelPreview +import kotlinx.coroutines.launch + +@Composable +fun AddToWalletView( + navController: NavHostController, + rawCredential: String, + rawCredentialsViewModel: IRawCredentialsViewModel +) { + val credential = AchievementCredentialItem(rawCredential) + val scope = rememberCoroutineScope() + + Column( + Modifier + .padding(all = 20.dp) + .padding(top = 20.dp) + ) { + Text( + text = "Review Info", + textAlign = TextAlign.Center, + fontFamily = Inter, + fontWeight = FontWeight.Bold, + fontSize = 24.sp, + color = TextHeader, + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 20.dp), + ) + + credential.borderedListComponent() + + Column( + Modifier + .verticalScroll(rememberScrollState()) + .weight(1f, false) + ) { + credential.detailsComponent() + } + + Spacer(Modifier.weight(1f)) + + Button( + onClick = { + scope.launch { + rawCredentialsViewModel.saveRawCredential(RawCredentials( + rawCredential = rawCredential + )) + navController.popBackStack() + } + }, + shape = RoundedCornerShape(5.dp), + colors = ButtonDefaults.buttonColors( + containerColor = CTAButtonGreen, + contentColor = Color.White, + ), + modifier = Modifier + .fillMaxWidth() + ) { + Text( + text = "Add to Wallet", + fontFamily = Inter, + fontWeight = FontWeight.SemiBold, + color = Color.White, + ) + } + + Button( + onClick = { + navController.popBackStack() + }, + shape = RoundedCornerShape(5.dp), + colors = ButtonDefaults.buttonColors( + containerColor = Color.Transparent, + contentColor = SecondaryButtonRed, + ), + modifier = Modifier + .fillMaxWidth() + ) { + Text( + text = "Close", + fontFamily = Inter, + fontWeight = FontWeight.SemiBold, + color = SecondaryButtonRed, + ) + } + } +} + +@Preview(showBackground = true) +@Composable +fun AddToWalletPreview() { + var navController: NavHostController = rememberNavController() + + MobileSdkTheme { + AddToWalletView( + navController = navController, + rawCredential = "{}", + rawCredentialsViewModel = RawCredentialsViewModelPreview() + ) + } +} \ No newline at end of file diff --git a/example/src/main/java/com/spruceid/mobilesdkexample/wallet/WalletHomeView.kt b/example/src/main/java/com/spruceid/mobilesdkexample/wallet/WalletHomeView.kt index 471f824..6e3f685 100644 --- a/example/src/main/java/com/spruceid/mobilesdkexample/wallet/WalletHomeView.kt +++ b/example/src/main/java/com/spruceid/mobilesdkexample/wallet/WalletHomeView.kt @@ -1,40 +1,58 @@ package com.spruceid.mobilesdkexample.wallet +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.navigation.NavController +import com.spruceid.mobilesdkexample.R +import com.spruceid.mobilesdkexample.navigation.Screen import com.spruceid.mobilesdkexample.ui.theme.Inter import com.spruceid.mobilesdkexample.ui.theme.TextHeader -import com.spruceid.mobilesdkexample.ui.theme.MobileSdkTheme +import com.spruceid.mobilesdkexample.ui.theme.Primary import com.spruceid.mobilesdkexample.utils.vcs import com.spruceid.mobilesdkexample.utils.mdocBase64 +import com.spruceid.mobilesdkexample.viewmodels.IRawCredentialsViewModel @Composable -fun WalletHomeView() { +fun WalletHomeView( + navController: NavController, + rawCredentialsViewModel: IRawCredentialsViewModel +) { Column( Modifier .padding(all = 20.dp) .padding(top = 20.dp) ) { - WalletHomeHeader() - WalletHomeBody() + WalletHomeHeader(navController = navController) + WalletHomeBody(rawCredentialsViewModel = rawCredentialsViewModel) } } @Composable -fun WalletHomeHeader() { +fun WalletHomeHeader(navController: NavController) { Row(verticalAlignment = Alignment.CenterVertically) { Text( text = "Spruce Wallet", @@ -44,28 +62,46 @@ fun WalletHomeHeader() { color = TextHeader ) Spacer(Modifier.weight(1f)) + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .width(36.dp) + .height(36.dp) + .padding(start = 4.dp) + .clip(shape = RoundedCornerShape(8.dp)) + .background(Primary) + .clickable { + navController.navigate(Screen.WalletSettingsHomeScreen.route) + } + ) { + Image( + painter = painterResource(id = R.drawable.user), + contentDescription = stringResource(id = R.string.user), + modifier = Modifier + .width(20.dp) + .height(20.dp) + ) + } } } @Composable -fun WalletHomeBody() { - Column( +fun WalletHomeBody(rawCredentialsViewModel: IRawCredentialsViewModel) { + val rawCredentials by rawCredentialsViewModel.rawCredentials.collectAsState() + + LazyColumn( Modifier .fillMaxWidth() - .verticalScroll(rememberScrollState()) .padding(top = 20.dp) ) { - vcs.map { vc -> - GenericCredentialListItems(vc = vc) + items(rawCredentials) { rawCredential -> + AchievementCredentialItem(rawCredential.rawCredential).component() + } + item { + vcs.map { vc -> + GenericCredentialListItems(vc = vc) + } + ShareableCredentialListItems(mdocBase64 = mdocBase64) } - ShareableCredentialListItems(mdocBase64 = mdocBase64) } } - -@Preview(showBackground = true) -@Composable -fun WalletHomeViewPreview() { - MobileSdkTheme { - WalletHomeView() - } -} \ No newline at end of file diff --git a/example/src/main/java/com/spruceid/mobilesdkexample/wallet/WalletUtils.kt b/example/src/main/java/com/spruceid/mobilesdkexample/wallet/WalletUtils.kt index e68ed0b..e3ce68a 100644 --- a/example/src/main/java/com/spruceid/mobilesdkexample/wallet/WalletUtils.kt +++ b/example/src/main/java/com/spruceid/mobilesdkexample/wallet/WalletUtils.kt @@ -10,6 +10,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.asImageBitmap import androidx.core.content.ContextCompat +import org.json.JSONObject @Composable fun BitmapImage( @@ -48,4 +49,29 @@ fun checkAndRequestBluetoothPermissions( // Request permissions launcher.launch(permissions) } +} + +fun keyPathFinder(json: Any, path: MutableList): Any { + try { + val firstKey = path.first() + val element = (json as JSONObject)[firstKey] + path.removeAt(0) + if (path.isNotEmpty()) { + return keyPathFinder(element, path) + } + return element + } catch (e: Exception) { + return "" + } +} + +fun splitCamelCase(s: String): String { + return s.replace( + String.format( + "%s|%s|%s", + "(?<=[A-Z])(?=[A-Z][a-z])", + "(?<=[^A-Z])(?=[A-Z])", + "(?<=[A-Za-z])(?=[^A-Za-z])" + ).toRegex(), " " + ).replaceFirstChar(Char::titlecase) } \ No newline at end of file diff --git a/example/src/main/java/com/spruceid/mobilesdkexample/walletsettings/WalletSettingsHomeView.kt b/example/src/main/java/com/spruceid/mobilesdkexample/walletsettings/WalletSettingsHomeView.kt new file mode 100644 index 0000000..bf35efe --- /dev/null +++ b/example/src/main/java/com/spruceid/mobilesdkexample/walletsettings/WalletSettingsHomeView.kt @@ -0,0 +1,114 @@ +package com.spruceid.mobilesdkexample.walletsettings + +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.rotate +import androidx.compose.ui.draw.scale +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.navigation.NavController +import com.spruceid.mobilesdkexample.R +import com.spruceid.mobilesdkexample.ui.theme.Inter +import com.spruceid.mobilesdkexample.ui.theme.TextHeader +import com.spruceid.mobilesdkexample.ui.theme.VerifiedRedInvalid +import com.spruceid.mobilesdkexample.viewmodels.IRawCredentialsViewModel +import kotlinx.coroutines.launch + +@Composable +fun WalletSettingsHomeView( + navController: NavController, + rawCredentialsViewModel: IRawCredentialsViewModel +) { + Column( + Modifier + .padding(all = 20.dp) + .padding(top = 20.dp) + ) { + WalletSettingsHomeHeader( + onBack = { + navController.popBackStack() + } + ) + WalletSettingsHomeBody(rawCredentialsViewModel) + } +} + +@Composable +fun WalletSettingsHomeHeader( + onBack: () -> Unit +) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.clickable { + onBack() + } + ) { + Image( + painter = painterResource(id = R.drawable.chevron), + contentDescription = stringResource(id = R.string.chevron), + modifier = Modifier + .rotate(180f) + .scale(0.7f) + ) + Text( + text = "Wallet Settings", + fontFamily = Inter, + fontWeight = FontWeight.SemiBold, + fontSize = 24.sp, + color = TextHeader, + modifier = Modifier.padding(start = 10.dp) + ) + Spacer(Modifier.weight(1f)) + } + +} + +@Composable +fun WalletSettingsHomeBody(rawCredentialsViewModel: IRawCredentialsViewModel) { + val scope = rememberCoroutineScope() + + Column( + Modifier + .padding(horizontal = 20.dp) + .padding(top = 10.dp), + ) { + Button( + onClick = { + scope.launch { + rawCredentialsViewModel.deleteAllRawCredentials() + } + }, + shape = RoundedCornerShape(5.dp), + colors = ButtonDefaults.buttonColors( + containerColor = VerifiedRedInvalid, + contentColor = Color.White, + ), + modifier = Modifier + .fillMaxWidth() + ) { + Text( + text = "Delete all added credentials", + fontFamily = Inter, + fontWeight = FontWeight.SemiBold, + color = Color.White, + ) + } + } +} \ No newline at end of file diff --git a/example/src/main/res/drawable/ic_launcher_background.xml b/example/src/main/res/drawable/ic_launcher_background.xml index 07d5da9..ca3826a 100644 --- a/example/src/main/res/drawable/ic_launcher_background.xml +++ b/example/src/main/res/drawable/ic_launcher_background.xml @@ -1,170 +1,74 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + xmlns:android="http://schemas.android.com/apk/res/android"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/example/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..036d09b --- /dev/null +++ b/example/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/example/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/example/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..036d09b --- /dev/null +++ b/example/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/example/src/main/res/mipmap-anydpi/ic_launcher.xml b/example/src/main/res/mipmap-anydpi/ic_launcher.xml deleted file mode 100644 index 6f3b755..0000000 --- a/example/src/main/res/mipmap-anydpi/ic_launcher.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/example/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/example/src/main/res/mipmap-anydpi/ic_launcher_round.xml deleted file mode 100644 index 6f3b755..0000000 --- a/example/src/main/res/mipmap-anydpi/ic_launcher_round.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/example/src/main/res/mipmap-hdpi/ic_launcher.webp b/example/src/main/res/mipmap-hdpi/ic_launcher.webp index c209e78..0347617 100644 Binary files a/example/src/main/res/mipmap-hdpi/ic_launcher.webp and b/example/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/example/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/example/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000..c6b84ed Binary files /dev/null and b/example/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp differ diff --git a/example/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/example/src/main/res/mipmap-hdpi/ic_launcher_round.webp index b2dfe3d..bd4a5c9 100644 Binary files a/example/src/main/res/mipmap-hdpi/ic_launcher_round.webp and b/example/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/example/src/main/res/mipmap-mdpi/ic_launcher.webp b/example/src/main/res/mipmap-mdpi/ic_launcher.webp index 4f0f1d6..42c4043 100644 Binary files a/example/src/main/res/mipmap-mdpi/ic_launcher.webp and b/example/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/example/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/example/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000..fcec82a Binary files /dev/null and b/example/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp differ diff --git a/example/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/example/src/main/res/mipmap-mdpi/ic_launcher_round.webp index 62b611d..d83010e 100644 Binary files a/example/src/main/res/mipmap-mdpi/ic_launcher_round.webp and b/example/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/example/src/main/res/mipmap-xhdpi/ic_launcher.webp b/example/src/main/res/mipmap-xhdpi/ic_launcher.webp index 948a307..1eb3ba1 100644 Binary files a/example/src/main/res/mipmap-xhdpi/ic_launcher.webp and b/example/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/example/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/example/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000..de6cbc9 Binary files /dev/null and b/example/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp differ diff --git a/example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp index 1b9a695..699d061 100644 Binary files a/example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and b/example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/example/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/example/src/main/res/mipmap-xxhdpi/ic_launcher.webp index 28d4b77..96086c9 100644 Binary files a/example/src/main/res/mipmap-xxhdpi/ic_launcher.webp and b/example/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/example/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/example/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000..e509b0b Binary files /dev/null and b/example/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp differ diff --git a/example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp index 9287f50..1d1d167 100644 Binary files a/example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and b/example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp index aa7d642..c952590 100644 Binary files a/example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and b/example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/example/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/example/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000..98dccc2 Binary files /dev/null and b/example/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp differ diff --git a/example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp index 9126ae3..f319c59 100644 Binary files a/example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and b/example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/example/src/main/res/values/colors.xml b/example/src/main/res/values/colors.xml index dfaaf12..5e9ab92 100644 --- a/example/src/main/res/values/colors.xml +++ b/example/src/main/res/values/colors.xml @@ -18,4 +18,6 @@ #FFBE123C #FF44403C #FF2F6AE1 + #FFE11D48 + #FF087455 \ No newline at end of file diff --git a/example/src/main/res/values/ic_launcher_background.xml b/example/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 0000000..f8282e3 --- /dev/null +++ b/example/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #EEECE8 + \ No newline at end of file diff --git a/example/src/main/res/values/strings.xml b/example/src/main/res/values/strings.xml index f7e09e8..290b1a7 100644 --- a/example/src/main/res/values/strings.xml +++ b/example/src/main/res/values/strings.xml @@ -1,5 +1,5 @@ - MobileSdk Example + SpruceKit Reference App User profile Scan QR Code Valid