├── .gitignore
├── .idea
├── .gitignore
├── .name
├── appInsightsSettings.xml
├── compiler.xml
├── deploymentTargetDropDown.xml
├── gradle.xml
├── inspectionProfiles
│ └── Project_Default.xml
├── kotlinc.xml
├── migrations.xml
├── misc.xml
└── vcs.xml
├── README.md
├── app
├── .gitignore
├── build.gradle.kts
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── appmason
│ │ └── jetplayground
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── appmason
│ │ │ └── jetplayground
│ │ │ ├── MainActivity.kt
│ │ │ ├── otp_verifier
│ │ │ ├── receiver
│ │ │ │ └── OTPReceiver.kt
│ │ │ └── util
│ │ │ │ └── AppSignatureHelper.kt
│ │ │ └── ui
│ │ │ ├── components
│ │ │ ├── AutoFill.kt
│ │ │ └── OtpInputField.kt
│ │ │ ├── navigation
│ │ │ ├── NavGraph.kt
│ │ │ └── Screens.kt
│ │ │ ├── screens
│ │ │ ├── OtpScreen.kt
│ │ │ └── SplashScreen.kt
│ │ │ └── theme
│ │ │ ├── Color.kt
│ │ │ ├── Theme.kt
│ │ │ └── Type.kt
│ └── res
│ │ ├── drawable
│ │ ├── ic_back.xml
│ │ ├── ic_launcher_background.xml
│ │ └── ic_launcher_foreground.xml
│ │ ├── mipmap-anydpi
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── raw
│ │ └── loading.json
│ │ ├── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ │ └── xml
│ │ ├── backup_rules.xml
│ │ └── data_extraction_rules.xml
│ └── test
│ └── java
│ └── com
│ └── appmason
│ └── jetplayground
│ └── ExampleUnitTest.kt
├── art
└── screens
│ ├── otp_autofill.png
│ ├── otp_ui.png
│ └── otp_verification.gif
├── build.gradle.kts
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle.kts
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | ComposeOtpVerify
--------------------------------------------------------------------------------
/.idea/appInsightsSettings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/deploymentTargetDropDown.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/.idea/kotlinc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/migrations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## OTP Verification with Jetpack Compose
2 |
3 | This repository contains a custom-built OTP (One Time Password) Input Field and integration with [Google's SMS Retriever API](https://developers.google.com/identity/sms-retriever/overview)
4 | for OTP fetch and populate functionality, developed using Jetpack Compose. This component is designed to cater to modern Android applications requiring OTP verification, offering a blend of customization and ease of use.
5 |
6 | 
7 | 
8 |
9 | > This is not a library. This is a demo project to experiment and try out a functionality.
10 |
11 | ### Screenshots
12 |
13 |
14 |
15 | OTP Verification
16 | OTP Screen UI
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | ### Features
25 | - Integration with [Google's SMS Retriever API](https://developers.google.com/identity/sms-retriever/overview) to auto verify OTP and populate.
26 | - OTP input field can be set up for any length of OTP.
27 | - Supports automatic filling of the OTP, a convenient feature for OTPs received from servers or other sources.
28 | - Offers options to show a cursor, and control its blinking, enhancing the user experience.
29 |
30 | ### Note on Compose AutoFill
31 |
32 | > Right now Jetpack Compose does not have good support for [Autofill](https://developer.android.com/guide/topics/text/autofill).
33 | > There have been some temporary solutions like [ExplicitAutofillTypesDemo.kt](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/autofill/ExplicitAutofillTypesDemo.kt).
34 | > Google is working to add support and we may have it in the future. Autofill will benefit from filling fields like name, emails, credit card numbers, and OTPs from IME directly.
35 | >
36 | > Open issues related to Compose Autofill:
37 | > - https://issuetracker.google.com/issues/268596603
38 | > - https://issuetracker.google.com/issues/265911809
39 | > - https://issuetracker.google.com/issues/176949051
40 |
41 | I did some experiments with the temporary solution and looks like this.
42 | But it does not work smoothly and has many issues:
43 |
44 |
45 |
46 | Compose Autofill
47 |
48 |
49 |
50 |
51 |
52 |
53 | ### Reference
54 |
55 | Thanks to these resources which helped me to learn the concepts:
56 | - https://proandroiddev.com/jetpack-compose-otp-input-field-bcfa22c85e5f
57 | - https://medium.com/@aayan.talukdar/android-automatic-otp-retrieval-in-android-and-jetpackcompose-17be181909a5
58 | - https://bryanherbst.com/2021/04/13/compose-autofill/
59 |
60 |
61 | ### Find this project useful?
62 |
63 | - Support it by clicking the ⭐️ button on the upper right of this page. ✌️
64 |
65 | ### License
66 |
67 | ```
68 | MIT License
69 |
70 | Copyright (c) 2024 Pushpal Roy
71 |
72 | Permission is hereby granted, free of charge, to any person obtaining a
73 | copy of this software and associated documentation files (the "Software"),
74 | to deal in the Software without restriction, including without limitation
75 | the rights to use, copy, modify, merge, publish, distribute, sublicense,
76 | and/or sell copies of the Software, and to permit persons to whom the
77 | Software is furnished to do so, subject to the following conditions:
78 |
79 | The above copyright notice and this permission notice shall be included
80 | in all copies or substantial portions of the Software.
81 |
82 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
83 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
84 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
85 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
86 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
87 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
88 | ```
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.application")
3 | id("org.jetbrains.kotlin.android")
4 | }
5 |
6 | android {
7 | namespace = "com.appmason.composeotpverify"
8 | compileSdk = 34
9 |
10 | defaultConfig {
11 | applicationId = "com.appmason.composeotpverify"
12 | minSdk = 28
13 | targetSdk = 34
14 | versionCode = 1
15 | versionName = "1.0"
16 |
17 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
18 | vectorDrawables {
19 | useSupportLibrary = true
20 | }
21 | }
22 |
23 | buildTypes {
24 | release {
25 | isMinifyEnabled = false
26 | proguardFiles(
27 | getDefaultProguardFile("proguard-android-optimize.txt"),
28 | "proguard-rules.pro"
29 | )
30 | }
31 | }
32 | compileOptions {
33 | sourceCompatibility = JavaVersion.VERSION_1_8
34 | targetCompatibility = JavaVersion.VERSION_1_8
35 | }
36 | kotlinOptions {
37 | jvmTarget = "1.8"
38 | }
39 | buildFeatures {
40 | compose = true
41 | }
42 | composeOptions {
43 | kotlinCompilerExtensionVersion = "1.5.1"
44 | }
45 | packaging {
46 | resources {
47 | excludes += "/META-INF/{AL2.0,LGPL2.1}"
48 | }
49 | }
50 | }
51 |
52 | dependencies {
53 |
54 | implementation("androidx.core:core-ktx:1.12.0")
55 | implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0")
56 | implementation("androidx.lifecycle:lifecycle-runtime-compose:2.7.0")
57 | implementation("androidx.activity:activity-compose:1.8.2")
58 | implementation(platform("androidx.compose:compose-bom:2023.10.01"))
59 | implementation("androidx.compose.ui:ui")
60 | implementation("androidx.compose.ui:ui-graphics")
61 | implementation("androidx.compose.ui:ui-tooling-preview")
62 | implementation("androidx.compose.material3:material3:1.1.2")
63 | implementation("com.airbnb.android:lottie-compose:6.3.0")
64 | implementation("androidx.navigation:navigation-compose:2.7.6")
65 | implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7")
66 |
67 | //SMSRetrieval API Dependencies for Auto OTP Verification
68 | implementation("com.google.android.gms:play-services-auth-api-phone:18.0.1")
69 | implementation("com.google.android.gms:play-services-auth:20.7.0")
70 |
71 | testImplementation("junit:junit:4.13.2")
72 | androidTestImplementation("androidx.test.ext:junit:1.1.5")
73 | androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
74 | androidTestImplementation(platform("androidx.compose:compose-bom:2023.08.00"))
75 | androidTestImplementation("androidx.compose.ui:ui-test-junit4")
76 | debugImplementation("androidx.compose.ui:ui-tooling")
77 | debugImplementation("androidx.compose.ui:ui-test-manifest")
78 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/appmason/jetplayground/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.appmason.jetplayground
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.appmason.jetplayground", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
15 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/java/com/appmason/jetplayground/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.appmason.jetplayground
2 |
3 | import android.os.Bundle
4 | import android.util.Log
5 | import androidx.activity.ComponentActivity
6 | import androidx.activity.compose.setContent
7 | import androidx.navigation.compose.rememberNavController
8 | import com.appmason.jetplayground.otp_verifier.util.AppSignatureHelper
9 | import com.appmason.jetplayground.ui.navigation.SetupNavGraph
10 | import com.appmason.jetplayground.ui.theme.JetPlaygroundTheme
11 |
12 | class MainActivity : ComponentActivity() {
13 | override fun onCreate(savedInstanceState: Bundle?) {
14 | super.onCreate(savedInstanceState)
15 |
16 | /**
17 | * For OTP verification
18 | *
19 | * For development, once the app signature has is fetched, a OTP can be sent from any device like:
20 | * "DO NOT SHARE: Your JetPlayground OTP code is 643908. Message ID: j7fPi8fNhTk."
21 | *
22 | * Here "j7fPi8fNhTk" is the unique hash for this app which needs to be added below the SMS.
23 | * Without the correct hash, your app won't receive the message callback. This only needs to be
24 | * generated once per app and stored.
25 | *
26 | * For production app, use keytool to generate the hash:
27 | * https://developers.google.com/identity/sms-retriever/verify#computing_your_apps_hash_string
28 | */
29 | val signatureHelper = AppSignatureHelper(this)
30 | val appSignatures = signatureHelper.appSignatures
31 | for (signature in appSignatures) {
32 | Log.e("Otp", "App Signature for OTP: $signature")
33 | }
34 |
35 | setContent {
36 | JetPlaygroundTheme {
37 | val navController = rememberNavController()
38 | SetupNavGraph(navController = navController)
39 | }
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/appmason/jetplayground/otp_verifier/receiver/OTPReceiver.kt:
--------------------------------------------------------------------------------
1 | package com.appmason.jetplayground.otp_verifier.receiver
2 |
3 | import android.content.BroadcastReceiver
4 | import android.content.Context
5 | import android.content.Intent
6 | import android.os.Bundle
7 | import android.util.Log
8 | import com.google.android.gms.auth.api.phone.SmsRetriever
9 | import com.google.android.gms.auth.api.phone.SmsRetrieverClient
10 | import com.google.android.gms.common.api.CommonStatusCodes
11 | import com.google.android.gms.common.api.Status
12 |
13 | /**
14 | * BroadcastReceiver to wait for SMS messages. This can be registered either
15 | * in the AndroidManifest or at runtime. Should filter Intents on
16 | * SmsRetriever.SMS_RETRIEVED_ACTION.
17 | *
18 | * TODO: Working in Android 13 (API 33) and 14 (API 34), but not working in Android 12 (API 31)
19 | *
20 | */
21 | class OTPReceiver : BroadcastReceiver() {
22 | private var otpReceiveListener: OTPReceiveListener? = null
23 |
24 | fun init(otpReceiveListener: OTPReceiveListener?) {
25 | this.otpReceiveListener = otpReceiveListener
26 | }
27 |
28 | override fun onReceive(context: Context?, intent: Intent?) {
29 | if (SmsRetriever.SMS_RETRIEVED_ACTION == intent?.action) {
30 | val extras: Bundle? = intent.extras
31 | val status: Status = extras?.get(SmsRetriever.EXTRA_STATUS) as Status
32 |
33 | when (status.statusCode) {
34 | CommonStatusCodes.SUCCESS -> {
35 | // Get SMS message contents
36 | val msg = extras.getString(SmsRetriever.EXTRA_SMS_MESSAGE) as String
37 | Log.e("OTPReceiver", "SMS Received in OTPReceiver: $msg")
38 |
39 | // extract the 6-digit code from the SMS
40 | val smsCode = msg.let { "[0-9]{6}".toRegex().find(it) }
41 | Log.e("OTPReceiver", "OTP fetched from SMS in OTPReceiver: $smsCode")
42 |
43 | smsCode?.value?.let { otpReceiveListener?.onOTPReceived(it) }
44 | }
45 |
46 | CommonStatusCodes.TIMEOUT -> {
47 | otpReceiveListener?.onOTPTimeOut()
48 | }
49 | }
50 | }
51 | }
52 |
53 | interface OTPReceiveListener {
54 | fun onOTPReceived(otp: String?)
55 | fun onOTPTimeOut()
56 | }
57 | }
58 |
59 | fun startSMSRetrieverClient(context: Context) {
60 | val client: SmsRetrieverClient = SmsRetriever.getClient(context)
61 | val smsRetrieverTask = client.startSmsRetriever()
62 | smsRetrieverTask.addOnSuccessListener {
63 | Log.e("OTPReceiver", "startSMSRetrieverClient addOnSuccessListener")
64 | }
65 | smsRetrieverTask.addOnFailureListener { e ->
66 | Log.e("OTPReceiver", "startSMSRetrieverClient addOnFailureListener" + e.stackTrace)
67 | }
68 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/appmason/jetplayground/otp_verifier/util/AppSignatureHelper.kt:
--------------------------------------------------------------------------------
1 | package com.appmason.jetplayground.otp_verifier.util
2 |
3 | import android.content.Context
4 | import android.content.ContextWrapper
5 | import android.content.pm.PackageInfo
6 | import android.content.pm.PackageManager
7 | import android.util.Log
8 | import java.nio.charset.StandardCharsets
9 | import java.security.MessageDigest
10 | import java.security.NoSuchAlgorithmException
11 | import java.util.Arrays
12 | import android.content.pm.Signature;
13 | import android.util.Base64;
14 |
15 | /**
16 | * DO NOT USE THIS IN PRODUCTION
17 | *
18 | * This class is only for development purpose. For production app, use keytool to generate the hash:
19 | * https://developers.google.com/identity/sms-retriever/verify#computing_your_apps_hash_string
20 | *
21 | * This is a helper class to generate your message hash to be included in your SMS message.
22 | * Once the hash has been generated this class can be deleted.
23 | *
24 | * Without the correct hash, your app won't receive the message callback. This only needs to be
25 | * generated once per app and stored. Then you can remove this helper class from your code.
26 | */
27 | class AppSignatureHelper(context: Context?) : ContextWrapper(context) {
28 | val appSignatures: ArrayList
29 | /**
30 | * Get all the app signatures for the current package
31 | * @return
32 | */
33 | get() {
34 | val appCodes = ArrayList()
35 | try {
36 | // Get all package signatures for the current package
37 | val packageName = packageName
38 | val packageManager = packageManager
39 | val packageInfo: PackageInfo =
40 | packageManager.getPackageInfo(
41 | packageName,
42 | PackageManager.GET_SIGNING_CERTIFICATES
43 | )
44 |
45 | val signatures: Array = if (packageInfo.signingInfo != null) {
46 | // New method (API level 28 and above)
47 | if (packageInfo.signingInfo.hasMultipleSigners()) {
48 | // Handle multiple signers if necessary
49 | packageInfo.signingInfo.apkContentsSigners
50 | } else {
51 | // Single signer
52 | packageInfo.signingInfo.signingCertificateHistory
53 | }
54 | } else {
55 | // Old method (deprecated)
56 | packageInfo.signatures
57 | }
58 |
59 | // For each signature create a compatible hash
60 | for (signature in signatures) {
61 | val hash = hash(packageName, signature.toCharsString())
62 | if (hash != null) {
63 | appCodes.add(String.format("%s", hash))
64 | }
65 | }
66 | } catch (e: PackageManager.NameNotFoundException) {
67 | Log.e(TAG, "Unable to find package to obtain hash.", e)
68 | }
69 | return appCodes
70 | }
71 |
72 | companion object {
73 | val TAG = AppSignatureHelper::class.java.simpleName
74 | private const val HASH_TYPE = "SHA-256"
75 | private const val NUM_HASHED_BYTES = 9
76 | private const val NUM_BASE64_CHAR = 11
77 | private fun hash(packageName: String, signature: String): String? {
78 | val appInfo = "$packageName $signature"
79 | try {
80 | val messageDigest = MessageDigest.getInstance(HASH_TYPE)
81 | messageDigest.update(appInfo.toByteArray(StandardCharsets.UTF_8))
82 | var hashSignature = messageDigest.digest()
83 |
84 | // truncated into NUM_HASHED_BYTES
85 | hashSignature = Arrays.copyOfRange(hashSignature, 0, NUM_HASHED_BYTES)
86 | // encode into Base64
87 | var base64Hash: String =
88 | Base64.encodeToString(hashSignature, Base64.NO_PADDING or Base64.NO_WRAP)
89 | base64Hash = base64Hash.substring(0, NUM_BASE64_CHAR)
90 | Log.d(TAG, String.format("pkg: %s -- hash: %s", packageName, base64Hash))
91 | return base64Hash
92 | } catch (e: NoSuchAlgorithmException) {
93 | Log.e(TAG, "hash:NoSuchAlgorithm", e)
94 | }
95 | return null
96 | }
97 | }
98 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/appmason/jetplayground/ui/components/AutoFill.kt:
--------------------------------------------------------------------------------
1 | package com.appmason.jetplayground.ui.components
2 |
3 | import androidx.compose.foundation.layout.Box
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.ui.ExperimentalComposeUiApi
6 | import androidx.compose.ui.Modifier
7 | import androidx.compose.ui.autofill.AutofillNode
8 | import androidx.compose.ui.autofill.AutofillType
9 | import androidx.compose.ui.composed
10 | import androidx.compose.ui.focus.onFocusChanged
11 | import androidx.compose.ui.layout.boundsInWindow
12 | import androidx.compose.ui.layout.onGloballyPositioned
13 | import androidx.compose.ui.platform.LocalAutofill
14 | import androidx.compose.ui.platform.LocalAutofillTree
15 |
16 | /**
17 | * Referred from:
18 | * https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/autofill/ExplicitAutofillTypesDemo.kt
19 | *
20 | * We do not have a proper support for Autofill in Compose right now.
21 | *
22 | * Google is working to add support:
23 | * https://issuetracker.google.com/issues/268596603
24 | * https://issuetracker.google.com/issues/265911809
25 | * https://issuetracker.google.com/issues/176949051
26 | */
27 | @OptIn(ExperimentalComposeUiApi::class)
28 | @Composable
29 | fun Autofill(
30 | autofillTypes: List,
31 | onFill: ((String) -> Unit),
32 | content: @Composable (AutofillNode) -> Unit
33 | ) {
34 | val autofillNode = AutofillNode(
35 | onFill = onFill,
36 | autofillTypes = autofillTypes
37 | )
38 |
39 | val autofillTree = LocalAutofillTree.current
40 | autofillTree += autofillNode
41 |
42 | Box(
43 | Modifier.onGloballyPositioned {
44 | autofillNode.boundingBox = it.boundsInWindow()
45 | }
46 | ) {
47 | content(autofillNode)
48 | }
49 | }
50 |
51 | /**
52 | * Provides autofill to a view.
53 | *
54 | * This should be removed once compose completely supports autofill nicely.
55 | * Google is working to add support:
56 | * https://issuetracker.google.com/issues/268596603
57 | * https://issuetracker.google.com/issues/265911809
58 | * https://issuetracker.google.com/issues/176949051
59 | */
60 | @OptIn(ExperimentalComposeUiApi::class)
61 | fun Modifier.autofill(
62 | autofillTypes: List,
63 | onFill: ((String) -> Unit),
64 | ) = composed {
65 | val autofill = LocalAutofill.current
66 | val autofillNode = AutofillNode(onFill = onFill, autofillTypes = autofillTypes)
67 | LocalAutofillTree.current += autofillNode
68 | this
69 | .onGloballyPositioned {
70 | autofillNode.boundingBox = it.boundsInWindow()
71 | }
72 | .onFocusChanged { focusState ->
73 | autofill?.run {
74 | if (focusState.isFocused) {
75 | requestAutofillForNode(autofillNode)
76 | } else {
77 | cancelAutofillForNode(autofillNode)
78 | }
79 | }
80 | }
81 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/appmason/jetplayground/ui/components/OtpInputField.kt:
--------------------------------------------------------------------------------
1 | package com.appmason.jetplayground.ui.components
2 |
3 | import androidx.compose.animation.AnimatedVisibility
4 | import androidx.compose.foundation.background
5 | import androidx.compose.foundation.border
6 | import androidx.compose.foundation.layout.Arrangement
7 | import androidx.compose.foundation.layout.Box
8 | import androidx.compose.foundation.layout.Row
9 | import androidx.compose.foundation.layout.Spacer
10 | import androidx.compose.foundation.layout.height
11 | import androidx.compose.foundation.layout.padding
12 | import androidx.compose.foundation.layout.width
13 | import androidx.compose.foundation.shape.RoundedCornerShape
14 | import androidx.compose.foundation.text.BasicTextField
15 | import androidx.compose.foundation.text.KeyboardOptions
16 | import androidx.compose.material3.MaterialTheme
17 | import androidx.compose.material3.Text
18 | import androidx.compose.runtime.Composable
19 | import androidx.compose.runtime.LaunchedEffect
20 | import androidx.compose.runtime.mutableStateOf
21 | import androidx.compose.runtime.remember
22 | import androidx.compose.ui.Alignment
23 | import androidx.compose.ui.Modifier
24 | import androidx.compose.ui.text.TextRange
25 | import androidx.compose.ui.text.input.ImeAction
26 | import androidx.compose.ui.text.input.KeyboardType
27 | import androidx.compose.ui.text.input.TextFieldValue
28 | import androidx.compose.ui.text.style.TextAlign
29 | import androidx.compose.ui.unit.dp
30 | import com.appmason.jetplayground.ui.theme.BorderDark
31 | import com.appmason.jetplayground.ui.theme.BorderLight
32 | import kotlinx.coroutines.delay
33 |
34 | /**
35 | * A composable function for creating an OTP input field.
36 | *
37 | * This OTP input field allows for the entry of a One Time Password (OTP) with a configurable number of characters.
38 | * It supports automatic population of OTP from different sources (e.g., server).
39 | *
40 | * @param modifier Modifier for styling and layout of the input field.
41 | * @param otpText The current text of the OTP input field.
42 | * @param otpLength The length of the OTP. Default is 6 characters.
43 | * @param shouldShowCursor Boolean flag to indicate if the cursor should be shown.
44 | * @param shouldCursorBlink Boolean flag to indicate if the cursor should blink.
45 | * @param onOtpModified Lambda function that is triggered when the OTP text changes.
46 | * It provides the updated text and a flag indicating if the OTP is complete.
47 | * @throws IllegalArgumentException if the initial otpText length is greater than otpLength.
48 | *
49 | * Usage example:
50 | * OtpInputField(
51 | * otpText = viewModel.otpText,
52 | * otpLength = 6,
53 | * onOtpTextChange = { otp, isComplete -> /* handle OTP change */ }
54 | * )
55 | */
56 | @Composable
57 | fun OtpInputField(
58 | modifier: Modifier = Modifier,
59 | otpText: String,
60 | otpLength: Int = 6,
61 | shouldShowCursor: Boolean = false,
62 | shouldCursorBlink: Boolean = false,
63 | onOtpModified: (String, Boolean) -> Unit
64 | ) {
65 | LaunchedEffect(Unit) {
66 | if (otpText.length > otpLength) {
67 | throw IllegalArgumentException("OTP should be $otpLength digits")
68 | }
69 | }
70 | BasicTextField(
71 | modifier = modifier,
72 | value = TextFieldValue(otpText, selection = TextRange(otpText.length)),
73 | onValueChange = {
74 | if (it.text.length <= otpLength) {
75 | onOtpModified.invoke(it.text, it.text.length == otpLength)
76 | }
77 | },
78 | keyboardOptions = KeyboardOptions(
79 | keyboardType = KeyboardType.NumberPassword,
80 | imeAction = ImeAction.Done
81 | ),
82 | decorationBox = {
83 | Row(horizontalArrangement = Arrangement.Center) {
84 | repeat(otpLength) { index ->
85 | CharacterContainer(
86 | index = index,
87 | text = otpText,
88 | shouldShowCursor = shouldShowCursor,
89 | shouldCursorBlink = shouldCursorBlink,
90 | )
91 | Spacer(modifier = Modifier.width(8.dp))
92 | }
93 | }
94 | }
95 | )
96 | }
97 |
98 | /**
99 | * An internal composable function used within [OtpInputField] to render individual character containers.
100 | *
101 | * Each character container displays a single character of the OTP and manages cursor visibility and blinking.
102 | *
103 | * @param index The position of this character in the OTP.
104 | * @param text The current text of the OTP input field.
105 | * @param shouldShowCursor Boolean flag to indicate if the cursor should be shown for this container.
106 | * @param shouldCursorBlink Boolean flag to indicate if the cursor should blink when shown.
107 | *
108 | * Note: This function cannot be used outside the context of [OtpInputField] as it is tailored to its specific use-case.
109 | */
110 | @Composable
111 | internal fun CharacterContainer(
112 | index: Int,
113 | text: String,
114 | shouldShowCursor: Boolean,
115 | shouldCursorBlink: Boolean,
116 | ) {
117 | val isFocused = text.length == index
118 | val character = when {
119 | index < text.length -> text[index].toString()
120 | else -> ""
121 | }
122 |
123 | // Cursor visibility state
124 | val cursorVisible = remember { mutableStateOf(shouldShowCursor) }
125 |
126 | // Blinking effect for the cursor
127 | LaunchedEffect(key1 = isFocused) {
128 | if (isFocused && shouldShowCursor && shouldCursorBlink) {
129 | while (true) {
130 | delay(800) // Adjust the blinking speed here
131 | cursorVisible.value = !cursorVisible.value
132 | }
133 | }
134 | }
135 |
136 | Box(contentAlignment = Alignment.Center) {
137 | Text(
138 | modifier = Modifier
139 | .width(36.dp)
140 | .border(
141 | width = when {
142 | isFocused -> 2.dp
143 | else -> 1.dp
144 | },
145 | color = when {
146 | isFocused -> BorderDark
147 | else -> BorderLight
148 | },
149 | shape = RoundedCornerShape(6.dp)
150 | )
151 | .padding(2.dp),
152 | text = character,
153 | style = MaterialTheme.typography.headlineLarge,
154 | color = if (isFocused) BorderLight else BorderDark,
155 | textAlign = TextAlign.Center
156 | )
157 |
158 | // Display cursor when focused
159 | AnimatedVisibility(visible = isFocused && cursorVisible.value) {
160 | Box(
161 | modifier = Modifier
162 | .align(Alignment.Center)
163 | .width(2.dp)
164 | .height(24.dp) // Adjust height according to your design
165 | .background(BorderDark)
166 | )
167 | }
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/app/src/main/java/com/appmason/jetplayground/ui/navigation/NavGraph.kt:
--------------------------------------------------------------------------------
1 | package com.appmason.jetplayground.ui.navigation
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.navigation.NavHostController
5 | import androidx.navigation.compose.NavHost
6 | import androidx.navigation.compose.composable
7 | import com.appmason.jetplayground.ui.screens.OtpScreen
8 | import com.appmason.jetplayground.ui.screens.SplashScreen
9 |
10 | @Composable
11 | fun SetupNavGraph(navController: NavHostController) {
12 | NavHost(
13 | navController = navController,
14 | startDestination = Screen.Splash.route
15 | ) {
16 | composable(route = Screen.Splash.route) {
17 | SplashScreen(navController = navController)
18 | }
19 | composable(route = Screen.OtpScreen.route) {
20 | OtpScreen(navController = navController)
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/appmason/jetplayground/ui/navigation/Screens.kt:
--------------------------------------------------------------------------------
1 | package com.appmason.jetplayground.ui.navigation
2 |
3 | sealed class Screen(val route: String) {
4 | data object Splash : Screen("splash_screen")
5 | data object OtpScreen : Screen("otp_screen")
6 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/appmason/jetplayground/ui/screens/OtpScreen.kt:
--------------------------------------------------------------------------------
1 | package com.appmason.jetplayground.ui.screens
2 |
3 | import android.app.Activity
4 | import android.content.Context
5 | import android.content.IntentFilter
6 | import android.os.Build
7 | import android.util.Log
8 | import androidx.annotation.RequiresApi
9 | import androidx.compose.foundation.background
10 | import androidx.compose.foundation.clickable
11 | import androidx.compose.foundation.layout.Box
12 | import androidx.compose.foundation.layout.Column
13 | import androidx.compose.foundation.layout.WindowInsets
14 | import androidx.compose.foundation.layout.WindowInsetsSides
15 | import androidx.compose.foundation.layout.fillMaxSize
16 | import androidx.compose.foundation.layout.fillMaxWidth
17 | import androidx.compose.foundation.layout.only
18 | import androidx.compose.foundation.layout.padding
19 | import androidx.compose.foundation.layout.size
20 | import androidx.compose.foundation.layout.systemBars
21 | import androidx.compose.material3.Button
22 | import androidx.compose.material3.CenterAlignedTopAppBar
23 | import androidx.compose.material3.ExperimentalMaterial3Api
24 | import androidx.compose.material3.Icon
25 | import androidx.compose.material3.MaterialTheme
26 | import androidx.compose.material3.Scaffold
27 | import androidx.compose.material3.Surface
28 | import androidx.compose.material3.Text
29 | import androidx.compose.material3.TopAppBarDefaults
30 | import androidx.compose.runtime.Composable
31 | import androidx.compose.runtime.LaunchedEffect
32 | import androidx.compose.runtime.getValue
33 | import androidx.compose.runtime.mutableStateOf
34 | import androidx.compose.runtime.remember
35 | import androidx.compose.runtime.setValue
36 | import androidx.compose.ui.Alignment
37 | import androidx.compose.ui.ExperimentalComposeUiApi
38 | import androidx.compose.ui.Modifier
39 | import androidx.compose.ui.draw.drawWithContent
40 | import androidx.compose.ui.focus.FocusRequester
41 | import androidx.compose.ui.focus.focusRequester
42 | import androidx.compose.ui.graphics.Color
43 | import androidx.compose.ui.graphics.CompositingStrategy
44 | import androidx.compose.ui.graphics.graphicsLayer
45 | import androidx.compose.ui.graphics.toArgb
46 | import androidx.compose.ui.platform.LocalContext
47 | import androidx.compose.ui.platform.LocalSoftwareKeyboardController
48 | import androidx.compose.ui.platform.LocalView
49 | import androidx.compose.ui.res.painterResource
50 | import androidx.compose.ui.text.style.TextAlign
51 | import androidx.compose.ui.unit.dp
52 | import androidx.lifecycle.compose.LifecycleResumeEffect
53 | import androidx.navigation.NavHostController
54 | import com.appmason.composeotpverify.R
55 | import com.appmason.jetplayground.otp_verifier.receiver.OTPReceiver
56 | import com.appmason.jetplayground.otp_verifier.receiver.startSMSRetrieverClient
57 | import com.appmason.jetplayground.ui.components.OtpInputField
58 | import com.google.android.gms.auth.api.phone.SmsRetriever
59 |
60 | @OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class)
61 | @RequiresApi(Build.VERSION_CODES.TIRAMISU)
62 | @Composable
63 | fun OtpScreen(navController: NavHostController) {
64 | val context = LocalContext.current
65 | var otpValue by remember { mutableStateOf("") }
66 | var isOtpFilled by remember { mutableStateOf(false) }
67 | val focusRequester = remember { FocusRequester() }
68 | val keyboardController = LocalSoftwareKeyboardController.current
69 |
70 | /**
71 | * Right now we don't have support for Autofill in Compose.
72 | * See [com.appmason.jetplayground.ui.components.Autofill] for some temporary solutions.
73 | *
74 | * If we have support in the future and want user to autofill OTP from keyboard manually,
75 | * then we do not need to fetch OTP automatically using Google SMS Retriever API and in
76 | * that case, we can totally remove this [OtpReceiverEffect] and let Autofill handle it.
77 | * But Google SMS Retriever API is a great way anyways to fetch and populate OTP!
78 | */
79 | OtpReceiverEffect(
80 | context = context,
81 | onOtpReceived = { otp ->
82 | otpValue = otp
83 | if (otpValue.length == 6) {
84 | keyboardController?.hide()
85 | isOtpFilled = true
86 | }
87 | }
88 | )
89 |
90 | LaunchedEffect(Unit) {
91 | focusRequester.requestFocus()
92 | keyboardController?.show()
93 | }
94 |
95 | /**
96 | * Set status bar color for this screen
97 | */
98 | (LocalView.current.context as Activity).window.statusBarColor = Color.White.toArgb()
99 |
100 | /**
101 | * OTP Screen UI starts here
102 | */
103 | Scaffold(
104 | topBar = {
105 | CenterAlignedTopAppBar(
106 | modifier = Modifier
107 | .graphicsLayer { compositingStrategy = CompositingStrategy.Offscreen }
108 | .drawWithContent {
109 | drawContent()
110 | },
111 | navigationIcon = {
112 | Box(
113 | Modifier
114 | .size(48.dp)
115 | .clickable { navController.popBackStack() }) {
116 | Icon(
117 | painter = painterResource(id = R.drawable.ic_back),
118 | tint = Color.DarkGray,
119 | contentDescription = "Back",
120 | modifier = Modifier.align(Alignment.Center)
121 | )
122 | }
123 | },
124 | colors = TopAppBarDefaults.topAppBarColors(
125 | containerColor = Color.White,
126 | titleContentColor = Color.DarkGray,
127 | actionIconContentColor = Color.DarkGray
128 | ),
129 | title = { Text(text = "Enter One Time Password") },
130 | windowInsets = WindowInsets.systemBars.only(WindowInsetsSides.Horizontal)
131 | )
132 | },
133 | bottomBar = {
134 | Button(
135 | onClick = {},
136 | enabled = isOtpFilled,
137 | modifier = Modifier
138 | .fillMaxWidth()
139 | .padding(24.dp),
140 | ) {
141 | Text(text = "Continue")
142 | }
143 | }
144 | ) { innerPadding ->
145 | Surface(
146 | modifier = Modifier
147 | .fillMaxSize()
148 | .background(Color.White)
149 | .padding(24.dp),
150 | color = Color.White
151 | ) {
152 | Column(
153 | modifier = Modifier
154 | .fillMaxWidth()
155 | .padding(innerPadding)
156 | ) {
157 | Text(
158 | modifier = Modifier
159 | .fillMaxWidth()
160 | .padding(24.dp, 0.dp),
161 | text = "Please verify your phone number with the OTP we sent to (***)***-2193.",
162 | style = MaterialTheme.typography.bodyMedium,
163 | color = Color.DarkGray,
164 | textAlign = TextAlign.Center
165 | )
166 | Surface(
167 | modifier = Modifier
168 | .fillMaxWidth()
169 | .background(Color.White)
170 | .padding(24.dp),
171 | color = Color.White
172 | ) {
173 | OtpInputField(
174 | modifier = Modifier
175 | .padding(top = 48.dp)
176 | .focusRequester(focusRequester),
177 | otpText = otpValue,
178 | shouldCursorBlink = false,
179 | onOtpModified = { value, otpFilled ->
180 | otpValue = value
181 | isOtpFilled = otpFilled
182 | if (otpFilled) {
183 | keyboardController?.hide()
184 | }
185 | }
186 | )
187 | }
188 | }
189 | }
190 | }
191 |
192 | }
193 |
194 | @RequiresApi(Build.VERSION_CODES.TIRAMISU)
195 | @Composable
196 | fun OtpReceiverEffect(
197 | context: Context,
198 | onOtpReceived: (String) -> Unit
199 | ) {
200 | val otpReceiver = remember { OTPReceiver() }
201 |
202 | /**
203 | * This function should not be used to listen for Lifecycle.Event.ON_DESTROY because Compose
204 | * stops recomposing after receiving a Lifecycle.Event.ON_STOP and will never be aware of an
205 | * ON_DESTROY to launch onEvent.
206 | *
207 | * This function should also not be used to launch tasks in response to callback events by way
208 | * of storing callback data as a Lifecycle.State in a MutableState. Instead, see currentStateAsState
209 | * to obtain a State that may be used to launch jobs in response to state changes.
210 | */
211 | LifecycleResumeEffect {
212 | // add ON_RESUME effect here
213 | Log.e("OTPReceiverEffect", "SMS retrieval has been started.")
214 | startSMSRetrieverClient(context)
215 | otpReceiver.init(object : OTPReceiver.OTPReceiveListener {
216 | override fun onOTPReceived(otp: String?) {
217 | Log.e("OTPReceiverEffect ", "OTP Received: $otp")
218 | otp?.let { onOtpReceived(it) }
219 | try {
220 | Log.e("OTPReceiverEffect ", "Unregistering receiver")
221 | context.unregisterReceiver(otpReceiver)
222 | } catch (e: IllegalArgumentException) {
223 | Log.e("OTPReceiverEffect ", "Error in registering receiver: ${e.message}}")
224 | }
225 | }
226 |
227 | override fun onOTPTimeOut() {
228 | Log.e("OTPReceiverEffect ", "Timeout")
229 | }
230 | })
231 | try {
232 | Log.e("OTPReceiverEffect ", "Lifecycle.Event.ON_RESUME")
233 | Log.e("OTPReceiverEffect ", "Registering receiver")
234 | context.registerReceiver(
235 | otpReceiver,
236 | IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION),
237 | Context.RECEIVER_EXPORTED
238 | )
239 | } catch (e: IllegalArgumentException) {
240 | Log.e("OTPReceiverEffect ", "Error in registering receiver: ${e.message}}")
241 | }
242 | onPauseOrDispose {
243 | // add clean up for work kicked off in the ON_RESUME effect here
244 | try {
245 | Log.e("OTPReceiverEffect ", "Lifecycle.Event.ON_PAUSE")
246 | Log.e("OTPReceiverEffect ", "Unregistering receiver")
247 | context.unregisterReceiver(otpReceiver)
248 | } catch (e: IllegalArgumentException) {
249 | Log.e("OTPReceiverEffect ", "Error in unregistering receiver: ${e.message}}")
250 | }
251 | }
252 | }
253 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/appmason/jetplayground/ui/screens/SplashScreen.kt:
--------------------------------------------------------------------------------
1 | package com.appmason.jetplayground.ui.screens
2 |
3 | import androidx.compose.foundation.background
4 | import androidx.compose.foundation.layout.Box
5 | import androidx.compose.foundation.layout.fillMaxSize
6 | import androidx.compose.material3.MaterialTheme
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.runtime.getValue
9 | import androidx.compose.ui.Modifier
10 | import androidx.navigation.NavHostController
11 | import com.airbnb.lottie.compose.LottieAnimation
12 | import com.airbnb.lottie.compose.LottieCompositionSpec
13 | import com.airbnb.lottie.compose.animateLottieCompositionAsState
14 | import com.airbnb.lottie.compose.rememberLottieComposition
15 | import com.appmason.composeotpverify.R
16 | import com.appmason.jetplayground.ui.navigation.Screen
17 |
18 |
19 | @Composable
20 | fun SplashScreen(navController: NavHostController) {
21 | val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(R.raw.loading))
22 | val logoAnimationState = animateLottieCompositionAsState(
23 | composition = composition,
24 | speed = 2f
25 | )
26 | if (logoAnimationState.isAtEnd && logoAnimationState.isPlaying) {
27 | navController.navigate(Screen.OtpScreen.route)
28 | }
29 | Box(
30 | modifier = Modifier
31 | .fillMaxSize()
32 | .background(MaterialTheme.colorScheme.primary)
33 | ) {
34 | LottieAnimation(
35 | composition = composition,
36 | progress = { logoAnimationState.progress }
37 | )
38 | }
39 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/appmason/jetplayground/ui/theme/Color.kt:
--------------------------------------------------------------------------------
1 | package com.appmason.jetplayground.ui.theme
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | val Purple80 = Color(0xFFD0BCFF)
6 | val PurpleGrey80 = Color(0xFFCCC2DC)
7 | val Pink80 = Color(0xFFEFB8C8)
8 |
9 | val Purple40 = Color(0xFF6650a4)
10 | val PurpleGrey40 = Color(0xFF625b71)
11 | val Pink40 = Color(0xFF7D5260)
12 |
13 | val BorderLight = Color(0xFFBCD3E6)
14 | val BorderDark = Color(0xFF63798D)
--------------------------------------------------------------------------------
/app/src/main/java/com/appmason/jetplayground/ui/theme/Theme.kt:
--------------------------------------------------------------------------------
1 | package com.appmason.jetplayground.ui.theme
2 |
3 | import android.app.Activity
4 | import android.os.Build
5 | import androidx.compose.foundation.isSystemInDarkTheme
6 | import androidx.compose.material3.MaterialTheme
7 | import androidx.compose.material3.darkColorScheme
8 | import androidx.compose.material3.dynamicDarkColorScheme
9 | import androidx.compose.material3.dynamicLightColorScheme
10 | import androidx.compose.material3.lightColorScheme
11 | import androidx.compose.runtime.Composable
12 | import androidx.compose.runtime.SideEffect
13 | import androidx.compose.ui.graphics.toArgb
14 | import androidx.compose.ui.platform.LocalContext
15 | import androidx.compose.ui.platform.LocalView
16 | import androidx.core.view.WindowCompat
17 |
18 | private val DarkColorScheme = darkColorScheme(
19 | primary = Purple80,
20 | secondary = PurpleGrey80,
21 | tertiary = Pink80
22 | )
23 |
24 | private val LightColorScheme = lightColorScheme(
25 | primary = Purple40,
26 | secondary = PurpleGrey40,
27 | tertiary = Pink40
28 |
29 | /* Other default colors to override
30 | background = Color(0xFFFFFBFE),
31 | surface = Color(0xFFFFFBFE),
32 | onPrimary = Color.White,
33 | onSecondary = Color.White,
34 | onTertiary = Color.White,
35 | onBackground = Color(0xFF1C1B1F),
36 | onSurface = Color(0xFF1C1B1F),
37 | */
38 | )
39 |
40 | @Composable
41 | fun JetPlaygroundTheme(
42 | darkTheme: Boolean = isSystemInDarkTheme(),
43 | // Dynamic color is available on Android 12+
44 | dynamicColor: Boolean = true,
45 | content: @Composable () -> Unit
46 | ) {
47 | val colorScheme = when {
48 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
49 | val context = LocalContext.current
50 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
51 | }
52 |
53 | darkTheme -> DarkColorScheme
54 | else -> LightColorScheme
55 | }
56 | val view = LocalView.current
57 | if (!view.isInEditMode) {
58 | SideEffect {
59 | val window = (view.context as Activity).window
60 | window.statusBarColor = colorScheme.primary.toArgb()
61 | WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
62 | }
63 | }
64 |
65 | MaterialTheme(
66 | colorScheme = colorScheme,
67 | typography = Typography,
68 | content = content
69 | )
70 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/appmason/jetplayground/ui/theme/Type.kt:
--------------------------------------------------------------------------------
1 | package com.appmason.jetplayground.ui.theme
2 |
3 | import androidx.compose.material3.Typography
4 | import androidx.compose.ui.text.TextStyle
5 | import androidx.compose.ui.text.font.FontFamily
6 | import androidx.compose.ui.text.font.FontWeight
7 | import androidx.compose.ui.unit.sp
8 |
9 | // Set of Material typography styles to start with
10 | val Typography = Typography(
11 | bodyLarge = TextStyle(
12 | fontFamily = FontFamily.Default,
13 | fontWeight = FontWeight.Normal,
14 | fontSize = 16.sp,
15 | lineHeight = 24.sp,
16 | letterSpacing = 0.5.sp
17 | )
18 | /* Other default text styles to override
19 | titleLarge = TextStyle(
20 | fontFamily = FontFamily.Default,
21 | fontWeight = FontWeight.Normal,
22 | fontSize = 22.sp,
23 | lineHeight = 28.sp,
24 | letterSpacing = 0.sp
25 | ),
26 | labelSmall = TextStyle(
27 | fontFamily = FontFamily.Default,
28 | fontWeight = FontWeight.Medium,
29 | fontSize = 11.sp,
30 | lineHeight = 16.sp,
31 | letterSpacing = 0.5.sp
32 | )
33 | */
34 | )
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_back.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pushpalroy/ComposeOtpVerify/e57c00881dac850888b245f01ce31dde1684e09d/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pushpalroy/ComposeOtpVerify/e57c00881dac850888b245f01ce31dde1684e09d/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pushpalroy/ComposeOtpVerify/e57c00881dac850888b245f01ce31dde1684e09d/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pushpalroy/ComposeOtpVerify/e57c00881dac850888b245f01ce31dde1684e09d/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pushpalroy/ComposeOtpVerify/e57c00881dac850888b245f01ce31dde1684e09d/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pushpalroy/ComposeOtpVerify/e57c00881dac850888b245f01ce31dde1684e09d/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pushpalroy/ComposeOtpVerify/e57c00881dac850888b245f01ce31dde1684e09d/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pushpalroy/ComposeOtpVerify/e57c00881dac850888b245f01ce31dde1684e09d/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pushpalroy/ComposeOtpVerify/e57c00881dac850888b245f01ce31dde1684e09d/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pushpalroy/ComposeOtpVerify/e57c00881dac850888b245f01ce31dde1684e09d/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/raw/loading.json:
--------------------------------------------------------------------------------
1 | {
2 | "v": "4.8.0",
3 | "meta": {
4 | "g": "LottieFiles AE 3.5.3",
5 | "a": "",
6 | "k": "",
7 | "d": "",
8 | "tc": ""
9 | },
10 | "fr": 30,
11 | "ip": 0,
12 | "op": 56,
13 | "w": 500,
14 | "h": 500,
15 | "nm": "Check Mark",
16 | "ddd": 0,
17 | "assets": [],
18 | "layers": [
19 | {
20 | "ddd": 0,
21 | "ind": 1,
22 | "ty": 4,
23 | "nm": "line 2",
24 | "sr": 1,
25 | "ks": {
26 | "o": {
27 | "a": 0,
28 | "k": 100,
29 | "ix": 11
30 | },
31 | "r": {
32 | "a": 0,
33 | "k": 0,
34 | "ix": 10
35 | },
36 | "p": {
37 | "a": 0,
38 | "k": [
39 | 250,
40 | 250,
41 | 0
42 | ],
43 | "ix": 2
44 | },
45 | "a": {
46 | "a": 0,
47 | "k": [
48 | 133.203,
49 | 133.203,
50 | 0
51 | ],
52 | "ix": 1
53 | },
54 | "s": {
55 | "a": 0,
56 | "k": [
57 | 100,
58 | 100,
59 | 100
60 | ],
61 | "ix": 6
62 | }
63 | },
64 | "ao": 0,
65 | "shapes": [
66 | {
67 | "ty": "gr",
68 | "it": [
69 | {
70 | "ind": 0,
71 | "ty": "sh",
72 | "ix": 1,
73 | "ks": {
74 | "a": 0,
75 | "k": {
76 | "i": [
77 | [
78 | 67.153,
79 | 0
80 | ],
81 | [
82 | 0,
83 | 67.153
84 | ],
85 | [
86 | -67.153,
87 | 0
88 | ],
89 | [
90 | 0,
91 | -67.153
92 | ]
93 | ],
94 | "o": [
95 | [
96 | -67.153,
97 | 0
98 | ],
99 | [
100 | 0,
101 | -67.153
102 | ],
103 | [
104 | 67.153,
105 | 0
106 | ],
107 | [
108 | 0,
109 | 67.153
110 | ]
111 | ],
112 | "v": [
113 | [
114 | 0,
115 | 121.785
116 | ],
117 | [
118 | -121.786,
119 | -0.001
120 | ],
121 | [
122 | 0,
123 | -121.785
124 | ],
125 | [
126 | 121.786,
127 | -0.001
128 | ]
129 | ],
130 | "c": true
131 | },
132 | "ix": 2
133 | },
134 | "nm": "Path 1",
135 | "mn": "ADBE Vector Shape - Group",
136 | "hd": false
137 | },
138 | {
139 | "ty": "st",
140 | "c": {
141 | "a": 0,
142 | "k": [
143 | 0.043137254902,
144 | 0.160784313725,
145 | 0.658823529412,
146 | 1
147 | ],
148 | "ix": 3
149 | },
150 | "o": {
151 | "a": 0,
152 | "k": 100,
153 | "ix": 4
154 | },
155 | "w": {
156 | "a": 0,
157 | "k": 4.567,
158 | "ix": 5
159 | },
160 | "lc": 1,
161 | "lj": 1,
162 | "ml": 10,
163 | "bm": 0,
164 | "nm": "Stroke 1",
165 | "mn": "ADBE Vector Graphic - Stroke",
166 | "hd": false
167 | },
168 | {
169 | "ty": "tr",
170 | "p": {
171 | "a": 0,
172 | "k": [
173 | 133.203,
174 | 133.203
175 | ],
176 | "ix": 2
177 | },
178 | "a": {
179 | "a": 0,
180 | "k": [
181 | 0,
182 | 0
183 | ],
184 | "ix": 1
185 | },
186 | "s": {
187 | "a": 0,
188 | "k": [
189 | 100,
190 | 100
191 | ],
192 | "ix": 3
193 | },
194 | "r": {
195 | "a": 0,
196 | "k": 0,
197 | "ix": 6
198 | },
199 | "o": {
200 | "a": 0,
201 | "k": 100,
202 | "ix": 7
203 | },
204 | "sk": {
205 | "a": 0,
206 | "k": 0,
207 | "ix": 4
208 | },
209 | "sa": {
210 | "a": 0,
211 | "k": 0,
212 | "ix": 5
213 | },
214 | "nm": "Transform"
215 | }
216 | ],
217 | "nm": "Group 1",
218 | "np": 2,
219 | "cix": 2,
220 | "bm": 0,
221 | "ix": 1,
222 | "mn": "ADBE Vector Group",
223 | "hd": false
224 | },
225 | {
226 | "ty": "tm",
227 | "s": {
228 | "a": 1,
229 | "k": [
230 | {
231 | "i": {
232 | "x": [
233 | 0.667
234 | ],
235 | "y": [
236 | 1
237 | ]
238 | },
239 | "o": {
240 | "x": [
241 | 0.333
242 | ],
243 | "y": [
244 | 0
245 | ]
246 | },
247 | "t": 15.015,
248 | "s": [
249 | 0
250 | ]
251 | },
252 | {
253 | "t": 25.025512817505,
254 | "s": [
255 | 100
256 | ]
257 | }
258 | ],
259 | "ix": 1
260 | },
261 | "e": {
262 | "a": 1,
263 | "k": [
264 | {
265 | "i": {
266 | "x": [
267 | 0.667
268 | ],
269 | "y": [
270 | 1
271 | ]
272 | },
273 | "o": {
274 | "x": [
275 | 0.333
276 | ],
277 | "y": [
278 | 0
279 | ]
280 | },
281 | "t": 5.005,
282 | "s": [
283 | 0
284 | ]
285 | },
286 | {
287 | "t": 15.014770630005,
288 | "s": [
289 | 100
290 | ]
291 | }
292 | ],
293 | "ix": 2
294 | },
295 | "o": {
296 | "a": 0,
297 | "k": 0,
298 | "ix": 3
299 | },
300 | "m": 1,
301 | "ix": 2,
302 | "nm": "Trim Paths 1",
303 | "mn": "ADBE Vector Filter - Trim",
304 | "hd": false
305 | }
306 | ],
307 | "ip": 5.00500500500501,
308 | "op": 58.0580580580581,
309 | "st": 5.00500500500501,
310 | "bm": 0
311 | },
312 | {
313 | "ddd": 0,
314 | "ind": 2,
315 | "ty": 4,
316 | "nm": "line 1",
317 | "sr": 1,
318 | "ks": {
319 | "o": {
320 | "a": 0,
321 | "k": 100,
322 | "ix": 11
323 | },
324 | "r": {
325 | "a": 0,
326 | "k": 0,
327 | "ix": 10
328 | },
329 | "p": {
330 | "a": 0,
331 | "k": [
332 | 250,
333 | 250,
334 | 0
335 | ],
336 | "ix": 2
337 | },
338 | "a": {
339 | "a": 0,
340 | "k": [
341 | 126.5,
342 | 126.5,
343 | 0
344 | ],
345 | "ix": 1
346 | },
347 | "s": {
348 | "a": 0,
349 | "k": [
350 | 100,
351 | 100,
352 | 100
353 | ],
354 | "ix": 6
355 | }
356 | },
357 | "ao": 0,
358 | "shapes": [
359 | {
360 | "ty": "gr",
361 | "it": [
362 | {
363 | "ind": 0,
364 | "ty": "sh",
365 | "ix": 1,
366 | "ks": {
367 | "a": 0,
368 | "k": {
369 | "i": [
370 | [
371 | 60.103,
372 | 0
373 | ],
374 | [
375 | 0,
376 | 60.103
377 | ],
378 | [
379 | -60.103,
380 | 0
381 | ],
382 | [
383 | 0,
384 | -60.103
385 | ]
386 | ],
387 | "o": [
388 | [
389 | -60.103,
390 | 0
391 | ],
392 | [
393 | 0,
394 | -60.103
395 | ],
396 | [
397 | 60.103,
398 | 0
399 | ],
400 | [
401 | 0,
402 | 60.103
403 | ]
404 | ],
405 | "v": [
406 | [
407 | 0,
408 | 109
409 | ],
410 | [
411 | -109,
412 | 0
413 | ],
414 | [
415 | 0,
416 | -109
417 | ],
418 | [
419 | 109,
420 | 0
421 | ]
422 | ],
423 | "c": true
424 | },
425 | "ix": 2
426 | },
427 | "nm": "Path 1",
428 | "mn": "ADBE Vector Shape - Group",
429 | "hd": false
430 | },
431 | {
432 | "ty": "st",
433 | "c": {
434 | "a": 0,
435 | "k": [
436 | 0.043137254902,
437 | 0.160784313725,
438 | 0.658823529412,
439 | 1
440 | ],
441 | "ix": 3
442 | },
443 | "o": {
444 | "a": 0,
445 | "k": 100,
446 | "ix": 4
447 | },
448 | "w": {
449 | "a": 0,
450 | "k": 7,
451 | "ix": 5
452 | },
453 | "lc": 1,
454 | "lj": 1,
455 | "ml": 10,
456 | "bm": 0,
457 | "nm": "Stroke 1",
458 | "mn": "ADBE Vector Graphic - Stroke",
459 | "hd": false
460 | },
461 | {
462 | "ty": "tr",
463 | "p": {
464 | "a": 0,
465 | "k": [
466 | 126.5,
467 | 126.5
468 | ],
469 | "ix": 2
470 | },
471 | "a": {
472 | "a": 0,
473 | "k": [
474 | 0,
475 | 0
476 | ],
477 | "ix": 1
478 | },
479 | "s": {
480 | "a": 0,
481 | "k": [
482 | 100,
483 | 100
484 | ],
485 | "ix": 3
486 | },
487 | "r": {
488 | "a": 0,
489 | "k": 0,
490 | "ix": 6
491 | },
492 | "o": {
493 | "a": 0,
494 | "k": 100,
495 | "ix": 7
496 | },
497 | "sk": {
498 | "a": 0,
499 | "k": 0,
500 | "ix": 4
501 | },
502 | "sa": {
503 | "a": 0,
504 | "k": 0,
505 | "ix": 5
506 | },
507 | "nm": "Transform"
508 | }
509 | ],
510 | "nm": "Group 1",
511 | "np": 2,
512 | "cix": 2,
513 | "bm": 0,
514 | "ix": 1,
515 | "mn": "ADBE Vector Group",
516 | "hd": false
517 | },
518 | {
519 | "ty": "tm",
520 | "s": {
521 | "a": 1,
522 | "k": [
523 | {
524 | "i": {
525 | "x": [
526 | 0.667
527 | ],
528 | "y": [
529 | 1
530 | ]
531 | },
532 | "o": {
533 | "x": [
534 | 0.333
535 | ],
536 | "y": [
537 | 0
538 | ]
539 | },
540 | "t": 10.01,
541 | "s": [
542 | 0
543 | ]
544 | },
545 | {
546 | "t": 20.0205078125,
547 | "s": [
548 | 100
549 | ]
550 | }
551 | ],
552 | "ix": 1
553 | },
554 | "e": {
555 | "a": 1,
556 | "k": [
557 | {
558 | "i": {
559 | "x": [
560 | 0.667
561 | ],
562 | "y": [
563 | 1
564 | ]
565 | },
566 | "o": {
567 | "x": [
568 | 0.333
569 | ],
570 | "y": [
571 | 0
572 | ]
573 | },
574 | "t": 0,
575 | "s": [
576 | 0
577 | ]
578 | },
579 | {
580 | "t": 10.009765625,
581 | "s": [
582 | 100
583 | ]
584 | }
585 | ],
586 | "ix": 2
587 | },
588 | "o": {
589 | "a": 0,
590 | "k": 0,
591 | "ix": 3
592 | },
593 | "m": 1,
594 | "ix": 2,
595 | "nm": "Trim Paths 1",
596 | "mn": "ADBE Vector Filter - Trim",
597 | "hd": false
598 | }
599 | ],
600 | "ip": 0,
601 | "op": 58.0580580580581,
602 | "st": 0,
603 | "bm": 0
604 | },
605 | {
606 | "ddd": 0,
607 | "ind": 3,
608 | "ty": 4,
609 | "nm": "check mark",
610 | "sr": 1,
611 | "ks": {
612 | "o": {
613 | "a": 0,
614 | "k": 100,
615 | "ix": 11
616 | },
617 | "r": {
618 | "a": 0,
619 | "k": 0,
620 | "ix": 10
621 | },
622 | "p": {
623 | "a": 0,
624 | "k": [
625 | 247.728,
626 | 250,
627 | 0
628 | ],
629 | "ix": 2
630 | },
631 | "a": {
632 | "a": 0,
633 | "k": [
634 | 93.963,
635 | 79.058,
636 | 0
637 | ],
638 | "ix": 1
639 | },
640 | "s": {
641 | "a": 1,
642 | "k": [
643 | {
644 | "i": {
645 | "x": [
646 | 0.667,
647 | 0.667,
648 | 0.667
649 | ],
650 | "y": [
651 | 1,
652 | 1,
653 | 1
654 | ]
655 | },
656 | "o": {
657 | "x": [
658 | 0.333,
659 | 0.333,
660 | 0.333
661 | ],
662 | "y": [
663 | 0,
664 | 0,
665 | 0
666 | ]
667 | },
668 | "t": 25,
669 | "s": [
670 | 0,
671 | 0,
672 | 100
673 | ]
674 | },
675 | {
676 | "i": {
677 | "x": [
678 | 0.667,
679 | 0.667,
680 | 0.667
681 | ],
682 | "y": [
683 | 1,
684 | 1,
685 | 1
686 | ]
687 | },
688 | "o": {
689 | "x": [
690 | 0.333,
691 | 0.333,
692 | 0.333
693 | ],
694 | "y": [
695 | 0,
696 | 0,
697 | 0
698 | ]
699 | },
700 | "t": 35,
701 | "s": [
702 | 120,
703 | 120,
704 | 100
705 | ]
706 | },
707 | {
708 | "i": {
709 | "x": [
710 | 0.667,
711 | 0.667,
712 | 0.667
713 | ],
714 | "y": [
715 | 1,
716 | 1,
717 | 1
718 | ]
719 | },
720 | "o": {
721 | "x": [
722 | 0.167,
723 | 0.167,
724 | 0.167
725 | ],
726 | "y": [
727 | 0,
728 | 0,
729 | 0
730 | ]
731 | },
732 | "t": 40,
733 | "s": [
734 | 100,
735 | 100,
736 | 100
737 | ]
738 | },
739 | {
740 | "i": {
741 | "x": [
742 | 0.833,
743 | 0.833,
744 | 0.833
745 | ],
746 | "y": [
747 | 1,
748 | 1,
749 | 1
750 | ]
751 | },
752 | "o": {
753 | "x": [
754 | 0.167,
755 | 0.167,
756 | 0.167
757 | ],
758 | "y": [
759 | 0,
760 | 0,
761 | 0
762 | ]
763 | },
764 | "t": 45,
765 | "s": [
766 | 100,
767 | 100,
768 | 100
769 | ]
770 | },
771 | {
772 | "t": 55,
773 | "s": [
774 | 0,
775 | 0,
776 | 100
777 | ]
778 | }
779 | ],
780 | "ix": 6
781 | }
782 | },
783 | "ao": 0,
784 | "shapes": [
785 | {
786 | "ty": "gr",
787 | "it": [
788 | {
789 | "ind": 0,
790 | "ty": "sh",
791 | "ix": 1,
792 | "ks": {
793 | "a": 0,
794 | "k": {
795 | "i": [
796 | [
797 | 0,
798 | 0
799 | ],
800 | [
801 | 0,
802 | 0
803 | ],
804 | [
805 | 0,
806 | 0
807 | ]
808 | ],
809 | "o": [
810 | [
811 | 0,
812 | 0
813 | ],
814 | [
815 | 0,
816 | 0
817 | ],
818 | [
819 | 0,
820 | 0
821 | ]
822 | ],
823 | "v": [
824 | [
825 | -53.61,
826 | 8.434
827 | ],
828 | [
829 | -21.259,
830 | 38.706
831 | ],
832 | [
833 | 53.61,
834 | -38.706
835 | ]
836 | ],
837 | "c": false
838 | },
839 | "ix": 2
840 | },
841 | "nm": "Path 1",
842 | "mn": "ADBE Vector Shape - Group",
843 | "hd": false
844 | },
845 | {
846 | "ty": "st",
847 | "c": {
848 | "a": 0,
849 | "k": [
850 | 1,
851 | 1,
852 | 1,
853 | 1
854 | ],
855 | "ix": 3
856 | },
857 | "o": {
858 | "a": 0,
859 | "k": 100,
860 | "ix": 4
861 | },
862 | "w": {
863 | "a": 0,
864 | "k": 16.141,
865 | "ix": 5
866 | },
867 | "lc": 2,
868 | "lj": 2,
869 | "bm": 0,
870 | "nm": "Stroke 1",
871 | "mn": "ADBE Vector Graphic - Stroke",
872 | "hd": false
873 | },
874 | {
875 | "ty": "tr",
876 | "p": {
877 | "a": 0,
878 | "k": [
879 | 93.963,
880 | 79.058
881 | ],
882 | "ix": 2
883 | },
884 | "a": {
885 | "a": 0,
886 | "k": [
887 | 0,
888 | 0
889 | ],
890 | "ix": 1
891 | },
892 | "s": {
893 | "a": 0,
894 | "k": [
895 | 100,
896 | 100
897 | ],
898 | "ix": 3
899 | },
900 | "r": {
901 | "a": 0,
902 | "k": 0,
903 | "ix": 6
904 | },
905 | "o": {
906 | "a": 0,
907 | "k": 100,
908 | "ix": 7
909 | },
910 | "sk": {
911 | "a": 0,
912 | "k": 0,
913 | "ix": 4
914 | },
915 | "sa": {
916 | "a": 0,
917 | "k": 0,
918 | "ix": 5
919 | },
920 | "nm": "Transform"
921 | }
922 | ],
923 | "nm": "Group 1",
924 | "np": 2,
925 | "cix": 2,
926 | "bm": 0,
927 | "ix": 1,
928 | "mn": "ADBE Vector Group",
929 | "hd": false
930 | }
931 | ],
932 | "ip": 10,
933 | "op": 68.0580580580581,
934 | "st": 10,
935 | "bm": 0
936 | },
937 | {
938 | "ddd": 0,
939 | "ind": 4,
940 | "ty": 4,
941 | "nm": "circle",
942 | "sr": 1,
943 | "ks": {
944 | "o": {
945 | "a": 0,
946 | "k": 100,
947 | "ix": 11
948 | },
949 | "r": {
950 | "a": 0,
951 | "k": 0,
952 | "ix": 10
953 | },
954 | "p": {
955 | "a": 0,
956 | "k": [
957 | 250,
958 | 250,
959 | 0
960 | ],
961 | "ix": 2
962 | },
963 | "a": {
964 | "a": 0,
965 | "k": [
966 | 98.25,
967 | 98.25,
968 | 0
969 | ],
970 | "ix": 1
971 | },
972 | "s": {
973 | "a": 1,
974 | "k": [
975 | {
976 | "i": {
977 | "x": [
978 | 0.667,
979 | 0.667,
980 | 0.667
981 | ],
982 | "y": [
983 | 1,
984 | 1,
985 | 1
986 | ]
987 | },
988 | "o": {
989 | "x": [
990 | 0.333,
991 | 0.333,
992 | 0.333
993 | ],
994 | "y": [
995 | 0,
996 | 0,
997 | 0
998 | ]
999 | },
1000 | "t": 15,
1001 | "s": [
1002 | 0,
1003 | 0,
1004 | 100
1005 | ]
1006 | },
1007 | {
1008 | "i": {
1009 | "x": [
1010 | 0.667,
1011 | 0.667,
1012 | 0.667
1013 | ],
1014 | "y": [
1015 | 1,
1016 | 1,
1017 | 1
1018 | ]
1019 | },
1020 | "o": {
1021 | "x": [
1022 | 0.333,
1023 | 0.333,
1024 | 0.333
1025 | ],
1026 | "y": [
1027 | 0,
1028 | 0,
1029 | 0
1030 | ]
1031 | },
1032 | "t": 25,
1033 | "s": [
1034 | 115,
1035 | 115,
1036 | 100
1037 | ]
1038 | },
1039 | {
1040 | "i": {
1041 | "x": [
1042 | 0.667,
1043 | 0.667,
1044 | 0.667
1045 | ],
1046 | "y": [
1047 | 1,
1048 | 1,
1049 | 1
1050 | ]
1051 | },
1052 | "o": {
1053 | "x": [
1054 | 0.167,
1055 | 0.167,
1056 | 0.167
1057 | ],
1058 | "y": [
1059 | 0,
1060 | 0,
1061 | 0
1062 | ]
1063 | },
1064 | "t": 30,
1065 | "s": [
1066 | 100,
1067 | 100,
1068 | 100
1069 | ]
1070 | },
1071 | {
1072 | "i": {
1073 | "x": [
1074 | 0.833,
1075 | 0.833,
1076 | 0.833
1077 | ],
1078 | "y": [
1079 | 1,
1080 | 1,
1081 | 1
1082 | ]
1083 | },
1084 | "o": {
1085 | "x": [
1086 | 0.167,
1087 | 0.167,
1088 | 0.167
1089 | ],
1090 | "y": [
1091 | 0,
1092 | 0,
1093 | 0
1094 | ]
1095 | },
1096 | "t": 45,
1097 | "s": [
1098 | 100,
1099 | 100,
1100 | 100
1101 | ]
1102 | },
1103 | {
1104 | "t": 55,
1105 | "s": [
1106 | 0,
1107 | 0,
1108 | 100
1109 | ]
1110 | }
1111 | ],
1112 | "ix": 6
1113 | }
1114 | },
1115 | "ao": 0,
1116 | "shapes": [
1117 | {
1118 | "ty": "gr",
1119 | "it": [
1120 | {
1121 | "ind": 0,
1122 | "ty": "sh",
1123 | "ix": 1,
1124 | "ks": {
1125 | "a": 0,
1126 | "k": {
1127 | "i": [
1128 | [
1129 | 0,
1130 | -54.124
1131 | ],
1132 | [
1133 | 54.124,
1134 | 0
1135 | ],
1136 | [
1137 | 0,
1138 | 54.124
1139 | ],
1140 | [
1141 | -54.124,
1142 | 0
1143 | ]
1144 | ],
1145 | "o": [
1146 | [
1147 | 0,
1148 | 54.124
1149 | ],
1150 | [
1151 | -54.124,
1152 | 0
1153 | ],
1154 | [
1155 | 0,
1156 | -54.124
1157 | ],
1158 | [
1159 | 54.124,
1160 | 0
1161 | ]
1162 | ],
1163 | "v": [
1164 | [
1165 | 98,
1166 | 0
1167 | ],
1168 | [
1169 | 0,
1170 | 98
1171 | ],
1172 | [
1173 | -98,
1174 | 0
1175 | ],
1176 | [
1177 | 0,
1178 | -98
1179 | ]
1180 | ],
1181 | "c": true
1182 | },
1183 | "ix": 2
1184 | },
1185 | "nm": "Path 1",
1186 | "mn": "ADBE Vector Shape - Group",
1187 | "hd": false
1188 | },
1189 | {
1190 | "ty": "fl",
1191 | "c": {
1192 | "a": 0,
1193 | "k": [
1194 | 0,
1195 | 0.784313785329,
1196 | 0.521568627451,
1197 | 1
1198 | ],
1199 | "ix": 4
1200 | },
1201 | "o": {
1202 | "a": 0,
1203 | "k": 100,
1204 | "ix": 5
1205 | },
1206 | "r": 1,
1207 | "bm": 0,
1208 | "nm": "Fill 1",
1209 | "mn": "ADBE Vector Graphic - Fill",
1210 | "hd": false
1211 | },
1212 | {
1213 | "ty": "tr",
1214 | "p": {
1215 | "a": 0,
1216 | "k": [
1217 | 98.25,
1218 | 98.25
1219 | ],
1220 | "ix": 2
1221 | },
1222 | "a": {
1223 | "a": 0,
1224 | "k": [
1225 | 0,
1226 | 0
1227 | ],
1228 | "ix": 1
1229 | },
1230 | "s": {
1231 | "a": 0,
1232 | "k": [
1233 | 100,
1234 | 100
1235 | ],
1236 | "ix": 3
1237 | },
1238 | "r": {
1239 | "a": 0,
1240 | "k": 0,
1241 | "ix": 6
1242 | },
1243 | "o": {
1244 | "a": 0,
1245 | "k": 100,
1246 | "ix": 7
1247 | },
1248 | "sk": {
1249 | "a": 0,
1250 | "k": 0,
1251 | "ix": 4
1252 | },
1253 | "sa": {
1254 | "a": 0,
1255 | "k": 0,
1256 | "ix": 5
1257 | },
1258 | "nm": "Transform"
1259 | }
1260 | ],
1261 | "nm": "Group 1",
1262 | "np": 2,
1263 | "cix": 2,
1264 | "bm": 0,
1265 | "ix": 1,
1266 | "mn": "ADBE Vector Group",
1267 | "hd": false
1268 | }
1269 | ],
1270 | "ip": 0,
1271 | "op": 58.0580580580581,
1272 | "st": 0,
1273 | "bm": 0
1274 | },
1275 | {
1276 | "ddd": 0,
1277 | "ind": 5,
1278 | "ty": 4,
1279 | "nm": "circles",
1280 | "sr": 1,
1281 | "ks": {
1282 | "o": {
1283 | "a": 1,
1284 | "k": [
1285 | {
1286 | "i": {
1287 | "x": [
1288 | 0.667
1289 | ],
1290 | "y": [
1291 | 1
1292 | ]
1293 | },
1294 | "o": {
1295 | "x": [
1296 | 0.333
1297 | ],
1298 | "y": [
1299 | 0
1300 | ]
1301 | },
1302 | "t": 50,
1303 | "s": [
1304 | 100
1305 | ]
1306 | },
1307 | {
1308 | "t": 55,
1309 | "s": [
1310 | 0
1311 | ]
1312 | }
1313 | ],
1314 | "ix": 11
1315 | },
1316 | "r": {
1317 | "a": 0,
1318 | "k": 0,
1319 | "ix": 10
1320 | },
1321 | "p": {
1322 | "a": 0,
1323 | "k": [
1324 | 248.685,
1325 | 246.851,
1326 | 0
1327 | ],
1328 | "ix": 2
1329 | },
1330 | "a": {
1331 | "a": 0,
1332 | "k": [
1333 | 123.082,
1334 | 121.196,
1335 | 0
1336 | ],
1337 | "ix": 1
1338 | },
1339 | "s": {
1340 | "a": 1,
1341 | "k": [
1342 | {
1343 | "i": {
1344 | "x": [
1345 | 0.667,
1346 | 0.667,
1347 | 0.667
1348 | ],
1349 | "y": [
1350 | 1,
1351 | 1,
1352 | 1
1353 | ]
1354 | },
1355 | "o": {
1356 | "x": [
1357 | 0.333,
1358 | 0.333,
1359 | 0.333
1360 | ],
1361 | "y": [
1362 | 0,
1363 | 0,
1364 | 0
1365 | ]
1366 | },
1367 | "t": 35,
1368 | "s": [
1369 | 0,
1370 | 0,
1371 | 100
1372 | ]
1373 | },
1374 | {
1375 | "i": {
1376 | "x": [
1377 | 0.667,
1378 | 0.667,
1379 | 0.667
1380 | ],
1381 | "y": [
1382 | 1,
1383 | 1,
1384 | 1
1385 | ]
1386 | },
1387 | "o": {
1388 | "x": [
1389 | 0.333,
1390 | 0.333,
1391 | 0.333
1392 | ],
1393 | "y": [
1394 | 0,
1395 | 0,
1396 | 0
1397 | ]
1398 | },
1399 | "t": 50,
1400 | "s": [
1401 | 100,
1402 | 100,
1403 | 100
1404 | ]
1405 | },
1406 | {
1407 | "t": 55,
1408 | "s": [
1409 | 120,
1410 | 120,
1411 | 100
1412 | ]
1413 | }
1414 | ],
1415 | "ix": 6
1416 | }
1417 | },
1418 | "ao": 0,
1419 | "shapes": [
1420 | {
1421 | "ty": "gr",
1422 | "it": [
1423 | {
1424 | "ind": 0,
1425 | "ty": "sh",
1426 | "ix": 1,
1427 | "ks": {
1428 | "a": 0,
1429 | "k": {
1430 | "i": [
1431 | [
1432 | 0,
1433 | -1.964
1434 | ],
1435 | [
1436 | 1.965,
1437 | 0
1438 | ],
1439 | [
1440 | 0,
1441 | 1.964
1442 | ],
1443 | [
1444 | -1.964,
1445 | 0
1446 | ]
1447 | ],
1448 | "o": [
1449 | [
1450 | 0,
1451 | 1.964
1452 | ],
1453 | [
1454 | -1.964,
1455 | 0
1456 | ],
1457 | [
1458 | 0,
1459 | -1.964
1460 | ],
1461 | [
1462 | 1.965,
1463 | 0
1464 | ]
1465 | ],
1466 | "v": [
1467 | [
1468 | 3.557,
1469 | 0
1470 | ],
1471 | [
1472 | -0.001,
1473 | 3.556
1474 | ],
1475 | [
1476 | -3.557,
1477 | 0
1478 | ],
1479 | [
1480 | -0.001,
1481 | -3.556
1482 | ]
1483 | ],
1484 | "c": true
1485 | },
1486 | "ix": 2
1487 | },
1488 | "nm": "Path 1",
1489 | "mn": "ADBE Vector Shape - Group",
1490 | "hd": false
1491 | },
1492 | {
1493 | "ty": "fl",
1494 | "c": {
1495 | "a": 0,
1496 | "k": [
1497 | 1,
1498 | 0.674509803922,
1499 | 0.384313755409,
1500 | 1
1501 | ],
1502 | "ix": 4
1503 | },
1504 | "o": {
1505 | "a": 0,
1506 | "k": 100,
1507 | "ix": 5
1508 | },
1509 | "r": 1,
1510 | "bm": 0,
1511 | "nm": "Fill 1",
1512 | "mn": "ADBE Vector Graphic - Fill",
1513 | "hd": false
1514 | },
1515 | {
1516 | "ty": "tr",
1517 | "p": {
1518 | "a": 0,
1519 | "k": [
1520 | 65.54,
1521 | 32.998
1522 | ],
1523 | "ix": 2
1524 | },
1525 | "a": {
1526 | "a": 0,
1527 | "k": [
1528 | 0,
1529 | 0
1530 | ],
1531 | "ix": 1
1532 | },
1533 | "s": {
1534 | "a": 0,
1535 | "k": [
1536 | 100,
1537 | 100
1538 | ],
1539 | "ix": 3
1540 | },
1541 | "r": {
1542 | "a": 0,
1543 | "k": 0,
1544 | "ix": 6
1545 | },
1546 | "o": {
1547 | "a": 0,
1548 | "k": 100,
1549 | "ix": 7
1550 | },
1551 | "sk": {
1552 | "a": 0,
1553 | "k": 0,
1554 | "ix": 4
1555 | },
1556 | "sa": {
1557 | "a": 0,
1558 | "k": 0,
1559 | "ix": 5
1560 | },
1561 | "nm": "Transform"
1562 | }
1563 | ],
1564 | "nm": "Group 1",
1565 | "np": 2,
1566 | "cix": 2,
1567 | "bm": 0,
1568 | "ix": 1,
1569 | "mn": "ADBE Vector Group",
1570 | "hd": false
1571 | },
1572 | {
1573 | "ty": "gr",
1574 | "it": [
1575 | {
1576 | "ind": 0,
1577 | "ty": "sh",
1578 | "ix": 1,
1579 | "ks": {
1580 | "a": 0,
1581 | "k": {
1582 | "i": [
1583 | [
1584 | 0,
1585 | -5.855
1586 | ],
1587 | [
1588 | 5.855,
1589 | 0
1590 | ],
1591 | [
1592 | 0,
1593 | 5.855
1594 | ],
1595 | [
1596 | -5.856,
1597 | 0
1598 | ]
1599 | ],
1600 | "o": [
1601 | [
1602 | 0,
1603 | 5.855
1604 | ],
1605 | [
1606 | -5.856,
1607 | 0
1608 | ],
1609 | [
1610 | 0,
1611 | -5.855
1612 | ],
1613 | [
1614 | 5.855,
1615 | 0
1616 | ]
1617 | ],
1618 | "v": [
1619 | [
1620 | 10.602,
1621 | 0
1622 | ],
1623 | [
1624 | 0,
1625 | 10.602
1626 | ],
1627 | [
1628 | -10.602,
1629 | 0
1630 | ],
1631 | [
1632 | 0,
1633 | -10.602
1634 | ]
1635 | ],
1636 | "c": true
1637 | },
1638 | "ix": 2
1639 | },
1640 | "nm": "Path 1",
1641 | "mn": "ADBE Vector Shape - Group",
1642 | "hd": false
1643 | },
1644 | {
1645 | "ty": "fl",
1646 | "c": {
1647 | "a": 0,
1648 | "k": [
1649 | 1,
1650 | 0.674509803922,
1651 | 0.384313755409,
1652 | 1
1653 | ],
1654 | "ix": 4
1655 | },
1656 | "o": {
1657 | "a": 0,
1658 | "k": 100,
1659 | "ix": 5
1660 | },
1661 | "r": 1,
1662 | "bm": 0,
1663 | "nm": "Fill 1",
1664 | "mn": "ADBE Vector Graphic - Fill",
1665 | "hd": false
1666 | },
1667 | {
1668 | "ty": "tr",
1669 | "p": {
1670 | "a": 0,
1671 | "k": [
1672 | 233.331,
1673 | 79.874
1674 | ],
1675 | "ix": 2
1676 | },
1677 | "a": {
1678 | "a": 0,
1679 | "k": [
1680 | 0,
1681 | 0
1682 | ],
1683 | "ix": 1
1684 | },
1685 | "s": {
1686 | "a": 0,
1687 | "k": [
1688 | 100,
1689 | 100
1690 | ],
1691 | "ix": 3
1692 | },
1693 | "r": {
1694 | "a": 0,
1695 | "k": 0,
1696 | "ix": 6
1697 | },
1698 | "o": {
1699 | "a": 0,
1700 | "k": 100,
1701 | "ix": 7
1702 | },
1703 | "sk": {
1704 | "a": 0,
1705 | "k": 0,
1706 | "ix": 4
1707 | },
1708 | "sa": {
1709 | "a": 0,
1710 | "k": 0,
1711 | "ix": 5
1712 | },
1713 | "nm": "Transform"
1714 | }
1715 | ],
1716 | "nm": "Group 2",
1717 | "np": 2,
1718 | "cix": 2,
1719 | "bm": 0,
1720 | "ix": 2,
1721 | "mn": "ADBE Vector Group",
1722 | "hd": false
1723 | },
1724 | {
1725 | "ty": "gr",
1726 | "it": [
1727 | {
1728 | "ind": 0,
1729 | "ty": "sh",
1730 | "ix": 1,
1731 | "ks": {
1732 | "a": 0,
1733 | "k": {
1734 | "i": [
1735 | [
1736 | 0,
1737 | -7.129
1738 | ],
1739 | [
1740 | 7.129,
1741 | 0
1742 | ],
1743 | [
1744 | 0,
1745 | 7.129
1746 | ],
1747 | [
1748 | -7.129,
1749 | 0
1750 | ]
1751 | ],
1752 | "o": [
1753 | [
1754 | 0,
1755 | 7.129
1756 | ],
1757 | [
1758 | -7.129,
1759 | 0
1760 | ],
1761 | [
1762 | 0,
1763 | -7.129
1764 | ],
1765 | [
1766 | 7.129,
1767 | 0
1768 | ]
1769 | ],
1770 | "v": [
1771 | [
1772 | 12.908,
1773 | 0
1774 | ],
1775 | [
1776 | 0,
1777 | 12.908
1778 | ],
1779 | [
1780 | -12.908,
1781 | 0
1782 | ],
1783 | [
1784 | 0,
1785 | -12.908
1786 | ]
1787 | ],
1788 | "c": true
1789 | },
1790 | "ix": 2
1791 | },
1792 | "nm": "Path 1",
1793 | "mn": "ADBE Vector Shape - Group",
1794 | "hd": false
1795 | },
1796 | {
1797 | "ty": "fl",
1798 | "c": {
1799 | "a": 0,
1800 | "k": [
1801 | 0.305882352941,
1802 | 0.84313731474,
1803 | 1,
1804 | 1
1805 | ],
1806 | "ix": 4
1807 | },
1808 | "o": {
1809 | "a": 0,
1810 | "k": 100,
1811 | "ix": 5
1812 | },
1813 | "r": 1,
1814 | "bm": 0,
1815 | "nm": "Fill 1",
1816 | "mn": "ADBE Vector Graphic - Fill",
1817 | "hd": false
1818 | },
1819 | {
1820 | "ty": "tr",
1821 | "p": {
1822 | "a": 0,
1823 | "k": [
1824 | 13.158,
1825 | 79.874
1826 | ],
1827 | "ix": 2
1828 | },
1829 | "a": {
1830 | "a": 0,
1831 | "k": [
1832 | 0,
1833 | 0
1834 | ],
1835 | "ix": 1
1836 | },
1837 | "s": {
1838 | "a": 0,
1839 | "k": [
1840 | 100,
1841 | 100
1842 | ],
1843 | "ix": 3
1844 | },
1845 | "r": {
1846 | "a": 0,
1847 | "k": 0,
1848 | "ix": 6
1849 | },
1850 | "o": {
1851 | "a": 0,
1852 | "k": 100,
1853 | "ix": 7
1854 | },
1855 | "sk": {
1856 | "a": 0,
1857 | "k": 0,
1858 | "ix": 4
1859 | },
1860 | "sa": {
1861 | "a": 0,
1862 | "k": 0,
1863 | "ix": 5
1864 | },
1865 | "nm": "Transform"
1866 | }
1867 | ],
1868 | "nm": "Group 3",
1869 | "np": 2,
1870 | "cix": 2,
1871 | "bm": 0,
1872 | "ix": 3,
1873 | "mn": "ADBE Vector Group",
1874 | "hd": false
1875 | },
1876 | {
1877 | "ty": "gr",
1878 | "it": [
1879 | {
1880 | "ind": 0,
1881 | "ty": "sh",
1882 | "ix": 1,
1883 | "ks": {
1884 | "a": 0,
1885 | "k": {
1886 | "i": [
1887 | [
1888 | 0,
1889 | -3.475
1890 | ],
1891 | [
1892 | 3.475,
1893 | 0
1894 | ],
1895 | [
1896 | 0,
1897 | 3.475
1898 | ],
1899 | [
1900 | -3.474,
1901 | 0
1902 | ]
1903 | ],
1904 | "o": [
1905 | [
1906 | 0,
1907 | 3.475
1908 | ],
1909 | [
1910 | -3.474,
1911 | 0
1912 | ],
1913 | [
1914 | 0,
1915 | -3.475
1916 | ],
1917 | [
1918 | 3.475,
1919 | 0
1920 | ]
1921 | ],
1922 | "v": [
1923 | [
1924 | 6.292,
1925 | 0
1926 | ],
1927 | [
1928 | -0.001,
1929 | 6.292
1930 | ],
1931 | [
1932 | -6.292,
1933 | 0
1934 | ],
1935 | [
1936 | -0.001,
1937 | -6.292
1938 | ]
1939 | ],
1940 | "c": true
1941 | },
1942 | "ix": 2
1943 | },
1944 | "nm": "Path 1",
1945 | "mn": "ADBE Vector Shape - Group",
1946 | "hd": false
1947 | },
1948 | {
1949 | "ty": "fl",
1950 | "c": {
1951 | "a": 0,
1952 | "k": [
1953 | 0.305882352941,
1954 | 0.84313731474,
1955 | 1,
1956 | 1
1957 | ],
1958 | "ix": 4
1959 | },
1960 | "o": {
1961 | "a": 0,
1962 | "k": 100,
1963 | "ix": 5
1964 | },
1965 | "r": 1,
1966 | "bm": 0,
1967 | "nm": "Fill 1",
1968 | "mn": "ADBE Vector Graphic - Fill",
1969 | "hd": false
1970 | },
1971 | {
1972 | "ty": "tr",
1973 | "p": {
1974 | "a": 0,
1975 | "k": [
1976 | 239.623,
1977 | 146.646
1978 | ],
1979 | "ix": 2
1980 | },
1981 | "a": {
1982 | "a": 0,
1983 | "k": [
1984 | 0,
1985 | 0
1986 | ],
1987 | "ix": 1
1988 | },
1989 | "s": {
1990 | "a": 0,
1991 | "k": [
1992 | 100,
1993 | 100
1994 | ],
1995 | "ix": 3
1996 | },
1997 | "r": {
1998 | "a": 0,
1999 | "k": 0,
2000 | "ix": 6
2001 | },
2002 | "o": {
2003 | "a": 0,
2004 | "k": 100,
2005 | "ix": 7
2006 | },
2007 | "sk": {
2008 | "a": 0,
2009 | "k": 0,
2010 | "ix": 4
2011 | },
2012 | "sa": {
2013 | "a": 0,
2014 | "k": 0,
2015 | "ix": 5
2016 | },
2017 | "nm": "Transform"
2018 | }
2019 | ],
2020 | "nm": "Group 4",
2021 | "np": 2,
2022 | "cix": 2,
2023 | "bm": 0,
2024 | "ix": 4,
2025 | "mn": "ADBE Vector Group",
2026 | "hd": false
2027 | },
2028 | {
2029 | "ty": "gr",
2030 | "it": [
2031 | {
2032 | "ind": 0,
2033 | "ty": "sh",
2034 | "ix": 1,
2035 | "ks": {
2036 | "a": 0,
2037 | "k": {
2038 | "i": [
2039 | [
2040 | 0,
2041 | -1.782
2042 | ],
2043 | [
2044 | 1.782,
2045 | 0
2046 | ],
2047 | [
2048 | 0,
2049 | 1.782
2050 | ],
2051 | [
2052 | -1.782,
2053 | 0
2054 | ]
2055 | ],
2056 | "o": [
2057 | [
2058 | 0,
2059 | 1.782
2060 | ],
2061 | [
2062 | -1.782,
2063 | 0
2064 | ],
2065 | [
2066 | 0,
2067 | -1.782
2068 | ],
2069 | [
2070 | 1.782,
2071 | 0
2072 | ]
2073 | ],
2074 | "v": [
2075 | [
2076 | 3.227,
2077 | 0
2078 | ],
2079 | [
2080 | 0,
2081 | 3.227
2082 | ],
2083 | [
2084 | -3.227,
2085 | 0
2086 | ],
2087 | [
2088 | 0,
2089 | -3.227
2090 | ]
2091 | ],
2092 | "c": true
2093 | },
2094 | "ix": 2
2095 | },
2096 | "nm": "Path 1",
2097 | "mn": "ADBE Vector Shape - Group",
2098 | "hd": false
2099 | },
2100 | {
2101 | "ty": "fl",
2102 | "c": {
2103 | "a": 0,
2104 | "k": [
2105 | 0.392156892664,
2106 | 1,
2107 | 0.662745098039,
2108 | 1
2109 | ],
2110 | "ix": 4
2111 | },
2112 | "o": {
2113 | "a": 0,
2114 | "k": 100,
2115 | "ix": 5
2116 | },
2117 | "r": 1,
2118 | "bm": 0,
2119 | "nm": "Fill 1",
2120 | "mn": "ADBE Vector Graphic - Fill",
2121 | "hd": false
2122 | },
2123 | {
2124 | "ty": "tr",
2125 | "p": {
2126 | "a": 0,
2127 | "k": [
2128 | 16.716,
2129 | 159.823
2130 | ],
2131 | "ix": 2
2132 | },
2133 | "a": {
2134 | "a": 0,
2135 | "k": [
2136 | 0,
2137 | 0
2138 | ],
2139 | "ix": 1
2140 | },
2141 | "s": {
2142 | "a": 0,
2143 | "k": [
2144 | 100,
2145 | 100
2146 | ],
2147 | "ix": 3
2148 | },
2149 | "r": {
2150 | "a": 0,
2151 | "k": 0,
2152 | "ix": 6
2153 | },
2154 | "o": {
2155 | "a": 0,
2156 | "k": 100,
2157 | "ix": 7
2158 | },
2159 | "sk": {
2160 | "a": 0,
2161 | "k": 0,
2162 | "ix": 4
2163 | },
2164 | "sa": {
2165 | "a": 0,
2166 | "k": 0,
2167 | "ix": 5
2168 | },
2169 | "nm": "Transform"
2170 | }
2171 | ],
2172 | "nm": "Group 5",
2173 | "np": 2,
2174 | "cix": 2,
2175 | "bm": 0,
2176 | "ix": 5,
2177 | "mn": "ADBE Vector Group",
2178 | "hd": false
2179 | },
2180 | {
2181 | "ty": "gr",
2182 | "it": [
2183 | {
2184 | "ind": 0,
2185 | "ty": "sh",
2186 | "ix": 1,
2187 | "ks": {
2188 | "a": 0,
2189 | "k": {
2190 | "i": [
2191 | [
2192 | 0,
2193 | -5.758
2194 | ],
2195 | [
2196 | 5.758,
2197 | 0
2198 | ],
2199 | [
2200 | 0,
2201 | 5.758
2202 | ],
2203 | [
2204 | -5.759,
2205 | 0
2206 | ]
2207 | ],
2208 | "o": [
2209 | [
2210 | 0,
2211 | 5.758
2212 | ],
2213 | [
2214 | -5.759,
2215 | 0
2216 | ],
2217 | [
2218 | 0,
2219 | -5.758
2220 | ],
2221 | [
2222 | 5.758,
2223 | 0
2224 | ]
2225 | ],
2226 | "v": [
2227 | [
2228 | 10.427,
2229 | 0
2230 | ],
2231 | [
2232 | 0,
2233 | 10.426
2234 | ],
2235 | [
2236 | -10.427,
2237 | 0
2238 | ],
2239 | [
2240 | 0,
2241 | -10.426
2242 | ]
2243 | ],
2244 | "c": true
2245 | },
2246 | "ix": 2
2247 | },
2248 | "nm": "Path 1",
2249 | "mn": "ADBE Vector Shape - Group",
2250 | "hd": false
2251 | },
2252 | {
2253 | "ty": "fl",
2254 | "c": {
2255 | "a": 0,
2256 | "k": [
2257 | 0.392156892664,
2258 | 1,
2259 | 0.662745098039,
2260 | 1
2261 | ],
2262 | "ix": 4
2263 | },
2264 | "o": {
2265 | "a": 0,
2266 | "k": 100,
2267 | "ix": 5
2268 | },
2269 | "r": 1,
2270 | "bm": 0,
2271 | "nm": "Fill 1",
2272 | "mn": "ADBE Vector Graphic - Fill",
2273 | "hd": false
2274 | },
2275 | {
2276 | "ty": "tr",
2277 | "p": {
2278 | "a": 0,
2279 | "k": [
2280 | 208.393,
2281 | 211.092
2282 | ],
2283 | "ix": 2
2284 | },
2285 | "a": {
2286 | "a": 0,
2287 | "k": [
2288 | 0,
2289 | 0
2290 | ],
2291 | "ix": 1
2292 | },
2293 | "s": {
2294 | "a": 0,
2295 | "k": [
2296 | 100,
2297 | 100
2298 | ],
2299 | "ix": 3
2300 | },
2301 | "r": {
2302 | "a": 0,
2303 | "k": 0,
2304 | "ix": 6
2305 | },
2306 | "o": {
2307 | "a": 0,
2308 | "k": 100,
2309 | "ix": 7
2310 | },
2311 | "sk": {
2312 | "a": 0,
2313 | "k": 0,
2314 | "ix": 4
2315 | },
2316 | "sa": {
2317 | "a": 0,
2318 | "k": 0,
2319 | "ix": 5
2320 | },
2321 | "nm": "Transform"
2322 | }
2323 | ],
2324 | "nm": "Group 6",
2325 | "np": 2,
2326 | "cix": 2,
2327 | "bm": 0,
2328 | "ix": 6,
2329 | "mn": "ADBE Vector Group",
2330 | "hd": false
2331 | },
2332 | {
2333 | "ty": "gr",
2334 | "it": [
2335 | {
2336 | "ind": 0,
2337 | "ty": "sh",
2338 | "ix": 1,
2339 | "ks": {
2340 | "a": 0,
2341 | "k": {
2342 | "i": [
2343 | [
2344 | 0,
2345 | -2.054
2346 | ],
2347 | [
2348 | 2.054,
2349 | 0
2350 | ],
2351 | [
2352 | 0,
2353 | 2.054
2354 | ],
2355 | [
2356 | -2.053,
2357 | 0
2358 | ]
2359 | ],
2360 | "o": [
2361 | [
2362 | 0,
2363 | 2.054
2364 | ],
2365 | [
2366 | -2.053,
2367 | 0
2368 | ],
2369 | [
2370 | 0,
2371 | -2.054
2372 | ],
2373 | [
2374 | 2.054,
2375 | 0
2376 | ]
2377 | ],
2378 | "v": [
2379 | [
2380 | 3.719,
2381 | 0
2382 | ],
2383 | [
2384 | -0.001,
2385 | 3.719
2386 | ],
2387 | [
2388 | -3.719,
2389 | 0
2390 | ],
2391 | [
2392 | -0.001,
2393 | -3.719
2394 | ]
2395 | ],
2396 | "c": true
2397 | },
2398 | "ix": 2
2399 | },
2400 | "nm": "Path 1",
2401 | "mn": "ADBE Vector Shape - Group",
2402 | "hd": false
2403 | },
2404 | {
2405 | "ty": "fl",
2406 | "c": {
2407 | "a": 0,
2408 | "k": [
2409 | 0.929411824544,
2410 | 0.243137269862,
2411 | 0.243137269862,
2412 | 1
2413 | ],
2414 | "ix": 4
2415 | },
2416 | "o": {
2417 | "a": 0,
2418 | "k": 100,
2419 | "ix": 5
2420 | },
2421 | "r": 1,
2422 | "bm": 0,
2423 | "nm": "Fill 1",
2424 | "mn": "ADBE Vector Graphic - Fill",
2425 | "hd": false
2426 | },
2427 | {
2428 | "ty": "tr",
2429 | "p": {
2430 | "a": 0,
2431 | "k": [
2432 | 187.583,
2433 | 32.998
2434 | ],
2435 | "ix": 2
2436 | },
2437 | "a": {
2438 | "a": 0,
2439 | "k": [
2440 | 0,
2441 | 0
2442 | ],
2443 | "ix": 1
2444 | },
2445 | "s": {
2446 | "a": 0,
2447 | "k": [
2448 | 100,
2449 | 100
2450 | ],
2451 | "ix": 3
2452 | },
2453 | "r": {
2454 | "a": 0,
2455 | "k": 0,
2456 | "ix": 6
2457 | },
2458 | "o": {
2459 | "a": 0,
2460 | "k": 100,
2461 | "ix": 7
2462 | },
2463 | "sk": {
2464 | "a": 0,
2465 | "k": 0,
2466 | "ix": 4
2467 | },
2468 | "sa": {
2469 | "a": 0,
2470 | "k": 0,
2471 | "ix": 5
2472 | },
2473 | "nm": "Transform"
2474 | }
2475 | ],
2476 | "nm": "Group 7",
2477 | "np": 2,
2478 | "cix": 2,
2479 | "bm": 0,
2480 | "ix": 7,
2481 | "mn": "ADBE Vector Group",
2482 | "hd": false
2483 | },
2484 | {
2485 | "ty": "gr",
2486 | "it": [
2487 | {
2488 | "ind": 0,
2489 | "ty": "sh",
2490 | "ix": 1,
2491 | "ks": {
2492 | "a": 0,
2493 | "k": {
2494 | "i": [
2495 | [
2496 | 0,
2497 | -6.803
2498 | ],
2499 | [
2500 | 6.802,
2501 | 0
2502 | ],
2503 | [
2504 | 0,
2505 | 6.803
2506 | ],
2507 | [
2508 | -6.803,
2509 | 0
2510 | ]
2511 | ],
2512 | "o": [
2513 | [
2514 | 0,
2515 | 6.803
2516 | ],
2517 | [
2518 | -6.803,
2519 | 0
2520 | ],
2521 | [
2522 | 0,
2523 | -6.803
2524 | ],
2525 | [
2526 | 6.802,
2527 | 0
2528 | ]
2529 | ],
2530 | "v": [
2531 | [
2532 | 12.318,
2533 | 0
2534 | ],
2535 | [
2536 | 0.001,
2537 | 12.317
2538 | ],
2539 | [
2540 | -12.318,
2541 | 0
2542 | ],
2543 | [
2544 | 0.001,
2545 | -12.317
2546 | ]
2547 | ],
2548 | "c": true
2549 | },
2550 | "ix": 2
2551 | },
2552 | "nm": "Path 1",
2553 | "mn": "ADBE Vector Shape - Group",
2554 | "hd": false
2555 | },
2556 | {
2557 | "ty": "fl",
2558 | "c": {
2559 | "a": 0,
2560 | "k": [
2561 | 0.929411824544,
2562 | 0.243137269862,
2563 | 0.243137269862,
2564 | 1
2565 | ],
2566 | "ix": 4
2567 | },
2568 | "o": {
2569 | "a": 0,
2570 | "k": 100,
2571 | "ix": 5
2572 | },
2573 | "r": 1,
2574 | "bm": 0,
2575 | "nm": "Fill 1",
2576 | "mn": "ADBE Vector Graphic - Fill",
2577 | "hd": false
2578 | },
2579 | {
2580 | "ty": "tr",
2581 | "p": {
2582 | "a": 0,
2583 | "k": [
2584 | 55.34,
2585 | 221.518
2586 | ],
2587 | "ix": 2
2588 | },
2589 | "a": {
2590 | "a": 0,
2591 | "k": [
2592 | 0,
2593 | 0
2594 | ],
2595 | "ix": 1
2596 | },
2597 | "s": {
2598 | "a": 0,
2599 | "k": [
2600 | 100,
2601 | 100
2602 | ],
2603 | "ix": 3
2604 | },
2605 | "r": {
2606 | "a": 0,
2607 | "k": 0,
2608 | "ix": 6
2609 | },
2610 | "o": {
2611 | "a": 0,
2612 | "k": 100,
2613 | "ix": 7
2614 | },
2615 | "sk": {
2616 | "a": 0,
2617 | "k": 0,
2618 | "ix": 4
2619 | },
2620 | "sa": {
2621 | "a": 0,
2622 | "k": 0,
2623 | "ix": 5
2624 | },
2625 | "nm": "Transform"
2626 | }
2627 | ],
2628 | "nm": "Group 8",
2629 | "np": 2,
2630 | "cix": 2,
2631 | "bm": 0,
2632 | "ix": 8,
2633 | "mn": "ADBE Vector Group",
2634 | "hd": false
2635 | },
2636 | {
2637 | "ty": "gr",
2638 | "it": [
2639 | {
2640 | "ind": 0,
2641 | "ty": "sh",
2642 | "ix": 1,
2643 | "ks": {
2644 | "a": 0,
2645 | "k": {
2646 | "i": [
2647 | [
2648 | 0,
2649 | -5.582
2650 | ],
2651 | [
2652 | 5.582,
2653 | 0
2654 | ],
2655 | [
2656 | 0,
2657 | 5.582
2658 | ],
2659 | [
2660 | -5.583,
2661 | 0
2662 | ]
2663 | ],
2664 | "o": [
2665 | [
2666 | 0,
2667 | 5.582
2668 | ],
2669 | [
2670 | -5.583,
2671 | 0
2672 | ],
2673 | [
2674 | 0,
2675 | -5.582
2676 | ],
2677 | [
2678 | 5.582,
2679 | 0
2680 | ]
2681 | ],
2682 | "v": [
2683 | [
2684 | 10.107,
2685 | 0
2686 | ],
2687 | [
2688 | 0,
2689 | 10.107
2690 | ],
2691 | [
2692 | -10.108,
2693 | 0
2694 | ],
2695 | [
2696 | 0,
2697 | -10.107
2698 | ]
2699 | ],
2700 | "c": true
2701 | },
2702 | "ix": 2
2703 | },
2704 | "nm": "Path 1",
2705 | "mn": "ADBE Vector Shape - Group",
2706 | "hd": false
2707 | },
2708 | {
2709 | "ty": "fl",
2710 | "c": {
2711 | "a": 0,
2712 | "k": [
2713 | 0.490196108351,
2714 | 0.294117647059,
2715 | 0.752941236309,
2716 | 1
2717 | ],
2718 | "ix": 4
2719 | },
2720 | "o": {
2721 | "a": 0,
2722 | "k": 100,
2723 | "ix": 5
2724 | },
2725 | "r": 1,
2726 | "bm": 0,
2727 | "nm": "Fill 1",
2728 | "mn": "ADBE Vector Graphic - Fill",
2729 | "hd": false
2730 | },
2731 | {
2732 | "ty": "tr",
2733 | "p": {
2734 | "a": 0,
2735 | "k": [
2736 | 128.624,
2737 | 10.357
2738 | ],
2739 | "ix": 2
2740 | },
2741 | "a": {
2742 | "a": 0,
2743 | "k": [
2744 | 0,
2745 | 0
2746 | ],
2747 | "ix": 1
2748 | },
2749 | "s": {
2750 | "a": 0,
2751 | "k": [
2752 | 100,
2753 | 100
2754 | ],
2755 | "ix": 3
2756 | },
2757 | "r": {
2758 | "a": 0,
2759 | "k": 0,
2760 | "ix": 6
2761 | },
2762 | "o": {
2763 | "a": 0,
2764 | "k": 100,
2765 | "ix": 7
2766 | },
2767 | "sk": {
2768 | "a": 0,
2769 | "k": 0,
2770 | "ix": 4
2771 | },
2772 | "sa": {
2773 | "a": 0,
2774 | "k": 0,
2775 | "ix": 5
2776 | },
2777 | "nm": "Transform"
2778 | }
2779 | ],
2780 | "nm": "Group 9",
2781 | "np": 2,
2782 | "cix": 2,
2783 | "bm": 0,
2784 | "ix": 9,
2785 | "mn": "ADBE Vector Group",
2786 | "hd": false
2787 | },
2788 | {
2789 | "ty": "gr",
2790 | "it": [
2791 | {
2792 | "ind": 0,
2793 | "ty": "sh",
2794 | "ix": 1,
2795 | "ks": {
2796 | "a": 0,
2797 | "k": {
2798 | "i": [
2799 | [
2800 | 0,
2801 | -2.903
2802 | ],
2803 | [
2804 | 2.903,
2805 | 0
2806 | ],
2807 | [
2808 | 0,
2809 | 2.903
2810 | ],
2811 | [
2812 | -2.903,
2813 | 0
2814 | ]
2815 | ],
2816 | "o": [
2817 | [
2818 | 0,
2819 | 2.903
2820 | ],
2821 | [
2822 | -2.903,
2823 | 0
2824 | ],
2825 | [
2826 | 0,
2827 | -2.903
2828 | ],
2829 | [
2830 | 2.903,
2831 | 0
2832 | ]
2833 | ],
2834 | "v": [
2835 | [
2836 | 5.257,
2837 | 0
2838 | ],
2839 | [
2840 | -0.001,
2841 | 5.257
2842 | ],
2843 | [
2844 | -5.257,
2845 | 0
2846 | ],
2847 | [
2848 | -0.001,
2849 | -5.257
2850 | ]
2851 | ],
2852 | "c": true
2853 | },
2854 | "ix": 2
2855 | },
2856 | "nm": "Path 1",
2857 | "mn": "ADBE Vector Shape - Group",
2858 | "hd": false
2859 | },
2860 | {
2861 | "ty": "fl",
2862 | "c": {
2863 | "a": 0,
2864 | "k": [
2865 | 0.490196108351,
2866 | 0.294117647059,
2867 | 0.752941236309,
2868 | 1
2869 | ],
2870 | "ix": 4
2871 | },
2872 | "o": {
2873 | "a": 0,
2874 | "k": 100,
2875 | "ix": 5
2876 | },
2877 | "r": 1,
2878 | "bm": 0,
2879 | "nm": "Fill 1",
2880 | "mn": "ADBE Vector Graphic - Fill",
2881 | "hd": false
2882 | },
2883 | {
2884 | "ty": "tr",
2885 | "p": {
2886 | "a": 0,
2887 | "k": [
2888 | 135.955,
2889 | 236.884
2890 | ],
2891 | "ix": 2
2892 | },
2893 | "a": {
2894 | "a": 0,
2895 | "k": [
2896 | 0,
2897 | 0
2898 | ],
2899 | "ix": 1
2900 | },
2901 | "s": {
2902 | "a": 0,
2903 | "k": [
2904 | 100,
2905 | 100
2906 | ],
2907 | "ix": 3
2908 | },
2909 | "r": {
2910 | "a": 0,
2911 | "k": 0,
2912 | "ix": 6
2913 | },
2914 | "o": {
2915 | "a": 0,
2916 | "k": 100,
2917 | "ix": 7
2918 | },
2919 | "sk": {
2920 | "a": 0,
2921 | "k": 0,
2922 | "ix": 4
2923 | },
2924 | "sa": {
2925 | "a": 0,
2926 | "k": 0,
2927 | "ix": 5
2928 | },
2929 | "nm": "Transform"
2930 | }
2931 | ],
2932 | "nm": "Group 10",
2933 | "np": 2,
2934 | "cix": 2,
2935 | "bm": 0,
2936 | "ix": 10,
2937 | "mn": "ADBE Vector Group",
2938 | "hd": false
2939 | }
2940 | ],
2941 | "ip": 0,
2942 | "op": 58.0580580580581,
2943 | "st": 0,
2944 | "bm": 0
2945 | }
2946 | ],
2947 | "markers": []
2948 | }
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | ComposeOtpVerify
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
12 |
13 |
19 |
--------------------------------------------------------------------------------
/app/src/test/java/com/appmason/jetplayground/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.appmason.jetplayground
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/art/screens/otp_autofill.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pushpalroy/ComposeOtpVerify/e57c00881dac850888b245f01ce31dde1684e09d/art/screens/otp_autofill.png
--------------------------------------------------------------------------------
/art/screens/otp_ui.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pushpalroy/ComposeOtpVerify/e57c00881dac850888b245f01ce31dde1684e09d/art/screens/otp_ui.png
--------------------------------------------------------------------------------
/art/screens/otp_verification.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pushpalroy/ComposeOtpVerify/e57c00881dac850888b245f01ce31dde1684e09d/art/screens/otp_verification.gif
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | plugins {
3 | id("com.android.application") version "8.2.1" apply false
4 | id("org.jetbrains.kotlin.android") version "1.9.0" apply false
5 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pushpalroy/ComposeOtpVerify/e57c00881dac850888b245f01ce31dde1684e09d/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Jan 17 11:05:46 CST 2024
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | gradlePluginPortal()
6 | }
7 | }
8 | dependencyResolutionManagement {
9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
10 | repositories {
11 | google()
12 | mavenCentral()
13 | }
14 | }
15 |
16 | rootProject.name = "ComposeOtpVerify"
17 | include(":app")
--------------------------------------------------------------------------------