diff --git a/.gitignore b/.gitignore index 13b2127afa..4cc00a7e1f 100644 --- a/.gitignore +++ b/.gitignore @@ -13,10 +13,7 @@ local.properties .gradle # User-specific configurations -.idea/caches/ -.idea/libraries/ -.idea/*.xml -.idea/codeStyles +.idea # LayoutInspector files /captures diff --git a/app/build.gradle b/app/build.gradle index e6cb6cd396..0b53a8df5e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -68,16 +68,17 @@ dependencies { implementation 'androidx.core:core-ktx:1.6.0' implementation 'androidx.appcompat:appcompat:1.3.1' - implementation 'androidx.constraintlayout:constraintlayout:2.1.0' + implementation 'androidx.fragment:fragment-ktx:1.3.6' + implementation 'androidx.constraintlayout:constraintlayout:2.1.1' implementation 'com.google.android.material:material:1.4.0' implementation "androidx.core:core-ktx:1.6.0" - implementation 'com.google.android.play:core:1.10.0' + implementation 'com.google.android.play:core:1.10.2' implementation 'com.google.android.play:core-ktx:1.8.1' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' - implementation 'com.tangem:blockchain:develop-38' - implementation 'com.tangem.tangem-sdk-kotlin:core:develop-70' - implementation 'com.tangem.tangem-sdk-kotlin:android:develop-70' + implementation 'com.tangem:blockchain:develop-42' + implementation 'com.tangem.tangem-sdk-kotlin:core:develop-91' + implementation 'com.tangem.tangem-sdk-kotlin:android:develop-91' // WebView implementation "androidx.browser:browser:1.3.0" @@ -114,17 +115,19 @@ dependencies { // Network and Json implementation 'com.squareup.retrofit2:retrofit:2.8.1' implementation 'com.squareup.retrofit2:converter-moshi:2.6.0' - implementation 'com.squareup.moshi:moshi:1.9.2' - implementation "com.squareup.moshi:moshi-kotlin:1.9.2" + implementation 'com.squareup.moshi:moshi:1.12.0' + implementation "com.squareup.moshi:moshi-kotlin:1.12.0" implementation 'com.squareup.okhttp3:logging-interceptor:4.2.2' // // //Crypto implementation "com.madgag.spongycastle:core:1.58.0.0" + // animation + implementation "com.airbnb.android:lottie:3.4.0" testImplementation 'junit:junit:4.13.2' - testImplementation "com.google.truth:truth:1.0.1" - androidTestImplementation 'androidx.test.ext:junit:1.1.2' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' + testImplementation "com.google.truth:truth:1.1.3" + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' } \ No newline at end of file diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index e80938f7d9..0cf3af21e6 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit e80938f7d9ba378a91ac544a9170597e64afa696 +Subproject commit 0cf3af21e6f441e6323a1ba127ee7518168797df diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index 89ede4d80d..6f99403572 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -17,7 +17,6 @@ import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.TangemSdkManager import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction -import com.tangem.tap.features.home.redux.HomeAction import com.tangem.wallet.R import kotlinx.android.synthetic.main.activity_main.* import kotlinx.coroutines.CoroutineExceptionHandler @@ -60,8 +59,8 @@ class MainActivity : AppCompatActivity(), SnackbarHandler { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) + systemActions() store.state.globalState.feedbackManager?.updateAcivity(this) - requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; store.dispatch(NavigationAction.ActivityCreated(WeakReference(this))) tangemSdk = TangemSdk.init(this, TangemSdkManager.config) @@ -70,13 +69,20 @@ class MainActivity : AppCompatActivity(), SnackbarHandler { store.dispatch(WalletConnectAction.RestoreSessions) } + private fun systemActions() { + // makes the status bar text dark + window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR + requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + } + override fun onResume() { super.onResume() notificationsHandler = NotificationsHandler(fragment_container) - if (supportFragmentManager.backStackEntryCount == 0 || - store.state.globalState.scanNoteResponse == null - ) { - store.dispatch(HomeAction.CheckIfFirstLaunch) + + val backStackIsEmpty = supportFragmentManager.backStackEntryCount == 0 + val isScannedBefore = store.state.globalState.scanResponse != null + val isOnboardingServiceActive = store.state.globalState.onboardingManager != null + if (backStackIsEmpty || (!isOnboardingServiceActive && !isScannedBefore)) { store.dispatch(NavigationAction.NavigateTo(AppScreen.Home)) } intentHandler.handleIntent(intent) diff --git a/app/src/main/java/com/tangem/tap/common/DialogManager.kt b/app/src/main/java/com/tangem/tap/common/DialogManager.kt index f58e132937..114c17c7cd 100644 --- a/app/src/main/java/com/tangem/tap/common/DialogManager.kt +++ b/app/src/main/java/com/tangem/tap/common/DialogManager.kt @@ -2,9 +2,12 @@ package com.tangem.tap.common import android.app.Dialog import android.content.Context +import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.common.redux.global.GlobalState import com.tangem.tap.features.details.redux.walletconnect.WalletConnectDialog import com.tangem.tap.features.details.ui.walletconnect.dialogs.* +import com.tangem.tap.features.onboarding.AddressInfoBottomSheetDialog +import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog import com.tangem.tap.store import com.tangem.wallet.R import org.rekotlin.StoreSubscriber @@ -37,35 +40,38 @@ class DialogManager : StoreSubscriber { val context = context ?: return if (dialog != null) return - when (state.dialog) { + dialog = when (state.dialog) { + is AppDialog.ScanFailsDialog -> ScanFailsDialog.create(context) + is AppDialog.AddressInfoDialog -> AddressInfoBottomSheetDialog(state.dialog, context) is WalletConnectDialog.UnsupportedCard -> - dialog = SimpleAlertDialog.create( - titleRes = R.string.wallet_connect, - messageRes = R.string.wallet_connect_scanner_error_no_ethereum_wallet, - context = context + SimpleAlertDialog.create( + titleRes = R.string.wallet_connect, + messageRes = R.string.wallet_connect_scanner_error_no_ethereum_wallet, + context = context ) is WalletConnectDialog.OpeningSessionRejected -> { - dialog = SimpleAlertDialog.create( - titleRes = R.string.wallet_connect, - messageRes = R.string.wallet_connect_same_wcuri, - context = context + SimpleAlertDialog.create( + titleRes = R.string.wallet_connect, + messageRes = R.string.wallet_connect_same_wcuri, + context = context ) } is WalletConnectDialog.SessionTimeout -> { - dialog = SimpleAlertDialog.create( - titleRes = R.string.wallet_connect, - messageRes = R.string.wallet_connect_error_timeout, - context = context + SimpleAlertDialog.create( + titleRes = R.string.wallet_connect, + messageRes = R.string.wallet_connect_error_timeout, + context = context ) } is WalletConnectDialog.ApproveWcSession -> - dialog = ApproveWcSessionDialog.create(state.dialog.session, context) + ApproveWcSessionDialog.create(state.dialog.session, context) is WalletConnectDialog.ClipboardOrScanQr -> - dialog = ClipboardOrScanQrDialog.create(state.dialog.clipboardUri, context) + ClipboardOrScanQrDialog.create(state.dialog.clipboardUri, context) is WalletConnectDialog.RequestTransaction -> - dialog = TransactionDialog.create(state.dialog.dialogData, context) + TransactionDialog.create(state.dialog.dialogData, context) is WalletConnectDialog.PersonalSign -> - dialog = PersonalSignDialog.create(state.dialog.data, context) + PersonalSignDialog.create(state.dialog.data, context) + else -> null } dialog?.show() } diff --git a/app/src/main/java/com/tangem/tap/common/Handler.kt b/app/src/main/java/com/tangem/tap/common/Handler.kt new file mode 100644 index 0000000000..f36d4d1541 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/Handler.kt @@ -0,0 +1,26 @@ +package com.tangem.tap.common + +import android.os.Handler +import android.os.HandlerThread +import android.os.Looper + +private val uiHandler = Handler(Looper.getMainLooper()) +private val backgroundHandler = Handler(HandlerThread("AppMainHandlerThread").apply { start() }.looper) + +fun postUi(ms: Long = 0, func: Runnable) { + if (ms == 0L) uiHandler.post { func.run() } else uiHandler.postDelayed(func, ms) +} + +fun postBackground(ms: Long = 0, func: Runnable) { + if (ms == 0L) backgroundHandler.post { func.run() } else backgroundHandler.postDelayed(func, ms) +} + +fun post(ms: Long = 0, func: Runnable) { + if (ms == 0L) { + func.run() + } else { + val currentLooper = Looper.myLooper() ?: return + + Handler(currentLooper).postDelayed(func, ms) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/entities/Button.kt b/app/src/main/java/com/tangem/tap/common/entities/Button.kt index 986a170564..a2d4127c8f 100644 --- a/app/src/main/java/com/tangem/tap/common/entities/Button.kt +++ b/app/src/main/java/com/tangem/tap/common/entities/Button.kt @@ -1,3 +1,17 @@ package com.tangem.tap.common.entities -open class Button(val enabled: Boolean) \ No newline at end of file +import com.tangem.tap.features.send.redux.states.ButtonState +import com.tangem.tap.features.wallet.redux.ProgressState + +open class Button(val enabled: Boolean) + +open class IndeterminateProgressButton( + val state: ButtonState +) : Button(state != ButtonState.DISABLED) { + + val progressState: ProgressState + get() = when (state) { + ButtonState.PROGRESS -> ProgressState.Loading + else -> ProgressState.Done + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Collections.kt b/app/src/main/java/com/tangem/tap/common/extensions/Collections.kt index d1c417e842..8492ae44f1 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Collections.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Collections.kt @@ -14,4 +14,18 @@ fun MutableList.removeBy(predicate: (T) -> Boolean): Boolean { val toRemove = this.filter(predicate) this.removeAll(toRemove) return toRemove.isNotEmpty() +} + +fun MutableList.replaceBy(item: T, predicate: (T) -> Boolean): Boolean { + val toRemove = this.filter(predicate) + if (toRemove.isEmpty()) return false + + val indexes = toRemove.map { indexOf(it) } + this.removeAll(toRemove) + indexes.forEach { this.add(it, item) } + return true +} + +fun MutableList.replaceByOrAdd(item: T, predicate: (T) -> Boolean) { + if (!replaceBy(item, predicate)) add(item) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Coroutine.kt b/app/src/main/java/com/tangem/tap/common/extensions/Coroutine.kt new file mode 100644 index 0000000000..63c62dfdc2 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/extensions/Coroutine.kt @@ -0,0 +1,16 @@ +package com.tangem.tap.common.extensions + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +/** +[REDACTED_AUTHOR] + */ +suspend fun withMainContext(block: suspend CoroutineScope.() -> T) { + withContext(Dispatchers.Main, block) +} + +suspend fun withIOContext(block: suspend CoroutineScope.() -> T) { + withContext(Dispatchers.IO, block) +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt index d56ae0af85..02c794329c 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt @@ -1,9 +1,12 @@ package com.tangem.tap.common.extensions +import androidx.activity.OnBackPressedCallback import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentManager +import com.tangem.common.extensions.VoidCallback import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.common.redux.navigation.FragmentShareTransition import com.tangem.tap.features.details.ui.DetailsConfirmFragment import com.tangem.tap.features.details.ui.DetailsFragment import com.tangem.tap.features.details.ui.DetailsSecurityFragment @@ -13,17 +16,35 @@ import com.tangem.tap.features.details.ui.walletconnect.QrScanFragment import com.tangem.tap.features.details.ui.walletconnect.WalletConnectSessionsFragment import com.tangem.tap.features.disclaimer.ui.DisclaimerFragment import com.tangem.tap.features.home.HomeFragment +import com.tangem.tap.features.onboarding.products.note.OnboardingNoteFragment +import com.tangem.tap.features.onboarding.products.otherCards.OnboardingOtherCardsFragment +import com.tangem.tap.features.onboarding.products.wallet.OnboardingWalletFragment import com.tangem.tap.features.send.ui.SendFragment import com.tangem.tap.features.tokens.ui.AddTokensFragment +import com.tangem.tap.features.wallet.ui.OnboardingTwinsFragment import com.tangem.tap.features.wallet.ui.WalletDetailsFragment import com.tangem.tap.features.wallet.ui.WalletFragment -import com.tangem.tap.features.wallet.ui.dialogs.TwinsOnboardingFragment import com.tangem.wallet.R -fun FragmentActivity.openFragment(screen: AppScreen, addToBackStack: Boolean = true) { +fun FragmentActivity.openFragment( + screen: AppScreen, + addToBackstack: Boolean, + fgShareTransition: FragmentShareTransition? = null +) { val transaction = this.supportFragmentManager.beginTransaction() - transaction.replace(R.id.fragment_container, fragmentFactory(screen), screen.name) - if (addToBackStack && screen != AppScreen.Home) transaction.addToBackStack(null) + val fragment = fragmentFactory(screen) + fgShareTransition?.apply { + fragment.sharedElementEnterTransition = enterTransitionSet + fragment.sharedElementReturnTransition = exitTransitionSet + transaction.setReorderingAllowed(true) + shareElements.forEach { shareElement -> + shareElement.wView.get()?.let { view -> + transaction.addSharedElement(view, shareElement.name) + } + } + } + transaction.replace(R.id.fragment_container, fragment, screen.name) + if (addToBackstack && screen != AppScreen.Home) transaction.addToBackStack(null) transaction.commitAllowingStateLoss() } @@ -38,9 +59,22 @@ fun FragmentActivity.getPreviousScreen(): AppScreen? { return tag?.let { AppScreen.valueOf(tag) } } +fun FragmentActivity.addOnBackPressedDispatcher( + isEnabled: Boolean = true, + onBackPressed: VoidCallback +): OnBackPressedCallback = (object : OnBackPressedCallback(isEnabled) { + override fun handleOnBackPressed() { + onBackPressed() + } +}).also { this.onBackPressedDispatcher.addCallback(it) } + private fun fragmentFactory(screen: AppScreen): Fragment { return when (screen) { AppScreen.Home -> HomeFragment() + AppScreen.OnboardingNote -> OnboardingNoteFragment() + AppScreen.OnboardingWallet -> OnboardingWalletFragment() + AppScreen.OnboardingTwins -> OnboardingTwinsFragment() + AppScreen.OnboardingOther -> OnboardingOtherCardsFragment() AppScreen.Wallet -> WalletFragment() AppScreen.Send -> SendFragment() AppScreen.Details -> DetailsFragment() @@ -49,7 +83,6 @@ private fun fragmentFactory(screen: AppScreen): Fragment { AppScreen.Disclaimer -> DisclaimerFragment() AppScreen.CreateTwinWalletWarning -> TwinWalletWarningFragment() AppScreen.CreateTwinWallet -> CreateTwinWalletFragment() - AppScreen.TwinsOnboarding -> TwinsOnboardingFragment() AppScreen.AddTokens -> AddTokensFragment() AppScreen.WalletDetails -> WalletDetailsFragment() AppScreen.WalletConnectSessions -> WalletConnectSessionsFragment() diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Store.kt b/app/src/main/java/com/tangem/tap/common/extensions/Store.kt index 99e6d8fe5d..f5fd832f2b 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Store.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Store.kt @@ -1,6 +1,9 @@ package com.tangem.tap.common.extensions -import com.tangem.tap.common.redux.AppState +import com.tangem.tap.common.redux.StateDialog +import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.common.redux.navigation.NavigationAction +import com.tangem.tap.domain.TapError import com.tangem.tap.scope import com.tangem.tap.store import kotlinx.coroutines.Dispatchers @@ -8,8 +11,57 @@ import kotlinx.coroutines.launch import org.rekotlin.Action import org.rekotlin.Store -fun Store.dispatchOnMain(action: Action) { +fun Store<*>.dispatchOnMain(action: Action) { scope.launch(Dispatchers.Main) { store.dispatch(action) } -} \ No newline at end of file +} + +fun Store<*>.dispatchOpenUrl(url: String) { + store.dispatch(NavigationAction.OpenUrl(url)) +} + +fun Store<*>.dispatchNotification(resId: Int) { + scope.launch(Dispatchers.Main) { + store.dispatch(GlobalAction.ShowNotification(resId)) + } +} + +fun Store<*>.dispatchToastNotification(resId: Int) { + scope.launch(Dispatchers.Main) { + store.dispatch(GlobalAction.ShowToastNotification(resId)) + } +} + + +fun Store<*>.dispatchErrorNotification(error: TapError) { + scope.launch(Dispatchers.Main) { + store.dispatch(GlobalAction.ShowErrorNotification(error)) + } +} + +/** + * @param fatal used to indicate errors that should not normally have occurred + */ +fun Store<*>.dispatchDebugErrorNotification(message: String, fatal: Boolean = false) { + val prefix = if (fatal) "FATAL ERROR: " else "DEBUG ERROR: " + dispatchDebugErrorNotification(TapError.CustomError("$prefix $message")) +} + +fun Store<*>.dispatchDebugErrorNotification(error: TapError) { + scope.launch(Dispatchers.Main) { + store.dispatch(GlobalAction.DebugShowErrorNotification(error)) + } +} + +fun Store<*>.dispatchDialogShow(dialog: StateDialog) { + scope.launch(Dispatchers.Main) { + store.dispatch(GlobalAction.ShowDialog(dialog)) + } +} + +fun Store<*>.dispatchDialogHide() { + scope.launch(Dispatchers.Main) { + store.dispatch(GlobalAction.HideDialog) + } +} diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Toast.kt b/app/src/main/java/com/tangem/tap/common/extensions/Toast.kt new file mode 100644 index 0000000000..23d969af40 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/extensions/Toast.kt @@ -0,0 +1,31 @@ +package com.tangem.tap.common.extensions + +import android.content.Context +import android.view.View +import android.widget.Toast +import androidx.annotation.StringRes +import androidx.fragment.app.Fragment + +fun Context.toast(message: String, length: Int = Toast.LENGTH_LONG) { + Toast.makeText(this, message, length).show() +} + +fun Context.toast(@StringRes messageRes: Int, length: Int = Toast.LENGTH_LONG) { + Toast.makeText(this, this.getString(messageRes), length).show() +} + +fun View.toast(message: String, length: Int = Toast.LENGTH_LONG) { + context.toast(message, length) +} + +fun View.toast(@StringRes messageRes: Int, length: Int = Toast.LENGTH_LONG) { + context.toast(context.getString(messageRes), length) +} + +fun Fragment.toast(message: String, length: Int = Toast.LENGTH_LONG) { + context?.toast(message, length) +} + +fun Fragment.toast(@StringRes messageRes: Int, length: Int = Toast.LENGTH_LONG) { + context?.let { it.toast(it.getString(messageRes), length) } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Transition.kt b/app/src/main/java/com/tangem/tap/common/extensions/Transition.kt new file mode 100644 index 0000000000..69fbba97b6 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/extensions/Transition.kt @@ -0,0 +1,24 @@ +package com.tangem.tap.common.extensions + +import androidx.transition.Transition + +/** +[REDACTED_AUTHOR] + */ +inline fun Transition.addListener( + crossinline onStart: (animator: Transition) -> Unit = {}, + crossinline onEnd: (animator: Transition) -> Unit = {}, + crossinline onCancel: (animator: Transition) -> Unit = {}, + crossinline onPause: (animator: Transition) -> Unit = {}, + crossinline onRepeat: (animator: Transition) -> Unit = {} +): Transition.TransitionListener { + val listener = object : Transition.TransitionListener { + override fun onTransitionStart(transition: Transition) = onStart(transition) + override fun onTransitionEnd(transition: Transition) = onEnd(transition) + override fun onTransitionCancel(transition: Transition) = onCancel(transition) + override fun onTransitionPause(transition: Transition) = onPause(transition) + override fun onTransitionResume(transition: Transition) = onRepeat(transition) + } + addListener(listener) + return listener +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/UI.kt b/app/src/main/java/com/tangem/tap/common/extensions/UI.kt index d29b032ef3..e468ec2203 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/UI.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/UI.kt @@ -5,6 +5,7 @@ import android.content.* import android.content.res.Resources import android.graphics.drawable.Drawable import android.os.Build +import android.os.Bundle import android.util.TypedValue import android.view.View import android.view.ViewGroup @@ -16,6 +17,7 @@ import androidx.annotation.StringRes import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import com.google.android.material.card.MaterialCardView +import com.tangem.common.extensions.VoidCallback fun Fragment.getDrawable(@DrawableRes drawableResId: Int): Drawable? { return ContextCompat.getDrawable(requireContext(), drawableResId) @@ -76,8 +78,8 @@ fun Context.dpToPixels(dp: Int): Int = TypedValue.COMPLEX_UNIT_DIP, dp.toFloat(), this.resources.displayMetrics ).toInt() -tailrec fun Context?.getActivity(): Activity? = this as? Activity ?: - (this as? ContextWrapper)?.baseContext?.getActivity() +tailrec fun Context?.getActivity(): Activity? = this as? Activity + ?: (this as? ContextWrapper)?.baseContext?.getActivity() fun MaterialCardView.setMargins( marginLeftDp: Int = 16, @@ -141,4 +143,19 @@ fun Context.shareText(text: String) { fun Fragment.shareText(text: String) { requireContext().shareText(text) +} + +fun Context.safeStartActivity( + intent: Intent, + options: Bundle? = null, + fallback: ((ActivityNotFoundException) -> Unit)? = null, + finally: VoidCallback? = null +) { + try { + this.startActivity(intent, options) + } catch (ex: ActivityNotFoundException) { + fallback?.invoke(ex) + } finally { + finally?.invoke() + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/ViewGroup.kt b/app/src/main/java/com/tangem/tap/common/extensions/ViewGroup.kt index fb9c20bfdb..81be964cb1 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/ViewGroup.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/ViewGroup.kt @@ -1,6 +1,7 @@ package com.tangem.tap.common.extensions import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup import android.view.ViewParent import androidx.core.view.forEach @@ -15,11 +16,8 @@ import timber.log.Timber /** [REDACTED_AUTHOR] */ -fun ViewGroup.inflate(viewToInflate: Int, rootView: ViewGroup?, parent: ViewGroup) { - if (rootView == null) { - val inflatedView = LayoutInflater.from(context).inflate(viewToInflate, rootView) - parent.addView(inflatedView) - } +fun ViewGroup.inflate(viewToInflate: Int, attachToRoot: Boolean = false): View { + return LayoutInflater.from(context).inflate(viewToInflate, this, attachToRoot) } fun ViewParent?.beginDelayedTransition(transition: Transition = AutoTransition()) { diff --git a/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt b/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt new file mode 100644 index 0000000000..8f7402ff1f --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt @@ -0,0 +1,59 @@ +package com.tangem.tap.common.extensions + +import com.tangem.blockchain.common.Wallet +import com.tangem.blockchain.common.WalletManager +import com.tangem.common.services.Result +import com.tangem.tap.domain.TapError +import com.tangem.tap.domain.extensions.amountToCreateAccount +import com.tangem.tap.domain.extensions.isNoAccountError +import com.tangem.tap.domain.getFirstToken +import com.tangem.tap.domain.topup.TradeCryptoHelper +import com.tangem.tap.features.wallet.redux.AddressData +import com.tangem.tap.features.wallet.redux.reducers.createAddressesData +import com.tangem.tap.network.NetworkConnectivity +import com.tangem.tap.store + +/** +[REDACTED_AUTHOR] + */ +suspend fun WalletManager.safeUpdate(): Result = try { + update() + Result.Success(wallet) +} catch (exception: Exception) { + if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) { + Result.Failure(TapError.NoInternetConnection) + } else { + val blockchain = wallet.blockchain + val amountToCreateAccount = blockchain.amountToCreateAccount(wallet.getFirstToken()) + + if (blockchain.isNoAccountError(exception) && amountToCreateAccount != null) { + Result.Failure(TapError.WalletManagerUpdate.NoAccountError(amountToCreateAccount.toString())) + } else { + val message = exception.localizedMessage ?: "Unknown exception during WalletManager update" + Result.Failure(TapError.WalletManagerUpdate.InternalError(message)) + } + } +} + +fun WalletManager?.getToUpUrl(): String? { + val wallet = this?.wallet ?: return null + val config = store.state.globalState.configManager?.config ?: return null + val defaultAddress = wallet.address + + return TradeCryptoHelper.getUrl( + TradeCryptoHelper.Action.Buy, + wallet.blockchain, + wallet.blockchain.currency, + defaultAddress, + config.moonPayApiKey, + config.moonPayApiSecretKey + ) +} + +fun WalletManager?.getAddressData(): AddressData? { + val wallet = this?.wallet ?: return null + + val addressDataList = wallet.createAddressesData() + return if (addressDataList.isEmpty()) null + else addressDataList[0] +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/images/Picasso.kt b/app/src/main/java/com/tangem/tap/common/images/Picasso.kt index c3b1d139a3..7d2a956bde 100644 --- a/app/src/main/java/com/tangem/tap/common/images/Picasso.kt +++ b/app/src/main/java/com/tangem/tap/common/images/Picasso.kt @@ -3,9 +3,12 @@ package com.tangem.tap.common.images import android.app.Application import com.squareup.picasso.OkHttp3Downloader import com.squareup.picasso.Picasso +import com.tangem.wallet.BuildConfig import okhttp3.Cache import okhttp3.CacheControl import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import timber.log.Timber import java.io.File import java.util.concurrent.TimeUnit @@ -17,12 +20,15 @@ class PicassoHelper { val picasso = Picasso.Builder(application) .downloader(OkHttp3Downloader(getOkHttpForPicasso(application))) .build() + picasso.isLoggingEnabled = BuildConfig.DEBUG Picasso.setSingletonInstance(picasso) } private fun getOkHttpForPicasso(application: Application): OkHttpClient { val okHttpBuilder = OkHttpClient.Builder() okHttpBuilder.cache(Cache(File(application.filesDir, "artworks"), Long.MAX_VALUE)) + okHttpBuilder.callTimeout(15000, TimeUnit.MILLISECONDS) + okHttpBuilder.addInterceptor { chain -> val cacheControl = CacheControl.Builder() .maxStale(KEEP_CACHE_MAX_DAYS, TimeUnit.DAYS) @@ -33,7 +39,25 @@ class PicassoHelper { .build() chain.proceed(neverExpireRequest) } + addDebugInterceptors(okHttpBuilder) + return okHttpBuilder.build() } + + private fun addDebugInterceptors(okHttpBuilder: OkHttpClient.Builder) { + if (!BuildConfig.DEBUG) return + + val picassoInterceptor = HttpLoggingInterceptor(PicassoOkHttpLogger()).apply { + level = HttpLoggingInterceptor.Level.BODY + } + okHttpBuilder.addInterceptor(picassoInterceptor) + } + + } +} + +private class PicassoOkHttpLogger : HttpLoggingInterceptor.Logger { + override fun log(message: String) { + Timber.d(message) } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/Animations.kt b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/Animations.kt new file mode 100644 index 0000000000..a95488d44f --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/Animations.kt @@ -0,0 +1,147 @@ +package com.tangem.tap.common.leapfrogWidget + +import android.animation.Animator +import android.animation.AnimatorSet +import android.animation.ObjectAnimator +import android.animation.ValueAnimator +import android.view.View +import android.view.animation.DecelerateInterpolator +import android.view.animation.LinearInterpolator + +/** +[REDACTED_AUTHOR] + */ +//fun View.setForeground(hasForegroundAtEnd: Boolean) { +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { +// foreground = getForegroundDrawable() +// } +//} + +//fun View.getForegroundDrawable(): Drawable? { +// return AppCompatResources.getDrawable(context, R.drawable.shape_rectangle_rounded_8_op_20)?.also { +// it.alpha = 60 +// } +//} + +typealias ProgressListener = (Int) -> Unit + +fun createProgressListener(duration: Long, listener: ProgressListener?): Animator { + val valueAnimator = ValueAnimator.ofInt(0, 100) + valueAnimator.duration = duration + valueAnimator.addUpdateListener { listener?.invoke((it.animatedValue as? Int) ?: 0) } + return valueAnimator +} + +fun View.unfoldAnimation(animDuration: Long, properties: LeapViewProperties): AnimatorSet { + val translate = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation) + translate.duration = animDuration + return AnimatorSet().apply { playTogether(translate) } +} + +fun View.foldAnimation(animDuration: Long, properties: LeapViewProperties): AnimatorSet { + val translate = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation) + translate.duration = animDuration + + return AnimatorSet().apply { + playSequentially(translate) + } +} + +fun View.leapAnimation(animDuration: Long, properties: LeapViewProperties, overLift: Float): AnimatorSet { + val halfDuration = animDuration / 2 + + val upTo = (height.toFloat() + overLift) * -1 + val translateUp = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, upTo) + translateUp.duration = halfDuration + translateUp.interpolator = LinearInterpolator() + + val scaleDuration = halfDuration - halfDuration / 5 + val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X, properties.scale) + val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scale) + val scale = AnimatorSet().apply { + startDelay = scaleDuration + duration = scaleDuration + interpolator = LinearInterpolator() + playTogether(scaleX, scaleY) + } + + val elevation = elevationAnimator(properties.elevationStart, properties.elevationEnd, halfDuration, 100) + val translateDown = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation) + translateDown.startDelay = halfDuration + translateDown.duration = halfDuration + translateDown.interpolator = LinearInterpolator() + + return AnimatorSet().apply { + playTogether(translateUp, scale, elevation, translateDown) + } +} + +fun View.leapBackAnimation(animDuration: Long, properties: LeapViewProperties, calculator: PropertyCalculator): AnimatorSet { + val halfDuration = animDuration / 2 + + val upTo = ((height).toFloat() - calculator.yTranslationFactor) * -1 + val translateUp = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, upTo) + translateUp.duration = halfDuration + translateUp.interpolator = LinearInterpolator() + + val scaleDuration = halfDuration - halfDuration / 5 + val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X, properties.scale) + val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scale) + val scale = AnimatorSet().apply { + startDelay = scaleDuration + duration = scaleDuration + interpolator = LinearInterpolator() + playTogether(scaleX, scaleY) + } + + val elevation = elevationAnimator(properties.elevationStart, properties.elevationEnd, halfDuration, 100) + val translateDown = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation) + translateDown.startDelay = halfDuration + translateDown.duration = halfDuration + + return AnimatorSet().apply { + playTogether(translateUp, scale, elevation, translateDown) + } +} + +fun View.pullUpAnimation(leapDuration: Long, properties: LeapViewProperties): AnimatorSet { + val pullDuration = leapDuration / 2 + val delayDuration = leapDuration / 2 + + val translateUp = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation) + val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X, properties.scale) + val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scale) + val elevation = elevationAnimator(properties.elevationStart, properties.elevationEnd) + + return AnimatorSet().apply { + startDelay = delayDuration + duration = pullDuration + interpolator = DecelerateInterpolator() + playTogether(translateUp, scaleX, scaleY, elevation) + } +} + +fun View.pullDownAnimation(leapDuration: Long, properties: LeapViewProperties): AnimatorSet { + val pullDuration = leapDuration / 2 + val delayDuration = leapDuration / 4 + + val translate = ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, properties.yTranslation) + val scaleX = ObjectAnimator.ofFloat(this, View.SCALE_X, properties.scale) + val scaleY = ObjectAnimator.ofFloat(this, View.SCALE_Y, properties.scale) + val elevation = elevationAnimator(properties.elevationStart, properties.elevationEnd) + + return AnimatorSet().apply { + startDelay = delayDuration + duration = pullDuration + interpolator = DecelerateInterpolator() + playTogether(translate, scaleX, scaleY, elevation) + } +} + +fun View.elevationAnimator(start: Float, end: Float, startDelay: Long? = null, duration: Long? = null): Animator { + val elevationAnimator = ValueAnimator.ofFloat(start, end) + elevationAnimator.addUpdateListener { this.elevation = (it.animatedValue as? Float) ?: start } + startDelay?.let { elevationAnimator.startDelay = it } + duration?.let { elevationAnimator.duration = it } + return elevationAnimator +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapView.kt b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapView.kt new file mode 100644 index 0000000000..be2d63b664 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapView.kt @@ -0,0 +1,144 @@ +package com.tangem.tap.common.leapfrogWidget + +import android.view.View + +/** +[REDACTED_AUTHOR] + */ +class LeapView( + val view: View, + val index: Int, + position: Int, + private val maximalPosition: Int, + private val calculator: PropertyCalculator, +) { + var state: LeapViewState + private set + + val initialState: LeapViewState + + init { + val initialProperties = createInitialProperty(position) + state = LeapViewState(index, position, position, initialProperties) + initialState = state.copy() + } + + fun initView() { + applyState(initialState) + } + + fun leap(): LeapFrogAnimation { + val previousPosition = state.currentPosition + val currentPosition = when (previousPosition) { + 0 -> maximalPosition + else -> state.currentPosition - 1 + } + + state = state.copy( + currentPosition = currentPosition, + previousPosition = previousPosition, + properties = createLeapAnimationProperty(previousPosition, currentPosition) + ) + + return when { + previousPosition == 0 && currentPosition == maximalPosition -> LeapFrogAnimation.LEAP + else -> LeapFrogAnimation.PULL + } + } + + fun leapBack(): LeapFrogAnimation { + val previousPosition = state.currentPosition + val currentPosition = when (previousPosition) { + maximalPosition -> 0 + else -> state.currentPosition + 1 + } + state = state.copy( + currentPosition = currentPosition, + previousPosition = previousPosition, + properties = createLeapAnimationProperty(previousPosition, currentPosition) + ) + + return when { + previousPosition == maximalPosition && currentPosition == 0 -> LeapFrogAnimation.LEAP + else -> LeapFrogAnimation.PULL + } + } + + fun fold() { + state = state.copy(properties = state.properties.toFold() + ) + } + + fun unfold() { + state = state.copy(properties = state.properties.toUnfold(calculator)) + } + + fun applyState(state: LeapViewState) { + this.state = state + setProperties(this.state.properties) + } + + private fun setProperties(properties: LeapViewProperties) { + view.elevation = properties.elevationEnd + view.translationY = properties.yTranslation + view.scaleX = properties.scale + view.scaleY = properties.scale +// initialProperties.hasForeground + } + + private fun createInitialProperty(endPosition: Int): LeapViewProperties { + return LeapViewProperties( + endPosition, + endPosition, + calculator.scale(endPosition), + calculator.elevation(endPosition, maximalPosition + 1), + calculator.elevation(endPosition, maximalPosition + 1), + calculator.yTranslation(0), + calculator.hasForeground(endPosition), + ) + } + + private fun createLeapAnimationProperty(startPosition: Int, endPosition: Int): LeapViewProperties { + return LeapViewProperties( + startPosition, + endPosition, + calculator.scale(endPosition), + calculator.elevation(startPosition, maximalPosition + 1), + calculator.elevation(endPosition, maximalPosition + 1), + calculator.yTranslation(endPosition), + calculator.hasForeground(endPosition), + ) + } + + private fun LeapViewProperties.toUnfold(calculator: PropertyCalculator): LeapViewProperties { + return this.copy(yTranslation = calculator.yTranslation(this.positionEnd)) + } + + private fun LeapViewProperties.toFold(): LeapViewProperties { + return this.copy(yTranslation = 0f) + } + + override fun toString(): String = "index: $index, position: ${state.currentPosition}, code: ${view.hashCode()}" + +} + +data class LeapViewState( + val index: Int, + val currentPosition: Int, + val previousPosition: Int, + val properties: LeapViewProperties +) + +enum class LeapFrogAnimation { + LEAP, PULL +} + +data class LeapViewProperties( + val positionStart: Int, + val positionEnd: Int, + val scale: Float, + val elevationStart: Float, + val elevationEnd: Float, + val yTranslation: Float, + val hasForeground: Boolean, +) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapfrogWidget.kt b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapfrogWidget.kt new file mode 100644 index 0000000000..884cef3f94 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/LeapfrogWidget.kt @@ -0,0 +1,258 @@ +package com.tangem.tap.common.leapfrogWidget + +import android.animation.Animator +import android.animation.AnimatorSet +import android.view.animation.AccelerateDecelerateInterpolator +import android.widget.FrameLayout +import androidx.core.animation.doOnEnd +import androidx.core.view.children +import com.tangem.tangem_sdk_new.extensions.dpToPx + +/** +[REDACTED_AUTHOR] + */ +class LeapfrogWidget( + private val parentContainer: FrameLayout, + private val calculator: PropertyCalculator = PropertyCalculator(), +) { + private val foldAnimationDuration = 500L + private val leapAnimationDuration = 800L + private val leapBackAnimationDuration = 800L + + private val leapViews = mutableListOf() + + private var foldUnfoldProgress: Int = 100 + private var leapProgress: Int = 100 + private var leapBackProgress: Int = 100 + + var isFolded: Boolean = true + private set + + init { + calculator.setTranslationConverter { parentContainer.dpToPx(it) } + if (viewIsFullFledged()) { + val maxPosition = parentContainer.childCount - 1 + parentContainer.children.forEachIndexed { index, view -> + val initialPosition = maxPosition - index + leapViews.add(LeapView(view, index, initialPosition, maxPosition, calculator)) + } + } + } + + /** + * Apply initial properties to the views + */ + fun initViews() { + isFolded = true + leapViews.forEach { it.initView() } + } + + fun fold(onEnd: () -> Unit = {}) { + val animator = foldAnimator() + if (animator == null) { + onEnd() + } else { + animator.doOnEnd { onEnd() } + animator.start() + } + } + + fun unfold(onEnd: () -> Unit = {}) { + val animator = unfoldAnimator() + if (animator == null) { + onEnd() + } else { + animator.doOnEnd { onEnd() } + animator.start() + } + } + + fun foldAnimator(): Animator? { + if (!canFoldUnfold() || isFolded) return null + + return createFoldUnfoldAnimator(true, foldAnimationDuration) + } + + fun unfoldAnimator(): Animator? { + if (!canFoldUnfold() || !isFolded) return null + + return createFoldUnfoldAnimator(false, foldAnimationDuration) + } + + private fun createFoldUnfoldAnimator(isFoldAnimation: Boolean, duration: Long): Animator { + val animatorsList = mutableListOf() + leapViews.forEach { + if (isFoldAnimation) { + it.fold() + animatorsList.add(it.view.foldAnimation(duration, it.state.properties)) + } else { + it.unfold() + animatorsList.add(it.view.unfoldAnimation(duration, it.state.properties)) + } + } + animatorsList.add(createProgressListener(duration) { + foldUnfoldProgress = it + if (it == 100) isFolded = isFoldAnimation + }) + + val animator = AnimatorSet() + animator.interpolator = AccelerateDecelerateInterpolator() + animator.playTogether(animatorsList.toList()) + return animator + } + + fun leap(onEnd: () -> Unit = {}) { + if (!canLeap()) { + onEnd() + return + } + + val duration = leapAnimationDuration + val animatorsList = mutableListOf() + leapViews.forEach { + when (it.leap()) { + LeapFrogAnimation.LEAP -> { + val overLift = calculator.overLift(leapViews.size) + animatorsList.add(it.view.leapAnimation(duration, it.state.properties, overLift)) + } + LeapFrogAnimation.PULL -> { + animatorsList.add(it.view.pullUpAnimation(duration, it.state.properties)) + } + } + } + animatorsList.add(createProgressListener(duration) { leapProgress = it }) + + val animator = AnimatorSet() + animator.playTogether(animatorsList.toList()) + animator.doOnEnd { onEnd() } + animator.start() + } + + fun leapBack(onEnd: () -> Unit = {}) { + if (!canLeapBack()) { + onEnd() + return + } + + val duration = leapBackAnimationDuration + val animatorsList = mutableListOf() + leapViews.forEach { + when (it.leapBack()) { + LeapFrogAnimation.LEAP -> { + animatorsList.add(it.view.leapBackAnimation(duration, it.state.properties, calculator)) + } + LeapFrogAnimation.PULL -> { + animatorsList.add(it.view.pullDownAnimation(duration, it.state.properties)) + } + } + } + animatorsList.add(createProgressListener(duration) { leapBackProgress = it }) + + val animator = AnimatorSet() + animator.playTogether(animatorsList.toList()) + animator.doOnEnd { onEnd() } + animator.start() + } + + fun getViewsCount(): Int = parentContainer.childCount + + fun getViewPositionByIndex(index: Int): Int = leapViews.first { it.index == index }.state.currentPosition + + fun getViewByPosition(position: Int): LeapView = leapViews.first { it.state.currentPosition == position } + + fun getState(): LeapfrogWidgetState = LeapfrogWidgetState(isFolded, leapViews.map { it.state }) + + fun applyState(state: LeapfrogWidgetState) { + isFolded = state.isFolded + state.leapViewStates.forEach { state -> + leapViews.firstOrNull { it.index == state.index }?.applyState(state) + } + } + + private fun viewIsFullFledged(): Boolean = parentContainer.childCount > 1 + + private fun canFoldUnfold(): Boolean { + return when { + !viewIsFullFledged() -> false + foldUnfoldInProgress() -> false + leapInProgress() || leapBackInProgress() -> false + else -> true + } + } + + private fun canLeap(): Boolean { + return when { + !viewIsFullFledged() -> false + isFolded -> false + leapBackInProgress() -> false + leapProgress < 70 -> false + else -> true + } + } + + private fun canLeapBack(): Boolean { + return when { + !viewIsFullFledged() -> false + isFolded -> false + leapInProgress() -> false + leapBackProgress < 70 -> false + else -> true + } + } + + private fun foldUnfoldInProgress(): Boolean = foldUnfoldProgress != 100 + private fun leapInProgress(): Boolean = leapProgress != 100 + private fun leapBackInProgress(): Boolean = leapBackProgress != 100 +} + +data class LeapfrogWidgetState( + val isFolded: Boolean, + val leapViewStates: List +) + +class PropertyCalculator( + val elevationFactor: Float = 1f, + val decreaseScaleFactor: Float = 0.1f, + val yTranslationFactor: Float = 20f, + val decreaseOverLiftingFactor: Float = 0.85f, +) { + // 0 view it is view on the top of stack, but it is last in parent.children + + private var translationValueConverter: ((Float) -> Float)? = null + fun setTranslationConverter(converter: (Float) -> Float) { + translationValueConverter = converter + } + + fun scale(position: Int): Float { + return 1f - decreaseScaleFactor * position + } + + fun elevation(position: Int, count: Int): Float { + return elevationFactor * (count - 1 - position) + } + + fun yTranslation(position: Int): Float { + val yTranslation = yTranslationFactor * position + return convertTranslation(yTranslation) + } + + fun hasForeground(position: Int): Boolean { + return position != 0 + } + + fun overLift(viewCount: Int): Float { + val initialOverLift = when { + yTranslationFactor < 0f -> yTranslationFactor * decreaseOverLiftingFactor * viewCount * -1 + else -> 10f + } + val scaleOverLift = when { + decreaseScaleFactor == 0f -> initialOverLift + else -> initialOverLift * decreaseOverLiftingFactor - initialOverLift * decreaseScaleFactor + } + return convertTranslation(scaleOverLift) + } + + private fun convertTranslation(value: Float): Float { + return translationValueConverter?.invoke(value) ?: value + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/TestLeapfrogFragment.kt b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/TestLeapfrogFragment.kt new file mode 100644 index 0000000000..1a41ff35be --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/TestLeapfrogFragment.kt @@ -0,0 +1,68 @@ +package com.tangem.tap.common.leapfrogWidget + +import android.os.Bundle +import android.view.View +import android.widget.FrameLayout +import androidx.fragment.app.Fragment +import androidx.transition.TransitionInflater +import com.tangem.tap.domain.twins.TwinsCardWidget +import com.tangem.wallet.R +import kotlinx.android.synthetic.main.test_leapfrog_fragment.* + +class TestLeapfrogFragment : Fragment(R.layout.test_leapfrog_fragment) { + + private lateinit var twinsCardWidget: TwinsCardWidget + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val inflater = TransitionInflater.from(requireContext()) + exitTransition = inflater.inflateTransition(R.transition.fade) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val leapfrogContainer: FrameLayout = view.findViewById(R.id.leapfrog_views_container) + val leapfrog = LeapfrogWidget(leapfrogContainer) + twinsCardWidget = TwinsCardWidget(leapfrog) { 200f } + + btn_twin_welcome.setOnClickListener { + twinsCardWidget.toWelcome() + } + btn_twin_to_leapfrog.setOnClickListener { + twinsCardWidget.toLeapfrog() + } + btn_twin_activate.setOnClickListener { + twinsCardWidget.toActivate() + } + + + btn_lp_init.setOnClickListener { + leapfrog.initViews() + } + btn_lp_unfold.setOnClickListener { + leapfrog.unfold() + } + btn_lp_fold.setOnClickListener { + leapfrog.fold() + } + btn_lp_leap.setOnClickListener { + leapfrog.leap() + } + btn_lp_leap_back.setOnClickListener { + leapfrog.leapBack() + } + } + + override fun onStart() { + super.onStart() + leapfrogWidgetState?.let { twinsCardWidget.leapfrogWidget.applyState(it) } + } + + override fun onStop() { + super.onStop() + leapfrogWidgetState = twinsCardWidget.leapfrogWidget.getState() + } +} + +private var leapfrogWidgetState: LeapfrogWidgetState? = null \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt index 57ed1eaf2c..38c76819b3 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt @@ -6,6 +6,9 @@ import com.tangem.tap.features.details.redux.DetailsReducer import com.tangem.tap.features.details.redux.walletconnect.WalletConnectReducer import com.tangem.tap.features.disclaimer.redux.DisclaimerReducer import com.tangem.tap.features.home.redux.HomeReducer +import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteReducer +import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsReducer +import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletReducer import com.tangem.tap.features.send.redux.reducers.SendScreenReducer import com.tangem.tap.features.tokens.redux.TokensReducer import com.tangem.tap.features.wallet.redux.reducers.WalletReducer @@ -19,6 +22,9 @@ fun appReducer(action: Action, state: AppState?): AppState { navigationState = NavigationReducer.reduce(action, state), globalState = globalReducer(action, state), homeState = HomeReducer.reduce(action, state), + onboardingNoteState = OnboardingNoteReducer.reduce(action, state), + onboardingWalletState = OnboardingWalletReducer.reduce(action, state), + onboardingOtherCardsState = OnboardingOtherCardsReducer.reduce(action, state), walletState = WalletReducer.reduce(action, state), sendState = SendScreenReducer.reduce(action, state.sendState), detailsState = DetailsReducer.reduce(action, state), diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt index 02deb7d980..40e0c80077 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt @@ -1,7 +1,7 @@ package com.tangem.tap.common.redux +import com.tangem.tap.common.redux.global.GlobalMiddleware import com.tangem.tap.common.redux.global.GlobalState -import com.tangem.tap.common.redux.global.globalMiddleware import com.tangem.tap.common.redux.navigation.NavigationState import com.tangem.tap.common.redux.navigation.navigationMiddleware import com.tangem.tap.features.details.redux.DetailsMiddleware @@ -12,7 +12,13 @@ import com.tangem.tap.features.disclaimer.redux.DisclaimerMiddleware import com.tangem.tap.features.disclaimer.redux.DisclaimerState import com.tangem.tap.features.home.redux.HomeMiddleware import com.tangem.tap.features.home.redux.HomeState -import com.tangem.tap.features.send.redux.middlewares.sendMiddleware +import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteMiddleware +import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteState +import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsMiddleware +import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsState +import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletMiddleware +import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletState +import com.tangem.tap.features.send.redux.middlewares.SendMiddleware import com.tangem.tap.features.send.redux.states.SendState import com.tangem.tap.features.tokens.redux.TokensMiddleware import com.tangem.tap.features.tokens.redux.TokensState @@ -25,6 +31,9 @@ data class AppState( val navigationState: NavigationState = NavigationState(), val globalState: GlobalState = GlobalState(), val homeState: HomeState = HomeState(), + val onboardingNoteState: OnboardingNoteState = OnboardingNoteState(), + val onboardingWalletState: OnboardingWalletState = OnboardingWalletState(), + val onboardingOtherCardsState: OnboardingOtherCardsState = OnboardingOtherCardsState(), val walletState: WalletState = WalletState(), val sendState: SendState = SendState(), val detailsState: DetailsState = DetailsState(), @@ -36,10 +45,14 @@ data class AppState( companion object { fun getMiddleware(): List> { return listOf( - logMiddleware, navigationMiddleware, notificationsMiddleware, globalMiddleware, - HomeMiddleware().homeMiddleware, + logMiddleware, navigationMiddleware, notificationsMiddleware, + GlobalMiddleware.handler, + HomeMiddleware.handler, + OnboardingNoteMiddleware.handler, + OnboardingWalletMiddleware.handler, + OnboardingOtherCardsMiddleware.handler, WalletMiddleware().walletMiddleware, - sendMiddleware, + SendMiddleware().sendMiddleware, DetailsMiddleware().detailsMiddleware, DisclaimerMiddleware().disclaimerMiddleware, TokensMiddleware().tokensMiddleware, diff --git a/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt b/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt index 8a55bcfafe..2ace551205 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt @@ -9,6 +9,7 @@ import com.tangem.tap.domain.MultiMessageError import com.tangem.tap.domain.TapError import com.tangem.tap.domain.assembleErrors import com.tangem.tap.notificationsHandler +import com.tangem.wallet.BuildConfig import org.rekotlin.Action import org.rekotlin.Middleware import java.lang.ref.WeakReference @@ -63,23 +64,29 @@ fun getMessageString(context: Context, message: Int, args: List?): String { val notificationsMiddleware: Middleware = { dispatch, state -> { next -> { action -> - when (action) { - is NotificationAction -> notificationsHandler?.showNotification(action.messageResource) - is ToastNotificationAction -> notificationsHandler?.showToastNotification(action.messageResource) - is ErrorAction -> { - when (action.error) { - is MultiMessageError -> { - val multiError = action.error as MultiMessageError - notificationsHandler?.showNotification(multiError.assembleErrors(), multiError.builder) - } - else -> { - val args = (action.error as? ArgError)?.args ?: listOf() - notificationsHandler?.showNotification(action.error.localizedMessage, args) - } - } + handleNotificationAction(action) + next(action) + } + } +} + +private fun handleNotificationAction(action: Action) { + if (action is Debug && !BuildConfig.DEBUG) return + + when (action) { + is NotificationAction -> notificationsHandler?.showNotification(action.messageResource) + is ToastNotificationAction -> notificationsHandler?.showToastNotification(action.messageResource) + is ErrorAction -> { + when (action.error) { + is MultiMessageError -> { + val multiError = action.error as MultiMessageError + notificationsHandler?.showNotification(multiError.assembleErrors(), multiError.builder) + } + else -> { + val args = (action.error as? ArgError)?.args ?: listOf() + notificationsHandler?.showNotification(action.error.messageResource, args) } } - next(action) } } } @@ -94,4 +101,10 @@ interface NotificationAction : Action { interface ErrorAction : Action { val error: TapError -} \ No newline at end of file +} + +// Processed only in the debug builds +interface Debug +interface DebugNotification : Debug, NotificationAction +interface DebugToastNotification : Debug, ToastNotificationAction +interface DebugErrorAction : Debug, ErrorAction \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt b/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt new file mode 100644 index 0000000000..36e1a749a5 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt @@ -0,0 +1,17 @@ +package com.tangem.tap.common.redux + +import com.tangem.tap.features.wallet.redux.AddressData + +/** +[REDACTED_AUTHOR] + */ +interface StateDialog + +sealed class AppDialog : StateDialog { + object ScanFailsDialog : AppDialog() + data class AddressInfoDialog( + val addressData: AddressData, + val onCopyAddress: () -> Unit, + val onExploreAddress: () -> Unit + ) : AppDialog() +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt index c1c9137aec..297ba9fd40 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt @@ -2,10 +2,13 @@ package com.tangem.tap.common.redux.global import com.tangem.blockchain.common.WalletManager import com.tangem.common.CompletionResult +import com.tangem.common.core.TangemError +import com.tangem.tap.common.redux.* +import com.tangem.tap.domain.TapError import com.tangem.tap.domain.configurable.config.ConfigManager import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager -import com.tangem.tap.domain.tasks.ScanNoteResponse +import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.features.details.redux.SecurityOption import com.tangem.tap.features.feedback.EmailData import com.tangem.tap.features.feedback.FeedbackManager @@ -14,13 +17,34 @@ import org.rekotlin.Action sealed class GlobalAction : Action { + // notifications + data class ShowNotification(override val messageResource: Int) : GlobalAction(), NotificationAction + data class ShowToastNotification(override val messageResource: Int) : GlobalAction(), ToastNotificationAction + data class ShowErrorNotification(override val error: TapError) : GlobalAction(), ErrorAction + data class DebugShowErrorNotification(override val error: TapError) : GlobalAction(), DebugErrorAction + + // dialogs + data class ShowDialog(val stateDialog: StateDialog) : GlobalAction() + object HideDialog : GlobalAction() + + sealed class Onboarding { + data class Start(val scanResponse: ScanResponse) : GlobalAction() + object Stop : GlobalAction() + } + + data class ScanCard( + val onSuccess: ((ScanResponse) -> Unit)? = null, + val onFailure: ((TangemError) -> Unit)? = null, + val messageResId: Int? = null, + ) : GlobalAction() + object ScanFailsCounter { - data class ChooseBehavior(val result: CompletionResult) : GlobalAction() + data class ChooseBehavior(val result: CompletionResult) : GlobalAction() object Reset : GlobalAction() object Increment : GlobalAction() } - data class SaveScanNoteResponse(val scanNoteResponse: ScanNoteResponse) : GlobalAction() + data class SaveScanNoteResponse(val scanResponse: ScanResponse) : GlobalAction() data class SetIfCardVerifiedOnline(val verified: Boolean) : GlobalAction() @@ -45,9 +69,6 @@ sealed class GlobalAction : Action { data class SendFeedback(val emailData: EmailData) : GlobalAction() data class UpdateFeedbackInfo(val walletManagers: List) : GlobalAction() - data class ShowDialog(val stateDialog: StateDialog) : GlobalAction() - object HideDialog : GlobalAction() - object GetMoonPayUserStatus : GlobalAction() { data class Success(val moonPayUserStatus: MoonPayUserStatus) : GlobalAction() } diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt index d90e263263..ea80ab463c 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt @@ -3,20 +3,30 @@ package com.tangem.tap.common.redux.global import com.tangem.common.CompletionResult import com.tangem.common.core.TangemSdkError import com.tangem.common.services.Result +import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler +import com.tangem.tap.common.extensions.dispatchDialogShow import com.tangem.tap.common.extensions.dispatchOnMain +import com.tangem.tap.common.extensions.withMainContext +import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.common.redux.AppState import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager -import com.tangem.tap.features.home.redux.HomeAction import com.tangem.tap.features.send.redux.SendAction import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.network.moonpay.MoonpayService import com.tangem.tap.preferencesStorage import com.tangem.tap.scope import com.tangem.tap.store +import com.tangem.tap.tangemSdkManager import kotlinx.coroutines.launch import org.rekotlin.Middleware -val globalMiddleware: Middleware = { dispatch, appState -> +class GlobalMiddleware { + companion object { + val handler = globalMiddlewareHandler + } +} + +private val globalMiddlewareHandler: Middleware = { dispatch, appState -> { nextDispatch -> { action -> when (action) { @@ -27,8 +37,7 @@ val globalMiddleware: Middleware = { dispatch, appState -> if (action.result.error is TangemSdkError.UserCancelled) { store.dispatch(GlobalAction.ScanFailsCounter.Increment) if (store.state.globalState.scanCardFailsCounter >= 2) { - store.dispatch(HomeAction.ShowDialog.ScanFails) - store.dispatch(WalletAction.ShowDialog.ScanFails) + store.dispatchDialogShow(AppDialog.ScanFailsDialog) } } else { store.dispatch(GlobalAction.ScanFailsCounter.Reset) @@ -63,7 +72,7 @@ val globalMiddleware: Middleware = { dispatch, appState -> } is GlobalAction.UpdateFeedbackInfo -> { store.state.globalState.feedbackManager?.infoHolder - ?.setWalletsInfo(action.walletManagers) + ?.setWalletsInfo(action.walletManagers) } is GlobalAction.GetMoonPayUserStatus -> { val apiKey = appState()?.globalState?.configManager?.config?.moonPayApiKey @@ -72,12 +81,27 @@ val globalMiddleware: Middleware = { dispatch, appState -> val userStatusResponse = MoonpayService().getUserStatus(apiKey) if (userStatusResponse is Result.Success) { store.dispatchOnMain( - GlobalAction.GetMoonPayUserStatus.Success(userStatusResponse.data) + GlobalAction.GetMoonPayUserStatus.Success(userStatusResponse.data) ) } } } } + is GlobalAction.ScanCard -> { + scope.launch { + val result = tangemSdkManager.scanProduct(FirebaseAnalyticsHandler, action.messageResId) + store.dispatch(GlobalAction.ScanFailsCounter.ChooseBehavior(result)) + withMainContext { + when (result) { + is CompletionResult.Success -> { + tangemSdkManager.changeDisplayedCardIdNumbersCount(result.data.card) + action.onSuccess?.invoke(result.data) + } + is CompletionResult.Failure -> action.onFailure?.invoke(result.error) + } + } + } + } } nextDispatch(action) } diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt index e1f1fa2217..29ec84876e 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt @@ -1,6 +1,8 @@ package com.tangem.tap.common.redux.global import com.tangem.tap.common.redux.AppState +import com.tangem.tap.features.onboarding.OnboardingManager +import com.tangem.tap.preferencesStorage import org.rekotlin.Action fun globalReducer(action: Action, state: AppState): GlobalState { @@ -10,6 +12,14 @@ fun globalReducer(action: Action, state: AppState): GlobalState { val globalState = state.globalState return when (action) { + is GlobalAction.Onboarding.Start -> { + val usedCardsPrefStorage = preferencesStorage.usedCardsPrefStorage + val onboardingState = OnboardingManager(action.scanResponse, usedCardsPrefStorage) + globalState.copy(onboardingManager = onboardingState) + } + is GlobalAction.Onboarding.Stop -> { + globalState.copy(onboardingManager = null) + } is GlobalAction.ScanFailsCounter.Increment -> { globalState.copy(scanCardFailsCounter = globalState.scanCardFailsCounter + 1) } @@ -17,7 +27,7 @@ fun globalReducer(action: Action, state: AppState): GlobalState { globalState.copy(scanCardFailsCounter = 0) } is GlobalAction.SaveScanNoteResponse -> - globalState.copy(scanNoteResponse = action.scanNoteResponse) + globalState.copy(scanResponse = action.scanResponse) is GlobalAction.ChangeAppCurrency -> { globalState.copy(appCurrency = action.appCurrency) } @@ -29,17 +39,17 @@ fun globalReducer(action: Action, state: AppState): GlobalState { } is GlobalAction.SetWarningManager -> globalState.copy(warningManager = action.warningManager) is GlobalAction.UpdateWalletSignedHashes -> { - val wallet = globalState.scanNoteResponse?.card - ?.wallet(action.walletPublicKey) - ?.copy( - totalSignedHashes = action.walletSignedHashes, - remainingSignatures = action.remainingSignatures - ) - val card = globalState.scanNoteResponse?.card - wallet?.let { globalState.scanNoteResponse.card.updateWallet(wallet) } + val wallet = globalState.scanResponse?.card + ?.wallet(action.walletPublicKey) + ?.copy( + totalSignedHashes = action.walletSignedHashes, + remainingSignatures = action.remainingSignatures + ) + val card = globalState.scanResponse?.card + wallet?.let { globalState.scanResponse.card.updateWallet(wallet) } if (card != null) { - globalState.copy(scanNoteResponse = globalState.scanNoteResponse.copy(card = card)) + globalState.copy(scanResponse = globalState.scanResponse.copy(card = card)) } else { globalState } diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt index f370fca90c..e27641b854 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt @@ -1,33 +1,33 @@ package com.tangem.tap.common.redux.global import com.tangem.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY +import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.domain.PayIdManager import com.tangem.tap.domain.TapWalletManager import com.tangem.tap.domain.configurable.config.ConfigManager import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager -import com.tangem.tap.domain.tasks.ScanNoteResponse +import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.features.feedback.FeedbackManager +import com.tangem.tap.features.onboarding.OnboardingManager import com.tangem.tap.network.coinmarketcap.CoinMarketCapService import com.tangem.tap.network.moonpay.MoonPayUserStatus import org.rekotlin.StateType data class GlobalState( - val scanNoteResponse: ScanNoteResponse? = null, - val cardVerifiedOnline: Boolean = false, - val tapWalletManager: TapWalletManager = TapWalletManager(), - val payIdManager: PayIdManager = PayIdManager(), - val coinMarketCapService: CoinMarketCapService = CoinMarketCapService(), - val configManager: ConfigManager? = null, - val warningManager: WarningMessagesManager? = null, - val feedbackManager: FeedbackManager? = null, - val appCurrency: FiatCurrencyName = DEFAULT_FIAT_CURRENCY, - val scanCardFailsCounter: Int = 0, - val dialog: StateDialog? = null, - val moonPayUserStatus: MoonPayUserStatus? = null + val scanResponse: ScanResponse? = null, + val onboardingManager: OnboardingManager? = null, + val cardVerifiedOnline: Boolean = false, + val tapWalletManager: TapWalletManager = TapWalletManager(), + val payIdManager: PayIdManager = PayIdManager(), + val coinMarketCapService: CoinMarketCapService = CoinMarketCapService(), + val configManager: ConfigManager? = null, + val warningManager: WarningMessagesManager? = null, + val feedbackManager: FeedbackManager? = null, + val appCurrency: FiatCurrencyName = DEFAULT_FIAT_CURRENCY, + val scanCardFailsCounter: Int = 0, + val dialog: StateDialog? = null, + val moonPayUserStatus: MoonPayUserStatus? = null, ) : StateType typealias CryptoCurrencyName = String -typealias FiatCurrencyName = String - - -interface StateDialog \ No newline at end of file +typealias FiatCurrencyName = String \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt index 6da57dfec8..331dc07e1a 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt @@ -1,12 +1,17 @@ package com.tangem.tap.common.redux.navigation +import android.view.View import androidx.fragment.app.FragmentActivity +import androidx.transition.TransitionSet import org.rekotlin.Action import java.lang.ref.WeakReference sealed class NavigationAction : Action { - data class NavigateTo(val screen: AppScreen, val addToBackstack: Boolean = true) : - NavigationAction() + data class NavigateTo( + val screen: AppScreen, + val fragmentShareTransition: FragmentShareTransition? = null, + val addToBackstack: Boolean = true + ) : NavigationAction() data class PopBackTo(val screen: AppScreen? = null) : NavigationAction() @@ -14,4 +19,12 @@ sealed class NavigationAction : Action { data class ActivityCreated(val activity: WeakReference) : NavigationAction() object ActivityDestroyed : NavigationAction() -} \ No newline at end of file +} + +data class FragmentShareTransition( + val shareElements: List, + val enterTransitionSet: TransitionSet, + val exitTransitionSet: TransitionSet +) + +data class ShareElement(val wView: WeakReference, val name: String) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt index 43c8ed406e..31fe730615 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt @@ -13,7 +13,11 @@ val navigationMiddleware: Middleware = { dispatch, state -> val navState = state()?.navigationState when (action) { is NavigationAction.NavigateTo -> { - navState?.activity?.get()?.openFragment(action.screen, action.addToBackstack) + navState?.activity?.get()?.openFragment( + action.screen, + action.addToBackstack, + action.fragmentShareTransition + ) } is NavigationAction.PopBackTo -> { if (action.screen == AppScreen.Home) { diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt index ed59b9b2d5..4fa02150f0 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationState.kt @@ -5,12 +5,19 @@ import org.rekotlin.StateType import java.lang.ref.WeakReference data class NavigationState( - val backStack: List = listOf(AppScreen.Home), - val activity: WeakReference? = null + val backStack: List = listOf(AppScreen.Home), + val activity: WeakReference? = null ) : StateType enum class AppScreen { - Home, Wallet, WalletDetails, Send, Details, DetailsConfirm, DetailsSecurity, Disclaimer, - CreateTwinWalletWarning, CreateTwinWallet, TwinsOnboarding, AddTokens, WalletConnectSessions, + Home, + Disclaimer, + OnboardingNote, OnboardingWallet, OnboardingTwins, OnboardingOther, + Wallet, WalletDetails, + Send, + Details, DetailsConfirm, DetailsSecurity, + CreateTwinWalletWarning, CreateTwinWallet, + AddTokens, + WalletConnectSessions, QrScan } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/toggleWidget/IndeterminateProgressButtonWidget.kt b/app/src/main/java/com/tangem/tap/common/toggleWidget/IndeterminateProgressButtonWidget.kt index c6cb5f22bd..b4eacfff4e 100644 --- a/app/src/main/java/com/tangem/tap/common/toggleWidget/IndeterminateProgressButtonWidget.kt +++ b/app/src/main/java/com/tangem/tap/common/toggleWidget/IndeterminateProgressButtonWidget.kt @@ -5,6 +5,7 @@ import android.view.View import com.google.android.material.button.MaterialButton import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.show +import com.tangem.tap.features.wallet.redux.ProgressState /** [REDACTED_AUTHOR] @@ -12,7 +13,7 @@ import com.tangem.tap.common.extensions.show open class IndeterminateProgressButtonWidget( private val button: MaterialButton, private val progress: View, - initialState: ProgressState = ProgressState.None + initialState: ProgressState = ProgressState.Done ) : ViewStateWidget { private var initialButtonText: CharSequence = button.text @@ -23,12 +24,14 @@ open class IndeterminateProgressButtonWidget( changeState(initialState) } + override val mainView: View = button + override fun changeState(state: WidgetState) { val progressState = state as? ProgressState ?: return when (progressState) { - is ProgressState.None -> switchToNone() - is ProgressState.Progress -> switchToProgress() + ProgressState.Done, ProgressState.Error -> switchToNone() + ProgressState.Loading -> switchToProgress() } } diff --git a/app/src/main/java/com/tangem/tap/common/toggleWidget/RefreshBalanceWidget.kt b/app/src/main/java/com/tangem/tap/common/toggleWidget/RefreshBalanceWidget.kt new file mode 100644 index 0000000000..e7ef08e026 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/toggleWidget/RefreshBalanceWidget.kt @@ -0,0 +1,100 @@ +package com.tangem.tap.common.toggleWidget + +import android.view.View +import android.view.ViewGroup +import android.view.animation.* +import android.widget.ViewSwitcher +import com.tangem.tap.features.wallet.redux.ProgressState +import com.tangem.wallet.R + +/** +[REDACTED_AUTHOR] + */ +class RefreshBalanceWidget( + private val root: ViewGroup +) : ViewStateWidget { + + var isShowing: Boolean? = null + private set + + override val mainView: View = root.findViewById(R.id.btn_refresh_balance) + + + private val viewSwitcher: ViewSwitcher by lazy { mainView.findViewById(R.id.switcher_refresh_arrow_to_progress) } + + private val arrowView = mainView.findViewById(R.id.imv_arrow_refresh) + + private var progressViewAnimation: Animation? = null + + init { + viewSwitcher.inAnimation = AlphaAnimation(0f, 1f).apply { + duration = 400 + interpolator = LinearInterpolator() + } + viewSwitcher.outAnimation = AlphaAnimation(1f, 0f).apply { + duration = 400 + interpolator = LinearInterpolator() + } + } + + override fun changeState(state: WidgetState) { + val progressState = state as? ProgressState ?: return + + val currentStateByView = getState() + + if (progressState == currentStateByView) return + + animateState(progressState) + viewSwitcher.showNext() + + } + + private fun animateState(state: ProgressState) { + when (state) { + ProgressState.Done, ProgressState.Error -> { + progressViewAnimation?.cancel() + } + ProgressState.Loading -> { + val animation = RotateAnimation( + 0f, 360f, + Animation.RELATIVE_TO_SELF, 0.5f, + Animation.RELATIVE_TO_SELF, 0.5f + ) + animation.duration = 700 + animation.interpolator = AccelerateInterpolator() + animation.repeatCount = -1 + arrowView.startAnimation(animation) + progressViewAnimation = animation + } + } + } + + fun show(show: Boolean) { + if (show == isShowing) return + + isShowing = show + if (show) { + mainView.startAnimation(ShowAnimation()) + } else { + mainView.startAnimation(AlphaAnimation(1f, 0f).apply { fillAfter = true }) + } + } + + private fun isArrowRefreshActive(): Boolean = viewSwitcher.currentView.id == R.id.fl_arrow_refresh_container + + private fun getState(): ProgressState = if (isArrowRefreshActive()) ProgressState.Done else ProgressState.Loading +} + +class ShowAnimation : AnimationSet(true) { + init { + addAnimation(ScaleAnimation( + 0f, 1f, + 0f, 1f, + Animation.RELATIVE_TO_SELF, 0.5f, + Animation.RELATIVE_TO_SELF, 0.5f) + ) + addAnimation(AlphaAnimation(0f, 1f)) + duration = 300 + interpolator = AnticipateOvershootInterpolator() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/toggleWidget/ViewStateWidget.kt b/app/src/main/java/com/tangem/tap/common/toggleWidget/ViewStateWidget.kt index 246268de7a..48ba0a7291 100644 --- a/app/src/main/java/com/tangem/tap/common/toggleWidget/ViewStateWidget.kt +++ b/app/src/main/java/com/tangem/tap/common/toggleWidget/ViewStateWidget.kt @@ -1,5 +1,7 @@ package com.tangem.tap.common.toggleWidget +import android.view.View + /** [REDACTED_AUTHOR] @@ -7,10 +9,6 @@ package com.tangem.tap.common.toggleWidget interface WidgetState interface ViewStateWidget { + val mainView: View fun changeState(state: WidgetState) -} - -sealed class ProgressState : WidgetState { - object None : ProgressState() - data class Progress(val progress: Int = 0) : ProgressState() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/transitions/OnboardingTransitions.kt b/app/src/main/java/com/tangem/tap/common/transitions/OnboardingTransitions.kt new file mode 100644 index 0000000000..32785f0147 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/transitions/OnboardingTransitions.kt @@ -0,0 +1,44 @@ +package com.tangem.tap.common.transitions + +import androidx.transition.* + +/** +[REDACTED_AUTHOR] + */ +class FrontCardEnterTransition : TransitionSet() { + init { + ordering = ORDERING_TOGETHER; + addTransition(Fade()) + addTransition(ChangeImageTransform()) // изменения внутри ImageView + addTransition(ChangeTransform()) // изменение размеров, углов накона + addTransition(ChangeBounds()) // изменение положения + } +} + +class FrontCardExitTransition : TransitionSet() { + init { + ordering = ORDERING_TOGETHER; + addTransition(Fade()) + addTransition(ChangeImageTransform()) + addTransition(ChangeTransform()) + addTransition(ChangeBounds()) + } +} + +class HomeToOnboardingTransition : TransitionSet() { + init { + ordering = ORDERING_TOGETHER; + addTransition(Fade()) + addTransition(ChangeTransform()) + addTransition(ChangeBounds()) + } +} + +class InternalNoteLayoutTransition : TransitionSet() { + init { + ordering = ORDERING_TOGETHER; + addTransition(ChangeTransform()) + addTransition(ChangeBounds()) + addTransition(Fade()) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/ProductType.kt b/app/src/main/java/com/tangem/tap/domain/ProductType.kt new file mode 100644 index 0000000000..02f0246c82 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/ProductType.kt @@ -0,0 +1,8 @@ +package com.tangem.tap.domain + +/** +[REDACTED_AUTHOR] + */ +enum class ProductType { + Note, Wallet, Twin, Other +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt index 56fe47fb45..5253a9f91c 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -1,5 +1,6 @@ package com.tangem.tap.domain +import CreateProductWalletAndRescanTask import android.content.Context import com.tangem.Message import com.tangem.TangemSdk @@ -20,9 +21,9 @@ import com.tangem.tap.common.analytics.AnalyticsEvent import com.tangem.tap.common.analytics.AnalyticsHandler import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler import com.tangem.tap.domain.tasks.CreateWalletAndRescanTask -import com.tangem.tap.domain.tasks.ScanNoteResponse -import com.tangem.tap.domain.tasks.ScanNoteTask -import com.tangem.tap.domain.twins.isTwinCard +import com.tangem.tap.domain.tasks.product.ScanProductTask +import com.tangem.tap.domain.tasks.product.ScanResponse +import com.tangem.tap.domain.twins.isTangemTwin import com.tangem.wallet.R import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -31,20 +32,34 @@ import kotlin.coroutines.suspendCoroutine class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Context) { - suspend fun scanNote( - analyticsHandler: AnalyticsHandler, messageRes: Int? = null, - ): CompletionResult { + suspend fun scanProduct( + analyticsHandler: AnalyticsHandler, + messageRes: Int? = null, + ): CompletionResult { analyticsHandler.triggerEvent(AnalyticsEvent.READY_TO_SCAN, null) - val result = runTaskAsyncReturnOnMain(ScanNoteTask(), - initialMessage = Message( - context.getString(messageRes ?: R.string.initial_message_scan_header) - )) - if (result is CompletionResult.Failure) { + + val message = Message(context.getString(messageRes ?: R.string.initial_message_scan_header)) + return runTaskAsyncReturnOnMain(ScanProductTask(), null, message) + .also { sendScanFailuresToAnalytics(analyticsHandler, it) } + } + + suspend fun createProductWallet(scanResponse: ScanResponse): CompletionResult { + return runTaskAsync( + CreateProductWalletAndRescanTask(scanResponse.productType), + scanResponse.card.cardId, + Message(context.getString(R.string.initial_message_create_wallet_body)) + ) + } + + private fun sendScanFailuresToAnalytics( + analyticsHandler: AnalyticsHandler, + result: CompletionResult + ) { + if (result is CompletionResult.Failure && result.error is TangemSdkError) { (result.error as? TangemSdkError)?.let { error -> analyticsHandler.logCardSdkError(error, FirebaseAnalyticsHandler.ActionToLog.Scan) } } - return result } suspend fun createWallet(cardId: String?): CompletionResult { @@ -106,7 +121,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co } fun changeDisplayedCardIdNumbersCount(card: Card) { - tangemSdk.config.cardIdDisplayedNumbersCount = if (card.isTwinCard()) 4 else null + tangemSdk.config.cardIdDisplayedNumbersCount = if (card.isTangemTwin()) 4 else null } companion object { diff --git a/app/src/main/java/com/tangem/tap/domain/TapBlockchainConfig.kt b/app/src/main/java/com/tangem/tap/domain/TapBlockchainConfig.kt deleted file mode 100644 index b9ec4705a5..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/TapBlockchainConfig.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.tangem.tap.domain - -class TapBlockchainConfig { - companion object { - val blockchairApiKey: String = "A___0Shpsu4KagE7oSabrw20DfXAqWlT" - val blockcypherTokens: Set = setOf( - "aa8184b0e0894b88a5688e01b3dc1e82", - "56c4ca23c6484c8f8864c32fde4def8d", - "66a8a37c5e9d4d2c9bb191acfe7f93aa" - ) - val infuraProjectId: String = "613a0b14833145968b1f656240c7d245" - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt index ca57897238..c84e3f0d29 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt @@ -16,12 +16,13 @@ interface MultiMessageError : TapErrors { } sealed class TapError( - @StringRes val localizedMessage: Int, - override val args: List? = null + @StringRes val messageResource: Int, + override val args: List? = null ) : Throwable(), TapErrors, ArgError { object UnknownError : TapError(R.string.send_error_unknown) - data class CustomError(val customMessage: String) : TapError(R.string.common_custom_string, listOf(customMessage)) + open class CustomError(val customMessage: String) : TapError(R.string.common_custom_string, listOf(customMessage)) + object ScanCardError : TapError(R.string.scan_card_error) object PayIdAlreadyCreated : TapError(R.string.wallet_create_payid_error_already_created) object PayIdCreatingError : TapError(R.string.wallet_create_payid_error_message) object PayIdEmptyField : TapError(R.string.wallet_create_payid_empty) @@ -38,8 +39,13 @@ sealed class TapError( object DustChange : TapError(R.string.send_error_dust_change) data class CreateAccountUnderfunded(override val args: List) : TapError(R.string.send_error_no_target_account) - sealed class XmlError { - object AssetAccountNotCreated: TapError(R.string.send_error_no_account_xlm) + sealed class XmlError { + object AssetAccountNotCreated : TapError(R.string.send_error_no_account_xlm) + } + + sealed class WalletManagerUpdate { + class NoAccountError(amountToCreateAccount: String): CustomError(amountToCreateAccount) + class InternalError(message: String): CustomError(message) } data class ValidateTransactionErrors( @@ -60,7 +66,7 @@ fun TapErrors.assembleErrors(): MutableList?>> { val idList = mutableListOf?>>() when (this) { is MultiMessageError -> this.errorList.forEach { idList.addAll(it.assembleErrors()) } - is TapError -> idList.add(Pair(this.localizedMessage, this.args)) + is TapError -> idList.add(Pair(this.messageResource, this.args)) } return idList } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt index f40128eb85..9133792893 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -5,6 +5,8 @@ import com.tangem.common.card.Card import com.tangem.common.services.Result import com.tangem.tap.common.analytics.AnalyticsEvent import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler +import com.tangem.tap.common.extensions.dispatchDebugErrorNotification +import com.tangem.tap.common.extensions.safeUpdate import com.tangem.tap.common.redux.global.FiatCurrencyName import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.currenciesRepository @@ -12,10 +14,10 @@ import com.tangem.tap.domain.TapWorkarounds.isStart2Coin import com.tangem.tap.domain.TapWorkarounds.isTestCard import com.tangem.tap.domain.configurable.config.ConfigManager import com.tangem.tap.domain.extensions.* -import com.tangem.tap.domain.tasks.ScanNoteResponse +import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.domain.tokens.CardCurrencies -import com.tangem.tap.domain.twins.TwinsHelper -import com.tangem.tap.domain.twins.isTwinCard +import com.tangem.tap.domain.twins.getTwinCardNumber +import com.tangem.tap.domain.twins.isTangemTwin import com.tangem.tap.features.tokens.redux.TokensAction import com.tangem.tap.features.wallet.redux.Currency import com.tangem.tap.features.wallet.redux.WalletAction @@ -38,28 +40,17 @@ class TapWalletManager { by lazy { WalletManagerFactory(blockchainSdkConfig) } suspend fun loadWalletData(walletManager: WalletManager) { - val result = try { - walletManager.update() - Result.Success(walletManager.wallet) - } catch (exception: Exception) { - Result.Failure(exception) - } - handleUpdateWalletResult(result, walletManager) + handleUpdateWalletResult(walletManager.safeUpdate(), walletManager) } suspend fun updateWallet(walletManager: WalletManager) { - val result = try { - walletManager.update() - Result.Success(walletManager.wallet) - } catch (exception: Exception) { - Result.Failure(exception) - } + val result = walletManager.safeUpdate() withContext(Dispatchers.Main) { when (result) { is Result.Success -> store.dispatch(WalletAction.UpdateWallet.Success(result.data)) is Result.Failure -> - store.dispatch(WalletAction.UpdateWallet.Failure(result.error?.localizedMessage)) + store.dispatch(WalletAction.UpdateWallet.Failure(result.error.localizedMessage)) } } @@ -86,7 +77,7 @@ class TapWalletManager { handleFiatRatesResult(results) } - suspend fun onCardScanned(data: ScanNoteResponse, addAnalyticsEvent: Boolean = false) { + suspend fun onCardScanned(data: ScanResponse, addAnalyticsEvent: Boolean = false) { if (addAnalyticsEvent) { FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.CARD_IS_SCANNED, data.card, data.walletData?.blockchain) } @@ -98,12 +89,9 @@ class TapWalletManager { store.dispatch(GlobalAction.SaveScanNoteResponse(data)) store.dispatch(WalletAction.SetIfTestnetCard(data.card.isTestCard)) store.dispatch(WalletAction.MultiWallet.SetIsMultiwalletAllowed(data.card.isMultiwalletAllowed)) - if (data.card.isTwinCard()) { - val cardNumber = TwinsHelper.getTwinCardNumber(data.card.cardId) - if (cardNumber != null) { - store.dispatch(WalletAction.TwinsAction.SetTwinCard( - cardNumber, isCreatingTwinCardsAllowed = true - )) + if (data.card.isTangemTwin()) { + data.card.getTwinCardNumber()?.let { + store.dispatch(WalletAction.TwinsAction.SetTwinCard(it, true)) } } @@ -116,7 +104,7 @@ class TapWalletManager { } } - private fun updateConfigManager(data: ScanNoteResponse) { + private fun updateConfigManager(data: ScanResponse) { val configManager = store.state.globalState.configManager val blockchain = data.getBlockchain() if (data.card.isStart2Coin) { @@ -132,7 +120,7 @@ class TapWalletManager { } } - suspend fun loadData(data: ScanNoteResponse) { + suspend fun loadData(data: ScanResponse) { withContext(Dispatchers.Main) { store.dispatch(WalletAction.LoadCardInfo(data.card)) @@ -142,7 +130,7 @@ class TapWalletManager { store.dispatch(WalletAction.LoadData.Failure(TapError.UnknownBlockchain)) } data.card.wallets.isEmpty() || - (data.card.isTwinCard() && data.secondTwinPublicKey == null) -> { + (data.card.isTangemTwin() && data.secondTwinPublicKey == null) -> { store.dispatch(WalletAction.EmptyWallet) } else -> { @@ -151,7 +139,7 @@ class TapWalletManager { val blockchain = data.getBlockchain() val primaryWalletManager = walletManagerFactory.makePrimaryWalletManager(data) - if (blockchain != null && primaryWalletManager != null) { + if (blockchain != Blockchain.Unknown && primaryWalletManager != null) { val primaryToken = data.getPrimaryToken() store.dispatch(WalletAction.MultiWallet.SetPrimaryBlockchain(blockchain)) @@ -228,14 +216,14 @@ class TapWalletManager { } } - suspend fun reloadData(data: ScanNoteResponse) { + suspend fun reloadData(data: ScanResponse) { withContext(Dispatchers.Main) { when { data.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> { store.dispatch(WalletAction.LoadData.Failure(TapError.UnknownBlockchain)) } data.card.wallets.isEmpty() || - (data.card.isTwinCard() && data.secondTwinPublicKey == null) -> { + (data.card.isTangemTwin() && data.secondTwinPublicKey == null) -> { store.dispatch(WalletAction.EmptyWallet) } else -> { @@ -260,23 +248,23 @@ class TapWalletManager { when (result) { is Result.Success -> store.dispatch(WalletAction.LoadWallet.Success(result.data)) is Result.Failure -> { - if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) { - store.dispatch(WalletAction.LoadData.Failure(TapError.NoInternetConnection)) - return@withContext - } - val error = result.error - val blockchain = walletManager.wallet.blockchain - if (error != null && blockchain.isNoAccountError(error)) { - val token = walletManager.wallet.getFirstToken() - val amountToCreateAccount = blockchain.amountToCreateAccount(token) - if (amountToCreateAccount != null) { - store.dispatch(WalletAction.LoadWallet.NoAccount( - walletManager.wallet, - amountToCreateAccount.toString())) - return@withContext + val error = (result.error as? TapError) ?: TapError.UnknownError + when (error) { + TapError.NoInternetConnection -> { + store.dispatch(WalletAction.LoadData.Failure(error)) + } + is TapError.WalletManagerUpdate.NoAccountError -> { + store.dispatch(WalletAction.LoadWallet + .NoAccount(walletManager.wallet, error.customMessage)) + } + is TapError.WalletManagerUpdate.InternalError -> { + store.dispatch(WalletAction.LoadWallet + .Failure(walletManager.wallet, error.customMessage)) + } + else -> { + store.dispatchDebugErrorNotification(error) } } - store.dispatch(WalletAction.LoadWallet.Failure(walletManager.wallet, result.error?.localizedMessage)) } } } diff --git a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt index 8af0e99415..9003c9f4fc 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt @@ -4,10 +4,10 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.common.card.Card import com.tangem.common.card.EllipticCurve import com.tangem.common.card.FirmwareVersion -import com.tangem.tap.domain.TapWorkarounds.isNote import com.tangem.tap.domain.TapWorkarounds.isStart2Coin +import com.tangem.tap.domain.TapWorkarounds.isTangemNote import com.tangem.tap.domain.extensions.getSingleWallet -import com.tangem.tap.domain.twins.isTwinCard +import com.tangem.tap.domain.twins.isTangemTwin import java.util.* object TapWorkarounds { @@ -28,26 +28,10 @@ object TapWorkarounds { return excludedBatch || excludedIssuerName } - fun Card.isNote(): Boolean { - return notesBatches.contains(batchId) - } + fun Card.isTangemNote(): Boolean = tangemNoteBatches.contains(batchId) + fun Card.isTangemWallet(): Boolean = tangemWalletBatches.contains(batchId) - fun Card.isMultiCurrencyWallet(): Boolean { - return multiCurrencyWalletsBatches.contains(batchId) - } - - val Card.noteCurrency: Blockchain? - get() { - return when (batchId) { - "AB01" -> Blockchain.Bitcoin - "AB02" -> Blockchain.Ethereum - "AB03" -> Blockchain.CardanoShelley - "AB04" -> Blockchain.Dogecoin - "AB05" -> Blockchain.Binance - "AB06" -> Blockchain.XRP - else -> null - } - } + fun Card.getTangemNoteBlockchain(): Blockchain? = tangemNoteBatches[batchId] private const val START_2_COIN_ISSUER = "start2coin" private const val TEST_CARD_BATCH = "99FF" @@ -64,21 +48,23 @@ object TapWorkarounds { "TTM BANK" ) - private val notesBatches = listOf( - "AB01", - "AB02", - "AB03", - "AB04", - "AB05", - "AB06", - ) + private val tangemWalletBatches = listOf("AC01") - private val multiCurrencyWalletsBatches = listOf("AC01") + private val tangemNoteBatches = mapOf( + "AB01" to Blockchain.Bitcoin, + "AB02" to Blockchain.Ethereum, + "AB03" to Blockchain.CardanoShelley, + "AB04" to Blockchain.Dogecoin, + "AB05" to Blockchain.Binance, + "AB06" to Blockchain.XRP, + ) } +val DELAY_SDK_DIALOG_CLOSE = 1400L + val Card.isMultiwalletAllowed: Boolean get() { - return !isTwinCard() && !isStart2Coin && !isNote() + return !isTangemTwin() && !isStart2Coin && !isTangemNote() && (firmwareVersion >= FirmwareVersion.MultiWalletAvailable || getSingleWallet()?.curve == EllipticCurve.Secp256k1) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/UrlBitmapLoader.kt b/app/src/main/java/com/tangem/tap/domain/UrlBitmapLoader.kt new file mode 100644 index 0000000000..c06d9b0001 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/UrlBitmapLoader.kt @@ -0,0 +1,49 @@ +package com.tangem.tap.domain + +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import android.os.Handler +import android.os.Looper +import com.squareup.picasso.Picasso +import com.squareup.picasso.Target +import com.tangem.common.services.Result + +/** +[REDACTED_AUTHOR] + */ +class UrlBitmapLoader { + private val mainHandler = Handler(Looper.getMainLooper()) + + fun loadBitmap(url: String, callback: (Result) -> Unit) { + val target = DownloadTarget(callback) + protectedFromGarbageCollectorTargets.add(target) + mainHandler.post { Picasso.get().load(url).into(target) } + } + + fun loadBitmap(url: String, target: DownloadTarget) { + protectedFromGarbageCollectorTargets.add(target) + Picasso.get().load(url).into(target) + } + +} + +private val protectedFromGarbageCollectorTargets = mutableListOf() + +// It adds the ability to trigger multiple downloads with a unique callback. +open class DownloadTarget( + val callback: (Result) -> Unit, +) : Target { + + override fun onBitmapLoaded(bitmap: Bitmap, from: Picasso.LoadedFrom) { + callback(Result.Success(bitmap)) + protectedFromGarbageCollectorTargets.remove(this) + } + + override fun onBitmapFailed(e: Exception?, errorDrawable: Drawable?) { + callback.invoke(Result.Failure(e ?: Exception("Unknown exception"))) + protectedFromGarbageCollectorTargets.remove(this) + } + + override fun onPrepareLoad(placeHolderDrawable: Drawable?) { + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/Blockchain.kt b/app/src/main/java/com/tangem/tap/domain/extensions/Blockchain.kt index 13946dd481..d658847574 100644 --- a/app/src/main/java/com/tangem/tap/domain/extensions/Blockchain.kt +++ b/app/src/main/java/com/tangem/tap/domain/extensions/Blockchain.kt @@ -16,7 +16,7 @@ fun Blockchain.isNoAccountError(exception: Throwable): Boolean { fun Blockchain.amountToCreateAccount(token: Token? = null): Double? { return when (this) { Blockchain.Stellar -> if (token?.symbol == NODL) 1.5 else 1.toDouble() - Blockchain.XRP -> 20.toDouble() + Blockchain.XRP -> 10.toDouble() else -> null } } diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt b/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt index be6eb20dcb..124ae27387 100644 --- a/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt +++ b/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt @@ -2,11 +2,24 @@ package com.tangem.tap.domain.extensions import com.tangem.common.card.Card import com.tangem.common.card.CardWallet +import com.tangem.common.extensions.toHexString +import com.tangem.common.services.Result +import com.tangem.operations.attestation.CardVerifyAndGetInfo +import com.tangem.operations.attestation.OnlineCardVerifier +import com.tangem.tap.domain.twins.TwinCardNumber +import com.tangem.tap.domain.twins.getTwinCardNumber +import com.tangem.tap.features.wallet.redux.Artwork fun Card.getSingleWallet(): CardWallet? { return wallets.firstOrNull() } +fun Card.hasWallets(): Boolean = wallets.isNotEmpty() + +fun Card.hasNoWallets(): Boolean = wallets.isEmpty() + +fun Card.hasSingleWallet(): Boolean = wallets.size == 1 + fun Card.hasSignedHashes(): Boolean { return wallets.any { it.totalSignedHashes ?: 0 > 0 } } @@ -15,6 +28,46 @@ fun Card.signedHashesCount(): Int { return wallets.map { it.totalSignedHashes ?: 0 }.sum() } +suspend fun Card.getOrLoadCardArtworkUrl(cardInfo: Result? = null): String { + fun ifAnyError(): String { + return when { + cardId.startsWith(Artwork.SERGIO_CARD_ID) -> Artwork.SERGIO_CARD_URL + cardId.startsWith(Artwork.MARTA_CARD_ID) -> Artwork.MARTA_CARD_URL + else -> { + when (getTwinCardNumber()) { + TwinCardNumber.First -> Artwork.TWIN_CARD_1 + TwinCardNumber.Second -> Artwork.TWIN_CARD_2 + else -> Artwork.DEFAULT_IMG_URL + } + } + } + } + + val cardInfoResult = cardInfo ?: OnlineCardVerifier().getCardInfo(cardId, cardPublicKey) + return when (cardInfoResult) { + is Result.Success -> { + val artworkId = cardInfoResult.data.artwork?.id + if (artworkId == null || artworkId.isEmpty()) { + ifAnyError() + } else { + OnlineCardVerifier.getUrlForArtwork(cardId, cardPublicKey.toHexString(), artworkId) + } + } + is Result.Failure -> ifAnyError() + } +} + +fun Card.getArtworkUrl(artworkId: String?): String? { + return when { + artworkId != null -> { + OnlineCardVerifier.getUrlForArtwork(cardId, cardPublicKey.toHexString(), artworkId) + } + cardId.startsWith(Artwork.SERGIO_CARD_ID) -> Artwork.SERGIO_CARD_URL + cardId.startsWith(Artwork.MARTA_CARD_ID) -> Artwork.MARTA_CARD_URL + else -> null + } +} + val Card.remainingSignatures: Int? get() = this.getSingleWallet()?.remainingSignatures diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt b/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt index 5ec3f1afa9..99ed8231c4 100644 --- a/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt +++ b/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt @@ -8,8 +8,8 @@ import com.tangem.common.card.CardWallet import com.tangem.common.card.EllipticCurve import com.tangem.common.extensions.hexToBytes import com.tangem.tap.domain.TapWorkarounds.isTestCard -import com.tangem.tap.domain.tasks.ScanNoteResponse -import com.tangem.tap.domain.twins.isTwinCard +import com.tangem.tap.domain.tasks.product.ScanResponse +import com.tangem.tap.domain.twins.isTangemTwin fun WalletManagerFactory.makeWalletManagerForApp( card: Card, @@ -50,20 +50,22 @@ fun WalletManagerFactory.makeWalletManagersForApp( } fun WalletManagerFactory.makePrimaryWalletManager( - data: ScanNoteResponse, + data: ScanResponse, ): WalletManager? { val card = data.card val blockchain = if (card.isTestCard) { - data.getBlockchain()?.getTestnetVersion() + data.getBlockchain().getTestnetVersion() } else { data.getBlockchain() } val supportedCurves = blockchain?.getSupportedCurves() ?: return null + val wallets = card.wallets.filter { wallet -> supportedCurves.contains(wallet.curve) } val wallet = selectWallet(wallets) val publicKey = wallet?.publicKey ?: return null - val curveToUse = wallet.curve ?: return null - return if (card.isTwinCard() && data.secondTwinPublicKey != null) { + val curveToUse = wallet.curve + + return if (card.isTangemTwin() && data.secondTwinPublicKey != null) { makeMultisigWalletManager( cardId = card.cardId, walletPublicKey = publicKey, pairPublicKey = data.secondTwinPublicKey.hexToBytes(), diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt index c75a37a709..1487f07e37 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletAndRescanTask.kt @@ -11,6 +11,7 @@ import com.tangem.operations.PreflightReadMode import com.tangem.operations.PreflightReadTask import com.tangem.operations.wallet.CreateWalletCommand +@Deprecated("Use CreateProductWalletAndRescanTask instead") class CreateWalletAndRescanTask : CardSessionRunnable { override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt index f1afec8e00..6de34f651a 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/CreateWalletsTask.kt @@ -9,19 +9,16 @@ import com.tangem.operations.PreflightReadMode import com.tangem.operations.PreflightReadTask import com.tangem.operations.wallet.CreateWalletCommand -class CreateWalletsTask(curves: List = emptyList()) : CardSessionRunnable { +@Deprecated("Use CreateProductWalletsResponse instead") +class CreateWalletsTask(curves: List? = null) : CardSessionRunnable { - private val curves = if (curves.isEmpty()) { - listOf( - EllipticCurve.Secp256k1, - EllipticCurve.Ed25519, - EllipticCurve.Secp256r1, - ) - } else { - curves - } + private val curves = curves ?: listOf( + EllipticCurve.Secp256k1, + EllipticCurve.Ed25519, + EllipticCurve.Secp256r1, + ) - var index = 0 + private var index = 0 override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { val curve = curves[index] @@ -29,8 +26,8 @@ class CreateWalletsTask(curves: List = emptyList()) : CardSession } private fun createWallet( - curve: EllipticCurve, session: CardSession, - callback: (result: CompletionResult) -> Unit + curve: EllipticCurve, session: CardSession, + callback: (result: CompletionResult) -> Unit ) { CreateWalletCommand(curve).run(session) { result -> @@ -49,12 +46,4 @@ class CreateWalletsTask(curves: List = emptyList()) : CardSession } } } -} - -private fun EllipticCurve.toWalletIndex(): Int { - return when (this) { - EllipticCurve.Secp256k1 -> 0 - EllipticCurve.Ed25519 -> 1 - EllipticCurve.Secp256r1 -> 2 - } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt index 784a2bc667..43425eab48 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/ScanNoteTask.kt @@ -1,60 +1,32 @@ package com.tangem.tap.domain.tasks import com.tangem.* -import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.BlockchainSdkConfig -import com.tangem.blockchain.common.Token import com.tangem.blockchain.common.WalletManagerFactory import com.tangem.common.CompletionResult import com.tangem.common.card.Card -import com.tangem.common.card.EllipticCurve -import com.tangem.common.card.WalletData +import com.tangem.common.card.FirmwareVersion import com.tangem.common.core.CardSession import com.tangem.common.core.CardSessionRunnable import com.tangem.common.core.TangemError import com.tangem.common.extensions.toHexString -import com.tangem.operations.CommandResponse import com.tangem.operations.ScanTask import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand +import com.tangem.tap.domain.ProductType import com.tangem.tap.domain.TapSdkError import com.tangem.tap.domain.TapWorkarounds.isExcluded -import com.tangem.tap.domain.TapWorkarounds.isMultiCurrencyWallet -import com.tangem.tap.domain.TapWorkarounds.noteCurrency -import com.tangem.tap.domain.TapWorkarounds.isNote +import com.tangem.tap.domain.TapWorkarounds.isTangemNote import com.tangem.tap.domain.extensions.getSingleWallet +import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.domain.twins.TwinCardsManager -import com.tangem.tap.domain.twins.isTwinCard +import com.tangem.tap.domain.twins.isTangemTwin import com.tangem.tap.store -import com.tangem.wallet.R -data class ScanNoteResponse( - val card: Card, - val walletData: WalletData?, - val secondTwinPublicKey: String? = null, -) : CommandResponse { - fun getBlockchain(): Blockchain? { - if (card.noteCurrency != null) return card.noteCurrency - val blockchainName: String = walletData?.blockchain ?: return null - return Blockchain.fromId(blockchainName) - } - - fun getPrimaryToken(): Token? { - val cardToken = walletData?.token ?: return null - return Token( - cardToken.name, - cardToken.symbol, - cardToken.contractAddress, - cardToken.decimals, - Blockchain.fromId(walletData.blockchain) - ) - } -} - -class ScanNoteTask(val card: Card? = null) : CardSessionRunnable { +class ScanNoteTask(val card: Card? = null) : CardSessionRunnable { override fun run( session: CardSession, - callback: (result: CompletionResult) -> Unit, + callback: (result: CompletionResult) -> Unit, ) { ScanTask().run(session) { result -> when (result) { @@ -69,13 +41,13 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable= 4) { + } else if (!card.isTangemNote() && card.firmwareVersion >= FirmwareVersion.MultiWalletAvailable) { createMissingWalletsIfNeeded(card, session, callback) } else { callback(CompletionResult.Success( - ScanNoteResponse(card, session.environment.walletData))) + ScanResponse(card, ProductType.Other, session.environment.walletData))) } } } @@ -84,28 +56,27 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable) -> Unit, + callback: (result: CompletionResult) -> Unit, ) { val walletData = session.environment.walletData if (card.wallets.isEmpty()) { - callback(CompletionResult.Success( - ScanNoteResponse(card, walletData) - )) + callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, walletData))) return } val curvesPresent = card.wallets.map { it.curve } - val curvesToCreate = EllipticCurve.values().subtract(curvesPresent) +// val curvesToCreate = EllipticCurve.values().subtract(curvesPresent) + val curvesToCreate = card.supportedCurves.subtract(curvesPresent) if (curvesToCreate.isEmpty()) { - callback(CompletionResult.Success(ScanNoteResponse(card, walletData))) + callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, walletData))) return } CreateWalletsTask(curvesToCreate.toList()).run(session) { result -> when (result) { is CompletionResult.Success -> - callback(CompletionResult.Success(ScanNoteResponse(result.data, walletData))) + callback(CompletionResult.Success(ScanResponse(result.data, ProductType.Other, walletData))) is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) } } @@ -114,34 +85,35 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable) -> Unit, + callback: (result: CompletionResult) -> Unit, ) { ReadIssuerDataCommand().run(session) { readDataResult -> when (readDataResult) { is CompletionResult.Success -> { val publicKey = card.getSingleWallet()?.publicKey if (publicKey == null) { - callback(CompletionResult.Success(ScanNoteResponse(card, null))) + callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, null))) return@run } val verified = TwinCardsManager.verifyTwinPublicKey( - readDataResult.data.issuerData, publicKey + readDataResult.data.issuerData, publicKey ) if (verified) { val twinPublicKey = readDataResult.data.issuerData.sliceArray(0 until 65) callback(CompletionResult.Success( - ScanNoteResponse( - card = card, - walletData = session.environment.walletData, - secondTwinPublicKey = twinPublicKey.toHexString()) + ScanResponse( + card = card, + ProductType.Other, + walletData = session.environment.walletData, + secondTwinPublicKey = twinPublicKey.toHexString()) )) return@run } else { - callback(CompletionResult.Success(ScanNoteResponse(card, null))) + callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, null))) } } is CompletionResult.Failure -> - callback(CompletionResult.Success(ScanNoteResponse(card, null))) + callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, null))) } } } @@ -149,7 +121,8 @@ class ScanNoteTask(val card: Card? = null) : CardSessionRunnable { + + override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { + val card = session.environment.card.guard { + callback(CompletionResult.Failure(TangemSdkError.CardError())) + return + } + CreateProductWalletTask(card, type).run(session) { result -> + when (result) { + is CompletionResult.Success -> PreflightReadTask(PreflightReadMode.FullCardRead).run(session, callback) + is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) + } + } + } +} + +private class CreateProductWalletTask( + private val card: Card, + private val type: ProductType, +) : CardSessionRunnable { + + override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { + val commandProcessor = when (type) { + ProductType.Note -> CreateWalletTangemNote() + ProductType.Twin -> throw UnsupportedOperationException("Use the TwinCardsManager to create a wallet") + ProductType.Wallet -> CreateWalletTangemWallet() + else -> CreateWalletOtherCards() + } + commandProcessor.proceed(card, session, callback) + } +} + +private class CreateWalletTangemNote : ProductCommandProcessor { + override fun proceed( + card: Card, + session: CardSession, + callback: (result: CompletionResult) -> Unit + ) { + if (card.supportedCurves.isEmpty()) { + callback(CompletionResult.Failure(TangemSdkError.CardError())) + return + } + + val curvesSupportedByBlockchain = card.getTangemNoteBlockchain()?.getSupportedCurves() + if (curvesSupportedByBlockchain == null || curvesSupportedByBlockchain.isEmpty()) { + callback(CompletionResult.Failure(TangemSdkError.CardError())) + return + } + + val intersectCurves = card.supportedCurves.intersect(curvesSupportedByBlockchain).toList() + if (intersectCurves.isEmpty()) { + callback(CompletionResult.Failure(TangemSdkError.CardError())) + } else { + val curve = if (intersectCurves.contains(EllipticCurve.Secp256k1)) { + EllipticCurve.Secp256k1 + } else { + intersectCurves[0] + } + CreateWalletCommand(curve).run(session, callback) + + } + } +} + +private class CreateWalletTangemWallet : ProductCommandProcessor { + override fun proceed( + card: Card, + session: CardSession, + callback: (result: CompletionResult) -> Unit + ) { + //TODO: + } +} + +private class CreateWalletOtherCards : ProductCommandProcessor { + + override fun proceed( + card: Card, + session: CardSession, + callback: (result: CompletionResult) -> Unit + ) { + val firmwareVersion = card.firmwareVersion + val task = if (firmwareVersion < FirmwareVersion.MultiWalletAvailable) { + CreateProductWalletsTask(listOf(card.supportedCurves.first())) + } else { + CreateProductWalletsTask(card.getCurvesForNonCreatedWallets()) + } + + task.run(session) { result -> + when (result) { + is CompletionResult.Success -> callback(CompletionResult.Success(result.data.createWalletResponses[0])) + is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletsTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletsTask.kt new file mode 100644 index 0000000000..fc00138a57 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletsTask.kt @@ -0,0 +1,49 @@ +package com.tangem.tap.domain.tasks.product + +import com.tangem.common.CompletionResult +import com.tangem.common.card.EllipticCurve +import com.tangem.common.core.CardSession +import com.tangem.common.core.CardSessionRunnable +import com.tangem.operations.CommandResponse +import com.tangem.operations.wallet.CreateWalletCommand +import com.tangem.operations.wallet.CreateWalletResponse + +/** +[REDACTED_AUTHOR] + */ +class CreateProductWalletsResponse( + val createWalletResponses: List +) : CommandResponse + +class CreateProductWalletsTask( + private val curves: List, +) : CardSessionRunnable { + + private val createdWalletsResponses = mutableListOf() + + override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { + val curve = curves[createdWalletsResponses.size] + createWallet(curve, session, callback) + } + + private fun createWallet( + curve: EllipticCurve, + session: CardSession, + callback: (result: CompletionResult) -> Unit + ) { + + CreateWalletCommand(curve).run(session) { result -> + when (result) { + is CompletionResult.Success -> { + createdWalletsResponses.add(result.data) + if (createdWalletsResponses.size == curves.size) { + callback(CompletionResult.Success(CreateProductWalletsResponse(createdWalletsResponses))) + return@run + } + createWallet(curves[createdWalletsResponses.size], session, callback) + } + is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/ProductCommandProcessor.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/ProductCommandProcessor.kt new file mode 100644 index 0000000000..c0810ff887 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/ProductCommandProcessor.kt @@ -0,0 +1,16 @@ +package com.tangem.tap.domain.tasks.product + +import com.tangem.common.CompletionResult +import com.tangem.common.card.Card +import com.tangem.common.core.CardSession + +/** +[REDACTED_AUTHOR] + */ +interface ProductCommandProcessor { + fun proceed( + card: Card, + session: CardSession, + callback: (result: CompletionResult) -> Unit, + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt new file mode 100644 index 0000000000..cdb63d456b --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt @@ -0,0 +1,183 @@ +package com.tangem.tap.domain.tasks.product + +import com.tangem.* +import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.common.Token +import com.tangem.common.CompletionResult +import com.tangem.common.card.Card +import com.tangem.common.card.EllipticCurve +import com.tangem.common.card.FirmwareVersion +import com.tangem.common.card.WalletData +import com.tangem.common.core.CardSession +import com.tangem.common.core.CardSessionRunnable +import com.tangem.common.core.TangemError +import com.tangem.common.extensions.toHexString +import com.tangem.operations.CommandResponse +import com.tangem.operations.PreflightReadMode +import com.tangem.operations.PreflightReadTask +import com.tangem.operations.ScanTask +import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand +import com.tangem.tap.domain.ProductType +import com.tangem.tap.domain.TapSdkError +import com.tangem.tap.domain.TapWorkarounds.getTangemNoteBlockchain +import com.tangem.tap.domain.TapWorkarounds.isExcluded +import com.tangem.tap.domain.TapWorkarounds.isTangemNote +import com.tangem.tap.domain.TapWorkarounds.isTangemWallet +import com.tangem.tap.domain.extensions.getSingleWallet +import com.tangem.tap.domain.twins.TwinCardsManager +import com.tangem.tap.domain.twins.isTangemTwin + +data class ScanResponse( + val card: Card, + val productType: ProductType, + val walletData: WalletData?, + val secondTwinPublicKey: String? = null, +) : CommandResponse { + + fun getBlockchain(): Blockchain { + if (productType == ProductType.Note) return card.getTangemNoteBlockchain() ?: return Blockchain.Unknown + val blockchainName: String = walletData?.blockchain ?: return Blockchain.Unknown + return Blockchain.fromId(blockchainName) + } + + fun getPrimaryToken(): Token? { + val cardToken = walletData?.token ?: return null + return Token( + cardToken.name, + cardToken.symbol, + cardToken.contractAddress, + cardToken.decimals, + Blockchain.fromId(walletData.blockchain) + ) + } +} + +class ScanProductTask(val card: Card? = null) : CardSessionRunnable { + + override fun run( + session: CardSession, + callback: (result: CompletionResult) -> Unit, + ) { + ScanTask().run(session) { result -> + when (result) { + is CompletionResult.Success -> { + val card = this.card ?: result.data + + val error = getErrorIfExcludedCard(card) + if (error != null) { + callback(CompletionResult.Failure(error)) + return@run + } + + val commandProcessor = when { + card.isTangemNote() -> ScanNoteProcessor() + card.isTangemTwin() -> ScanTwinProcessor() + card.isTangemWallet() -> ScanWalletProcessor() + else -> ScanOtherCardsProcessor() + } + commandProcessor.proceed(card, session, callback) + } + is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) + } + } + } + + private fun getErrorIfExcludedCard(card: Card): TangemError? { + if (card.isExcluded()) return TapSdkError.CardForDifferentApp + + return null + } +} + +private class ScanNoteProcessor : ProductCommandProcessor { + override fun proceed( + card: Card, + session: CardSession, + callback: (result: CompletionResult) -> Unit + ) { + callback(CompletionResult.Success(ScanResponse(card, ProductType.Note, session.environment.walletData))) + } +} + +private class ScanWalletProcessor : ProductCommandProcessor { + override fun proceed( + card: Card, + session: CardSession, + callback: (result: CompletionResult) -> Unit + ) { + callback(CompletionResult.Success(ScanResponse(card, ProductType.Wallet, session.environment.walletData))) + } +} + +private class ScanTwinProcessor : ProductCommandProcessor { + override fun proceed( + card: Card, + session: CardSession, + callback: (result: CompletionResult) -> Unit, + ) { + ReadIssuerDataCommand().run(session) { readDataResult -> + when (readDataResult) { + is CompletionResult.Success -> { + val publicKey = card.getSingleWallet()?.publicKey + if (publicKey == null) { + callback(CompletionResult.Success(ScanResponse(card, ProductType.Twin, null))) + return@run + } + val verified = TwinCardsManager.verifyTwinPublicKey(readDataResult.data.issuerData, publicKey) + if (verified) { + val twinPublicKey = readDataResult.data.issuerData.sliceArray(0 until 65) + val walletData = session.environment.walletData + val response = ScanResponse(card, ProductType.Twin, walletData, twinPublicKey.toHexString()) + callback(CompletionResult.Success(response)) + } else { + callback(CompletionResult.Success(ScanResponse(card, ProductType.Twin, null))) + } + } + is CompletionResult.Failure -> + callback(CompletionResult.Success(ScanResponse(card, ProductType.Twin, null))) + } + } + } + +} + +private class ScanOtherCardsProcessor : ProductCommandProcessor { + + override fun proceed(card: Card, session: CardSession, callback: (result: CompletionResult) -> Unit) { + val walletData = session.environment.walletData + + if (card.wallets.isEmpty() || card.firmwareVersion < FirmwareVersion.MultiWalletAvailable) { + callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, walletData))) + return + } + + val curvesToCreate = card.getCurvesForNonCreatedWallets() + if (curvesToCreate.isEmpty()) { + callback(CompletionResult.Success(ScanResponse(card, ProductType.Other, walletData))) + return + } + + CreateProductWalletsTask(curvesToCreate).run(session) { result -> + when (result) { + is CompletionResult.Success -> { + PreflightReadTask(PreflightReadMode.FullCardRead, card.cardId).run(session) { readResult -> + when (readResult) { + is CompletionResult.Success -> { + val response = ScanResponse(readResult.data, ProductType.Other, walletData) + callback(CompletionResult.Success(response)) + } + is CompletionResult.Failure -> callback(CompletionResult.Failure(readResult.error)) + } + } + } + is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) + } + } + } +} + +fun Card.getCurvesForNonCreatedWallets(): List { + val curvesPresent = wallets.map { it.curve } + val curvesForNonCreatedWallets = supportedCurves.subtract(curvesPresent) + return curvesForNonCreatedWallets.toList() +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt index aeef1b9481..c035973ac7 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/FinalizeTwinTask.kt @@ -6,16 +6,16 @@ import com.tangem.common.core.CardSession import com.tangem.common.core.CardSessionRunnable import com.tangem.operations.PreflightReadMode import com.tangem.operations.PreflightReadTask -import com.tangem.tap.domain.tasks.ScanNoteResponse -import com.tangem.tap.domain.tasks.ScanNoteTask +import com.tangem.tap.domain.tasks.product.ScanProductTask +import com.tangem.tap.domain.tasks.product.ScanResponse class FinalizeTwinTask( private val twinPublicKey: ByteArray, private val issuerKeys: KeyPair, -) : CardSessionRunnable { +) : CardSessionRunnable { override fun run( session: CardSession, - callback: (result: CompletionResult) -> Unit, + callback: (result: CompletionResult) -> Unit, ) { WriteProtectedIssuerDataTask(twinPublicKey, issuerKeys).run(session) { result -> when (result) { @@ -23,7 +23,7 @@ class FinalizeTwinTask( PreflightReadTask(PreflightReadMode.FullCardRead).run(session) { readResult -> when (readResult) { is CompletionResult.Success -> - ScanNoteTask(readResult.data).run(session, callback) + ScanProductTask(readResult.data).run(session, callback) is CompletionResult.Failure -> callback(CompletionResult.Failure(readResult.error)) } diff --git a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt index ccca28d77a..faa839cbe6 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt @@ -14,19 +14,19 @@ import com.tangem.common.extensions.toHexString import com.tangem.crypto.CryptoUtils import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler import com.tangem.tap.common.extensions.readAssetAsString -import com.tangem.tap.domain.tasks.ScanNoteResponse +import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.network.createMoshi import com.tangem.tap.tangemSdkManager -class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse, context: Context) { +class TwinCardsManager(private val scanResponse: ScanResponse, context: Context) { - private val currentCardId: String = scanNoteResponse.card.cardId + private val currentCardId: String = scanResponse.card.cardId private var currentCardPublicKey: String? = null private var secondCardPublicKey: String? = null private val issuerKeyPair: KeyPair = getIssuerKeys( - context, scanNoteResponse.card.issuer.publicKey.toHexString() + context, scanResponse.card.issuer.publicKey.toHexString() ) suspend fun createFirstWallet(message: Message): SimpleResult { @@ -43,7 +43,7 @@ class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse, context: FirebaseAnalyticsHandler.logCardSdkError( error, FirebaseAnalyticsHandler.ActionToLog.CreateWallet, - card = scanNoteResponse.card + card = scanResponse.card ) } return SimpleResult.failure(response.error) @@ -65,9 +65,7 @@ class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse, context: preparingMessage = preparingMessage, creatingWalletMessage = creatingWalletMessage ) - val response = tangemSdkManager.runTaskAsync( - task, null, initialMessage - ) + val response = tangemSdkManager.runTaskAsync(task, null, initialMessage) when (response) { is CompletionResult.Success -> { secondCardPublicKey = response.data.wallet.publicKey.toHexString() @@ -78,7 +76,7 @@ class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse, context: FirebaseAnalyticsHandler.logCardSdkError( error, FirebaseAnalyticsHandler.ActionToLog.CreateWallet, - card = scanNoteResponse.card + card = scanResponse.card ) } return SimpleResult.failure(response.error) @@ -87,7 +85,7 @@ class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse, context: } - suspend fun complete(message: Message): Result { + suspend fun complete(message: Message): Result { val response = tangemSdkManager.runTaskAsync( FinalizeTwinTask(secondCardPublicKey!!.hexToBytes(), issuerKeyPair), currentCardId, message @@ -99,7 +97,7 @@ class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse, context: FirebaseAnalyticsHandler.logCardSdkError( error, FirebaseAnalyticsHandler.ActionToLog.WriteIssuerData, - card = scanNoteResponse.card + card = scanResponse.card ) } Result.failure(response.error) @@ -109,11 +107,11 @@ class TwinCardsManager(private val scanNoteResponse: ScanNoteResponse, context: companion object { fun verifyTwinPublicKey(issuerData: ByteArray, cardWalletPublicKey: ByteArray?): Boolean { - if (issuerData.size < 65) return false + if (issuerData.size < 65 || cardWalletPublicKey == null) return false + val publicKey = issuerData.sliceArray(0 until 65) val signedKey = issuerData.sliceArray(65 until issuerData.size) - return (cardWalletPublicKey != null && - CryptoUtils.verify(cardWalletPublicKey, publicKey, signedKey)) + return CryptoUtils.verify(cardWalletPublicKey, publicKey, signedKey) } private fun getIssuerKeys(context: Context, publicKey: String): KeyPair { diff --git a/app/src/main/java/com/tangem/tap/domain/twins/TwinsCardWidget.kt b/app/src/main/java/com/tangem/tap/domain/twins/TwinsCardWidget.kt new file mode 100644 index 0000000000..63f891eb4d --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/twins/TwinsCardWidget.kt @@ -0,0 +1,152 @@ +package com.tangem.tap.domain.twins + +import android.animation.AnimatorSet +import android.animation.ObjectAnimator +import android.animation.PropertyValuesHolder +import android.view.View +import androidx.core.animation.doOnEnd +import com.tangem.tap.common.leapfrogWidget.LeapView +import com.tangem.tap.common.leapfrogWidget.LeapViewState +import com.tangem.tap.common.leapfrogWidget.LeapfrogWidget + +/** +[REDACTED_AUTHOR] + */ +class TwinsCardWidget( + val leapfrogWidget: LeapfrogWidget, + val getTopOfAnchorViewForActivateState: () -> Float +) { + + init { + if (leapfrogWidget.getViewsCount() != 2) throw UnsupportedOperationException() + } + + fun toWelcome(animate: Boolean = true, onEnd: () -> Unit = {}) { + val animator = createAnimator(animate, onEnd) + animator.playTogether( + createAnimator(TwinCardNumber.First, createWelcomeProperties(TwinCardNumber.First)), + createAnimator(TwinCardNumber.Second, createWelcomeProperties(TwinCardNumber.Second)) + ) + leapfrogWidget.fold { animator.start() } + } + + fun toLeapfrog(animate: Boolean = true, onEnd: () -> Unit = {}) { + val animator = createAnimator(animate, onEnd) + animator.playTogether( + createAnimator(TwinCardNumber.First, createLeapfrogProperties(TwinCardNumber.First)), + createAnimator(TwinCardNumber.Second, createLeapfrogProperties(TwinCardNumber.Second)) + ) + leapfrogWidget.fold { + animator.doOnEnd { + leapfrogWidget.initViews() + leapfrogWidget.unfold() + } + animator.start() + } + } + + fun toActivate(animate: Boolean = true, onEnd: () -> Unit = {}) { + val animator = createAnimator(animate, onEnd) + animator.playTogether( + createAnimator(TwinCardNumber.First, createActivateProperties(TwinCardNumber.First)), + createAnimator(TwinCardNumber.Second, createActivateProperties(TwinCardNumber.Second)) + ) + animator.start() + } + + private fun createAnimator(animate: Boolean, onEnd: () -> Unit): AnimatorSet { + return AnimatorSet().apply { + duration = if (animate) 400 else 0 + doOnEnd { onEnd() } + } + } + + private fun createAnimator(cardNumber: TwinCardNumber, properties: TwinsCardProperties): ObjectAnimator { + val view = getLeapViewByCardNumber(cardNumber).view + val animator = ObjectAnimator.ofPropertyValuesHolder(view, *properties.createValuesHolders().toTypedArray()) + view.elevation = properties.elevation + return animator + } + + private fun createWelcomeProperties(cardNumber: TwinCardNumber): TwinsCardProperties { + return when (cardNumber) { + TwinCardNumber.First -> { + TwinsCardProperties( + xTranslation = -180f, + yTranslation = -350f, + rotation = -3f, + elevation = 1f, + scale = 1.1f, + ) + } + TwinCardNumber.Second -> { + TwinsCardProperties( + xTranslation = 180f, + yTranslation = 10f, + rotation = -3f, + elevation = 0f, + scale = 1.1f, + ) + } + } + } + + private fun createLeapfrogProperties(cardNumber: TwinCardNumber): TwinsCardProperties { + val twinProperties = TwinsCardProperties.from(getLeapViewByCardNumber(cardNumber).state) + return twinProperties.copy( + xTranslation = 0f, + yTranslation = 0f, + rotation = 0f, + scale = 1f, + ) + } + + private fun createActivateProperties(cardNumber: TwinCardNumber): TwinsCardProperties { + val topOfAnchorView = getTopOfAnchorViewForActivateState() + val twinProperties = TwinsCardProperties.from(getLeapViewByCardNumber(cardNumber).state) + return twinProperties.copy( + xTranslation = twinProperties.xTranslation, + yTranslation = twinProperties.yTranslation - topOfAnchorView, + rotation = 0f, + scale = twinProperties.scale - 0.4f, + ) + } + + private fun getLeapViewByCardNumber(cardNumber: TwinCardNumber): LeapView { + return when (cardNumber) { + TwinCardNumber.First -> leapfrogWidget.getViewByPosition(0) + TwinCardNumber.Second -> leapfrogWidget.getViewByPosition(1) + } + } +} + +private data class TwinsCardProperties( + val xTranslation: Float = 0f, + val yTranslation: Float = 0f, + val rotation: Float = 0f, + val elevation: Float = 0f, + val scale: Float = 1.1f, +) { + + fun createValuesHolders(): List { + return listOf( + PropertyValuesHolder.ofFloat(View.TRANSLATION_X, xTranslation), + PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, yTranslation), + PropertyValuesHolder.ofFloat(View.ROTATION, rotation), + PropertyValuesHolder.ofFloat(View.SCALE_X, scale), + PropertyValuesHolder.ofFloat(View.SCALE_Y, scale), + ) + } + + companion object { + fun from(leapViewState: LeapViewState): TwinsCardProperties { + val leapViewProperties = leapViewState.properties + + return TwinsCardProperties( + yTranslation = leapViewProperties.yTranslation, + elevation = leapViewProperties.elevationEnd, + scale = leapViewProperties.scale, + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt b/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt index 941c8f46bb..ebcbb5ff2b 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/TwinsHelper.kt @@ -1,8 +1,6 @@ package com.tangem.tap.domain.twins import com.tangem.common.card.Card -import com.tangem.tap.common.extensions.isEven -import com.tangem.tap.domain.twins.TwinsHelper.Companion.getTwinCardNumber class TwinsHelper { companion object { @@ -35,22 +33,6 @@ class TwinsHelper { } } - fun getTwinsCardId(cardId: String): String? { - val cardIdWithNewSeries = when (getTwinCardNumber(cardId) ?: return null) { - TwinCardNumber.First -> { - val index = if (cardId.startsWith(firstCardSeries[0])) 0 else 1 - cardId.replace(firstCardSeries[index], secondCardSeries[index]) - } - TwinCardNumber.Second -> { - val index = if (cardId.startsWith(secondCardSeries[0])) 0 else 1 - cardId.replace(secondCardSeries[index], firstCardSeries[index]) - } - } - val cardIdWithoutChecksum = cardIdWithNewSeries.dropLast(1) - val checkSum = cardIdWithoutChecksum.calculateLuhn() - return cardIdWithoutChecksum + checkSum - } - fun getTwinCardIdForUser(cardId: String): String { if (cardId.length < 16) return cardId @@ -64,21 +46,6 @@ class TwinsHelper { } } -private fun String.calculateLuhn(): Int { - val checksum = this.reversed() - .mapIndexed { index, c -> - val digit = if (c in '0'..'9') c - '0' else c - 'A' - if (!index.isEven()) { - digit - } else { - val newDigit = digit * 2 - if (newDigit >= 10) newDigit - 9 else newDigit - } - }.sum() - .rem(10) - return (10 - checksum) % 10 -} - enum class TwinCardNumber(val number: Int) { First(1), Second(2); @@ -88,8 +55,12 @@ enum class TwinCardNumber(val number: Int) { } } -fun Card.isTwinCard(): Boolean { - return getTwinCardNumber(cardId) != null +fun Card.isTangemTwin(): Boolean { + return TwinsHelper.getTwinCardNumber(cardId) != null +} + +fun Card.getTwinCardNumber(): TwinCardNumber? { + return TwinsHelper.getTwinCardNumber(this.cardId) } fun Card.getTwinCardIdForUser(): String { diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt index 4d3882a2a7..74b37f90f4 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt @@ -7,7 +7,7 @@ import com.tangem.common.card.Card import com.tangem.operations.pins.CheckUserCodesResponse import com.tangem.tap.common.redux.NotificationAction import com.tangem.tap.common.redux.global.FiatCurrencyName -import com.tangem.tap.domain.tasks.ScanNoteResponse +import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.domain.termsOfUse.CardTou import com.tangem.tap.domain.twins.TwinCardNumber import com.tangem.tap.features.details.redux.twins.CreateTwinWallet @@ -18,13 +18,13 @@ import org.rekotlin.Action sealed class DetailsAction : Action { data class PrepareScreen( - val card: Card, - val scanNoteResponse: ScanNoteResponse, - val wallets: List, - val isCreatingTwinWalletAllowed: Boolean?, - val cardTou: CardTou, - val fiatCurrencyName: FiatCurrencyName, - val fiatCurrencies: List? = null, + val card: Card, + val scanResponse: ScanResponse, + val wallets: List, + val isCreatingTwinWalletAllowed: Boolean?, + val cardTou: CardTou, + val fiatCurrencyName: FiatCurrencyName, + val fiatCurrencies: List? = null, ) : DetailsAction() object ShowDisclaimer : DetailsAction() @@ -81,7 +81,7 @@ sealed class DetailsAction : Action { } data class LaunchThirdStep(val message: Message) : CreateTwinWalletAction() { - data class Success(val scanNoteResponse: ScanNoteResponse) : CreateTwinWalletAction() + data class Success(val scanResponse: ScanResponse) : CreateTwinWalletAction() object Failure : CreateTwinWalletAction() } } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt index 91f7a75152..d2bb330e40 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt @@ -7,11 +7,11 @@ import com.tangem.tap.domain.TapWorkarounds.isStart2Coin import com.tangem.tap.domain.extensions.isWalletDataSupported import com.tangem.tap.domain.extensions.signedHashesCount import com.tangem.tap.domain.extensions.toSendableAmounts -import com.tangem.tap.domain.twins.TwinsHelper -import com.tangem.tap.domain.twins.isTwinCard +import com.tangem.tap.domain.twins.getTwinCardNumber +import com.tangem.tap.domain.twins.isTangemTwin import com.tangem.tap.features.details.redux.twins.CreateTwinWalletReducer import com.tangem.tap.features.details.redux.twins.CreateTwinWalletState -import com.tangem.tap.features.wallet.models.toPendingTransactions +import com.tangem.tap.features.wallet.models.hasPendingTransactions import org.rekotlin.Action import java.util.* @@ -46,10 +46,10 @@ private fun internalReduce(action: Action, state: AppState): DetailsState { } private fun handlePrepareScreen(action: DetailsAction.PrepareScreen, state: DetailsState): DetailsState { - val twinsState = if (action.card.isTwinCard()) { + val twinsState = if (action.card.isTangemTwin()) { CreateTwinWalletState( - scanResponse = action.scanNoteResponse, - twinCardNumber = TwinsHelper.getTwinCardNumber(action.card.cardId), + scanResponse = action.scanResponse, + twinCardNumber = action.card.getTwinCardNumber(), createTwinWallet = null, showAlert = false, allowRecreatingWallet = action.isCreatingTwinWalletAllowed @@ -72,12 +72,10 @@ private fun handlePrepareScreen(action: DetailsAction.PrepareScreen, state: Deta private fun handleEraseWallet(action: DetailsAction.EraseWallet, state: DetailsState): DetailsState { return when (action) { DetailsAction.EraseWallet.Check -> { - val notAllowedByCard = - state.card?.settings?.isPermanentWallet == true - || state.card?.isWalletDataSupported == true + val notAllowedByAnyWallet = state.card?.wallets?.any { it.settings.isPermanent } ?: false + val notAllowedByCard = notAllowedByAnyWallet || state.card?.isWalletDataSupported == true val notEmpty = state.wallets.any { - !it.recentTransactions.toPendingTransactions(it.address).isNullOrEmpty() - || it.amounts.toSendableAmounts().isNotEmpty() + it.hasPendingTransactions() || it.amounts.toSendableAmounts().isNotEmpty() } val eraseWalletState = when { notAllowedByCard -> EraseWalletState.NotAllowedByCard @@ -191,10 +189,11 @@ private fun handleSecurityAction( private fun prepareAllowedSecurityOptions( card: Card?, currentSecurityOption: SecurityOption? ): EnumSet { - val prohibitDefaultPin = card?.settings?.isRemovingAccessCodeAllowed != true + val prohibitDefaultPin = card?.settings?.isResettingUserCodesAllowed != true + val allowedSecurityOptions = EnumSet.noneOf(SecurityOption::class.java) - if (card?.isTwinCard() == true) { + if (card?.isTangemTwin() == true) { allowedSecurityOptions.add(SecurityOption.PassCode) } if ((currentSecurityOption == SecurityOption.LongTap) || !prohibitDefaultPin) { diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt index fa81ea9a18..1a6c05c2b1 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletMiddleware.kt @@ -48,7 +48,7 @@ class CreateTwinWalletMiddleware { store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) } is DetailsAction.CreateTwinWalletAction.LaunchFirstStep -> { - store.state.globalState.scanNoteResponse?.let { + store.state.globalState.scanResponse?.let { twinsManager = TwinCardsManager(it, action.context) } scope.launch { @@ -106,7 +106,7 @@ class CreateTwinWalletMiddleware { } is DetailsAction.CreateTwinWalletAction.LaunchThirdStep.Success -> { scope.launch { - store.state.globalState.tapWalletManager.onCardScanned(action.scanNoteResponse) + store.state.globalState.tapWalletManager.onCardScanned(action.scanResponse) } store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletState.kt index 488ac431bc..22c96dd0bf 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/twins/CreateTwinWalletState.kt @@ -1,15 +1,15 @@ package com.tangem.tap.features.details.redux.twins -import com.tangem.tap.domain.tasks.ScanNoteResponse +import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.domain.twins.TwinCardNumber data class CreateTwinWalletState( - val scanResponse: ScanNoteResponse?, - val step: CreateTwinWalletStep = CreateTwinWalletStep.FirstStep, - val twinCardNumber: TwinCardNumber?, - val createTwinWallet: CreateTwinWallet?, - val showAlert: Boolean, - val allowRecreatingWallet: Boolean? = null + val scanResponse: ScanResponse?, + val step: CreateTwinWalletStep = CreateTwinWalletStep.FirstStep, + val twinCardNumber: TwinCardNumber?, + val createTwinWallet: CreateTwinWallet?, + val showAlert: Boolean, + val allowRecreatingWallet: Boolean? = null ) enum class CreateTwinWalletStep { FirstStep, SecondStep, ThirdStep } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt index 813dba5494..dff6c35c72 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt @@ -1,11 +1,10 @@ package com.tangem.tap.features.details.redux.walletconnect import com.tangem.blockchain.common.* -import com.tangem.common.CompletionResult import com.tangem.common.card.Card +import com.tangem.common.extensions.guard import com.tangem.common.extensions.toHexString import com.tangem.tap.* -import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.getFromClipboard import com.tangem.tap.common.redux.AppState @@ -18,9 +17,6 @@ import com.tangem.tap.domain.isMultiwalletAllowed import com.tangem.tap.domain.walletconnect.WalletConnectManager import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.wallet.R -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import org.rekotlin.Middleware class WalletConnectMiddleware { @@ -132,49 +128,35 @@ class WalletConnectMiddleware { } private fun handleScanCard(wcUri: String) { - scope.launch { - val result = tangemSdkManager.scanNote( - FirebaseAnalyticsHandler, - R.string.wallet_connect_scan_card_message - ) - withContext(Dispatchers.Main) { - when (result) { - is CompletionResult.Success -> { - val card = result.data.card - - if (!card.isMultiwalletAllowed) { - store.dispatchOnMain(WalletConnectAction.UnsupportedCard) - return@withContext - } - - val walletManager = getWalletManager(card) - if (walletManager == null) { - store.dispatchOnMain(WalletConnectAction.UnsupportedCard) - return@withContext - } - - val key = walletManager.wallet.publicKey - store.dispatchOnMain(WalletConnectAction.OpenSession( - wcUri = wcUri, - wallet = WalletForSession( - card.cardId, key.toHexString(), - isTestNet = card.isTestCard - ), - )) - } - is CompletionResult.Failure -> - store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession( - null - )) - } + store.dispatch(GlobalAction.ScanCard({ scanResponse -> + val card = scanResponse.card + if (!card.isMultiwalletAllowed) { + store.dispatchOnMain(WalletConnectAction.UnsupportedCard) + return@ScanCard } - } + + val walletManager = getWalletManager(card).guard { + store.dispatchOnMain(WalletConnectAction.UnsupportedCard) + return@ScanCard + } + + val key = walletManager.wallet.publicKey + store.dispatchOnMain(WalletConnectAction.OpenSession( + wcUri = wcUri, + wallet = WalletForSession( + card.cardId, key.toHexString(), + isTestNet = card.isTestCard + ), + )) + }, { + store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession(null)) + }, R.string.wallet_connect_scan_card_message)) } private fun getWalletManager(card: Card): WalletManager? { val factory = store.state.globalState.tapWalletManager.walletManagerFactory - return if (store.state.globalState.scanNoteResponse?.card?.cardId == card.cardId) { + return if (store.state.globalState.scanResponse?.card?.cardId == card.cardId) { store.state.walletState.getWalletManager(Blockchain.Ethereum) ?: factory.makeWalletManagerForApp(card, Blockchain.Ethereum) ?.also { walletManager -> diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt index 757584cc46..38aeaabc84 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt @@ -2,7 +2,7 @@ package com.tangem.tap.features.details.redux.walletconnect import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.common.WalletManager -import com.tangem.tap.common.redux.global.StateDialog +import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.features.details.ui.walletconnect.dialogs.PersonalSignDialogData import com.tangem.tap.features.details.ui.walletconnect.dialogs.TransactionRequestDialogData import com.trustwallet.walletconnect.models.WCPeerMeta diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt index 55f9711482..640cc47cf0 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt @@ -12,7 +12,7 @@ import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.twins.getTwinCardIdForUser -import com.tangem.tap.domain.twins.isTwinCard +import com.tangem.tap.domain.twins.isTangemTwin import com.tangem.tap.features.details.redux.DetailsAction import com.tangem.tap.features.details.redux.DetailsState import com.tangem.tap.features.details.redux.SecurityOption @@ -68,7 +68,7 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber { preferencesStorage.saveDisclaimerAccepted() - if (store.state.walletState.twinCardsState != null) { - val showOnboarding = !preferencesStorage.wasTwinsOnboardingShown() - if (showOnboarding) { - store.dispatch(NavigationAction.NavigateTo(AppScreen.TwinsOnboarding)) - } else { - store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) - } - } else { - store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) - } + store.dispatch(NavigationAction.PopBackTo()) + + //delayed sending used for better animation + post(550) { store.dispatch(HomeAction.ReadCard) } } } next(action) diff --git a/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt b/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt index fb7ebe023d..ec7a45dd3d 100644 --- a/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt @@ -58,6 +58,9 @@ class DisclaimerFragment : Fragment(R.layout.fragment_disclaimer), override fun newState(state: DisclaimerState) { if (activity == null) return - if (state.accepted) btn_accept.hide() + if (state.accepted) { + half_transparent_overlay.hide() + btn_accept.hide() + } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt index 29cd04dc73..d7b0c6d941 100644 --- a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt @@ -1,27 +1,62 @@ package com.tangem.tap.features.home -import android.app.Dialog import android.os.Bundle import android.view.View -import androidx.fragment.app.Fragment import androidx.transition.TransitionInflater -import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.common.redux.global.StateDialog +import com.tangem.tap.common.extensions.hide +import com.tangem.tap.common.redux.navigation.FragmentShareTransition +import com.tangem.tap.common.redux.navigation.ShareElement +import com.tangem.tap.common.toggleWidget.IndeterminateProgressButtonWidget +import com.tangem.tap.common.toggleWidget.ViewStateWidget +import com.tangem.tap.common.transitions.FrontCardEnterTransition +import com.tangem.tap.common.transitions.FrontCardExitTransition import com.tangem.tap.features.home.redux.HomeAction -import com.tangem.tap.features.home.redux.HomeDialog import com.tangem.tap.features.home.redux.HomeState -import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog +import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment import com.tangem.tap.store import com.tangem.wallet.R -import kotlinx.android.synthetic.main.fragment_home.* -import org.rekotlin.StoreSubscriber +import kotlinx.android.synthetic.main.fragment_onboarding_main.* +import kotlinx.android.synthetic.main.layout_onboarding_bottom_action_views.* +import kotlinx.android.synthetic.main.layout_onboarding_home.* +import java.lang.ref.WeakReference -class HomeFragment : Fragment(R.layout.fragment_home), StoreSubscriber { +class HomeFragment : BaseOnboardingFragment() { - private var dialog: Dialog? = null + private lateinit var btnScanCard: ViewStateWidget - override fun onStart() { - super.onStart() + override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_home + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val inflater = TransitionInflater.from(requireContext()) + exitTransition = inflater.inflateTransition(R.transition.fade) + store.dispatch(HomeAction.Init) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + toolbar.hide() + val shareTransition = FragmentShareTransition( + listOf(ShareElement(WeakReference(imv_front_card), imv_front_card.transitionName)), + FrontCardEnterTransition(), + FrontCardExitTransition() + ) + + store.dispatch(HomeAction.SetFragmentShareTransition(shareTransition)) + + tv_header.setText(R.string.home_welcome_header) + tv_body.setText(R.string.home_welcome_body) + + btn_main_action.setText(R.string.home_button_scan) + btn_main_action.setOnClickListener { store.dispatch(HomeAction.ReadCard) } + btn_alternative_action.setText(R.string.home_button_get_new_card) + btn_alternative_action.setOnClickListener { store.dispatch(HomeAction.GoToShop) } + + btnScanCard = IndeterminateProgressButtonWidget(btn_main_action, progress) + } + + override fun subscribeToStore() { store.subscribe(this) { state -> state.skipRepeats { oldState, newState -> oldState.homeState == newState.homeState @@ -29,51 +64,15 @@ class HomeFragment : Fragment(R.layout.fragment_home), StoreSubscriber { - if (dialog == null) dialog = ScanFailsDialog.create(requireContext()).apply { show() } - } - else -> { - dialog?.dismiss() - dialog = null - } - } - } - - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - btn_yes?.setOnClickListener { store.dispatch(HomeAction.ReadCard) } - btn_shop?.setOnClickListener { store.dispatch(HomeAction.GoToShop(requireContext())) } - - store.dispatch(GlobalAction.GetMoonPayUserStatus) - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - val inflater = TransitionInflater.from(requireContext()) - exitTransition = inflater.inflateTransition(R.transition.fade) + override fun onDestroyView() { + store.dispatch(HomeAction.SetFragmentShareTransition(null)) + super.onDestroyView() } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt index 4a575343c7..14f762f6c4 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt @@ -1,18 +1,20 @@ package com.tangem.tap.features.home.redux -import android.content.Context +import com.tangem.tap.common.entities.IndeterminateProgressButton +import com.tangem.tap.common.redux.navigation.FragmentShareTransition import org.rekotlin.Action sealed class HomeAction : Action { + // from ui object ReadCard : HomeAction() - data class GoToShop(val context: Context) : HomeAction() - object CheckIfFirstLaunch : HomeAction() { - data class Result(val firstLaunch: Boolean) : HomeAction() - } + object GoToShop : HomeAction() - object ShowDialog : HomeAction() { - object ScanFails : HomeAction() - } + // internal + data class ShouldScanCardOnResume(val shouldScanCard: Boolean):HomeAction() + object Init : HomeAction() + + data class SetFragmentShareTransition(val shareTransition: FragmentShareTransition?) : HomeAction() + data class SetTermsOfUseState(val isDisclaimerAccepted: Boolean) : HomeAction() + data class ChangeScanCardButtonState(val state: IndeterminateProgressButton) : HomeAction() - object HideDialog : HomeAction() } diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt index b084b208cb..46e097943a 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt @@ -1,80 +1,83 @@ package com.tangem.tap.features.home.redux -import android.content.Intent -import android.net.Uri -import androidx.core.content.ContextCompat.startActivity -import com.tangem.common.CompletionResult -import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler +import com.tangem.tap.common.entities.IndeterminateProgressButton +import com.tangem.tap.common.extensions.dispatchOpenUrl +import com.tangem.tap.common.post +import com.tangem.tap.common.postUi import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.common.redux.navigation.FragmentShareTransition import com.tangem.tap.common.redux.navigation.NavigationAction +import com.tangem.tap.domain.DELAY_SDK_DIALOG_CLOSE +import com.tangem.tap.features.onboarding.OnboardingHelper +import com.tangem.tap.features.send.redux.states.ButtonState import com.tangem.tap.preferencesStorage import com.tangem.tap.scope import com.tangem.tap.store -import com.tangem.tap.tangemSdkManager -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import org.rekotlin.Middleware class HomeMiddleware { - val homeMiddleware: Middleware = { dispatch, state -> - { next -> - { action -> - when (action) { - is HomeAction.CheckIfFirstLaunch -> { - store.dispatch( - HomeAction.CheckIfFirstLaunch.Result(preferencesStorage.getCountOfLaunches() == 1) - ) - } - is HomeAction.ReadCard -> { - scope.launch { - val result = tangemSdkManager.scanNote(FirebaseAnalyticsHandler) - withContext(Dispatchers.Main) { - store.dispatch(GlobalAction.ScanFailsCounter.ChooseBehavior(result)) - when (result) { - is CompletionResult.Success -> { - tangemSdkManager.changeDisplayedCardIdNumbersCount(result.data.card) - store.dispatch(GlobalAction.RestoreAppCurrency) - store.state.globalState.tapWalletManager.onCardScanned(result.data) - showDisclaimerOrNavigateToWallet() - } - } - } - } - } - is HomeAction.GoToShop -> { - val uri = Uri.parse(CARD_SHOP_URI) - val intent = Intent(Intent.ACTION_VIEW, uri) - startActivity(action.context, intent, null) - } - } - next(action) - } - } - } - - private fun showDisclaimerOrNavigateToWallet() { - if (!preferencesStorage.wasDisclaimerAccepted()) { - store.dispatch(NavigationAction.NavigateTo(AppScreen.Disclaimer)) - return - } - if (store.state.walletState.twinCardsState != null) { - val showOnboarding = !preferencesStorage.wasTwinsOnboardingShown() - if (showOnboarding) { - store.dispatch(NavigationAction.NavigateTo(AppScreen.TwinsOnboarding)) - return - } - } - store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) - } - companion object { - private const val CARD_SHOP_URI = + val handler = homeMiddleware + + const val CARD_SHOP_URI = "https://shop.tangem.com/?afmc=1i&utm_campaign=1i&utm_source=leaddyno&utm_medium=affiliate" } } +private val homeMiddleware: Middleware = { dispatch, state -> + { next -> + { action -> + when (action) { + is HomeAction.Init -> { + store.dispatch(GlobalAction.RestoreAppCurrency) + store.dispatch(GlobalAction.GetMoonPayUserStatus) + store.dispatch(HomeAction.SetTermsOfUseState(preferencesStorage.wasDisclaimerAccepted())) + } + is HomeAction.ShouldScanCardOnResume -> { + if (action.shouldScanCard) { + store.dispatch(HomeAction.ShouldScanCardOnResume(false)) + postUi(700) { store.dispatch(HomeAction.ReadCard) } + } + } + is HomeAction.ReadCard -> handleReadCard() + is HomeAction.GoToShop -> store.dispatchOpenUrl(HomeMiddleware.CARD_SHOP_URI) + } + next(action) + } + } +} +private fun handleReadCard() { + if (!preferencesStorage.wasDisclaimerAccepted()) { + store.dispatch(NavigationAction.NavigateTo(AppScreen.Disclaimer)) + } else { + changeButtonState(ButtonState.PROGRESS) + store.dispatch(GlobalAction.ScanCard({ scanResponse -> + if (OnboardingHelper.isOnboardingCase(scanResponse)) { + val navigateTo = OnboardingHelper.whereToNavigate(scanResponse) + store.dispatch(GlobalAction.Onboarding.Start(scanResponse)) + navigateTo(navigateTo, store.state.homeState.shareTransition) + } else { + scope.launch { store.state.globalState.tapWalletManager.onCardScanned(scanResponse) } + navigateTo(AppScreen.Wallet, null) + } + }, { + changeButtonState(ButtonState.ENABLED) + })) + } +} +private fun changeButtonState(state: ButtonState) { + val btnState = IndeterminateProgressButton(state) + store.dispatch(HomeAction.ChangeScanCardButtonState(btnState)) +} + +private fun navigateTo(screen: AppScreen, transition: FragmentShareTransition?) { + post(DELAY_SDK_DIALOG_CLOSE) { + changeButtonState(ButtonState.ENABLED) + store.dispatch(NavigationAction.NavigateTo(screen, transition)) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt index 3c031ed7b2..d4f11f31f9 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeReducer.kt @@ -13,18 +13,18 @@ private fun internalReduce(action: Action, state: AppState): HomeState { if (action !is HomeAction) return state.homeState - var homeState = state.homeState + var state = state.homeState when (action) { - is HomeAction.CheckIfFirstLaunch.Result -> { - homeState = homeState.copy(firstLaunch = action.firstLaunch) + is HomeAction.SetFragmentShareTransition -> { + state = state.copy(shareTransition = action.shareTransition) } - is HomeAction.ShowDialog.ScanFails -> { - homeState = homeState.copy(dialog = HomeDialog.ScanFailsDialog) + is HomeAction.ShouldScanCardOnResume -> { + state = state.copy(shouldScanCardOnResume = action.shouldScanCard) } - is HomeAction.HideDialog -> { - homeState = homeState.copy(dialog = null) + is HomeAction.ChangeScanCardButtonState -> { + state = state.copy(btnScanState = action.state) } } - return homeState + return state } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt index c9fce9b492..8c9949cf4c 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt @@ -1,13 +1,12 @@ package com.tangem.tap.features.home.redux -import com.tangem.tap.common.redux.global.StateDialog +import com.tangem.tap.common.entities.IndeterminateProgressButton +import com.tangem.tap.common.redux.navigation.FragmentShareTransition +import com.tangem.tap.features.send.redux.states.ButtonState import org.rekotlin.StateType data class HomeState( - val firstLaunch: Boolean = true, - val dialog: StateDialog? = null -) : StateType - -sealed class HomeDialog: StateDialog { - object ScanFailsDialog: HomeDialog() -} \ No newline at end of file + val shouldScanCardOnResume: Boolean = false, + val shareTransition: FragmentShareTransition? = null, + val btnScanState: IndeterminateProgressButton = IndeterminateProgressButton(ButtonState.ENABLED), +) : StateType \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/AddressInfoBottomSheetDialog.kt b/app/src/main/java/com/tangem/tap/features/onboarding/AddressInfoBottomSheetDialog.kt new file mode 100644 index 0000000000..0b5b229bc4 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/AddressInfoBottomSheetDialog.kt @@ -0,0 +1,44 @@ +package com.tangem.tap.features.onboarding + +import android.content.Context +import com.google.android.material.bottomsheet.BottomSheetDialog +import com.tangem.tap.common.extensions.copyToClipboard +import com.tangem.tap.common.extensions.dispatchDialogHide +import com.tangem.tap.common.redux.AppDialog +import com.tangem.tap.store +import com.tangem.wallet.R +import kotlinx.android.synthetic.main.dialog_onboarding_address_info.* +import kotlinx.android.synthetic.main.layout_pseudo_toolbar.* + +/** +[REDACTED_AUTHOR] + */ +class AddressInfoBottomSheetDialog( + private val stateDialog: AppDialog.AddressInfoDialog, + context: Context +) : BottomSheetDialog(context) { + + init { + this.setContentView(R.layout.dialog_onboarding_address_info) + setOnCancelListener { store.dispatchDialogHide() } + } + + override fun show() { + super.show() + val data = stateDialog.addressData + + imv_close.setOnClickListener { + dismissWithAnimation = true + cancel() + } + imv_qr_code.setImageBitmap(data.qrCode) + tv_address.text = data.address + btn_fl_copy_address.setOnClickListener { + context.copyToClipboard(data.address) + stateDialog.onCopyAddress() + } + btn_fl_explore.setOnClickListener { + stateDialog.onExploreAddress() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt new file mode 100644 index 0000000000..38b2517ad1 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt @@ -0,0 +1,27 @@ +package com.tangem.tap.features.onboarding + +import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.domain.ProductType +import com.tangem.tap.domain.extensions.hasWallets +import com.tangem.tap.domain.tasks.product.ScanResponse +import com.tangem.tap.preferencesStorage + +/** +[REDACTED_AUTHOR] + */ +class OnboardingHelper { + companion object { + + fun isOnboardingCase(response: ScanResponse): Boolean = if (response.card.hasWallets()) + preferencesStorage.usedCardsPrefStorage.activationIsStarted(response.card.cardId) else true + + fun whereToNavigate(scanResponse: ScanResponse): AppScreen { + return when (scanResponse.productType) { + ProductType.Note -> AppScreen.OnboardingNote + ProductType.Wallet -> AppScreen.OnboardingWallet + ProductType.Twin -> AppScreen.OnboardingTwins + ProductType.Other -> AppScreen.OnboardingOther + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingManager.kt b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingManager.kt new file mode 100644 index 0000000000..2b4894d5b9 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingManager.kt @@ -0,0 +1,128 @@ +package com.tangem.tap.features.onboarding + +import com.tangem.blockchain.common.AmountType +import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.common.WalletManager +import com.tangem.common.extensions.isZero +import com.tangem.common.services.Result +import com.tangem.operations.attestation.CardVerifyAndGetInfo +import com.tangem.operations.attestation.OnlineCardVerifier +import com.tangem.tap.common.extensions.isPositive +import com.tangem.tap.common.extensions.safeUpdate +import com.tangem.tap.domain.TapError +import com.tangem.tap.domain.UrlBitmapLoader +import com.tangem.tap.domain.extensions.getOrLoadCardArtworkUrl +import com.tangem.tap.domain.tasks.product.ScanResponse +import com.tangem.tap.features.wallet.models.hasPendingTransactions +import com.tangem.tap.features.wallet.redux.Artwork +import com.tangem.tap.features.wallet.redux.Currency +import com.tangem.tap.features.wallet.redux.ProgressState +import com.tangem.tap.persistence.UsedCardsPrefStorage +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.math.BigDecimal +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine + +/** +[REDACTED_AUTHOR] + */ +class OnboardingManager( + var scanResponse: ScanResponse, + val usedCardsPrefStorage: UsedCardsPrefStorage, +) { + + var cardInfo: Result? = null + private set + + suspend fun loadArtwork(): Artwork { + cardInfo = OnlineCardVerifier().getCardInfo(scanResponse.card.cardId, scanResponse.card.cardPublicKey) + return loadArtwork(scanResponse.card.getOrLoadCardArtworkUrl(cardInfo)) + } + + suspend fun loadArtwork(url: String): Artwork { + return withContext(Dispatchers.Main) { + suspendCoroutine { continuation -> + UrlBitmapLoader().loadBitmap(url) { + val result = when (it) { + is Result.Success -> Artwork(url, it.data) + is Result.Failure -> Artwork(url, null) + } + continuation.resume(result) + } + } + } + } + + suspend fun updateBalance(walletManager: WalletManager): OnboardingWalletBalance { + return when (val result = walletManager.safeUpdate()) { + is Result.Success -> { + val wallet = walletManager.wallet + val valueOfAmount = wallet.amounts[AmountType.Coin]?.value + if (valueOfAmount == null) { + val customError = TapError.CustomError("Amount is NULL") + OnboardingWalletBalance.criticalError(customError) + } else { + val currency = Currency.Blockchain(walletManager.wallet.blockchain) + val balance = if (valueOfAmount.isZero()) { + OnboardingWalletBalance.done(valueOfAmount, wallet.hasPendingTransactions(), currency) + } else { + OnboardingWalletBalance.done(valueOfAmount, false, currency) + } + balance + } + } + is Result.Failure -> { + val error = (result.error as? TapError) ?: TapError.UnknownError + when (error) { + is TapError.WalletManagerUpdate.NoAccountError -> OnboardingWalletBalance.error(error) + // NoInternetConnection, WalletManagerUpdate.InternalError + else -> OnboardingWalletBalance.criticalError(error) + } + } + } + } + + fun activationStarted(cardId: String) { + usedCardsPrefStorage.activationStarted(cardId) + } + + fun activationFinished(cardId: String) { + usedCardsPrefStorage.activationFinished(cardId) + } +} + +data class OnboardingWalletBalance( + val value: BigDecimal = BigDecimal.ZERO, + val currency: Currency.Blockchain = Currency.Blockchain(Blockchain.Unknown), + val hasIncomingTransaction: Boolean = false, + val state: ProgressState, + val error: TapError? = null, + val criticalError: TapError? = null, +) { + + fun balanceIsToppedUp(): Boolean = value.isPositive() || hasIncomingTransaction + + val amountToCreateAccount: String? + get() = if (error is TapError.WalletManagerUpdate.NoAccountError) error.customMessage else null + + companion object { + fun error(error: TapError): OnboardingWalletBalance = OnboardingWalletBalance( + state = ProgressState.Error, + error = error + ) + + fun criticalError(error: TapError): OnboardingWalletBalance = OnboardingWalletBalance( + state = ProgressState.Error, + criticalError = error + ) + + fun loading(value: BigDecimal = BigDecimal.ZERO): OnboardingWalletBalance = OnboardingWalletBalance( + value, + state = ProgressState.Loading + ) + + fun done(value: BigDecimal, hasTransactions: Boolean, currency: Currency.Blockchain): OnboardingWalletBalance = + OnboardingWalletBalance(value, currency, hasTransactions, ProgressState.Done) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/BaseOnboardingFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/BaseOnboardingFragment.kt new file mode 100644 index 0000000000..7cdbff7733 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/BaseOnboardingFragment.kt @@ -0,0 +1,46 @@ +package com.tangem.tap.features.onboarding.products + +import android.os.Bundle +import android.view.View +import com.tangem.tap.common.extensions.inflate +import com.tangem.tap.common.extensions.show +import com.tangem.tap.common.transitions.HomeToOnboardingTransition +import com.tangem.tap.features.send.BaseStoreFragment +import com.tangem.wallet.R +import kotlinx.android.synthetic.main.fragment_onboarding_main.* +import kotlinx.android.synthetic.main.view_confetti.* +import org.rekotlin.StoreSubscriber + +/** +[REDACTED_AUTHOR] + */ +abstract class BaseOnboardingFragment : BaseStoreFragment(R.layout.fragment_onboarding_main), StoreSubscriber { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + configureTransitions() + } + + override fun configureTransitions() { + enterTransition = HomeToOnboardingTransition() + exitTransition = HomeToOnboardingTransition() + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + onboarding_top_container.inflate(getOnboardingTopContainerId(), true) + } + + abstract fun getOnboardingTopContainerId(): Int + + protected fun showConfetti(show: Boolean) { + lav_confetti.show(show) + + if (show) { + lav_confetti.playAnimation() + } else { + lav_confetti.cancelAnimation() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt new file mode 100644 index 0000000000..6bf7ca96bf --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt @@ -0,0 +1,184 @@ +package com.tangem.tap.features.onboarding.products.note + +import android.graphics.Bitmap +import android.graphics.drawable.BitmapDrawable +import android.os.Bundle +import android.view.View +import android.view.animation.OvershootInterpolator +import android.widget.ImageView +import androidx.annotation.LayoutRes +import androidx.constraintlayout.widget.ConstraintSet +import androidx.core.view.isVisible +import androidx.transition.TransitionManager +import com.tangem.blockchain.common.Blockchain +import com.tangem.tangem_sdk_new.extensions.fadeIn +import com.tangem.tangem_sdk_new.extensions.fadeOut +import com.tangem.tap.common.extensions.getDrawableCompat +import com.tangem.tap.common.extensions.stripZeroPlainString +import com.tangem.tap.common.postUi +import com.tangem.tap.common.toggleWidget.RefreshBalanceWidget +import com.tangem.tap.common.transitions.InternalNoteLayoutTransition +import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment +import com.tangem.tap.features.onboarding.products.note.redux.* +import com.tangem.tap.store +import com.tangem.wallet.R +import kotlinx.android.synthetic.main.fragment_onboarding_main.* +import kotlinx.android.synthetic.main.layout_onboarding_bottom_action_views.* +import kotlinx.android.synthetic.main.layout_onboarding_note.* +import kotlinx.android.synthetic.main.view_onboarding_progress.* +import kotlinx.android.synthetic.main.view_onboarding_tv_balance.* + +/** +[REDACTED_AUTHOR] + */ +class OnboardingNoteFragment : BaseOnboardingFragment() { + + private lateinit var btnRefreshBalanceWidget: RefreshBalanceWidget + private var currentStep: OnboardingNoteStep = OnboardingNoteStep.None + + override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_note + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + toolbar.setTitle(R.string.onboarding_title) + btnRefreshBalanceWidget = RefreshBalanceWidget(onboarding_main_container) + val resources = AndroidResources( + OnboardingStringResources( + R.string.copy_toast_msg + ) + ) + + store.dispatch(OnboardingNoteAction.LoadCardArtwork) + store.dispatch(OnboardingNoteAction.SetResources(resources)) + store.dispatch(OnboardingNoteAction.DetermineStepOfScreen) + } + + override fun subscribeToStore() { + store.subscribe(this) { state -> + state.skipRepeats { oldState, newState -> + oldState.onboardingNoteState == newState.onboardingNoteState + }.select { it.onboardingNoteState } + } + storeSubscribersList.add(this) + } + + override fun newState(state: OnboardingNoteState) { + if (activity == null) return + + state.cardArtwork?.let { imv_front_card.swapToBitmapDrawable(it.artwork) } + pb_state.max = state.steps.size - 1 + pb_state.progress = state.progress + + when (state.currentStep) { + OnboardingNoteStep.None -> setupNoneState(state) + OnboardingNoteStep.CreateWallet -> setupCreateWalletState(state) + OnboardingNoteStep.TopUpWallet -> setupTopUpWalletState(state) + OnboardingNoteStep.Done -> setupDoneState(state) + } + setBalance(state) + showConfetti(state.showConfetti) + currentStep = state.currentStep + } + + private fun setBalance(state: OnboardingNoteState) { + if (state.walletBalance.currency.blockchain == Blockchain.Unknown) return + + val balanceValue = state.walletBalance.value.stripZeroPlainString() + val currency = state.walletBalance.currency.currencySymbol + tv_balance_value.text = "$balanceValue $currency" + } + + private fun setupNoneState(state: OnboardingNoteState) { + btn_main_action.isVisible = false + btn_alternative_action.isVisible = false + btnRefreshBalanceWidget.show(false) + tv_header.isVisible = false + tv_body.isVisible = false + + btn_main_action.text = "" + btn_alternative_action.text = "" + tv_header.text = "" + tv_body.text = "" + } + + private fun setupCreateWalletState(state: OnboardingNoteState) { + btn_main_action.setText(R.string.onboarding_create_wallet_button_create_wallet) + btn_main_action.setOnClickListener { store.dispatch(OnboardingNoteAction.CreateWallet) } + btn_alternative_action.setText(R.string.onboarding_button_what_does_it_mean) + btn_alternative_action.setOnClickListener { } + + btnRefreshBalanceWidget.mainView.setOnClickListener(null) + + tv_header.setText(R.string.onboarding_create_wallet_header) + tv_body.setText(R.string.onboarding_create_wallet_body) + + imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_circle)) + updateConstraints(state.currentStep, R.layout.lp_onboarding_create_wallet) + } + + private fun setupTopUpWalletState(state: OnboardingNoteState) { + btn_main_action.setText(R.string.onboarding_top_up_button_but_crypto) + btn_main_action.setOnClickListener { + store.dispatch(OnboardingNoteAction.TopUp) + } + + btn_alternative_action.setText(R.string.onboarding_top_up_button_show_wallet_address) + btn_alternative_action.setOnClickListener { + store.dispatch(OnboardingNoteAction.ShowAddressInfoDialog) + } + + tv_header.setText(R.string.onboarding_top_up_header) + tv_body.setText(R.string.onboarding_top_up_body) + + btnRefreshBalanceWidget.changeState(state.walletBalance.state) + if (btnRefreshBalanceWidget.isShowing != true) { + postUi(300) { + btnRefreshBalanceWidget.mainView.setOnClickListener { + store.dispatch(OnboardingNoteAction.Balance.Update) + } + } + } + imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_rectangle_rounded_8)) + updateConstraints(state.currentStep, R.layout.lp_onboarding_topup_wallet) + } + + private fun setupDoneState(state: OnboardingNoteState) { + btn_main_action.setText(R.string.onboarding_done_button_continue) + btn_main_action.setOnClickListener { + showConfetti(false) + store.dispatch(OnboardingNoteAction.Done) + } + + btn_alternative_action.isVisible = false + btn_alternative_action.setText("") + btn_alternative_action.setOnClickListener { } + btnRefreshBalanceWidget.mainView.setOnClickListener(null) + + tv_header.setText(R.string.onboarding_done_header) + tv_body.setText(R.string.onboarding_done_body) + + imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_rectangle_rounded_8)) + updateConstraints(state.currentStep, R.layout.lp_onboarding_done_activation) + } + + private fun updateConstraints(currentStep: OnboardingNoteStep, @LayoutRes layoutId: Int) { + if (this.currentStep == currentStep) return + + val constraintSet = ConstraintSet() + constraintSet.clone(requireContext(), layoutId) + constraintSet.applyTo(onboarding_main_container) + val transition = InternalNoteLayoutTransition() + transition.interpolator = OvershootInterpolator() + TransitionManager.beginDelayedTransition(onboarding_main_container, transition) + } +} + +fun ImageView.swapToBitmapDrawable(bitmap: Bitmap?, duration: Long = 200) { + if (drawable is BitmapDrawable && (drawable as BitmapDrawable).bitmap == bitmap) return + + fadeOut(duration) { + setImageBitmap(bitmap) + fadeIn(duration) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt new file mode 100644 index 0000000000..798f8f4136 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteAction.kt @@ -0,0 +1,36 @@ +package com.tangem.tap.features.onboarding.products.note.redux + +import com.tangem.blockchain.common.WalletManager +import com.tangem.tap.features.onboarding.OnboardingWalletBalance +import com.tangem.tap.features.wallet.redux.Artwork +import org.rekotlin.Action + +/** +[REDACTED_AUTHOR] + */ +sealed class OnboardingNoteAction : Action { + // from user, ui + data class SetResources(val resources: AndroidResources) : OnboardingNoteAction() + object LoadCardArtwork : OnboardingNoteAction() + object CreateWallet : OnboardingNoteAction() + object TopUp : OnboardingNoteAction() + object ShowAddressInfoDialog : OnboardingNoteAction() + + // from redux + class SetArtworkUrl(val artwork: Artwork) : OnboardingNoteAction() + data class SetWalletManager(val walletManager: WalletManager) : OnboardingNoteAction() + object DetermineStepOfScreen : OnboardingNoteAction() + object Done : OnboardingNoteAction() + + data class SetStepOfScreen(val step: OnboardingNoteStep) : OnboardingNoteAction() + + sealed class Balance { + object Update : OnboardingNoteAction() + data class Set(val balance: OnboardingWalletBalance) : OnboardingNoteAction() + } + + sealed class Confetti { + object Show : OnboardingNoteAction() + object Hide : OnboardingNoteAction() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt new file mode 100644 index 0000000000..0a925dc7ee --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt @@ -0,0 +1,151 @@ +package com.tangem.tap.features.onboarding.products.note.redux + +import com.tangem.common.CompletionResult +import com.tangem.common.extensions.guard +import com.tangem.tap.common.extensions.* +import com.tangem.tap.common.postUi +import com.tangem.tap.common.redux.AppDialog +import com.tangem.tap.common.redux.AppState +import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.common.redux.navigation.NavigationAction +import com.tangem.tap.domain.TapError +import com.tangem.tap.domain.extensions.hasWallets +import com.tangem.tap.domain.extensions.makePrimaryWalletManager +import com.tangem.tap.features.wallet.redux.ProgressState +import com.tangem.tap.scope +import com.tangem.tap.store +import com.tangem.tap.tangemSdkManager +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import org.rekotlin.Action +import org.rekotlin.DispatchFunction +import org.rekotlin.Middleware + +class OnboardingNoteMiddleware { + companion object { + val handler = onboardingNoteMiddleware + } +} + +private val onboardingNoteMiddleware: Middleware = { dispatch, state -> + { next -> + { action -> + handleNoteAction(action, dispatch) + next(action) + } + } +} + +private fun handleNoteAction(action: Action, dispatch: DispatchFunction) { + if (action !is OnboardingNoteAction) return + val globalState = store.state.globalState + val onboardingManager = globalState.onboardingManager ?: return + + val scanResponse = onboardingManager.scanResponse + val card = onboardingManager.scanResponse.card + val noteState = store.state.onboardingNoteState + + when (action) { + is OnboardingNoteAction.LoadCardArtwork -> { + scope.launch { + val artwork = onboardingManager.loadArtwork() + withMainContext { store.dispatch(OnboardingNoteAction.SetArtworkUrl(artwork)) } + } + } + is OnboardingNoteAction.DetermineStepOfScreen -> { + val step = when { + !card.hasWallets() -> OnboardingNoteStep.CreateWallet + noteState.walletBalance.balanceIsToppedUp() -> OnboardingNoteStep.Done + else -> OnboardingNoteStep.TopUpWallet + } + store.dispatch((OnboardingNoteAction.SetStepOfScreen(step))) + } + is OnboardingNoteAction.SetStepOfScreen -> { + when (action.step) { + OnboardingNoteStep.TopUpWallet -> { + store.dispatch(OnboardingNoteAction.Balance.Update) + } + OnboardingNoteStep.Done -> { + onboardingManager.activationFinished(card.cardId) + postUi(500) { store.dispatch(OnboardingNoteAction.Confetti.Show) } + } + } + } + is OnboardingNoteAction.CreateWallet -> { + scope.launch { + val result = tangemSdkManager.createProductWallet(scanResponse) + withMainContext { + when (result) { + is CompletionResult.Success -> { + val updatedResponse = scanResponse.copy(card = result.data) + onboardingManager.scanResponse = updatedResponse + onboardingManager.activationStarted(updatedResponse.card.cardId) + store.dispatch(OnboardingNoteAction.SetStepOfScreen(OnboardingNoteStep.TopUpWallet)) + } + is CompletionResult.Failure -> { +// do nothing + } + } + } + } + } + is OnboardingNoteAction.Balance.Update -> { + val walletManager = if (noteState.walletManager != null) { + noteState.walletManager + } else { + val wmFactory = globalState.tapWalletManager.walletManagerFactory + val walletManager = wmFactory.makePrimaryWalletManager(scanResponse).guard { + val message = "Loading cancelled. Cause: wallet manager didn't created" + val customError = TapError.CustomError(message) + store.dispatchErrorNotification(customError) + return + } + dispatch(OnboardingNoteAction.SetWalletManager(walletManager)) + walletManager + } + + val isLoadedBefore = noteState.walletBalance.state != ProgressState.Loading + val balanceIsLoading = noteState.walletBalance.copy( + state = ProgressState.Loading, + error = null, + criticalError = null + ) + store.dispatch(OnboardingNoteAction.Balance.Set(balanceIsLoading)) + + scope.launch { + val loadedBalance = onboardingManager.updateBalance(walletManager) + loadedBalance.criticalError?.let { store.dispatchErrorNotification(it) } + delay(if (isLoadedBefore) 0 else 300) + withMainContext { store.dispatch(OnboardingNoteAction.Balance.Set(loadedBalance)) } + } + + } + is OnboardingNoteAction.Balance.Set -> { + if (action.balance.balanceIsToppedUp()) { + store.dispatch(OnboardingNoteAction.SetStepOfScreen(OnboardingNoteStep.Done)) + } + } + is OnboardingNoteAction.ShowAddressInfoDialog -> { + val addressData = noteState.walletManager?.getAddressData() ?: return + val addressWasCopied = noteState.resources.strings.addressWasCopied + val appDialog = AppDialog.AddressInfoDialog( + addressData, + onCopyAddress = { store.dispatchToastNotification(addressWasCopied) }, + onExploreAddress = { store.dispatchOpenUrl(addressData.exploreUrl) } + ) + store.dispatchDialogShow(appDialog) + } + is OnboardingNoteAction.TopUp -> { + val topUpUrl = noteState.walletManager?.getToUpUrl() ?: return + store.dispatchOpenUrl(topUpUrl) + } + OnboardingNoteAction.Done -> { + store.dispatch(GlobalAction.Onboarding.Stop) + scope.launch { + globalState.tapWalletManager.onCardScanned(scanResponse) + store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt new file mode 100644 index 0000000000..03415eec84 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteReducer.kt @@ -0,0 +1,46 @@ +package com.tangem.tap.features.onboarding.products.note.redux + +import com.tangem.tap.common.redux.AppState +import com.tangem.tap.common.redux.global.GlobalAction +import org.rekotlin.Action + +class OnboardingNoteReducer { + companion object { + fun reduce(action: Action, state: AppState): OnboardingNoteState = internalReduce(action, state) + } +} + +private fun internalReduce(action: Action, appState: AppState): OnboardingNoteState { + var state = appState.onboardingNoteState + + when (action) { + is GlobalAction.Onboarding.Start -> { + state = OnboardingNoteState() + } + is OnboardingNoteAction.SetArtworkUrl -> { + state = state.copy(cardArtwork = action.artwork) + } + is OnboardingNoteAction.SetWalletManager -> { + state = state.copy(walletManager = action.walletManager) + } + is OnboardingNoteAction.SetResources -> { + state = state.copy(resources = action.resources) + } + is OnboardingNoteAction.Balance.Set -> { + state = state.copy(walletBalance = action.balance) + } + is OnboardingNoteAction.SetStepOfScreen -> { + if (action.step != state.currentStep && state.steps.contains(action.step)) { + state = state.copy(currentStep = action.step) + } + } + is OnboardingNoteAction.Confetti.Show -> { + state = state.copy(showConfetti = true) + } + is OnboardingNoteAction.Confetti.Hide -> { + state = state.copy(showConfetti = false) + } + } + + return state +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt new file mode 100644 index 0000000000..d4f1c2bcb7 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt @@ -0,0 +1,36 @@ +package com.tangem.tap.features.onboarding.products.note.redux + +import com.tangem.blockchain.common.WalletManager +import com.tangem.tap.features.onboarding.OnboardingWalletBalance +import com.tangem.tap.features.wallet.redux.Artwork +import org.rekotlin.StateType + +/** +[REDACTED_AUTHOR] + */ +data class OnboardingNoteState( + val walletManager: WalletManager? = null, + val resources: AndroidResources = AndroidResources(), + // UI + val cardArtwork: Artwork? = null, + val walletBalance: OnboardingWalletBalance = OnboardingWalletBalance.loading(), + val currentStep: OnboardingNoteStep = OnboardingNoteStep.None, + val steps: List = OnboardingNoteStep.values().toList(), + val showConfetti: Boolean = false, +) : StateType { + + val progress: Int + get() = steps.indexOf(currentStep) +} + +enum class OnboardingNoteStep { + None, CreateWallet, TopUpWallet, Done +} + +data class AndroidResources( + val strings: OnboardingStringResources = OnboardingStringResources() +) + +data class OnboardingStringResources( + val addressWasCopied: Int = -1 +) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt new file mode 100644 index 0000000000..ebdbe7d4c1 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/OnboardingOtherCardsFragment.kt @@ -0,0 +1,102 @@ +package com.tangem.tap.features.onboarding.products.otherCards + +import android.os.Bundle +import android.view.View +import android.view.animation.OvershootInterpolator +import androidx.annotation.LayoutRes +import androidx.constraintlayout.widget.ConstraintSet +import androidx.core.view.isVisible +import androidx.transition.TransitionManager +import com.tangem.tap.common.extensions.getDrawableCompat +import com.tangem.tap.common.transitions.InternalNoteLayoutTransition +import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment +import com.tangem.tap.features.onboarding.products.note.swapToBitmapDrawable +import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsAction +import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsState +import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsStep +import com.tangem.tap.store +import com.tangem.wallet.R +import kotlinx.android.synthetic.main.fragment_onboarding_main.* +import kotlinx.android.synthetic.main.layout_onboarding_bottom_action_views.* +import kotlinx.android.synthetic.main.layout_onboarding_note.* +import kotlinx.android.synthetic.main.view_onboarding_progress.* + +/** +[REDACTED_AUTHOR] + */ +class OnboardingOtherCardsFragment : BaseOnboardingFragment() { + + override fun getOnboardingTopContainerId(): Int = R.layout.layout_onboarding_note + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + toolbar.setTitle(R.string.onboarding_title) + store.dispatch(OnboardingOtherCardsAction.LoadCardArtwork) + store.dispatch(OnboardingOtherCardsAction.DetermineStepOfScreen) + } + + override fun subscribeToStore() { + store.subscribe(this) { state -> + state.skipRepeats { oldState, newState -> + oldState.onboardingOtherCardsState == newState.onboardingOtherCardsState + }.select { it.onboardingOtherCardsState } + } + storeSubscribersList.add(this) + } + + override fun newState(state: OnboardingOtherCardsState) { + if (activity == null) return + if (state.currentStep == OnboardingOtherCardsStep.None) return + + state.cardArtwork?.let { imv_front_card.swapToBitmapDrawable(it.artwork) } + pb_state.max = state.steps.size - 1 + pb_state.progress = state.progress + + when (state.currentStep) { + OnboardingOtherCardsStep.CreateWallet -> setupCreateWalletState(state) + OnboardingOtherCardsStep.Done -> setupDoneState(state) + } + showConfetti(state.showConfetti) + } + + private fun setupCreateWalletState(state: OnboardingOtherCardsState) { + btn_main_action.setText(R.string.onboarding_create_wallet_button_create_wallet) + btn_main_action.setOnClickListener { store.dispatch(OnboardingOtherCardsAction.CreateWallet) } + btn_alternative_action.setText(R.string.onboarding_button_what_does_it_mean) + btn_alternative_action.setOnClickListener { } + + tv_header.setText(R.string.onboarding_create_wallet_header) + tv_body.setText(R.string.onboarding_create_wallet_body) + + imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_circle)) + updateConstraints(R.layout.lp_onboarding_create_wallet) + } + + private fun setupDoneState(state: OnboardingOtherCardsState) { + btn_main_action.setText(R.string.onboarding_done_button_continue) + btn_main_action.setOnClickListener { + showConfetti(false) + store.dispatch(OnboardingOtherCardsAction.Done) + } + + btn_alternative_action.isVisible = false + btn_alternative_action.setText("") + btn_alternative_action.setOnClickListener { } + + tv_header.setText(R.string.onboarding_done_header) + tv_body.setText(R.string.onboarding_done_body) + + imv_card_background.setBackgroundDrawable(requireContext().getDrawableCompat(R.drawable.shape_rectangle_rounded_8)) + updateConstraints(R.layout.lp_onboarding_done) + } + + private fun updateConstraints(@LayoutRes layoutId: Int) { + val constraintSet = ConstraintSet() + constraintSet.clone(requireContext(), layoutId) + constraintSet.applyTo(onboarding_main_container) + val transition = InternalNoteLayoutTransition() + transition.interpolator = OvershootInterpolator() + TransitionManager.beginDelayedTransition(onboarding_main_container, transition) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsAction.kt new file mode 100644 index 0000000000..44bdec53c8 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsAction.kt @@ -0,0 +1,25 @@ +package com.tangem.tap.features.onboarding.products.otherCards.redux + +import com.tangem.tap.features.wallet.redux.Artwork +import org.rekotlin.Action + +/** +[REDACTED_AUTHOR] + */ +sealed class OnboardingOtherCardsAction : Action { + // from user, ui + object LoadCardArtwork : OnboardingOtherCardsAction() + object CreateWallet : OnboardingOtherCardsAction() + + // from redux + class SetArtworkUrl(val artwork: Artwork) : OnboardingOtherCardsAction() + object DetermineStepOfScreen : OnboardingOtherCardsAction() + object Done : OnboardingOtherCardsAction() + + data class SetStepOfScreen(val step: OnboardingOtherCardsStep) : OnboardingOtherCardsAction() + + sealed class Confetti { + object Show : OnboardingOtherCardsAction() + object Hide : OnboardingOtherCardsAction() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt new file mode 100644 index 0000000000..e775176f10 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt @@ -0,0 +1,91 @@ +package com.tangem.tap.features.onboarding.products.otherCards.redux + +import com.tangem.common.CompletionResult +import com.tangem.tap.common.extensions.withMainContext +import com.tangem.tap.common.postUi +import com.tangem.tap.common.redux.AppState +import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.common.redux.navigation.NavigationAction +import com.tangem.tap.domain.DELAY_SDK_DIALOG_CLOSE +import com.tangem.tap.domain.extensions.hasWallets +import com.tangem.tap.scope +import com.tangem.tap.store +import com.tangem.tap.tangemSdkManager +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import org.rekotlin.Action +import org.rekotlin.DispatchFunction +import org.rekotlin.Middleware + +class OnboardingOtherCardsMiddleware { + companion object { + val handler = onboardingOtherCardsMiddleware + } +} + +private val onboardingOtherCardsMiddleware: Middleware = { dispatch, state -> + { next -> + { action -> + handleOtherCardsAction(action, dispatch) + next(action) + } + } +} + +private fun handleOtherCardsAction(action: Action, dispatch: DispatchFunction) { + if (action !is OnboardingOtherCardsAction) return + val globalState = store.state.globalState + val onboardingManager = globalState.onboardingManager ?: return + + val card = onboardingManager.scanResponse.card + + when (action) { + is OnboardingOtherCardsAction.LoadCardArtwork -> { + scope.launch { + val artwork = onboardingManager.loadArtwork() + withMainContext { store.dispatch(OnboardingOtherCardsAction.SetArtworkUrl(artwork)) } + } + } + is OnboardingOtherCardsAction.DetermineStepOfScreen -> { + val step = when { + !card.hasWallets() -> OnboardingOtherCardsStep.CreateWallet + else -> OnboardingOtherCardsStep.Done + } + store.dispatch((OnboardingOtherCardsAction.SetStepOfScreen(step))) + } + is OnboardingOtherCardsAction.SetStepOfScreen -> { + if (action.step == OnboardingOtherCardsStep.Done) { + onboardingManager.activationFinished(card.cardId) + postUi(500) { store.dispatch(OnboardingOtherCardsAction.Confetti.Show) } + } + } + is OnboardingOtherCardsAction.CreateWallet -> { + scope.launch { + val result = tangemSdkManager.createProductWallet(onboardingManager.scanResponse) + withMainContext { + when (result) { + is CompletionResult.Success -> { + val updatedResponse = onboardingManager.scanResponse.copy(card = result.data) + onboardingManager.scanResponse = updatedResponse + onboardingManager.activationStarted(updatedResponse.card.cardId) + + delay(DELAY_SDK_DIALOG_CLOSE) + store.dispatch(OnboardingOtherCardsAction.SetStepOfScreen(OnboardingOtherCardsStep.Done)) + } + is CompletionResult.Failure -> { +// do nothing + } + } + } + } + } + OnboardingOtherCardsAction.Done -> { + store.dispatch(GlobalAction.Onboarding.Stop) + scope.launch { + globalState.tapWalletManager.onCardScanned(onboardingManager.scanResponse) + store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet)) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsReducer.kt new file mode 100644 index 0000000000..a6313b9199 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsReducer.kt @@ -0,0 +1,37 @@ +package com.tangem.tap.features.onboarding.products.otherCards.redux + +import com.tangem.tap.common.redux.AppState +import com.tangem.tap.common.redux.global.GlobalAction +import org.rekotlin.Action + +class OnboardingOtherCardsReducer { + companion object { + fun reduce(action: Action, state: AppState): OnboardingOtherCardsState = internalReduce(action, state) + } +} + +private fun internalReduce(action: Action, appState: AppState): OnboardingOtherCardsState { + var state = appState.onboardingOtherCardsState + + when (action) { + is GlobalAction.Onboarding.Start -> { + state = OnboardingOtherCardsState() + } + is OnboardingOtherCardsAction.SetArtworkUrl -> { + state = state.copy(cardArtwork = action.artwork) + } + is OnboardingOtherCardsAction.SetStepOfScreen -> { + if (action.step != state.currentStep && state.steps.contains(action.step)) { + state = state.copy(currentStep = action.step) + } + } + is OnboardingOtherCardsAction.Confetti.Show -> { + state = state.copy(showConfetti = true) + } + is OnboardingOtherCardsAction.Confetti.Hide -> { + state = state.copy(showConfetti = false) + } + } + + return state +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsState.kt new file mode 100644 index 0000000000..b306101506 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsState.kt @@ -0,0 +1,23 @@ +package com.tangem.tap.features.onboarding.products.otherCards.redux + +import com.tangem.tap.features.wallet.redux.Artwork +import org.rekotlin.StateType + +/** +[REDACTED_AUTHOR] + */ +data class OnboardingOtherCardsState( + // UI + val cardArtwork: Artwork? = null, + val showConfetti: Boolean = false, + val currentStep: OnboardingOtherCardsStep = OnboardingOtherCardsStep.None, + val steps: List = OnboardingOtherCardsStep.values().toList(), +) : StateType { + + val progress: Int + get() = steps.indexOf(currentStep) +} + +enum class OnboardingOtherCardsStep { + None, CreateWallet, Done +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/OnboardingWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/OnboardingWalletFragment.kt new file mode 100644 index 0000000000..d0463040bd --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/OnboardingWalletFragment.kt @@ -0,0 +1,42 @@ +package com.tangem.tap.features.onboarding.products.wallet + +import android.os.Bundle +import android.view.View +import androidx.transition.TransitionInflater +import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletState +import com.tangem.tap.features.send.BaseStoreFragment +import com.tangem.tap.store +import com.tangem.wallet.R +import org.rekotlin.StoreSubscriber + +/** +[REDACTED_AUTHOR] + */ +class OnboardingWalletFragment : BaseStoreFragment(R.layout.fragment_onboarding_wallet), StoreSubscriber { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val inflater = TransitionInflater.from(requireContext()) + enterTransition = inflater.inflateTransition(R.transition.fade) + exitTransition = inflater.inflateTransition(R.transition.fade) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + } + + override fun subscribeToStore() { + store.subscribe(this) { state -> + state.skipRepeats { oldState, newState -> + oldState.onboardingWalletState == newState.onboardingWalletState + }.select { it.onboardingWalletState } + } + storeSubscribersList.add(this) + } + + override fun newState(state: OnboardingWalletState) { + if (activity == null) return + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt new file mode 100644 index 0000000000..5cfd213d57 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt @@ -0,0 +1,19 @@ +package com.tangem.tap.features.onboarding.products.wallet.redux + +import org.rekotlin.Action + +/** +[REDACTED_AUTHOR] + */ +sealed class OnboardingWalletAction : Action { + object Init : OnboardingWalletAction() + object CreateWallet : OnboardingWalletAction() + object TopUp : OnboardingWalletAction() + object Done : OnboardingWalletAction() + + object ProceedBackup : OnboardingWalletAction() + + sealed class Backup : Action { + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt new file mode 100644 index 0000000000..c442090de4 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt @@ -0,0 +1,18 @@ +package com.tangem.tap.features.onboarding.products.wallet.redux + +import com.tangem.tap.common.redux.AppState +import org.rekotlin.Middleware + +class OnboardingWalletMiddleware { + companion object { + val handler = onboardingWalletMiddleware + } +} + +private val onboardingWalletMiddleware: Middleware = { dispatch, state -> + { next -> + { action -> + next(action) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt new file mode 100644 index 0000000000..427f44048e --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt @@ -0,0 +1,20 @@ +package com.tangem.tap.features.onboarding.products.wallet.redux + +import com.tangem.tap.common.redux.AppState +import org.rekotlin.Action + +class OnboardingWalletReducer { + companion object { + fun reduce(action: Action, state: AppState): OnboardingWalletState = internalReduce(action, state) + } +} + +private fun internalReduce(action: Action, appState: AppState): OnboardingWalletState { + var state = appState.onboardingWalletState + + when (action) { + + } + + return state +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt new file mode 100644 index 0000000000..5207a85b3d --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt @@ -0,0 +1,14 @@ +package com.tangem.tap.features.onboarding.products.wallet.redux + +import org.rekotlin.StateType + +/** +[REDACTED_AUTHOR] + */ +data class OnboardingWalletState( + val any: String? = null +) : StateType + +enum class OnboardingWalletStep { + None, CreateWallet, TopUpWallet, Backup, Done +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt b/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt index d7b8d2af90..bb3046db5e 100644 --- a/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt @@ -4,12 +4,9 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.activity.OnBackPressedCallback import androidx.fragment.app.Fragment import androidx.transition.TransitionInflater import com.google.android.material.snackbar.Snackbar -import com.tangem.tap.common.redux.navigation.NavigationAction -import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.store import com.tangem.wallet.R import kotlinx.android.synthetic.main.fragment_wallet.* @@ -27,18 +24,10 @@ abstract class BaseStoreFragment(layoutId: Int) : Fragment(layoutId) { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - val externalTransactionData = store.state.sendState.externalTransactionData - if (externalTransactionData == null) { - store.dispatch(NavigationAction.PopBackTo()) - } else { - store.dispatch( - WalletAction.TradeCryptoAction.FinishSelling(externalTransactionData.transactionId) - ) - } - } - }) + configureTransitions() + } + + protected open fun configureTransitions() { val inflater = TransitionInflater.from(requireContext()) enterTransition = inflater.inflateTransition(R.transition.slide_right) exitTransition = inflater.inflateTransition(R.transition.fade) @@ -51,7 +40,7 @@ abstract class BaseStoreFragment(layoutId: Int) : Fragment(layoutId) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - toolbar.setNavigationOnClickListener { activity?.onBackPressed() } + toolbar?.setNavigationOnClickListener { activity?.onBackPressed() } } override fun onStart() { @@ -62,6 +51,7 @@ abstract class BaseStoreFragment(layoutId: Int) : Fragment(layoutId) { override fun onStop() { storeSubscribersList.forEach { store.unsubscribe(it) } + storeSubscribersList.clear() super.onStop() } diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt index 921e29f667..b0b9263034 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt @@ -5,13 +5,13 @@ import com.tangem.blockchain.common.Amount import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.WalletManager import com.tangem.tap.common.redux.ErrorAction +import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.common.redux.ToastNotificationAction -import com.tangem.tap.common.redux.global.StateDialog import com.tangem.tap.domain.TapError import com.tangem.tap.domain.configurable.warningMessage.WarningMessage +import com.tangem.tap.features.send.redux.states.ButtonState import com.tangem.tap.features.send.redux.states.FeeType import com.tangem.tap.features.send.redux.states.MainCurrencyType -import com.tangem.tap.features.send.redux.states.SendButtonState import com.tangem.wallet.R import org.rekotlin.Action import java.math.BigDecimal @@ -137,7 +137,7 @@ sealed class SendActionUi : SendScreenActionUi { sealed class SendAction : SendScreenAction { - data class ChangeSendButtonState(val state: SendButtonState) : SendAction() + data class ChangeSendButtonState(val state: ButtonState) : SendAction() object SendSuccess : SendAction(), ToastNotificationAction { override val messageResource: Int = R.string.send_transaction_success } diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt index 2c5b8ebd58..26d2f8dd27 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt @@ -21,9 +21,9 @@ import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.extensions.minimalAmount import com.tangem.tap.features.send.redux.* import com.tangem.tap.features.send.redux.FeeAction.RequestFee +import com.tangem.tap.features.send.redux.states.ButtonState import com.tangem.tap.features.send.redux.states.ExternalTransactionData import com.tangem.tap.features.send.redux.states.MainCurrencyType -import com.tangem.tap.features.send.redux.states.SendButtonState import com.tangem.tap.features.send.redux.states.TransactionExtrasState import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.scope @@ -41,41 +41,43 @@ import java.util.* /** [REDACTED_AUTHOR] */ -val sendMiddleware: Middleware = { dispatch, appState -> - { nextDispatch -> - { action -> - when (action) { - is AddressPayIdActionUi -> AddressPayIdMiddleware().handle(action, appState(), dispatch) - is AmountActionUi -> AmountMiddleware().handle(action, appState(), dispatch) - is RequestFee -> RequestFeeMiddleware().handle(appState(), dispatch) - is SendActionUi.SendAmountToRecipient -> - verifyAndSendTransaction(action, appState(), dispatch) - is PrepareSendScreen -> setIfSendingToPayIdEnabled(appState(), dispatch) - is SendAction.Warnings.Update -> updateWarnings(dispatch) - is SendActionUi.CheckIfTransactionDataWasProvided -> { - val transactionData = appState()?.sendState?.externalTransactionData - if (transactionData != null) { - store.dispatchOnMain(AddressPayIdVerifyAction.AddressVerification.SetWalletAddress( - transactionData.destinationAddress, false - )) - store.dispatchOnMain(AmountActionUi.SetMainCurrency(MainCurrencyType.CRYPTO)) - store.dispatchOnMain(AmountActionUi.HandleUserInput(transactionData.amount)) - store.dispatchOnMain(AmountAction.SetAmount(transactionData.amount.toBigDecimal(), - false)) +class SendMiddleware { + val sendMiddleware: Middleware = { dispatch, appState -> + { nextDispatch -> + { action -> + when (action) { + is AddressPayIdActionUi -> AddressPayIdMiddleware().handle(action, appState(), dispatch) + is AmountActionUi -> AmountMiddleware().handle(action, appState(), dispatch) + is RequestFee -> RequestFeeMiddleware().handle(appState(), dispatch) + is SendActionUi.SendAmountToRecipient -> + verifyAndSendTransaction(action, appState(), dispatch) + is PrepareSendScreen -> setIfSendingToPayIdEnabled(appState(), dispatch) + is SendAction.Warnings.Update -> updateWarnings(dispatch) + is SendActionUi.CheckIfTransactionDataWasProvided -> { + val transactionData = appState()?.sendState?.externalTransactionData + if (transactionData != null) { + store.dispatchOnMain(AddressPayIdVerifyAction.AddressVerification.SetWalletAddress( + transactionData.destinationAddress, false + )) + store.dispatchOnMain(AmountActionUi.SetMainCurrency(MainCurrencyType.CRYPTO)) + store.dispatchOnMain(AmountActionUi.HandleUserInput(transactionData.amount)) + store.dispatchOnMain(AmountAction.SetAmount(transactionData.amount.toBigDecimal(), + false)) + } } } + nextDispatch(action) } - nextDispatch(action) } } -} +} private fun verifyAndSendTransaction( action: SendActionUi.SendAmountToRecipient, appState: AppState?, dispatch: (Action) -> Unit, ) { val sendState = appState?.sendState ?: return val walletManager = sendState.walletManager ?: return - val card = appState.globalState.scanNoteResponse?.card ?: return + val card = appState.globalState.scanResponse?.card ?: return val destinationAddress = sendState.addressPayIdState.destinationWalletAddress ?: return val typedAmount = sendState.amountState.amountToExtract ?: return val feeAmount = sendState.feeState.currentFee ?: return @@ -119,7 +121,7 @@ private fun sendTransaction( externalTransactionData: ExternalTransactionData?, dispatch: (Action) -> Unit, ) { - dispatch(SendAction.ChangeSendButtonState(SendButtonState.PROGRESS)) + dispatch(SendAction.ChangeSendButtonState(ButtonState.PROGRESS)) var txData = walletManager.createTransaction(amountToSend, feeAmount, destinationAddress) transactionExtras.xlmMemo?.memo?.let { txData = txData.copy(extras = StellarTransactionExtras(it)) } @@ -234,7 +236,7 @@ private fun sendTransaction( } } } - dispatch(SendAction.ChangeSendButtonState(SendButtonState.ENABLED)) + dispatch(SendAction.ChangeSendButtonState(ButtonState.ENABLED)) } } } diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt index 58b36afe00..673f19cb66 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt @@ -2,6 +2,7 @@ package com.tangem.tap.features.send.redux.reducers import com.tangem.blockchain.common.AmountType import com.tangem.tap.common.CurrencyConverter +import com.tangem.tap.common.entities.IndeterminateProgressButton import com.tangem.tap.features.send.redux.* import com.tangem.tap.features.send.redux.states.ExternalTransactionData import com.tangem.tap.features.send.redux.states.IdStateHolder @@ -41,7 +42,9 @@ class SendScreenReducer { private class SendReducer : SendInternalReducer { override fun handle(action: SendScreenAction, sendState: SendState): SendState { val result = when (action) { - is SendAction.ChangeSendButtonState -> sendState.copy(sendButtonState = action.state) + is SendAction.ChangeSendButtonState -> { + sendState.copy(sendButtonState = IndeterminateProgressButton(action.state)) + } is SendAction.Dialog.TezosWarningDialog -> sendState.copy(dialog = action) is SendAction.Dialog.SendTransactionFails -> sendState.copy(dialog = action) is SendAction.Dialog.Hide -> sendState.copy(dialog = null) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt index eed6fa793c..ed98986ae3 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt @@ -5,8 +5,9 @@ import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.WalletManager import com.tangem.common.extensions.isZero import com.tangem.tap.common.CurrencyConverter +import com.tangem.tap.common.entities.IndeterminateProgressButton import com.tangem.tap.common.entities.TapCurrency -import com.tangem.tap.common.redux.global.StateDialog +import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.common.text.DecimalDigitsInputFilter import com.tangem.tap.domain.TapError import com.tangem.tap.domain.configurable.warningMessage.WarningMessage @@ -39,7 +40,7 @@ data class SendState( val feeState: FeeState = FeeState(), val receiptState: ReceiptState = ReceiptState(), val sendWarningsList: List = listOf(), - val sendButtonState: SendButtonState = SendButtonState.DISABLED, + val sendButtonState: IndeterminateProgressButton = IndeterminateProgressButton(ButtonState.DISABLED), val dialog: StateDialog? = null, val externalTransactionData: ExternalTransactionData? = null ) : SendScreenState { @@ -87,7 +88,7 @@ data class SendState( } } - fun getButtonState(): SendButtonState = if (isReadyToSend()) SendButtonState.ENABLED else SendButtonState.DISABLED + fun getButtonState(): ButtonState = if (isReadyToSend()) ButtonState.ENABLED else ButtonState.DISABLED fun getTotalAmountToSend(value: BigDecimal = amountState.amountToSendCrypto): BigDecimal { val needToExtractFee = amountState.isCoinAmount() && feeState.feeIsIncluded @@ -117,7 +118,7 @@ data class SendState( } } -enum class SendButtonState { +enum class ButtonState { ENABLED, DISABLED, PROGRESS } diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt index 9736cc16e0..bb675cf3fe 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt @@ -7,6 +7,7 @@ import android.text.method.DigitsKeyListener import android.view.View import android.view.inputmethod.EditorInfo import android.widget.EditText +import androidx.activity.OnBackPressedCallback import androidx.core.view.postDelayed import androidx.core.widget.addTextChangedListener import androidx.recyclerview.widget.LinearLayoutManager @@ -20,6 +21,7 @@ import com.tangem.tap.common.entities.TapCurrency import com.tangem.tap.common.extensions.getFromClipboard import com.tangem.tap.common.extensions.setOnImeActionListener import com.tangem.tap.common.qrCodeScan.ScanQrCodeActivity +import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.common.snackBar.MaxAmountSnackbar import com.tangem.tap.common.text.truncateMiddleWith import com.tangem.tap.common.toggleWidget.* @@ -31,6 +33,7 @@ import com.tangem.tap.features.send.redux.FeeActionUi.* import com.tangem.tap.features.send.redux.states.FeeType import com.tangem.tap.features.send.redux.states.MainCurrencyType import com.tangem.tap.features.send.ui.stateSubscribers.SendStateSubscriber +import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.features.wallet.ui.adapters.SpacesItemDecoration import com.tangem.tap.features.wallet.ui.adapters.WarningMessagesAdapter import com.tangem.tap.mainScope @@ -60,6 +63,23 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { private val sendSubscriber = SendStateSubscriber(this) private lateinit var keyboardObserver: KeyboardObserver + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + val externalTransactionData = store.state.sendState.externalTransactionData + if (externalTransactionData == null) { + store.dispatch(NavigationAction.PopBackTo()) + } else { + store.dispatch( + WalletAction.TradeCryptoAction.FinishSelling(externalTransactionData.transactionId) + ) + } + } + }) + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt index 05c7af7d18..2471b257de 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt @@ -10,7 +10,6 @@ import androidx.core.text.bold import com.tangem.tap.common.extensions.* import com.tangem.tap.common.redux.getMessageString import com.tangem.tap.common.text.DecimalDigitsInputFilter -import com.tangem.tap.common.toggleWidget.ProgressState import com.tangem.tap.domain.MultiMessageError import com.tangem.tap.domain.assembleErrors import com.tangem.tap.features.send.BaseStoreFragment @@ -116,20 +115,8 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber } } - when (state.sendButtonState) { - SendButtonState.ENABLED -> { - fg.btnSend.isEnabled = true - sendFragment.sendBtn.changeState(ProgressState.None) - } - SendButtonState.DISABLED -> { - fg.btnSend.isEnabled = false - sendFragment.sendBtn.changeState(ProgressState.None) - } - SendButtonState.PROGRESS -> { - fg.btnSend.isEnabled = true - sendFragment.sendBtn.changeState(ProgressState.Progress()) - } - } + sendFragment.sendBtn.changeState(state.sendButtonState.progressState) + sendFragment.sendBtn.mainView.isEnabled = state.sendButtonState.enabled val rv = fg.rv_warning_messages val adapter = rv.adapter as? WarningMessagesAdapter ?: return @@ -185,7 +172,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber val messageList = multiError.assembleErrors().map { getMessageString(context, it.first, it.second) } multiError.builder(messageList) } - else -> context.getString(state.error.localizedMessage) + else -> context.getString(state.error.messageResource) } fg.tilAmountToSend.enableError(true, message) } else { diff --git a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt index f1bcb72e51..e8afbe2d62 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt @@ -14,7 +14,7 @@ class TokensMiddleware { { action -> when (action) { is TokensAction.LoadCurrencies -> { - val card = state()?.globalState?.scanNoteResponse?.card + val card = state()?.globalState?.scanResponse?.card val isTestcard = card?.isTestCard ?: false val tokens = currenciesRepository.getPopularTokens(isTestcard) val blockchains = currenciesRepository.getBlockchains( diff --git a/app/src/main/java/com/tangem/tap/features/wallet/models/PendingTransaction.kt b/app/src/main/java/com/tangem/tap/features/wallet/models/PendingTransaction.kt index 0ade915979..492513e02c 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/models/PendingTransaction.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/models/PendingTransaction.kt @@ -3,6 +3,7 @@ package com.tangem.tap.features.wallet.models import com.tangem.blockchain.common.Token import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.common.TransactionStatus +import com.tangem.blockchain.common.Wallet import com.tangem.tap.common.extensions.toFormattedString data class PendingTransaction( @@ -57,4 +58,12 @@ fun TransactionData.toPendingTransactionForToken(token: Token, walletAddress: St fun List.toPendingTransactionsForToken(token: Token, walletAddress: String): List { return this.mapNotNull { it.toPendingTransactionForToken(token, walletAddress) } +} + +fun Wallet.getPendingTransactions(): List { + return recentTransactions.toPendingTransactions(address) +} + +fun Wallet.hasPendingTransactions(): Boolean { + return recentTransactions.toPendingTransactions(address).isNotEmpty() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt index d2f58bf842..6fd84ec7cd 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt @@ -122,7 +122,6 @@ sealed class WalletAction : Action { object ShowDialog : WalletAction() { object QrCode : WalletAction() - object ScanFails : WalletAction() object SignedHashesMultiWalletDialog : WalletAction() object ChooseTradeActionDialog : WalletAction() } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt index 33b0093e4f..d078cd0526 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt @@ -5,8 +5,10 @@ import com.tangem.blockchain.common.* import com.tangem.blockchain.common.address.AddressType import com.tangem.blockchain.extensions.isAboveZero import com.tangem.tap.common.entities.Button +import com.tangem.tap.common.extensions.toQrCode +import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.common.redux.global.CryptoCurrencyName -import com.tangem.tap.common.redux.global.StateDialog +import com.tangem.tap.common.toggleWidget.WidgetState import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.extensions.toSendableAmounts import com.tangem.tap.domain.topup.TradeCryptoHelper @@ -141,12 +143,11 @@ sealed class WalletDialog: StateDialog { ) : WalletDialog() data class SelectAmountToSendDialog(val amounts: List?) : WalletDialog() - object ScanFailsDialog : WalletDialog() object SignedHashesMultiWalletDialog : WalletDialog() object ChooseTradeActionDialog : WalletDialog() } -enum class ProgressState { Loading, Done, Error } +enum class ProgressState: WidgetState { Loading, Done, Error } enum class ErrorType { NoInternetConnection } @@ -165,8 +166,9 @@ data class AddressData( val type: AddressType, val shareUrl: String, val exploreUrl: String, - val qrCode: Bitmap? = null -) +) { + val qrCode: Bitmap by lazy { shareUrl.toQrCode() } +} data class Artwork( val artworkId: String, @@ -180,6 +182,8 @@ data class Artwork( const val MARTA_CARD_ID = "BC02" const val TWIN_CARD_1 = "https://app.tangem.com/cards/card_tg085.png" const val TWIN_CARD_2 = "https://app.tangem.com/cards/card_tg086.png" + + const val TEMP_CARDANO = "https://verify.tangem.com/card/artwork?artworkId=card_ru039&CID=CB19000000040976&publicKey=0416E29423A6CC77CD07CBA52873E8F6F894B1AFB18EB3688ACC2C8D8E5AC84B80B0BA1B17B85E578E47044CE96BCFF3FB4499FA4941CAD3C1EF300A492B5B9659" } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt index b5c15c945c..026cc26e4d 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt @@ -8,7 +8,6 @@ import com.tangem.tap.common.redux.global.GlobalState import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.currenciesRepository -import com.tangem.tap.domain.TapWorkarounds.isTestCard import com.tangem.tap.domain.extensions.makeWalletManagerForApp import com.tangem.tap.domain.extensions.makeWalletManagersForApp import com.tangem.tap.features.wallet.redux.Currency @@ -36,7 +35,7 @@ class MultiWalletMiddleware { } } is WalletAction.MultiWallet.AddToken -> { - globalState?.scanNoteResponse?.card?.cardId?.let { + globalState?.scanResponse?.card?.cardId?.let { currenciesRepository.saveAddedToken(it, action.token) } addToken(action.token, walletState, globalState) @@ -45,7 +44,7 @@ class MultiWalletMiddleware { addTokens(action.tokens, walletState, globalState) } is WalletAction.MultiWallet.AddBlockchain -> { - globalState?.scanNoteResponse?.card?.let { card -> + globalState?.scanResponse?.card?.let { card -> currenciesRepository.saveAddedBlockchain(card.cardId, action.blockchain) if (walletState?.blockchains?.contains(action.blockchain) != true) { globalState.tapWalletManager.walletManagerFactory @@ -58,11 +57,11 @@ class MultiWalletMiddleware { store.dispatch(WalletAction.LoadWallet(blockchain = action.blockchain)) } is WalletAction.MultiWallet.SaveCurrencies -> { - val cardId = globalState?.scanNoteResponse?.card?.cardId + val cardId = globalState?.scanResponse?.card?.cardId cardId?.let { currenciesRepository.saveCardCurrencies(it, action.cardCurrencies) } } is WalletAction.MultiWallet.RemoveWallet -> { - val cardId = globalState?.scanNoteResponse?.card?.cardId + val cardId = globalState?.scanResponse?.card?.cardId when (val currency = action.walletData.currency) { is Currency.Blockchain -> { cardId?.let { @@ -80,7 +79,7 @@ class MultiWalletMiddleware { } } is WalletAction.MultiWallet.FindBlockchainsInUse -> { - val cardFirmware = globalState?.scanNoteResponse?.card?.firmwareVersion + val cardFirmware = globalState?.scanResponse?.card?.firmwareVersion val blockchains = currenciesRepository.getBlockchains(cardFirmware) .filterNot { walletState?.blockchains?.contains(it) == true } val walletManagers = @@ -117,7 +116,7 @@ class MultiWalletMiddleware { } } is WalletAction.MultiWallet.FindTokensInUse -> { - val card = globalState?.scanNoteResponse?.card ?: return + val card = globalState?.scanResponse?.card ?: return val walletManager = walletState?.getWalletManager(Blockchain.Ethereum) ?: globalState.tapWalletManager.walletManagerFactory.makeWalletManagerForApp( card = card, @@ -156,7 +155,7 @@ class MultiWalletMiddleware { } private fun addToken(token: Token, walletState: WalletState?, globalState: GlobalState?) { - val card = globalState?.scanNoteResponse?.card ?: return + val card = globalState?.scanResponse?.card ?: return val walletManager = walletState?.getWalletManager(token) ?: globalState.tapWalletManager.walletManagerFactory.makeWalletManagerForApp( card = card, @@ -186,7 +185,7 @@ class MultiWalletMiddleware { walletState: WalletState?, globalState: GlobalState?, ) { - val card = globalState?.scanNoteResponse?.card ?: return + val card = globalState?.scanResponse?.card ?: return val tokensWithManagers = tokens.map { token -> val walletManager = walletState?.getWalletManager(token) ?: globalState.tapWalletManager.walletManagerFactory.makeWalletManagerForApp( diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt index 8a680fcf1c..3b3a4f31c1 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt @@ -22,10 +22,10 @@ import com.tangem.tap.domain.extensions.toSendableAmounts import com.tangem.tap.domain.twins.TwinsHelper import com.tangem.tap.features.details.redux.DetailsAction import com.tangem.tap.features.details.redux.twins.CreateTwinWallet +import com.tangem.tap.features.home.redux.HomeAction import com.tangem.tap.features.send.redux.PrepareSendScreen import com.tangem.tap.features.wallet.redux.* import com.tangem.tap.network.NetworkStateChanged -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.rekotlin.Action import org.rekotlin.Middleware @@ -96,7 +96,7 @@ class WalletMiddleware { is WalletAction.CreateWallet -> { if (walletState?.twinCardsState != null) { store.dispatch(DetailsAction.CreateTwinWalletAction.ShowWarning( - globalState?.scanNoteResponse?.card?.cardId?.let { + globalState?.scanResponse?.card?.cardId?.let { TwinsHelper.getTwinCardNumber(it) }, CreateTwinWallet.CreateWallet @@ -104,12 +104,12 @@ class WalletMiddleware { } else { scope.launch { val result = tangemSdkManager.createWallet( - globalState?.scanNoteResponse?.card?.cardId + globalState?.scanResponse?.card?.cardId ) when (result) { is CompletionResult.Success -> { val scanNoteResponse = - globalState?.scanNoteResponse?.copy(card = result.data) + globalState?.scanResponse?.copy(card = result.data) scanNoteResponse?.let { globalState.tapWalletManager.onCardScanned(scanNoteResponse) } @@ -145,18 +145,8 @@ class WalletMiddleware { } is WalletAction.Scan -> { - scope.launch { - val result = tangemSdkManager.scanNote(FirebaseAnalyticsHandler) - scope.launch(Dispatchers.Main) { - store.dispatch(GlobalAction.ScanFailsCounter.ChooseBehavior(result)) - when (result) { - is CompletionResult.Success -> { - tangemSdkManager.changeDisplayedCardIdNumbersCount(result.data.card) - globalState?.tapWalletManager?.onCardScanned(result.data, true) - } - } - } - } + store.dispatch(HomeAction.ShouldScanCardOnResume(true)) + store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) } is WalletAction.LoadCardInfo -> { scope.launch { @@ -186,7 +176,7 @@ class WalletMiddleware { } is WalletAction.LoadData -> { scope.launch { - val scanNoteResponse = globalState?.scanNoteResponse + val scanNoteResponse = globalState?.scanResponse ?: return@launch if (!walletState?.wallets.isNullOrEmpty()) { globalState.tapWalletManager.reloadData(scanNoteResponse) @@ -196,7 +186,7 @@ class WalletMiddleware { } } is NetworkStateChanged -> { - globalState?.scanNoteResponse?.let { scanNoteResponse -> + globalState?.scanResponse?.let { scanNoteResponse -> store.dispatch(WalletAction.Warnings.CheckHashesCount.CheckHashesCountOnline) scope.launch { globalState.tapWalletManager.loadData(scanNoteResponse) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt index d3a6c2d046..39f84c1b09 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt @@ -17,7 +17,7 @@ import com.tangem.tap.domain.extensions.getSingleWallet import com.tangem.tap.domain.extensions.hasSignedHashes import com.tangem.tap.domain.extensions.remainingSignatures import com.tangem.tap.domain.isMultiwalletAllowed -import com.tangem.tap.domain.twins.isTwinCard +import com.tangem.tap.domain.twins.isTangemTwin import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.network.NetworkConnectivity import com.tangem.tap.preferencesStorage @@ -40,8 +40,8 @@ class WarningsMiddleware { } is WalletAction.Warnings.CheckHashesCount.CheckHashesCountOnline -> checkHashesCountOnline() is WalletAction.Warnings.CheckHashesCount.SaveCardId -> { - val cardId = globalState?.scanNoteResponse?.card?.cardId - cardId?.let { preferencesStorage.saveScannedCardId(it) } + val cardId = globalState?.scanResponse?.card?.cardId + cardId?.let { preferencesStorage.usedCardsPrefStorage.scanned(it) } } is WalletAction.Warnings.AppRating.RemindLater -> { preferencesStorage.appRatingLaunchObserver.applyDelayedShowing() @@ -81,7 +81,7 @@ class WarningsMiddleware { } private fun showCardWarningsIfNeeded(globalState: GlobalState?) { - globalState?.scanNoteResponse?.card?.let { card -> + globalState?.scanResponse?.card?.let { card -> globalState.warningManager?.removeWarnings(WarningMessage.Origin.Local) if (card.isTestCard) { addWarningMessage(WarningMessagesManager.testCardWarning(), autoUpdate = true) @@ -91,7 +91,7 @@ class WarningsMiddleware { showWarningLowRemainingSignaturesIfNeeded(card) if (card.firmwareVersion.type != FirmwareVersion.FirmwareType.Release) { addWarningMessage(WarningMessagesManager.devCardWarning()) - } else if (!preferencesStorage.wasCardScannedBefore(card.cardId)) { + } else if (!preferencesStorage.usedCardsPrefStorage.wasScanned(card.cardId)) { checkIfWarningNeeded(card)?.let { warning -> addWarningMessage(warning) } } if (card.firmwareVersion.type == FirmwareVersion.FirmwareType.Release) { @@ -119,7 +119,7 @@ class WarningsMiddleware { private fun checkIfWarningNeeded( card: Card, ): WarningMessage? { - if (card.isTwinCard()) return null + if (card.isTangemTwin()) return null if (card.isMultiwalletAllowed) { return if (card.hasSignedHashes()) { @@ -149,10 +149,10 @@ class WarningsMiddleware { if (store.state.walletState.hashesCountVerified != false) return if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) return - val card = store.state.globalState.scanNoteResponse?.card - if (card == null || preferencesStorage.wasCardScannedBefore(card.cardId)) return + val card = store.state.globalState.scanResponse?.card + if (card == null || preferencesStorage.usedCardsPrefStorage.wasScanned(card.cardId)) return - if (card.isTwinCard() || card.isMultiwalletAllowed) return + if (card.isTangemTwin() || card.isMultiwalletAllowed) return val validator = store.state.walletState.walletManagers.firstOrNull() as? SignatureCountValidator diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt index 9bdef4b340..d999216bba 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt @@ -2,12 +2,11 @@ package com.tangem.tap.features.wallet.redux.reducers import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.Wallet -import com.tangem.common.extensions.toHexString -import com.tangem.operations.attestation.OnlineCardVerifier import com.tangem.tap.common.extensions.* import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.FiatCurrencyName import com.tangem.tap.domain.TapError +import com.tangem.tap.domain.extensions.getArtworkUrl import com.tangem.tap.domain.getFirstToken import com.tangem.tap.domain.twins.TwinCardNumber import com.tangem.tap.features.wallet.redux.* @@ -215,29 +214,12 @@ private fun internalReduce(action: Action, state: AppState): WalletState { is WalletAction.LoadFiatRate.Success -> newState = setNewFiatRate(action.fiatRate, state.globalState.appCurrency, newState) is WalletAction.LoadArtwork -> { - val cardId = action.card.cardId - val cardPublicKey = action.card.cardPublicKey.toHexString() - val artworkUrl = when { - action.artworkId != null -> { - OnlineCardVerifier.getUrlForArtwork(cardId, cardPublicKey, action.artworkId) - } - action.card.cardId.startsWith(Artwork.SERGIO_CARD_ID) -> { - Artwork.SERGIO_CARD_URL - } - action.card.cardId.startsWith(Artwork.MARTA_CARD_ID) -> { - Artwork.MARTA_CARD_URL - } - newState.twinCardsState?.cardNumber != null -> { - when (newState.twinCardsState?.cardNumber) { + val artworkUrl = action.card.getArtworkUrl(action.artworkId) + ?: when (newState.twinCardsState?.cardNumber) { TwinCardNumber.First -> Artwork.TWIN_CARD_1 TwinCardNumber.Second -> Artwork.TWIN_CARD_2 - null -> Artwork.DEFAULT_IMG_URL + else -> Artwork.DEFAULT_IMG_URL } - } - else -> { - Artwork.DEFAULT_IMG_URL - } - } newState = newState.copy(cardImage = Artwork(artworkId = artworkUrl)) } is WalletAction.ShowDialog.QrCode -> { @@ -250,9 +232,6 @@ private fun internalReduce(action: Action, state: AppState): WalletState { ) ) } - is WalletAction.ShowDialog.ScanFails -> { - newState = newState.copy(walletDialog = WalletDialog.ScanFailsDialog) - } is WalletAction.ShowDialog.SignedHashesMultiWalletDialog -> { newState = newState.copy(walletDialog = WalletDialog.SignedHashesMultiWalletDialog) } @@ -294,30 +273,34 @@ private fun internalReduce(action: Action, state: AppState): WalletState { fun createAddressList(wallet: Wallet?, walletAddresses: WalletAddresses? = null): WalletAddresses? { if (wallet == null) return null + val listOfAddressData = wallet.createAddressesData() + // restore a selected wallet address + var indexOfSelectedWallet = 0 + walletAddresses?.let { + val index = + listOfAddressData.indexOfFirst { it.address == walletAddresses.selectedAddress.address } + if (index != -1) indexOfSelectedWallet = index + } + return WalletAddresses(listOfAddressData[indexOfSelectedWallet], listOfAddressData) +} + +fun Wallet.createAddressesData(): List { val listOfAddressData = mutableListOf() // put a defaultAddress at the first place - wallet.addresses.forEach { + addresses.forEach { val addressData = AddressData( - it.value, - it.type, - wallet.getShareUri(it.value), - wallet.getExploreUrl(it.value) + it.value, + it.type, + getShareUri(it.value), + getExploreUrl(it.value) ) - if (it.type == wallet.blockchain.defaultAddressType()) { + if (it.type == blockchain.defaultAddressType()) { listOfAddressData.add(0, addressData) } else { listOfAddressData.add(addressData) } } - - // restore a selected wallet address - var indexOfSelectedWallet = 0 - walletAddresses?.let { - val index = - listOfAddressData.indexOfFirst { it.address == walletAddresses.selectedAddress.address } - if (index != -1) indexOfSelectedWallet = index - } - return WalletAddresses(listOfAddressData[indexOfSelectedWallet], listOfAddressData) + return listOfAddressData } private fun handleCheckSignedHashesActions( diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/TwinsOnboardingFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/OnboardingTwinsFragment.kt similarity index 95% rename from app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/TwinsOnboardingFragment.kt rename to app/src/main/java/com/tangem/tap/features/wallet/ui/OnboardingTwinsFragment.kt index 1daec73f5b..7ecd724f9e 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/TwinsOnboardingFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/OnboardingTwinsFragment.kt @@ -1,4 +1,4 @@ -package com.tangem.tap.features.wallet.ui.dialogs +package com.tangem.tap.features.wallet.ui import android.os.Bundle import android.view.View @@ -15,7 +15,7 @@ import com.tangem.wallet.R import kotlinx.android.synthetic.main.fragment_twin_cards.* import kotlinx.android.synthetic.main.layout_twin_cards.* -class TwinsOnboardingFragment : Fragment(R.layout.fragment_twin_cards) { +class OnboardingTwinsFragment : Fragment(R.layout.fragment_twin_cards) { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt index c2ff298244..1592ae4eb2 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt @@ -12,7 +12,7 @@ import androidx.transition.TransitionInflater import com.squareup.picasso.Picasso import com.tangem.tap.common.SnackbarHandler import com.tangem.tap.common.extensions.* -import com.tangem.tap.common.redux.global.StateDialog +import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.features.wallet.models.PendingTransaction import com.tangem.tap.features.wallet.redux.* diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt index 98c2b0d6fe..dbd285226f 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt @@ -159,7 +159,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber { store.dispatch(GlobalAction.UpdateFeedbackInfo(store.state.walletState.walletManagers)) - store.state.globalState.scanNoteResponse?.let { scanNoteResponse -> + store.state.globalState.scanResponse?.let { scanNoteResponse -> store.dispatch(DetailsAction.PrepareScreen( scanNoteResponse.card, scanNoteResponse, store.state.walletState.walletManagers.map { it.wallet }, diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt index b3f63a0236..0f164965d4 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt @@ -2,10 +2,9 @@ package com.tangem.tap.features.wallet.ui.dialogs import android.content.Context import androidx.appcompat.app.AlertDialog +import com.tangem.tap.common.extensions.dispatchDialogHide import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.features.feedback.ScanFailsEmail -import com.tangem.tap.features.home.redux.HomeAction -import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.store import com.tangem.wallet.R @@ -23,10 +22,7 @@ class ScanFailsDialog { store.dispatch(GlobalAction.SendFeedback(ScanFailsEmail())) } setNegativeButton(R.string.common_cancel) { _, _ -> } - setOnDismissListener { - store.dispatch(HomeAction.HideDialog) - store.dispatch(WalletAction.HideDialog) - } + setOnDismissListener { store.dispatchDialogHide() } }.create() } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt index d40eaa4496..aa0aaee36c 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt @@ -4,7 +4,7 @@ import android.app.Dialog import androidx.recyclerview.widget.LinearLayoutManager import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.show -import com.tangem.tap.common.redux.global.StateDialog +import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.features.tokens.redux.TokensAction @@ -14,7 +14,6 @@ import com.tangem.tap.features.wallet.redux.WalletState import com.tangem.tap.features.wallet.ui.BalanceStatus import com.tangem.tap.features.wallet.ui.WalletFragment import com.tangem.tap.features.wallet.ui.adapters.WalletAdapter -import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog import com.tangem.tap.features.wallet.ui.dialogs.SignedHashesWarningDialog import com.tangem.tap.store import com.tangem.wallet.R @@ -134,9 +133,6 @@ class MultiWalletView : WalletView { val fragment = fragment ?: return val context = fragment.context ?: return when (walletDialog) { - is WalletDialog.ScanFailsDialog -> { - if (dialog == null) dialog = ScanFailsDialog.create(context).apply { show() } - } is WalletDialog.SignedHashesMultiWalletDialog -> { if (dialog == null) { dialog = SignedHashesWarningDialog.create(context).apply { show() } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt index b80b989b70..d2e4e77cc5 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt @@ -6,7 +6,7 @@ import android.view.ViewGroup import android.widget.Button import androidx.recyclerview.widget.LinearLayoutManager import com.tangem.tap.common.extensions.* -import com.tangem.tap.common.redux.global.StateDialog +import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.twins.TwinCardNumber @@ -107,7 +107,7 @@ class SingleWalletView : WalletView { tv_twin_card_number.hide() } if (twinCardsState?.showTwinOnboarding == true) { - store.dispatch(NavigationAction.NavigateTo(AppScreen.TwinsOnboarding)) + store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingTwins)) } @@ -248,9 +248,6 @@ class SingleWalletView : WalletView { this.show(walletDialog.amounts) } } - is WalletDialog.ScanFailsDialog -> { - if (dialog == null) dialog = ScanFailsDialog.create(context).apply { show() } - } is WalletDialog.SignedHashesMultiWalletDialog -> { if (dialog == null) { dialog = SignedHashesWarningDialog.create(context).apply { show() } diff --git a/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt b/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt index 871e3eb3d5..12d5eaa695 100644 --- a/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt +++ b/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt @@ -19,10 +19,13 @@ class PreferencesStorage(applicationContext: Application) { private val preferences: SharedPreferences = applicationContext.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE) val appRatingLaunchObserver: AppRatingLaunchObserver + val usedCardsPrefStorage: UsedCardsPrefStorage init { incrementLaunchCounter() appRatingLaunchObserver = AppRatingLaunchObserver(preferences, getCountOfLaunches()) + usedCardsPrefStorage = UsedCardsPrefStorage(preferences) + usedCardsPrefStorage.migrate() } private val fiatCurrenciesAdapter: JsonAdapter> by lazy { @@ -54,20 +57,11 @@ class PreferencesStorage(applicationContext: Application) { fun getCountOfLaunches(): Int = preferences.getInt(APP_LAUNCH_COUNT_KEY, 1) - fun saveScannedCardId(cardId: String) { - val scannedCardsIds: String = restoreScannedCardIds() - if (!scannedCardsIds.contains(cardId)) { - preferences.edit().putString(SCANNED_CARDS_IDS_KEY, "$scannedCardsIds$cardId, ").apply() - } - } - + @Deprecated("Use UsedCardsPrefStorage instead") fun wasCardScannedBefore(cardId: String): Boolean { - return restoreScannedCardIds().contains(cardId) + return usedCardsPrefStorage.wasScanned(cardId) } - private fun restoreScannedCardIds(): String = - preferences.getString(SCANNED_CARDS_IDS_KEY, "") ?: "" - fun saveDisclaimerAccepted() { preferences.edit().putBoolean(DISCLAIMER_ACCEPTED_KEY, true).apply() } @@ -93,7 +87,6 @@ class PreferencesStorage(applicationContext: Application) { private const val PREFERENCES_NAME = "tapPrefs" private const val APP_CURRENCY_KEY = "appCurrency" private const val FIAT_CURRENCIES_KEY = "fiatCurrencies" - private const val SCANNED_CARDS_IDS_KEY = "scannedCardIds" private const val DISCLAIMER_ACCEPTED_KEY = "disclaimerAccepted" private const val TWINS_ONBOARDING_SHOWN_KEY = "twinsOnboardingShown" private const val APP_LAUNCH_COUNT_KEY = "launchCount" @@ -102,20 +95,23 @@ class PreferencesStorage(applicationContext: Application) { } class AppRatingLaunchObserver( - private val preferences: SharedPreferences, - private val launchCounts: Int, + private val preferences: SharedPreferences, + private val launchCounts: Int, ) { private val K_SHOW_RATING_AT_LAUNCH_COUNT = "showRatingDialogAtLaunchCount" private val K_FUNDS_FOUND_DATE = "fundsFoundDate" private val K_USER_WAS_INTERACT_WITH_RATING = "userWasInteractWithRating" + private val FUNDS_FOUND_DATE_UNDEFINED = -1L private val deferShowing = 20 private val firstShowing = 3 private var fundsFoundDate: Calendar? = null init { - val dateTimeMs = preferences.getLong(K_FUNDS_FOUND_DATE, -1) - if (dateTimeMs > 0) fundsFoundDate = Calendar.getInstance().apply { timeInMillis = dateTimeMs } + val msWhenFundsWasFound = preferences.getLong(K_FUNDS_FOUND_DATE, FUNDS_FOUND_DATE_UNDEFINED) + if (msWhenFundsWasFound != FUNDS_FOUND_DATE_UNDEFINED) { + fundsFoundDate = Calendar.getInstance().apply { timeInMillis = msWhenFundsWasFound } + } } fun foundWalletWithFunds() { diff --git a/app/src/main/java/com/tangem/tap/persistence/UsedCardsPrefStorage.kt b/app/src/main/java/com/tangem/tap/persistence/UsedCardsPrefStorage.kt new file mode 100644 index 0000000000..17238689f5 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/persistence/UsedCardsPrefStorage.kt @@ -0,0 +1,102 @@ +package com.tangem.tap.persistence + +import android.content.SharedPreferences +import androidx.core.content.edit +import com.tangem.common.json.MoshiJsonConverter +import com.tangem.tap.common.extensions.replaceByOrAdd +import timber.log.Timber + +/** +[REDACTED_AUTHOR] + */ +class UsedCardsPrefStorage( + private val preferences: SharedPreferences, +) { + + private val jsonConverter = MoshiJsonConverter.INSTANCE + + internal fun migrate() { + val scannedIds = preferences.getString(SCANNED_CARDS_IDS_KEY, null) ?: return + + val usedCardsInfo = scannedIds.split(",").map { UsedCardInfo(it.trim(), true) } + if (save(usedCardsInfo.toMutableList())) { + preferences.edit { this.remove(SCANNED_CARDS_IDS_KEY) } + } + } + + fun scanned(cardId: String) { + val restoredList = restore() + val foundItem = findCardInfo(cardId, restoredList)?.copy(isScanned = true) + ?: UsedCardInfo(cardId, true) + + save(foundItem, restoredList) + } + + fun wasScanned(cardId: String): Boolean { + return findCardInfo(cardId)?.isScanned ?: false + } + + fun activationStarted(cardId: String) { + val restoredList = restore() + val foundItem = findCardInfo(cardId, restoredList)?.copy(isActivationStarted = true) + ?: UsedCardInfo(cardId, isActivationStarted = true) + + save(foundItem, restoredList) + } + + fun activationIsStarted(cardId: String): Boolean { + return findCardInfo(cardId)?.isActivationStarted ?: false + } + + fun activationFinished(cardId: String) { + val restoredList = restore() + val foundItem = findCardInfo(cardId, restoredList)?.copy(isActivationStarted = false) + ?: UsedCardInfo(cardId, isActivationStarted = false) + + save(foundItem, restoredList) + } + + private fun findCardInfo(cardId: String, list: MutableList? = null): UsedCardInfo? { + val findInList = list ?: restore() + return findInList.firstOrNull { it.cardId == cardId } + } + + private fun save(usedCardInfo: UsedCardInfo?, usedCardsInfo: MutableList) { + val info = usedCardInfo ?: return + + usedCardsInfo.replaceByOrAdd(info) { it.cardId == info.cardId } + save(usedCardsInfo) + } + + private fun save(list: MutableList): Boolean { + return try { + val json = jsonConverter.toJson(list) + preferences.edit { putString(USED_CARDS_INFO, json) } + true + } catch (ex: Exception) { + Timber.e(ex) + false + } + } + + private fun restore(): MutableList { + val json = preferences.getString(USED_CARDS_INFO, null) ?: return mutableListOf() + return try { + jsonConverter.fromJson(json, jsonConverter.typedList(UsedCardInfo::class.java))!! + } catch (ex: Exception) { + preferences.edit(true) { remove(USED_CARDS_INFO) } + mutableListOf() + } + } + + companion object { + private const val USED_CARDS_INFO = "usedCardsInfo" + private const val SCANNED_CARDS_IDS_KEY = "scannedCardIds" + } +} + +private data class UsedCardInfo( + val cardId: String, + val isScanned: Boolean = false, + val isActivationStarted: Boolean = false, +) \ No newline at end of file diff --git a/app/src/main/res/drawable/anim_progress_balance.xml b/app/src/main/res/drawable/anim_progress_balance.xml new file mode 100644 index 0000000000..ada4670265 --- /dev/null +++ b/app/src/main/res/drawable/anim_progress_balance.xml @@ -0,0 +1,9 @@ + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_half_transparent_overlay.xml b/app/src/main/res/drawable/bg_half_transparent_overlay.xml new file mode 100644 index 0000000000..4b5937d793 --- /dev/null +++ b/app/src/main/res/drawable/bg_half_transparent_overlay.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_activation_success.xml b/app/src/main/res/drawable/ic_activation_success.xml new file mode 100644 index 0000000000..7957c82493 --- /dev/null +++ b/app/src/main/res/drawable/ic_activation_success.xml @@ -0,0 +1,24 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_arrow_refresh.xml b/app/src/main/res/drawable/ic_arrow_refresh.xml new file mode 100644 index 0000000000..1acc645cec --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_refresh.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_copy_green.xml b/app/src/main/res/drawable/ic_copy_green.xml new file mode 100644 index 0000000000..a4145be642 --- /dev/null +++ b/app/src/main/res/drawable/ic_copy_green.xml @@ -0,0 +1,19 @@ + + + + diff --git a/app/src/main/res/drawable/ic_home_back_card.xml b/app/src/main/res/drawable/ic_home_back_card.xml new file mode 100644 index 0000000000..f15c47ba3f --- /dev/null +++ b/app/src/main/res/drawable/ic_home_back_card.xml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_home_front_card.xml b/app/src/main/res/drawable/ic_home_front_card.xml new file mode 100644 index 0000000000..19969e806e --- /dev/null +++ b/app/src/main/res/drawable/ic_home_front_card.xml @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_progress_balance.xml b/app/src/main/res/drawable/ic_progress_balance.xml new file mode 100644 index 0000000000..d31defe1e7 --- /dev/null +++ b/app/src/main/res/drawable/ic_progress_balance.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_share_green.xml b/app/src/main/res/drawable/ic_share_green.xml new file mode 100644 index 0000000000..57ee232b90 --- /dev/null +++ b/app/src/main/res/drawable/ic_share_green.xml @@ -0,0 +1,12 @@ + + + diff --git a/app/src/main/res/drawable/ripple.xml b/app/src/main/res/drawable/ripple.xml new file mode 100644 index 0000000000..c6e07a265f --- /dev/null +++ b/app/src/main/res/drawable/ripple.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_circle.xml b/app/src/main/res/drawable/shape_circle.xml index f857819f4d..59a662d141 100644 --- a/app/src/main/res/drawable/shape_circle.xml +++ b/app/src/main/res/drawable/shape_circle.xml @@ -1,6 +1,9 @@ - - - + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_rectangle_rounded_100.xml b/app/src/main/res/drawable/shape_rectangle_rounded_100.xml new file mode 100644 index 0000000000..7f26fb8be5 --- /dev/null +++ b/app/src/main/res/drawable/shape_rectangle_rounded_100.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_rectangle_rounded_8.xml b/app/src/main/res/drawable/shape_rectangle_rounded_8.xml new file mode 100644 index 0000000000..a788f39093 --- /dev/null +++ b/app/src/main/res/drawable/shape_rectangle_rounded_8.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_refresh_button.xml b/app/src/main/res/drawable/shape_refresh_button.xml new file mode 100644 index 0000000000..ac9508438d --- /dev/null +++ b/app/src/main/res/drawable/shape_refresh_button.xml @@ -0,0 +1,15 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout-h680dp/layout_onboarding_bottom_action_views.xml b/app/src/main/res/layout-h680dp/layout_onboarding_bottom_action_views.xml new file mode 100644 index 0000000000..a603013d47 --- /dev/null +++ b/app/src/main/res/layout-h680dp/layout_onboarding_bottom_action_views.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_onboarding_address_info.xml b/app/src/main/res/layout/dialog_onboarding_address_info.xml new file mode 100644 index 0000000000..92c780e969 --- /dev/null +++ b/app/src/main/res/layout/dialog_onboarding_address_info.xml @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_disclaimer.xml b/app/src/main/res/layout/fragment_disclaimer.xml index 0c771b9995..bec0ceace0 100644 --- a/app/src/main/res/layout/fragment_disclaimer.xml +++ b/app/src/main/res/layout/fragment_disclaimer.xml @@ -28,17 +28,15 @@ android:id="@+id/cl_details_confirm" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="33dp" app:layout_behavior="@string/appbar_scrolling_view_behavior"> + app:layout_constraintBottom_toBottomOf="parent"> @@ -62,18 +60,27 @@ app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintGuide_percent="0.37" /> + + + app:layout_constraintStart_toEndOf="@+id/guideline" + android:layout_marginBottom="24dp" /> diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml deleted file mode 100644 index 33157a5fe6..0000000000 --- a/app/src/main/res/layout/fragment_home.xml +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_onboarding_main.xml b/app/src/main/res/layout/fragment_onboarding_main.xml new file mode 100644 index 0000000000..86c9138e69 --- /dev/null +++ b/app/src/main/res/layout/fragment_onboarding_main.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_onboarding_wallet.xml b/app/src/main/res/layout/fragment_onboarding_wallet.xml new file mode 100644 index 0000000000..77d9ef65f8 --- /dev/null +++ b/app/src/main/res/layout/fragment_onboarding_wallet.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_onboarding_bottom_action_views.xml b/app/src/main/res/layout/layout_onboarding_bottom_action_views.xml new file mode 100644 index 0000000000..54f8722018 --- /dev/null +++ b/app/src/main/res/layout/layout_onboarding_bottom_action_views.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_onboarding_home.xml b/app/src/main/res/layout/layout_onboarding_home.xml new file mode 100644 index 0000000000..6e4d367320 --- /dev/null +++ b/app/src/main/res/layout/layout_onboarding_home.xml @@ -0,0 +1,36 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_onboarding_note.xml b/app/src/main/res/layout/layout_onboarding_note.xml new file mode 100644 index 0000000000..8393ecde9e --- /dev/null +++ b/app/src/main/res/layout/layout_onboarding_note.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_pseudo_toolbar.xml b/app/src/main/res/layout/layout_pseudo_toolbar.xml new file mode 100644 index 0000000000..f9ae5fe2ab --- /dev/null +++ b/app/src/main/res/layout/layout_pseudo_toolbar.xml @@ -0,0 +1,23 @@ + + + + + + diff --git a/app/src/main/res/layout/layout_token.xml b/app/src/main/res/layout/layout_token.xml index 8e03d11ca1..fc9486f79c 100644 --- a/app/src/main/res/layout/layout_token.xml +++ b/app/src/main/res/layout/layout_token.xml @@ -20,7 +20,7 @@ android:id="@+id/v_token_background" android:layout_width="match_parent" android:layout_height="0dp" - android:background="@color/backgroundGray" + android:background="@color/backgroundLightGray" app:layout_constraintTop_toBottomOf="@id/tv_token_title" app:layout_constraintBottom_toBottomOf="@id/tv_token_fiat_amount"/> diff --git a/app/src/main/res/layout/lp_onboarding_create_wallet.xml b/app/src/main/res/layout/lp_onboarding_create_wallet.xml new file mode 100644 index 0000000000..fa4505f353 --- /dev/null +++ b/app/src/main/res/layout/lp_onboarding_create_wallet.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/lp_onboarding_done.xml b/app/src/main/res/layout/lp_onboarding_done.xml new file mode 100644 index 0000000000..c6294b65ca --- /dev/null +++ b/app/src/main/res/layout/lp_onboarding_done.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/lp_onboarding_done_activation.xml b/app/src/main/res/layout/lp_onboarding_done_activation.xml new file mode 100644 index 0000000000..fab21fdfb2 --- /dev/null +++ b/app/src/main/res/layout/lp_onboarding_done_activation.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/lp_onboarding_topup_wallet.xml b/app/src/main/res/layout/lp_onboarding_topup_wallet.xml new file mode 100644 index 0000000000..65705f3fce --- /dev/null +++ b/app/src/main/res/layout/lp_onboarding_topup_wallet.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/test_leapfrog_fragment.xml b/app/src/main/res/layout/test_leapfrog_fragment.xml new file mode 100644 index 0000000000..52b720ad27 --- /dev/null +++ b/app/src/main/res/layout/test_leapfrog_fragment.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/test_leapfrog_widget.xml b/app/src/main/res/layout/test_leapfrog_widget.xml new file mode 100644 index 0000000000..3fbf992180 --- /dev/null +++ b/app/src/main/res/layout/test_leapfrog_widget.xml @@ -0,0 +1,23 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_confetti.xml b/app/src/main/res/layout/view_confetti.xml new file mode 100644 index 0000000000..ad07b6e0bd --- /dev/null +++ b/app/src/main/res/layout/view_confetti.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_onboarding_progress.xml b/app/src/main/res/layout/view_onboarding_progress.xml new file mode 100644 index 0000000000..415f8bd19c --- /dev/null +++ b/app/src/main/res/layout/view_onboarding_progress.xml @@ -0,0 +1,10 @@ + + diff --git a/app/src/main/res/layout/view_onboarding_refresh_balance.xml b/app/src/main/res/layout/view_onboarding_refresh_balance.xml new file mode 100644 index 0000000000..c9bf3aacb2 --- /dev/null +++ b/app/src/main/res/layout/view_onboarding_refresh_balance.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_onboarding_tv_balance.xml b/app/src/main/res/layout/view_onboarding_tv_balance.xml new file mode 100644 index 0000000000..5dd54d7067 --- /dev/null +++ b/app/src/main/res/layout/view_onboarding_tv_balance.xml @@ -0,0 +1,29 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/raw/anim_confetti.json b/app/src/main/res/raw/anim_confetti.json new file mode 100644 index 0000000000..f2d1e7f974 --- /dev/null +++ b/app/src/main/res/raw/anim_confetti.json @@ -0,0 +1 @@ +{"v":"5.6.3","fr":30,"ip":0,"op":120,"w":1242,"h":2688,"nm":"Comp 1","ddd":1,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 14","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[692,1011.9999999999999,0],"ix":2},"a":{"a":0,"k":[71,-332,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-48,204],[72.555,109.763],[-9.41,53.993],[11.376,60.133],[-20.778,48.214],[-43.71,34.149],[-28,80]],"o":[[48,-204],[-46.308,-70.056],[6.44,-36.951],[-18.438,-97.46],[10.775,-25.002],[128,-100],[28,-80]],"v":[[71,-332],[151,-620],[209.484,-736.613],[141,-856],[199.096,-959.097],[193,-1114],[371,-1280]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":14.077,"s":[0]},{"t":46.384765625,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":11.923,"s":[0]},{"t":42.0771484375,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim In Out Path","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":2,"op":112,"st":2,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 13","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[648,1040,0],"ix":2},"a":{"a":0,"k":[27,-304,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[28,112],[154,-50],[122,52],[60,164]],"o":[[0,0],[-24,-96],[-150.386,48.827],[-141.68,-60.388],[-60,-164]],"v":[[27,-304],[-107,-530],[-249,-720],[-335,-908],[-537,-1284]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3.308,"s":[0]},{"t":35.615234375,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-1,"s":[0]},{"t":27,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim In Out Path","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":112,"st":-5,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 12","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[674,1228,0],"ix":2},"a":{"a":0,"k":[53,-116,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-34,-98],[21,-81],[-20,-38],[-18,-48],[-8,-34],[-62,-38],[-24,-36],[-62,-30],[-84,-22]],"o":[[34,98],[-11.008,42.46],[12.721,24.17],[18,48],[9.222,39.192],[62,38],[24,36],[62,30],[84,22]],"v":[[53,-116],[134,25],[94,107],[181,157],[150,265],[237,323],[250,463],[383,540],[480,694]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.03137254901960784,0.807843137254902,0.3607843137254902,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":22.692,"s":[0]},{"t":55,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":20.538,"s":[0]},{"t":50.6923828125,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim In Out Path","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":6,"op":112,"st":6,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 11","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[704,1180,0],"ix":2},"a":{"a":0,"k":[83,-164,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-38,-60],[-40.126,23.279],[42.5,-0.5],[-35,2.5],[-47.28,-26.863],[-20,-30],[-21.113,-24.13],[-34,-42],[-16,-40]],"o":[[38,60],[65.5,-38],[-20.024,0.236],[41.06,-2.933],[44,25],[20,30],[14,16],[34,42],[16,40]],"v":[[83,-164],[166.5,-75],[217.5,-41.5],[222.5,-81.5],[377,-77],[378,1],[458,17],[478,91],[616,178]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":7.615,"s":[0]},{"t":39.9228515625,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3.308,"s":[0]},{"t":31.3076171875,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim In Out Path","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":112,"st":-3,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Shape Layer 10","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[730,1100,0],"ix":2},"a":{"a":0,"k":[109,-244,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-72,-10],[-51,10],[13,-16],[-24.905,-7.729],[-10,10]],"o":[[72,10],[51,-10],[-10.168,12.514],[29,9],[10,-10]],"v":[[109,-244],[483,-178],[533,-129],[556,-83],[618,-93]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":16.23,"s":[0]},{"t":48.5380859375,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":14.077,"s":[0]},{"t":44.23046875,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim In Out Path","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":3,"op":112,"st":3,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Shape Layer 9","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[678,1086,0],"ix":2},"a":{"a":0,"k":[57,-258,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-106,94],[-78,82],[-2,37],[-15,15],[-27.82,25.594],[-23,24],[-38.877,17.279],[-24,-14]],"o":[[106,-94],[78,-82],[2.405,-44.488],[18.028,-18.028],[25,-23],[19.594,-20.446],[36,-16],[24,14]],"v":[[57,-258],[204,-404],[337,-493],[346,-597],[426,-624],[454,-699],[542,-728],[611,-805]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":7.615,"s":[0]},{"t":39.9228515625,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3.308,"s":[0]},{"t":31.3076171875,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim In Out Path","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":112,"st":-3,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Shape Layer 8","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[640,1170,0],"ix":2},"a":{"a":0,"k":[19,-174,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[92,-22],[38,-38],[78,-8],[38,-10],[10,-20]],"o":[[-92,22],[-38,38],[-78,8],[-38,10],[-10,20]],"v":[[19,-174],[-115,-120],[-283,-46],[-423,-50],[-517,32]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":7.615,"s":[0]},{"t":39.9228515625,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5.462,"s":[0]},{"t":35.615234375,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim In Out Path","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Shape Layer 7","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[661,1191,0],"ix":2},"a":{"a":0,"k":[40,-153,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[16.398,-37.618],[29.992,-37.49],[41,-30],[60.195,-37.77],[80.591,1.465],[-31.635,-5.678],[109.189,24.816],[22,-30]],"o":[[-34,78],[-44,55],[-59.359,43.434],[-51,32],[-55,-1],[39,7],[-132,-30],[-22,30]],"v":[[40,-153],[-41,-58],[-104,84],[-253,140],[-359,228],[-381,175],[-459,204],[-652,321]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":18.385,"s":[0]},{"t":50.6923828125,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":14.077,"s":[0]},{"t":42.0771484375,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim In Out Path","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":2,"op":112,"st":2,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"Shape Layer 6","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[646,1126,0],"ix":2},"a":{"a":0,"k":[25,-218,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[90,52],[20,-18],[44,46],[60,-36],[34,64],[-46,-10],[34,-50],[52,12],[28,10]],"o":[[-90,-52],[-20,18],[-44,-46],[-60,36],[-34,-64],[46,10],[-34,50],[-52,-12],[-28,-10]],"v":[[25,-218],[-175,-248],[-271,-286],[-379,-328],[-487,-310],[-535,-444],[-469,-396],[-547,-342],[-627,-366]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5.462,"s":[0]},{"t":37.76953125,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3.308,"s":[0]},{"t":35.615234375,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim In Out Path","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":0,"ind":10,"ty":3,"nm":"Null 1","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":184,"ix":10},"p":{"a":0,"k":[633,1188,0],"ix":2},"a":{"a":0,"k":[12,-198,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":112,"st":0,"bm":0},{"ddd":1,"ind":11,"ty":4,"nm":"Shape Layer 41","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-11,"s":[0]},{"t":87.9189453125,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10,"x":"var $bm_rt;\n$bm_rt = transform.rotation;"},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0.179,"s":[6.395,-188.007,0],"to":[3.99,-626.075,396.962],"ti":[-3.99,626.075,-396.962]},{"t":34.7802734375,"s":[30.337,-3944.458,2381.774]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7,-27],[35,7],[-41.906,32.862],[0,0]],"o":[[-7,27],[32,27],[23.167,-18.167],[0,0]],"v":[[72,-317],[4,-279],[91,-263.167],[107.167,-289.667]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.345,0.381,0.671,0.5,0.491,0.265,0.624,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":12,"ty":4,"nm":"Shape Layer 40","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":80.50390625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0.179,"s":[6.395,-188.007,0],"to":[172.235,-647.46,153.434],"ti":[-172.235,647.46,-153.434]},{"t":80,"s":[1039.806,-4072.764,920.605]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":13,"ty":4,"nm":"Shape Layer 39","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0.179,"s":[0]},{"t":91.6259765625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0.179,"s":[6.395,-188.007,0],"to":[140.553,-166.199,-42.398],"ti":[-140.553,166.199,42.398]},{"t":15.0078125,"s":[849.715,-1185.203,-254.39]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":0,"ind":14,"ty":3,"nm":"Null 2","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":-49,"ix":10},"p":{"a":0,"k":[621,1344,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":112,"st":-4,"bm":0},{"ddd":0,"ind":15,"ty":3,"nm":"Null 3","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":82,"ix":10},"p":{"a":0,"k":[621,1344,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":112,"st":-4,"bm":0},{"ddd":1,"ind":16,"ty":4,"nm":"Shape Layer 105","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-11,"s":[0]},{"t":87.6826171875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10,"x":"var $bm_rt;\n$bm_rt = transform.rotation;"},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[727.321,51.433,435.455],"ti":[-727.321,-51.433,-435.455]},{"t":34.544921875,"s":[4250.962,186.167,2612.729]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7,-27],[35,7],[-41.906,32.862],[0,0]],"o":[[-7,27],[32,27],[23.167,-18.167],[0,0]],"v":[[72,-317],[4,-279],[91,-263.167],[107.167,-289.667]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.345,0.381,0.671,0.5,0.491,0.265,0.624,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":17,"ty":4,"nm":"Shape Layer 104","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":80.50390625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[527.848,55.985,153.434],"ti":[-527.848,-55.985,-153.434]},{"t":54.552734375,"s":[3054.124,213.477,920.605]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":18,"ty":4,"nm":"Shape Layer 103","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":91.6259765625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[4.145,138.496,-42.398],"ti":[-4.145,-138.496,42.398]},{"t":15.0078125,"s":[-88.093,708.543,-254.39]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":19,"ty":4,"nm":"Shape Layer 102","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":94.09765625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":-0.179,"s":[-112.965,-122.431,0],"to":[-61.615,187.567,26.907],"ti":[61.615,-187.567,-26.907]},{"t":31.0732421875,"s":[-482.654,1002.974,161.442]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":20,"ty":4,"nm":"Shape Layer 101","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-1.057,"s":[0]},{"t":84.2109375,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.824},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[-90.863,90.863,0],"ti":[88.083,-357.903,-768.642]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.164},"t":17.479,"s":[-658.144,422.748,0],"to":[-84.157,341.951,734.383],"ti":[-714.89,-84.078,-197.259]},{"t":86.6826171875,"s":[3631.196,927.217,1183.551]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":21,"ty":4,"nm":"Shape Layer 100","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":100.2763671875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.796},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[-134.704,42.073,0],"ti":[7.203,-205.415,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.157},"t":28,"s":[-921.188,130.006,0],"to":[-19.567,558.035,0],"ti":[-540.328,-229.432,0]},{"t":116,"s":[2320.778,1506.598,0]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":22,"ty":4,"nm":"Shape Layer 99","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":86.6826171875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.775},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[-115.257,9.245,0],"ti":[52.068,-163.912,-53.249]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.155},"t":19.951,"s":[-804.504,-66.963,0],"to":[-48.777,153.551,49.883],"ti":[-156.371,-109.917,-25.485]},{"t":91,"s":[2733.865,1051.661,319.492]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":23,"ty":4,"nm":"Shape Layer 98","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":146,"s":[1440]}],"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":146,"s":[1440]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.855},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[-138.996,-53.461,0],"ti":[-421.763,15.031,16.673]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.174},"t":21.187,"s":[-946.942,-443.198,0],"to":[421.763,-15.031,-16.673],"ti":[-560.76,-38.43,16.673]},{"t":100.2763671875,"s":[2417.616,-212.616,-100.037]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":24,"ty":4,"nm":"Shape Layer 97","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":144.7646484375,"s":[1080]}],"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":144.7646484375,"s":[59]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.673},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[-87.16,25.736,-45.544],"ti":[-273.542,-218.754,122.377]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.14},"t":31.073,"s":[-660.653,26.246,-273.265],"to":[129.994,103.957,-58.156],"ti":[-320.287,-255.677,-112.981]},{"t":111.3984375,"s":[3683.402,989.215,1150.251]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[3.444,9.757]],"o":[[0,0],[0,0],[0,0],[-4.941,-14]],"v":[[72,-317],[4,-279],[24.151,-217.936],[88.307,-269.617]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.345,0.381,0.671,0.5,0.491,0.265,0.624,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":25,"ty":4,"nm":"Shape Layer 96","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-9,"s":[0]},{"t":89.154296875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.853},"o":{"x":0.167,"y":0.167},"t":0,"s":[-89.038,-0.153,0],"to":[-552.568,-84.135,-9.152],"ti":[-1514.035,-462.134,43.281]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.183},"t":38,"s":[95.207,-392.324,-54.91],"to":[684.779,209.018,-19.576],"ti":[-315.276,-115.122,160.297]},{"t":97,"s":[1986.864,298.408,-1016.689]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":26,"ty":4,"nm":"Shape Layer 95","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-14,"s":[0]},{"t":81.740234375,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.774},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[-110.347,-144.949,-65.532],"ti":[-365.662,-268.839,-214.28]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.147},"t":26,"s":[-799.777,-997.863,-393.192],"to":[365.662,268.839,214.28],"ti":[-476.009,-413.788,-279.812]},{"t":76.796875,"s":[2056.275,1484.866,1285.683]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":27,"ty":4,"nm":"Shape Layer 94","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-9,"s":[0]},{"t":100.2763671875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[-90.481,-247.593,46.629],"ti":[90.481,247.593,-46.629]},{"t":44,"s":[-680.583,-1613.73,279.773]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":28,"ty":4,"nm":"Shape Layer 93","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":91.6259765625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[69.003,-110.281,-16.168],"ti":[-69.003,110.281,16.168]},{"t":32,"s":[276.323,-789.855,-97.005]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":29,"ty":4,"nm":"Shape Layer 92","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-10,"s":[0]},{"t":81.740234375,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[305.417,119.743,41.862],"ti":[-305.417,-119.743,-41.862]},{"t":27,"s":[1694.805,590.289,251.17]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":30,"ty":4,"nm":"Shape Layer 91","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":86.6826171875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[318.521,75.426,-43.518],"ti":[-318.521,-75.426,43.518]},{"t":24,"s":[1773.433,324.385,-261.107]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":31,"ty":4,"nm":"Shape Layer 90","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-4.765,"s":[0]},{"t":89.154296875,"s":[1080]}],"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":89.154296875,"s":[1800]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[77.354,134.863,-215.584],"ti":[-77.354,-134.863,215.584]},{"t":27.3662109375,"s":[326.43,681.005,-1293.506]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":32,"ty":4,"nm":"Shape Layer 89","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-1.057,"s":[0]},{"t":102.748046875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10,"x":"var $bm_rt;\n$bm_rt = transform.rotation;"},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[301.698,164.851,-47.459],"ti":[-301.698,-164.851,47.459]},{"t":103.9833984375,"s":[1697.223,866.674,-284.755]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[84.659,-333.362],[4,-279],[45.665,-243.617],[119.735,-297.935]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.345,0.381,0.671,0.5,0.491,0.265,0.624,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":33,"ty":4,"nm":"Shape Layer 88","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-17,"s":[0]},{"t":95.3330078125,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[159.503,223.195,-0.572],"ti":[-159.503,-223.195,0.572]},{"t":37.251953125,"s":[844.051,1216.741,-3.43]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":34,"ty":4,"nm":"Shape Layer 87","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-11,"s":[0]},{"t":101.51171875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[33.76,196.346,-7.096],"ti":[-33.76,-196.346,7.096]},{"t":40.958984375,"s":[89.594,1055.648,-42.576]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":35,"ty":4,"nm":"Shape Layer 86","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-11,"s":[0]},{"t":85.447265625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[-16.649,163.663,0],"ti":[16.649,-163.663,0]},{"t":24.89453125,"s":[-212.861,859.547,0]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":36,"ty":4,"nm":"Shape Layer 85","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":101.51171875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[-71.747,128.05,0],"ti":[71.747,-128.05,0]},{"t":49.609375,"s":[-543.449,645.87,0]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":37,"ty":4,"nm":"Shape Layer 84","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-4.765,"s":[0]},{"t":112.6337890625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.728},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[-81.837,27.324,33.544],"ti":[-153.275,-262.436,-33.544]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.131},"t":21,"s":[-603.986,41.512,201.262],"to":[153.275,262.436,33.544],"ti":[-235.112,-235.112,0]},{"t":50.845703125,"s":[806.688,1452.185,201.262]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":38,"ty":4,"nm":"Shape Layer 83","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-6,"s":[0]},{"t":85.447265625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.652},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[-171.831,20.57,3.486],"ti":[602.026,-193.886,-848.553]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.142},"t":35,"s":[-1143.953,0.988,20.917],"to":[-778.519,946.911,884.934],"ti":[-1189.71,-488.979,-884.978]},{"t":139,"s":[5994.306,2934.863,5330.786]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":39,"ty":4,"nm":"Shape Layer 82","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-23,"s":[0]},{"t":97.8046875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,135],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.883},"o":{"x":0.167,"y":0.167},"t":0,"s":[-112.965,-122.431,0],"to":[-120.682,-96.313,17.582],"ti":[57.484,-266.769,15.802]},{"i":{"x":0.833,"y":0.817},"o":{"x":0.167,"y":0.198},"t":15.008,"s":[-837.059,-700.31,105.493],"to":[-25.433,118.027,-6.991],"ti":[-511.749,-461.825,46.841]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.143},"t":54.553,"s":[271.713,386.113,403.89],"to":[308.693,278.579,-28.255],"ti":[-142.63,-100.689,10.833]},{"t":76.796875,"s":[1060.877,1065.009,-67.253]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":40,"ty":4,"nm":"Shape Layer 81","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-12,"s":[0]},{"t":102.748046875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[135.751,-389.001,217.62],"ti":[-135.751,389.001,-217.62]},{"t":27,"s":[676.808,-2462.175,1305.721]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7,-27],[35,7],[-41.906,32.862],[0,0]],"o":[[-7,27],[32,27],[23.167,-18.167],[0,0]],"v":[[72,-317],[4,-279],[91,-263.167],[107.167,-289.667]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.345,0.381,0.671,0.5,0.491,0.265,0.624,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":41,"ty":4,"nm":"Shape Layer 80","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-25,"s":[0]},{"t":81.740234375,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.849},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[-70.649,26.407,0],"ti":[-291.085,-122.356,90.248]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.171},"t":16,"s":[-561.59,30.269,0],"to":[291.085,122.356,-90.248],"ti":[-361.735,-95.95,90.248]},{"t":82.9755859375,"s":[1608.818,605.966,-541.488]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":42,"ty":4,"nm":"Shape Layer 79","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":95.3330078125,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.704},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[-49.159,-63.557,4.223],"ti":[-0.005,148.838,-20.562]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.144},"t":24,"s":[-432.65,-509.511,25.34],"to":[0.017,-477.286,65.936],"ti":[-415.767,197.78,-61.713]},{"t":91.6259765625,"s":[2061.954,-1696.192,395.618]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":43,"ty":4,"nm":"Shape Layer 78","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-20,"s":[0]},{"t":87.9189453125,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[33.977,-166.361,22.596],"ti":[-33.977,166.361,-22.596]},{"t":30,"s":[66.167,-1126.338,135.575]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":44,"ty":4,"nm":"Shape Layer 77","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-16,"s":[0]},{"t":99.041015625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[345.992,-256.663,189.679],"ti":[-345.992,256.663,-189.679]},{"t":27,"s":[1938.258,-1668.151,1138.074]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":45,"ty":4,"nm":"Shape Layer 76","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":45.90234375,"s":[1080]}],"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":105.2197265625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.826},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[120.875,-60.552,0],"ti":[-340.061,254.939,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.163},"t":27.366,"s":[587.554,-491.48,0],"to":[340.061,-254.939,0],"ti":[-219.187,194.387,0]},{"t":82.9755859375,"s":[1902.674,-1657.802,0]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":46,"ty":4,"nm":"Shape Layer 75","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-18,"s":[0]},{"t":86.6826171875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.926},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[208.471,-17.277,73.2],"ti":[-257.939,74.184,269.358]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.228},"t":15.897,"s":[1113.129,-231.834,439.203],"to":[257.939,-74.184,-269.358],"ti":[-49.469,56.907,342.559]},{"t":90,"s":[1409.942,-573.277,-1616.149]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":47,"ty":4,"nm":"Shape Layer 74","parent":15,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-24,"s":[0]},{"t":94.09765625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,311],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[-137.695,-128.17,0],"to":[308.185,154.066,17.938],"ti":[-308.185,-154.066,-17.938]},{"t":24.89453125,"s":[1711.415,796.226,107.63]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[51,51,51],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":48,"ty":4,"nm":"Shape Layer 73","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-11,"s":[0]},{"t":87.9189453125,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10,"x":"var $bm_rt;\n$bm_rt = transform.rotation;"},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-47.653,624.272,396.962],"ti":[47.653,-624.272,-396.962]},{"t":34.7802734375,"s":[-267.632,3580.053,2381.774]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7,-27],[35,7],[-41.906,32.862],[0,0]],"o":[[-7,27],[32,27],[23.167,-18.167],[0,0]],"v":[[72,-317],[4,-279],[91,-263.167],[107.167,-289.667]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.345,0.381,0.671,0.5,0.491,0.265,0.624,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":49,"ty":4,"nm":"Shape Layer 72","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":80.50390625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-143.363,403.815,153.434],"ti":[143.363,-403.815,-153.434]},{"t":54.552734375,"s":[-841.892,2257.314,920.605]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":50,"ty":4,"nm":"Shape Layer 71","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":91.6259765625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-196.222,199.787,-42.398],"ti":[196.222,-199.787,42.398]},{"t":32.30859375,"s":[-1159.042,1033.147,-254.39]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":51,"ty":4,"nm":"Shape Layer 70","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":94.09765625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.868},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-181.982,76.554,26.907],"ti":[353.39,-64.568,-26.907]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.207},"t":31.073,"s":[-1073.604,293.746,161.442],"to":[-353.39,64.568,26.907],"ti":[171.408,11.986,0]},{"t":73.08984375,"s":[-2102.054,221.83,161.442]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":52,"ty":4,"nm":"Shape Layer 69","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-1.057,"s":[0]},{"t":84.2109375,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-128.187,-8.964,0],"ti":[128.187,8.964,0]},{"t":33,"s":[-750.834,-219.36,0]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":53,"ty":4,"nm":"Shape Layer 68","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":100.2763671875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-120.126,-74.06,0],"ti":[120.126,74.06,0]},{"t":39,"s":[-702.471,-609.938,0]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":54,"ty":4,"nm":"Shape Layer 67","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":86.6826171875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.723},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-82.592,-80.92,0],"ti":[157.866,-68.24,-53.249]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.139},"t":27,"s":[-477.265,-651.099,0],"to":[-147.887,63.926,49.883],"ti":[-19.633,-190.127,-25.485]},{"t":80.50390625,"s":[-147.831,2175.133,319.492]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":55,"ty":4,"nm":"Shape Layer 66","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":146,"s":[1440]}],"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":146,"s":[1440]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.749},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-50.842,-139.975,0],"ti":[92.088,-447.218,16.673]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.14},"t":36,"s":[-286.764,-1005.43,0],"to":[-92.088,447.218,-16.673],"ti":[41.246,-587.193,16.673]},{"t":100.2763671875,"s":[-534.238,2517.728,-100.037]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":56,"ty":4,"nm":"Shape Layer 65","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":144.7646484375,"s":[1080]}],"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":144.7646484375,"s":[59]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.767},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[-6.333,-132.667,0],"ti":[83.687,200.398,-334.198]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.153},"t":26,"s":[-31.605,-984.007,0],"to":[-63.434,-151.901,253.321],"ti":[-17.165,-409.463,-112.981]},{"t":111.3984375,"s":[283.153,3184.715,1150.251]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[3.444,9.757]],"o":[[0,0],[0,0],[0,0],[-4.941,-14]],"v":[[72,-317],[4,-279],[24.151,-217.936],[88.307,-269.617]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.671,0.345,0.345,0.5,0.655,0.247,0.461,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":57,"ty":4,"nm":"Shape Layer 64","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-9,"s":[0]},{"t":89.154296875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.836},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[64.5,-169.594,-9.152],"ti":[-113.79,-117.061,113.731]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.168},"t":27,"s":[393.395,-1205.571,-54.91],"to":[155.887,160.368,-155.806],"ti":[28,-314.6,160.297]},{"t":79,"s":[225.395,682.029,-1016.689]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":58,"ty":4,"nm":"Shape Layer 63","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-14,"s":[0]},{"t":81.740234375,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.787},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[37,-178.375,-65.532],"ti":[-37,-452.343,-214.28]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.151},"t":28,"s":[228.395,-1258.256,-393.192],"to":[37,452.343,214.28],"ti":[0,-630.718,-279.812]},{"t":88,"s":[228.395,2526.051,1285.683]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":59,"ty":4,"nm":"Shape Layer 62","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-9,"s":[0]},{"t":100.2763671875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[127.5,-230.723,46.629],"ti":[-127.5,230.723,-46.629]},{"t":38,"s":[771.395,-1572.344,279.773]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":60,"ty":4,"nm":"Shape Layer 61","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":91.6259765625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[259.979,-20.273,-16.168],"ti":[-259.979,20.273,16.168]},{"t":19,"s":[1566.267,-309.648,-97.005]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":61,"ty":4,"nm":"Shape Layer 60","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-10,"s":[0]},{"t":81.740234375,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[110,309.06,41.862],"ti":[-110,-309.06,-41.862]},{"t":31,"s":[666.395,1666.35,251.17]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":62,"ty":4,"nm":"Shape Layer 59","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":86.6826171875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[111,205.897,-43.518],"ti":[-111,-205.897,43.518]},{"t":33,"s":[672.395,1047.376,-261.107]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":63,"ty":4,"nm":"Shape Layer 58","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-4.765,"s":[0]},{"t":89.154296875,"s":[1080]}],"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":89.154296875,"s":[1800]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[14.845,244.879,-215.584],"ti":[-14.845,-244.879,215.584]},{"t":27.3662109375,"s":[95.463,1281.264,-1293.506]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":64,"ty":4,"nm":"Shape Layer 57","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-1.057,"s":[0]},{"t":102.748046875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10,"x":"var $bm_rt;\n$bm_rt = transform.rotation;"},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[44.909,224.342,-47.459],"ti":[-44.909,-224.342,47.459]},{"t":58.259765625,"s":[287.74,1180.472,-284.755]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[84.659,-333.362],[4,-279],[45.665,-243.617],[119.735,-297.935]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.345,0.381,0.671,0.5,0.491,0.265,0.624,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":65,"ty":4,"nm":"Shape Layer 56","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-17,"s":[0]},{"t":95.3330078125,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-63.805,266.808,-0.572],"ti":[63.805,-266.808,0.572]},{"t":51,"s":[-364.539,1435.268,-3.43]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":66,"ty":4,"nm":"Shape Layer 55","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-11,"s":[0]},{"t":101.51171875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.877},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-126.036,154.294,-7.096],"ti":[142.905,-395.535,7.096]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.203},"t":39,"s":[-737.928,760.184,-42.576],"to":[-142.905,395.535,-7.096],"ti":[16.869,-241.241,0]},{"t":117,"s":[-839.144,2207.633,-42.576]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":67,"ty":4,"nm":"Shape Layer 54","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-11,"s":[0]},{"t":85.447265625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-233.472,311.697,-17.966],"ti":[233.472,-311.697,17.966]},{"t":24,"s":[-1382.547,1704.606,-107.798]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":68,"ty":4,"nm":"Shape Layer 53","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":101.51171875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.762},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-130.292,21.146,0],"ti":[485.802,-51.233,12.363]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.14},"t":22,"s":[-763.467,-38.702,0],"to":[-485.802,51.233,-12.363],"ti":[355.509,-30.087,12.363]},{"t":57,"s":[-2896.522,141.82,-74.176]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":69,"ty":4,"nm":"Shape Layer 52","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-4.765,"s":[0]},{"t":112.6337890625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.78},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-74.311,-43.837,33.544],"ti":[97.505,-287.852,-33.544]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.149},"t":34,"s":[-427.58,-428.6,201.262],"to":[-97.505,287.852,33.544],"ti":[23.194,-331.689,0]},{"t":102,"s":[-566.743,1561.534,201.262]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":70,"ty":4,"nm":"Shape Layer 51","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-6,"s":[0]},{"t":85.447265625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-124.762,-149.834,3.486],"ti":[124.762,149.834,-3.486]},{"t":26,"s":[-730.286,-1064.579,20.917]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":71,"ty":4,"nm":"Shape Layer 50","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-23,"s":[0]},{"t":97.8046875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,184],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.77},"o":{"x":0.167,"y":0.167},"t":0,"s":[18.288,-165.578,0],"to":[-6.486,-154.267,17.582],"ti":[239.046,-131.632,15.802]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.142},"t":30,"s":[-20.629,-1091.18,105.493],"to":[-105.762,58.238,-6.991],"ti":[-17.583,-173.702,10.833]},{"t":76.796875,"s":[-107.774,1499.363,-67.253]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":72,"ty":4,"nm":"Shape Layer 49","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-12,"s":[0]},{"t":102.748046875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[382.643,-152.755,217.62],"ti":[-382.643,152.755,-217.62]},{"t":33,"s":[2302.253,-1104.538,1305.721]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7,-27],[35,7],[-41.906,32.862],[0,0]],"o":[[-7,27],[32,27],[23.167,-18.167],[0,0]],"v":[[72,-317],[4,-279],[91,-263.167],[107.167,-289.667]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.345,0.381,0.671,0.5,0.491,0.265,0.624,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":73,"ty":4,"nm":"Shape Layer 48","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-25,"s":[0]},{"t":81.740234375,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.831},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[64.5,-93.5,0],"ti":[-64.5,-194.5,90.248]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.166},"t":25,"s":[393.395,-749.007,0],"to":[64.5,194.5,-90.248],"ti":[0,-288,90.248]},{"t":82.9755859375,"s":[393.395,978.993,-541.488]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":74,"ty":4,"nm":"Shape Layer 47","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":95.3330078125,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.885},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[65.626,-110,4.223],"ti":[-147.468,-10.238,-26.905]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.19},"t":20,"s":[400.149,-848.007,25.34],"to":[361.407,25.09,65.936],"ti":[-121.292,-241.908,-61.713]},{"t":91.6259765625,"s":[1127.898,603.439,395.618]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":75,"ty":4,"nm":"Shape Layer 46","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-20,"s":[0]},{"t":87.9189453125,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.816},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[147.845,-83.5,22.596],"ti":[-263.557,-41.975,24.609]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.159},"t":24,"s":[893.467,-689.007,135.575],"to":[408.719,65.094,-38.164],"ti":[88.693,-68.281,27.128]},{"t":74,"s":[-447.485,1243.193,-405.659]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":76,"ty":4,"nm":"Shape Layer 45","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-16,"s":[0]},{"t":99.041015625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.875},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[217.737,24,189.679],"ti":[-563.841,-88.203,-189.679]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.201},"t":26,"s":[1312.816,-44.007,1138.074],"to":[563.841,88.203,189.679],"ti":[-346.104,-64.203,0]},{"t":76.796875,"s":[3389.44,341.208,1138.074]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":77,"ty":4,"nm":"Shape Layer 44","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":45.90234375,"s":[1080]}],"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":105.2197265625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.866},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[125,51.5,0],"ti":[-125,-69.882,151.388]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.196},"t":27.366,"s":[756.395,120.993,0],"to":[125,69.882,-151.388],"ti":[0,-18.382,151.388]},{"t":72,"s":[756.395,231.284,-908.328]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":78,"ty":4,"nm":"Shape Layer 43","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-18,"s":[0]},{"t":86.6826171875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[149.808,146,73.2],"ti":[-149.808,-146,-73.2]},{"t":26,"s":[905.246,687.993,439.203]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":79,"ty":4,"nm":"Shape Layer 42","parent":14,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-24,"s":[0]},{"t":94.09765625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[85.912,333.667,17.938],"ti":[-85.912,-333.667,-17.938]},{"t":24.89453125,"s":[521.87,1813.993,107.63]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[64,64,64],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":80,"ty":4,"nm":"Shape Layer 38","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":94.09765625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[176.199,-89.062,26.907],"ti":[-176.199,89.062,-26.907]},{"t":31.0732421875,"s":[1063.587,-722.379,161.442]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":81,"ty":4,"nm":"Shape Layer 37","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-1.057,"s":[0]},{"t":84.2109375,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[128.5,0,0],"ti":[-128.5,0,0]},{"t":30,"s":[777.395,-188.007,0]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":82,"ty":4,"nm":"Shape Layer 36","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":100.2763671875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[125,65.5,0],"ti":[-125,-65.5,0]},{"t":22,"s":[756.395,204.993,0]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":83,"ty":4,"nm":"Shape Layer 35","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":86.6826171875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.782},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[88.036,74.962,0],"ti":[-235.276,83.183,-29.696]},{"i":{"x":0.833,"y":0.693},"o":{"x":0.167,"y":0.155},"t":30,"s":[534.609,261.763,0],"to":[283.126,-100.101,35.736],"ti":[32.848,188.294,-25.485]},{"t":123,"s":[-62.525,-2846.235,319.492]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":84,"ty":4,"nm":"Shape Layer 33","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":454.311,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":144.7646484375,"s":[59]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.737},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[42.167,-122.667,0],"ti":[-345.153,74.903,-198.64]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.151},"t":28,"s":[880.395,419.993,0],"to":[432.939,-93.953,249.162],"ti":[-17.165,-409.463,-112.981]},{"t":127,"s":[1064.167,5582.141,1150.251]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[3.444,9.757]],"o":[[0,0],[0,0],[0,0],[-4.941,-14]],"v":[[72,-317],[4,-279],[24.151,-217.936],[88.307,-269.617]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":85,"ty":4,"nm":"Shape Layer 32","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-9,"s":[0]},{"t":89.154296875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.841},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[21.5,-126.594,-9.152],"ti":[-109.46,-148.367,74.781]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.169},"t":25,"s":[756.395,396.429,-54.90999999999999],"to":[113.887,154.368,-77.806],"ti":[7.464,-520.935,-7.703]},{"t":124,"s":[711.613,3522.04,-8.689]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":86,"ty":4,"nm":"Shape Layer 31","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-14,"s":[0]},{"t":81.740234375,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.843},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[37,-178.375,-65.532],"ti":[-48.635,-750.211,-214.28]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.169},"t":29,"s":[849.395,85.744,-393.192],"to":[48.635,750.211,214.28],"ti":[-11.635,-928.586,-279.812]},{"t":145,"s":[919.2079999999999,5657.262,1285.683]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":87,"ty":4,"nm":"Shape Layer 30","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-9,"s":[0]},{"t":100.2763671875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[127.5,-230.723,46.629],"ti":[-127.5,230.723,-46.629]},{"t":25,"s":[1392.395,-228.34400000000002,279.773]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":88,"ty":4,"nm":"Shape Layer 29","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":91.6259765625,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[128.5,-20.273,-16.168],"ti":[-128.5,20.273,16.168]},{"t":44,"s":[1398.395,1034.352,-97.005]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":89,"ty":4,"nm":"Shape Layer 28","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-10,"s":[0]},{"t":81.740234375,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[110,309.06,41.862],"ti":[-110,-309.06,-41.862]},{"t":41,"s":[1287.395,3010.35,251.17]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":90,"ty":4,"nm":"Shape Layer 27","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":86.6826171875,"s":[1080]}],"ix":8},"ry":{"a":0,"k":0,"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[111,205.897,-43.518],"ti":[-111,-205.897,43.518]},{"t":31,"s":[1293.395,2391.376,-261.107]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":91,"ty":4,"nm":"Shape Layer 26","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-4.765,"s":[0]},{"t":89.154296875,"s":[1080]}],"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":89.154296875,"s":[1800]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[1,335.659,-215.584],"ti":[-1,-335.659,215.584]},{"t":58,"s":[633.395,3169.948,-1293.506]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":92,"ty":4,"nm":"Shape Layer 25","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-1.057,"s":[0]},{"t":102.748046875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10,"x":"var $bm_rt;\n$bm_rt = transform.rotation;"},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[-147.439,-471.248,-47.459],"ti":[147.439,471.248,47.459]},{"t":137.349609375,"s":[-878.24,-3015.497,-284.755]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[84.659,-333.362],[4,-279],[45.665,-243.617],[119.735,-297.935]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.345,0.381,0.671,0.5,0.491,0.265,0.624,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":93,"ty":4,"nm":"Shape Layer 24","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-17,"s":[0]},{"t":95.3330078125,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[53.803,-474.312,-0.572],"ti":[-53.803,474.312,0.572]},{"t":71,"s":[329.216,-3033.882,-3.43]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":0,"ind":94,"ty":4,"nm":"Shape Layer 23","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[436.949,-283.973,0],"ti":[-436.949,283.973,0]},{"t":71,"s":[2628.09,-1891.843,0]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":95,"ty":4,"nm":"Shape Layer 22","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-11,"s":[0]},{"t":85.447265625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[127.5,-103.954,0],"ti":[-127.5,103.954,0]},{"t":34,"s":[771.395,-811.733,0]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":96,"ty":4,"nm":"Shape Layer 21","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":101.51171875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[128.5,-30.183,0],"ti":[-128.5,30.183,0]},{"t":26,"s":[777.395,-369.106,0]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":97,"ty":4,"nm":"Shape Layer 20","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-4.765,"s":[0]},{"t":112.6337890625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.716},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[77.188,38.547,33.544],"ti":[-71.564,470.424,-33.544]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.136},"t":35,"s":[469.524,43.272,201.262],"to":[71.564,-470.424,33.544],"ti":[5.624,508.971,0]},{"t":100,"s":[435.782,-3010.554,201.262]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":98,"ty":4,"nm":"Shape Layer 19","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-6,"s":[0]},{"t":85.447265625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[134.91,140.766,3.486],"ti":[-134.91,-140.766,-3.486]},{"t":21,"s":[815.857,656.587,20.917]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":99,"ty":4,"nm":"Shape Layer 18","parent":10,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-23,"s":[0]},{"t":97.8046875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.828},"o":{"x":0.167,"y":0.167},"t":0,"s":[6.395,-188.007,0],"to":[17.232,153.439,17.582],"ti":[-229.282,147.986,15.802]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.165},"t":21,"s":[109.785,732.626,105.493],"to":[101.442,-65.474,-6.991],"ti":[29.657,172.052,10.833]},{"t":76.796875,"s":[76.884,-1982.01,-729.705]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":100,"ty":4,"nm":"Shape Layer 17","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-12,"s":[0]},{"t":102.748046875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[382.643,-152.755,217.62],"ti":[-382.643,152.755,-217.62]},{"t":25,"s":[2923.253,239.462,1305.721]}],"ix":2},"a":{"a":0,"k":[56.08,-285.558,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7,-27],[35,7],[-41.906,32.862],[0,0]],"o":[[-7,27],[32,27],[23.167,-18.167],[0,0]],"v":[[72,-317],[4,-279],[91,-263.167],[107.167,-289.667]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.345,0.381,0.671,0.5,0.491,0.265,0.624,1,0.638,0.15,0.577],"ix":9}},"s":{"a":0,"k":[0,0],"ix":5},"e":{"a":0,"k":[100,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":101,"ty":4,"nm":"Shape Layer 16","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-25,"s":[0]},{"t":81.740234375,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.829},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[64.5,-93.5,0],"ti":[-78.679,-339.952,90.248]},{"i":{"x":0.833,"y":0.714},"o":{"x":0.167,"y":0.165},"t":30,"s":[1014.395,594.993,0],"to":[78.679,339.952,-90.248],"ti":[-14.179,-433.452,90.248]},{"t":121,"s":[1099.471,3195.705,-541.488]}],"ix":2},"a":{"a":0,"k":[-262,-435,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[46,54],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"gf","o":{"a":0,"k":100,"ix":10},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.969,0.18,0.404,0.5,0.97,0.346,0.318,1,0.972,0.512,0.233],"ix":9}},"s":{"a":0,"k":[-49,0],"ix":5},"e":{"a":0,"k":[68,0],"ix":6},"t":1,"nm":"Gradient Fill 1","mn":"ADBE Vector Graphic - G-Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-262,-435],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":102,"ty":4,"nm":"Shape Layer 15","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.822]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":102,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.828},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[65.626,-110,4.223],"ti":[-147.468,-10.238,-26.905]},{"i":{"x":0.833,"y":0.887},"o":{"x":0.167,"y":0.163},"t":28,"s":[1021.149,495.993,25.34],"to":[361.407,25.09,65.936],"ti":[-126.131,-151.254,-61.713]},{"t":71,"s":[1777.9360000000001,1403.514,395.618]}],"ix":2},"a":{"a":0,"k":[14.5,-433,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[27,96],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.5,-433],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":103,"ty":4,"nm":"Shape Layer 5","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-20,"s":[0]},{"t":87.9189453125,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[147.845,-83.5,22.596],"ti":[-147.845,83.5,-22.596]},{"t":19,"s":[1514.467,654.993,135.575]}],"ix":2},"a":{"a":0,"k":[-180.917,-577,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[36,32],[26,-104]],"o":[[0,0],[-36,-32],[-11.157,44.627]],"v":[[-161,-650],[-193,-562],[-165,-504]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":14,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":104,"ty":4,"nm":"Shape Layer 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-16,"s":[0]},{"t":99.041015625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[393.671,83.751,189.679],"ti":[-393.671,-83.751,-189.679]},{"t":58,"s":[2989.419,1658.499,1138.074]}],"ix":2},"a":{"a":0,"k":[-45,-602,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[40,40],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-45,-602],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":105,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-2.293,"s":[0]},{"t":45.90234375,"s":[1080]}],"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-3.528,"s":[0]},{"t":105.2197265625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[249,109.5,0],"ti":[-249,-109.5,0]},{"t":68,"s":[2121.395,1812.9929999999997,0]}],"ix":2},"a":{"a":0,"k":[232.5,-595.5,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[11,67],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.17254901960784313,0.5529411764705883,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[232.5,-595.5],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":106,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-18,"s":[0]},{"t":86.6826171875,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[499.008,399.431,73.2],"ti":[-499.008,-399.431,-73.2]},{"t":96,"s":[3621.4449999999997,3552.5799999999995,439.20300000000003]}],"ix":2},"a":{"a":0,"k":[120,-142,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[32,44],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.22745098039215686,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[120,-142],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":1,"ind":107,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"rx":{"a":0,"k":0,"ix":8},"ry":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":-24,"s":[0]},{"t":94.09765625,"s":[1080]}],"ix":9},"rz":{"a":0,"k":0,"ix":10},"or":{"a":0,"k":[0,0,0],"ix":7},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[627.395,1155.993,0],"to":[535.305,103.127,17.938],"ti":[-535.305,-103.127,-17.938]},{"t":82,"s":[3839.226,1774.756,107.63000000000001]}],"ix":2},"a":{"a":0,"k":[116,-254,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[26,56],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0,0.9490196078431372,0.9490196078431372,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.5372549019607843,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[116,-254],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":112,"st":-1,"bm":0},{"ddd":0,"ind":108,"ty":1,"nm":"Medium Gray-Blue Solid 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[621,1344,0],"ix":2},"a":{"a":0,"k":[621,1344,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"sw":1242,"sh":2688,"sc":"#272d41","ip":112,"op":112,"st":0,"bm":0,"hidden":0}],"markers":[]} diff --git a/app/src/main/res/values-h680dp/dimens.xml b/app/src/main/res/values-h680dp/dimens.xml new file mode 100644 index 0000000000..b8c8d265ed --- /dev/null +++ b/app/src/main/res/values-h680dp/dimens.xml @@ -0,0 +1,9 @@ + + + -10dp + 0dp + 16dp + 106dp + 67dp + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 509d2b72be..5d0fe290b1 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -16,6 +16,7 @@ #C4291C #FFB71B + #FFFFFF #8E8E93 #636366 #48484A @@ -23,7 +24,7 @@ #2C2C2E #1C1C1E - #F9F9FA + #FFFFFF #F9F9F9 #F4F5F6 #DE000000 @@ -31,10 +32,13 @@ #14181D + #F3F3F3 #F8F8FB + #DADADF + #D0D0D5 #C7C7CC #D1D1D6 - #F2F2F7 + #C9C9CD #1F000000 #14212121 diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index fd2c18a380..496ccebf23 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -35,4 +35,10 @@ 32sp 16dp + -30dp + -20dp + 8dp + 70dp + 37dp + diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml index 9404210575..17edb1b769 100644 --- a/app/src/main/res/values/strings_untranslated.xml +++ b/app/src/main/res/values/strings_untranslated.xml @@ -1,4 +1,6 @@ + Scan card failed. Try again + Buy Sell Trade @@ -168,4 +170,29 @@ this wallet. Do you want to buy or sell crypto? Oops, the current version of the application is not ready to work with this card, please check for updates. + + + Welcome to Tangem + The safest way to buy, use and\n store cryptocurrency + Get a new card + + Activating card + What does it mean? + Create a wallet + Let’s generate all the keys on your card and create a secure wallet + Create wallet + + Top up your wallet + To get started, simply top up the card with any amount + Buy crypto + Show the wallet’s address + Scan the address to top up your wallet + + Done! + Your crypto card is activated and ready to be used + Continue + Balance + + Internal error: can\'t create wallet manager + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 3642ef243c..f008a00608 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -5,9 +5,11 @@ @color/colorPrimary @color/colorSecondary @color/darkGray6 - true @color/backgroundLightGray - false + false + true + true + @null - + + + + + + - \ No newline at end of file + + + + + + + + + + + + + \ No newline at end of file diff --git a/dependencies.gradle b/dependencies.gradle index 5567b6aa65..43b263845b 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -1,4 +1,4 @@ ext.versions = [ - kotlin : '1.5.20', - build_gradle: '7.0.0', + kotlin : '1.5.30', + build_gradle: '7.0.2', ]