From 12e53a3aa3947814ab23ed8f021e3c4b5b5a8c8e Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 23 Jun 2025 11:56:00 +0300 Subject: [PATCH] Updated on 2026-08-14 --- app/build.gradle.kts | 2 + app/src/main/AndroidManifest.xml | 9 +++- .../tap/di/RootAppComponentContextModule.kt | 4 +- ci_resources/Dockerfile.android | 54 ------------------- ci_resources/Dockerfile.lightweight | 26 --------- ....properties => tests_ci_gradle.properties} | 2 +- .../version/DefaultVersionProvider.kt | 4 +- core/decompose/build.gradle.kts | 8 ++- .../decompose/context/AppComponentContext.kt | 4 +- .../AppComponentContextChildrenFactory.kt | 4 +- .../context/DefaultAppComponentContext.kt | 2 + .../core/decompose/utils/ActivityHolder.kt | 16 ++++++ fastlane/Fastfile | 4 +- features/kyc/api/.gitignore | 1 + features/kyc/api/build.gradle.kts | 22 ++++++++ .../com/tangem/features/kyc/KycComponent.kt | 12 +++++ features/kyc/impl/.gitignore | 1 + features/kyc/impl/build.gradle.kts | 54 +++++++++++++++++++ .../features/kyc/DefaultKycComponent.kt | 39 ++++++++++++++ .../tangem/features/kyc/di/FeatureModule.kt | 16 ++++++ gradle/dependencies.toml | 6 ++- .../plugin/configuration/model/AppConfig.kt | 4 +- settings.gradle.kts | 4 ++ tangem-android-tools | 2 +- 24 files changed, 205 insertions(+), 95 deletions(-) delete mode 100644 ci_resources/Dockerfile.android delete mode 100644 ci_resources/Dockerfile.lightweight rename ci_resources/{tests_8gb_ram_ci_gradle.properties => tests_ci_gradle.properties} (86%) create mode 100644 core/decompose/src/main/kotlin/com/tangem/core/decompose/utils/ActivityHolder.kt create mode 100644 features/kyc/api/.gitignore create mode 100644 features/kyc/api/build.gradle.kts create mode 100644 features/kyc/api/src/main/kotlin/com/tangem/features/kyc/KycComponent.kt create mode 100644 features/kyc/impl/.gitignore create mode 100644 features/kyc/impl/build.gradle.kts create mode 100644 features/kyc/impl/src/main/kotlin/com/tangem/features/kyc/DefaultKycComponent.kt create mode 100644 features/kyc/impl/src/main/kotlin/com/tangem/features/kyc/di/FeatureModule.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d592ad2b98..794229daee 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -217,6 +217,8 @@ dependencies { implementation(projects.features.usedesk.impl) implementation(projects.features.hotWallet.api) implementation(projects.features.hotWallet.impl) + implementation(projects.features.kyc.api) + implementation(projects.features.kyc.impl) /** AndroidX libraries */ implementation(deps.androidx.core.ktx) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ac76057c04..a2c1f4e5fe 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -45,7 +45,14 @@ android:roundIcon="@mipmap/ic_launcher" android:supportsRtl="true" tools:ignore="GoogleAppIndexingWarning" - tools:replace="android:allowBackup, android:fullBackupContent, android:label"> + tools:replace="android:allowBackup, android:fullBackupContent, android:label, android:largeHeap"> + + + /etc/apt/sources.list.d/github-cli.list && \ - apt-get update && apt-get install -y gh && \ - apt-get clean - -RUN mkdir -p $ANDROID_HOME/cmdline-tools/latest && \ - wget https://dl.google.com/android/repository/commandlinetools-linux-9477386_latest.zip -O /tmp/cmdline-tools.zip && \ - unzip /tmp/cmdline-tools.zip -d $ANDROID_HOME/cmdline-tools/latest && \ - mv $ANDROID_HOME/cmdline-tools/latest/cmdline-tools/* $ANDROID_HOME/cmdline-tools/latest/ && \ - rm -rf $ANDROID_HOME/cmdline-tools/latest/cmdline-tools && \ - rm -f /tmp/cmdline-tools.zip && \ - yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses && \ - $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager "platform-tools" "platforms;android-31" "platforms;android-34" "build-tools;34.0.0" - -RUN wget https://github.com/lzhiyong/android-sdk-tools/releases/download/34.0.3/android-sdk-tools-static-aarch64.zip -O /tmp/android-sdk-tools-static-aarch64.zip && \ - unzip /tmp/android-sdk-tools-static-aarch64.zip -d /tmp/android-sdk-tools-static-arm && \ - cp -r /tmp/android-sdk-tools-static-arm/build-tools/* $ANDROID_HOME/build-tools/34.0.0/ && \ - rm -rf /tmp/android-sdk-tools-static-arm /tmp/android-sdk-tools-static-aarch64.zip - -RUN gem install bundler:2.5.23 -RUN gem install fastlane -v 2.225.0 -N -V -RUN gem install fastlane-plugin-firebase_app_distribution -v 0.9.1 -N -V - -COPY ../Gemfile Gemfile.lock ./ -RUN bundle install --jobs=4 --retry=3 --verbose - -RUN bundle exec fastlane -v - -CMD ["bash"] diff --git a/ci_resources/Dockerfile.lightweight b/ci_resources/Dockerfile.lightweight deleted file mode 100644 index c2b4b399b2..0000000000 --- a/ci_resources/Dockerfile.lightweight +++ /dev/null @@ -1,26 +0,0 @@ -FROM ubuntu:24.04 - -ENV LC_ALL=en_US.UTF-8 -ENV LANG=en_US.UTF-8 -ENV DEBIAN_FRONTEND=noninteractive - -RUN apt-get update && apt-get install -y \ - locales \ - curl \ - git \ - && locale-gen en_US.UTF-8 \ - && update-locale LANG=en_US.UTF-8 \ - && apt-get clean - -RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg && \ - chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg && \ - echo "deb [signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" > /etc/apt/sources.list.d/github-cli.list && \ - apt-get update && apt-get install -y gh && \ - apt-get clean - -RUN curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | bash && \ - apt-get install -y git-lfs && \ - git lfs install && \ - apt-get clean - -CMD ["bash"] diff --git a/ci_resources/tests_8gb_ram_ci_gradle.properties b/ci_resources/tests_ci_gradle.properties similarity index 86% rename from ci_resources/tests_8gb_ram_ci_gradle.properties rename to ci_resources/tests_ci_gradle.properties index 78577a0975..860c64d2b5 100644 --- a/ci_resources/tests_8gb_ram_ci_gradle.properties +++ b/ci_resources/tests_ci_gradle.properties @@ -8,6 +8,6 @@ org.gradle.caching=false android.useAndroidX=true android.nonTransitiveRClass=false -android.aapt2FromMavenOverride=/opt/android-sdk/build-tools/34.0.0/aapt2 +android.aapt2FromMavenOverride=/opt/android-sdk/build-tools/35.0.0/aapt2 kotlin.incremental=false diff --git a/core/config-toggles/src/main/kotlin/com/tangem/core/configtoggle/version/DefaultVersionProvider.kt b/core/config-toggles/src/main/kotlin/com/tangem/core/configtoggle/version/DefaultVersionProvider.kt index b55e9637c3..105ca52d15 100644 --- a/core/config-toggles/src/main/kotlin/com/tangem/core/configtoggle/version/DefaultVersionProvider.kt +++ b/core/config-toggles/src/main/kotlin/com/tangem/core/configtoggle/version/DefaultVersionProvider.kt @@ -28,9 +28,9 @@ internal class DefaultVersionProvider @Inject constructor( context.packageName, PackageManager.PackageInfoFlags.of(0), ) - .versionName + .versionName!! } else { - context.packageManager.getPackageInfo(context.packageName, 0).versionName + context.packageManager.getPackageInfo(context.packageName, 0).versionName!! } } diff --git a/core/decompose/build.gradle.kts b/core/decompose/build.gradle.kts index aa1b6e3c82..792dbe9a1c 100644 --- a/core/decompose/build.gradle.kts +++ b/core/decompose/build.gradle.kts @@ -1,13 +1,19 @@ plugins { - alias(deps.plugins.kotlin.jvm) + alias(deps.plugins.android.library) + alias(deps.plugins.kotlin.android) alias(deps.plugins.kotlin.kapt) id("configuration") } +android { + namespace = "com.tangem.core.decompose" +} + dependencies { api(projects.core.utils) api(deps.decompose) + api(deps.androidx.appCompat) implementation(deps.kotlin.coroutines) implementation(deps.hilt.core) diff --git a/core/decompose/src/main/kotlin/com/tangem/core/decompose/context/AppComponentContext.kt b/core/decompose/src/main/kotlin/com/tangem/core/decompose/context/AppComponentContext.kt index 9426735800..8201bba2c4 100644 --- a/core/decompose/src/main/kotlin/com/tangem/core/decompose/context/AppComponentContext.kt +++ b/core/decompose/src/main/kotlin/com/tangem/core/decompose/context/AppComponentContext.kt @@ -4,6 +4,7 @@ import com.arkivanov.decompose.ComponentContext import com.tangem.core.decompose.di.HiltComponentBuilderOwner import com.tangem.core.decompose.navigation.NavigationOwner import com.tangem.core.decompose.ui.UiMessageSenderOwner +import com.tangem.core.decompose.utils.ActivityHolder import com.tangem.core.decompose.utils.ComponentScopeOwner import com.tangem.core.decompose.utils.DispatchersOwner import com.tangem.core.decompose.utils.TagsOwner @@ -20,4 +21,5 @@ interface AppComponentContext : DispatchersOwner, UiMessageSenderOwner, HiltComponentBuilderOwner, - TagsOwner \ No newline at end of file + TagsOwner, + ActivityHolder \ No newline at end of file diff --git a/core/decompose/src/main/kotlin/com/tangem/core/decompose/context/AppComponentContextChildrenFactory.kt b/core/decompose/src/main/kotlin/com/tangem/core/decompose/context/AppComponentContextChildrenFactory.kt index 146a7e2bb0..2ae9d71656 100644 --- a/core/decompose/src/main/kotlin/com/tangem/core/decompose/context/AppComponentContextChildrenFactory.kt +++ b/core/decompose/src/main/kotlin/com/tangem/core/decompose/context/AppComponentContextChildrenFactory.kt @@ -10,6 +10,7 @@ import com.tangem.core.decompose.ui.DefaultUiMessageSender import com.tangem.core.decompose.ui.UiMessageHandler import com.tangem.core.decompose.ui.UiMessageSender import com.tangem.core.decompose.ui.UiMessageSenderOwner +import com.tangem.core.decompose.utils.ActivityHolder import com.tangem.core.decompose.utils.ComponentCoroutineScope import com.tangem.core.decompose.utils.DispatchersOwner import kotlinx.coroutines.CoroutineScope @@ -58,7 +59,8 @@ fun AppComponentContext.childByContext( NavigationOwner by this@childByContext, UiMessageSenderOwner by this@childByContext, DispatchersOwner by this@childByContext, - HiltComponentBuilderOwner by this@childByContext { + HiltComponentBuilderOwner by this@childByContext, + ActivityHolder by this@childByContext { override val tags: HashMap = HashMap() diff --git a/core/decompose/src/main/kotlin/com/tangem/core/decompose/context/DefaultAppComponentContext.kt b/core/decompose/src/main/kotlin/com/tangem/core/decompose/context/DefaultAppComponentContext.kt index ea9afa04a5..a8a4fac2f7 100644 --- a/core/decompose/src/main/kotlin/com/tangem/core/decompose/context/DefaultAppComponentContext.kt +++ b/core/decompose/src/main/kotlin/com/tangem/core/decompose/context/DefaultAppComponentContext.kt @@ -1,5 +1,6 @@ package com.tangem.core.decompose.context +import androidx.appcompat.app.AppCompatActivity import com.arkivanov.decompose.ComponentContext import com.arkivanov.essenty.instancekeeper.getOrCreate import com.tangem.core.decompose.di.ModelComponent @@ -17,6 +18,7 @@ class DefaultAppComponentContext( override val dispatchers: CoroutineDispatcherProvider, override val hiltComponentBuilder: ModelComponent.Builder, override val messageSender: UiMessageSender, + override val activity: AppCompatActivity, private val replaceRouter: Router? = null, ) : AppComponentContext, ComponentContext by componentContext { diff --git a/core/decompose/src/main/kotlin/com/tangem/core/decompose/utils/ActivityHolder.kt b/core/decompose/src/main/kotlin/com/tangem/core/decompose/utils/ActivityHolder.kt new file mode 100644 index 0000000000..5ef79e239d --- /dev/null +++ b/core/decompose/src/main/kotlin/com/tangem/core/decompose/utils/ActivityHolder.kt @@ -0,0 +1,16 @@ +package com.tangem.core.decompose.utils + +import androidx.appcompat.app.AppCompatActivity + +/** + * Interface for holding an [AppCompatActivity] instance. + * + * This interface is used to provide access to the activity in which the component is running. + */ +interface ActivityHolder { + + /** + * The [AppCompatActivity] instance associated with this context. + */ + val activity: AppCompatActivity +} \ No newline at end of file diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 950482041e..24ec4eb861 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -19,7 +19,7 @@ platform :android do desc "Run detekt" lane :detekt do - FileUtils.cp("../ci_resources/tests_8gb_ram_ci_gradle.properties", "../gradle.properties") + FileUtils.cp("../ci_resources/tests_ci_gradle.properties", "../gradle.properties") puts File.read("../gradle.properties") gradle(task: "detekt") end @@ -27,7 +27,7 @@ platform :android do desc "Run tests" lane :test do FileUtils.cp("../app/src/main/assets/tangem-app-config/android/google-services/dev/google-services.json", "../app") - FileUtils.cp("../ci_resources/tests_8gb_ram_ci_gradle.properties", "../gradle.properties") + FileUtils.cp("../ci_resources/tests_ci_gradle.properties", "../gradle.properties") puts File.read("../gradle.properties") gradle(task: "testDebugUnitTest") end diff --git a/features/kyc/api/.gitignore b/features/kyc/api/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/features/kyc/api/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/features/kyc/api/build.gradle.kts b/features/kyc/api/build.gradle.kts new file mode 100644 index 0000000000..3133f2c7de --- /dev/null +++ b/features/kyc/api/build.gradle.kts @@ -0,0 +1,22 @@ +plugins { + alias(deps.plugins.android.library) + alias(deps.plugins.kotlin.android) + id("configuration") +} + +android { + namespace = "com.tangem.features.kyc.api" +} + +dependencies { + + /* Project - Domain */ + implementation(projects.domain.models) + + /* Project - Core */ + implementation(projects.core.decompose) + implementation(projects.core.ui) + + /* Compose */ + implementation(deps.compose.runtime) +} \ No newline at end of file diff --git a/features/kyc/api/src/main/kotlin/com/tangem/features/kyc/KycComponent.kt b/features/kyc/api/src/main/kotlin/com/tangem/features/kyc/KycComponent.kt new file mode 100644 index 0000000000..6a18888000 --- /dev/null +++ b/features/kyc/api/src/main/kotlin/com/tangem/features/kyc/KycComponent.kt @@ -0,0 +1,12 @@ +package com.tangem.features.kyc + +import com.tangem.core.decompose.context.AppComponentContext + +interface KycComponent { + + fun launch() + + interface Factory { + fun create(appComponentContext: AppComponentContext): KycComponent + } +} \ No newline at end of file diff --git a/features/kyc/impl/.gitignore b/features/kyc/impl/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/features/kyc/impl/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/features/kyc/impl/build.gradle.kts b/features/kyc/impl/build.gradle.kts new file mode 100644 index 0000000000..86e6e641fb --- /dev/null +++ b/features/kyc/impl/build.gradle.kts @@ -0,0 +1,54 @@ +plugins { + alias(deps.plugins.android.library) + alias(deps.plugins.kotlin.android) + alias(deps.plugins.kotlin.kapt) + alias(deps.plugins.kotlin.serialization) + alias(deps.plugins.hilt.android) + id("configuration") +} + +android { + namespace = "com.tangem.features.kyc.impl" +} + +dependencies { + /** Api */ + implementation(projects.features.kyc.api) + + /** Core modules */ + implementation(projects.core.configToggles) + implementation(projects.core.analytics) + implementation(projects.core.analytics.models) + implementation(projects.core.utils) + implementation(projects.core.ui) + implementation(projects.core.res) + implementation(projects.core.decompose) + implementation(projects.core.navigation) + implementation(projects.core.datasource) + + /** Common */ + implementation(projects.common.ui) + implementation(projects.common.routing) + + /** Compose libraries */ + implementation(deps.compose.material3) + implementation(deps.compose.animation) + implementation(deps.compose.foundation) + implementation(deps.compose.ui) + implementation(deps.compose.ui.tooling) + implementation(deps.compose.coil) + implementation(deps.lottie.compose) + implementation(deps.decompose.ext.compose) + implementation(deps.androidx.activity.compose) + + /** Other libraries */ + implementation(deps.kotlin.immutable.collections) + implementation(deps.kotlin.serialization) + implementation(deps.timber) + implementation(deps.firebase.crashlytics) + implementation(deps.sumsub.sdk) + + /** DI */ + implementation(deps.hilt.android) + kapt(deps.hilt.kapt) +} \ No newline at end of file diff --git a/features/kyc/impl/src/main/kotlin/com/tangem/features/kyc/DefaultKycComponent.kt b/features/kyc/impl/src/main/kotlin/com/tangem/features/kyc/DefaultKycComponent.kt new file mode 100644 index 0000000000..d192cead2d --- /dev/null +++ b/features/kyc/impl/src/main/kotlin/com/tangem/features/kyc/DefaultKycComponent.kt @@ -0,0 +1,39 @@ +package com.tangem.features.kyc + +import com.sumsub.sns.core.SNSMobileSDK +import com.sumsub.sns.core.data.listener.TokenExpirationHandler +import com.tangem.core.decompose.context.AppComponentContext +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import java.util.Locale + +class DefaultKycComponent @AssistedInject constructor( + @Assisted appComponentContext: AppComponentContext, +) : KycComponent, AppComponentContext by appComponentContext { + + override fun launch() { + val accessToken = "123456" + + val tokenExpirationHandler = object : TokenExpirationHandler { + override fun onTokenExpired(): String? { + // Access token expired + // get a new one and pass it to the callback to re-initiate the SDK + val newToken = "..." // get a new token from your backend + return newToken + } + } + + val snsSdk = SNSMobileSDK.Builder(activity) + .withAccessToken(accessToken, onTokenExpiration = tokenExpirationHandler) + .withLocale(Locale("en")) + .build() + + snsSdk.launch() + } + + @AssistedFactory + interface Factory : KycComponent.Factory { + override fun create(appComponentContext: AppComponentContext): DefaultKycComponent + } +} \ No newline at end of file diff --git a/features/kyc/impl/src/main/kotlin/com/tangem/features/kyc/di/FeatureModule.kt b/features/kyc/impl/src/main/kotlin/com/tangem/features/kyc/di/FeatureModule.kt new file mode 100644 index 0000000000..c72f282775 --- /dev/null +++ b/features/kyc/impl/src/main/kotlin/com/tangem/features/kyc/di/FeatureModule.kt @@ -0,0 +1,16 @@ +package com.tangem.features.kyc.di + +import com.tangem.features.kyc.DefaultKycComponent +import com.tangem.features.kyc.KycComponent +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent + +@Module +@InstallIn(SingletonComponent::class) +internal interface FeatureModule { + + @Binds + fun bindComponentFactory(impl: DefaultKycComponent.Factory): KycComponent.Factory +} \ No newline at end of file diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 04c6f4b039..2473ea55b9 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -15,7 +15,7 @@ firebasePerf = "1.4.2" androidxActivityCompose = "1.8.0" androidxAppCompat = "1.5.1" androidxBrowser = "1.4.0" -androidxConstraintLayout = "2.1.4" +androidxConstraintLayout = "2.2.1" androidxKtx = "1.9.0" androidxSplashScreen = "1.0.1" androidxFragment = "1.8.5" @@ -32,7 +32,7 @@ compose-runtime = "1.7.4" compose-foundation = "1.7.4" compose-material = "1.7.4" compose-material3 = "1.3.1" -compose-constraint = "1.0.1" +compose-constraint = "1.1.1" compose-navigation = "2.7.7" compose-accompanist = "0.30.1" compose-paging = "3.2.1" @@ -90,6 +90,7 @@ room = "2.6.1" markdown = "0.7.2" markdownComposeView = "0.5.4" usedesk = "4.4.0" +sumsub = "1.36.0" # endregion Other libraries # region Tools @@ -277,4 +278,5 @@ markdown = { module = "org.jetbrains:markdown", version.ref = "markdown" } markdown-composeview = { module = "com.github.jeziellago:compose-markdown", version.ref = "markdownComposeView" } usedesk-chat-sdk = { module = "com.github.Usedesk.Android_SDK:chat-sdk", version.ref = "usedesk" } usedesk-chat-gui = { module = "com.github.Usedesk.Android_SDK:chat-gui", version.ref = "usedesk" } +sumsub-sdk = { module = "com.sumsub.sns:idensic-mobile-sdk", version.ref = "sumsub" } # endregion Other diff --git a/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/model/AppConfig.kt b/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/model/AppConfig.kt index 3db7478662..5ddd7ba12d 100644 --- a/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/model/AppConfig.kt +++ b/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/model/AppConfig.kt @@ -6,6 +6,6 @@ internal object AppConfig { const val versionName = "1.0.0-SNAPSHOT" // const val versionName = "100.0.0-SNAPSHOT" //TODO: [REDACTED_JIRA] const val minSdkVersion = 24 - const val targetSdkVersion = 34 - const val compileSdkVersion = 34 + const val targetSdkVersion = 35 + const val compileSdkVersion = 35 } \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index c827ecc8a0..eca5b3d022 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -118,6 +118,7 @@ dependencyResolutionManagement { } } maven("https://jitpack.io") + maven("https://maven.sumsub.com/repository/maven-public/") } versionCatalogs { @@ -247,6 +248,9 @@ include(":features:walletconnect:impl") include(":features:hot-wallet:api") include(":features:hot-wallet:impl") + +include(":features:kyc:api") +include(":features:kyc:impl") // endregion Feature modules // region Domain modules diff --git a/tangem-android-tools b/tangem-android-tools index 53e359bc2e..8d971eb9d9 160000 --- a/tangem-android-tools +++ b/tangem-android-tools @@ -1 +1 @@ -Subproject commit 53e359bc2ead37feeb9e70d7004c91cde07ccf9b +Subproject commit 8d971eb9d901358419da78408ecf7dc3f0c4331f