├── .editorconfig ├── .github └── workflows │ └── android.yml ├── .gitignore ├── LICENSE.md ├── README.md ├── app ├── .gitignore ├── build.gradle.kts └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ └── fonts │ │ └── vazir.ttf │ ├── java │ └── com │ │ └── pouyaheydari │ │ └── appupdater │ │ └── demo │ │ ├── ui │ │ ├── android │ │ │ └── MainActivity.kt │ │ └── compose │ │ │ ├── ComposeSampleActivity.kt │ │ │ └── theme │ │ │ ├── Theme.kt │ │ │ └── Type.kt │ │ └── utils │ │ ├── ComposePreviewData.kt │ │ ├── Constants.kt │ │ └── DSLPreviewData.kt │ └── res │ ├── layout │ └── activity_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── appupdater ├── .gitignore ├── build.gradle.kts ├── gradle.properties └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── pouyaheydari │ │ │ └── appupdater │ │ │ └── main │ │ │ ├── data │ │ │ └── mapper │ │ │ │ └── SelectedThemeMapper.kt │ │ │ ├── dsl │ │ │ └── DSLUtils.kt │ │ │ ├── ui │ │ │ ├── AppUpdaterDialog.kt │ │ │ ├── AppUpdaterViewModel.kt │ │ │ ├── AppUpdaterViewModelFactory.kt │ │ │ ├── UpdateInProgressDialog.kt │ │ │ ├── adapters │ │ │ │ ├── DirectRecyclerAdapter.kt │ │ │ │ └── StoresRecyclerAdapter.kt │ │ │ └── model │ │ │ │ ├── DialogScreenIntents.kt │ │ │ │ ├── DialogScreenStates.kt │ │ │ │ ├── UpdaterDialogData.kt │ │ │ │ ├── UpdaterFragmentModel.kt │ │ │ │ └── UserSelectedTheme.kt │ │ │ └── utils │ │ │ ├── EnumBundleExtension.kt │ │ │ ├── ErrorCallbackHolder.kt │ │ │ ├── GetDialogWidth.kt │ │ │ ├── ParcelableCompatApi.kt │ │ │ └── TypefaceHolder.kt │ └── res │ │ ├── drawable │ │ ├── dialog_background.xml │ │ ├── dialog_background_dark.xml │ │ └── update_in_progress_dialog_background_dark.xml │ │ ├── layout │ │ ├── download_direct_item.xml │ │ ├── download_stores_item.xml │ │ ├── fragment_app_updater_dialog.xml │ │ └── fragment_update_in_progress_dialog.xml │ │ └── values │ │ └── colors.xml │ └── test │ └── java │ └── com │ └── pouyaheydari │ └── appupdater │ └── main │ ├── data │ └── mapper │ │ └── SelectedThemeMapperTest.kt │ └── ui │ └── AppUpdaterViewModelTest.kt ├── build.gradle.kts ├── compose ├── .gitignore ├── build.gradle.kts ├── gradle.properties └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── pouyaheydari │ │ └── appupdater │ │ └── compose │ │ └── ui │ │ └── components │ │ └── AppUpdaterDialogComponentAndroidTest.kt │ ├── debug │ ├── AndroidManifest.xml │ └── java │ │ └── com │ │ └── pouyaheydari │ │ └── appupdater │ │ └── compose │ │ └── ComposeTestActivity.kt │ ├── main │ └── java │ │ └── com │ │ └── pouyaheydari │ │ └── appupdater │ │ └── compose │ │ ├── data │ │ └── mapper │ │ │ ├── UpdaterDialogUIMapper.kt │ │ │ └── UpdaterViewModelDataMapper.kt │ │ └── ui │ │ ├── AndroidAppUpdaterScreen.kt │ │ ├── AndroidAppUpdaterViewModel.kt │ │ ├── AndroidAppUpdaterViewModelFactory.kt │ │ ├── components │ │ ├── AppUpdaterDialogComponent.kt │ │ ├── DialogHeaderComponent.kt │ │ ├── DirectDownloadLinkComponent.kt │ │ ├── DividerComponent.kt │ │ ├── SquareStoreItemComponent.kt │ │ └── UpdateInProgressDialogComponent.kt │ │ ├── models │ │ ├── DialogHeaderModel.kt │ │ ├── DialogScreenIntents.kt │ │ ├── DialogScreenState.kt │ │ ├── UpdaterDialogData.kt │ │ ├── UpdaterDialogUIData.kt │ │ └── UpdaterViewModelData.kt │ │ ├── theme │ │ ├── Color.kt │ │ ├── Theme.kt │ │ └── Type.kt │ │ └── utils │ │ └── AppUpdaterPreviewData.kt │ └── test │ └── java │ └── com │ └── pouyaheydari │ └── appupdater │ └── compose │ ├── data │ └── mapper │ │ └── UpdaterViewModelDataMapperTest.kt │ └── ui │ ├── AndroidAppUpdaterScreenTest.kt │ ├── AndroidAppUpdaterViewModelTest.kt │ └── components │ ├── AppUpdaterDialogComponentTest.kt │ ├── DialogHeaderComponentTest.kt │ ├── DirectDownloadLinkComponentTest.kt │ ├── DividerComponentTest.kt │ ├── SquareStoreItemComponentTest.kt │ └── UpdateInProgressDialogComponentTest.kt ├── core ├── .gitignore ├── build.gradle.kts ├── gradle.properties └── src │ └── main │ └── java │ └── com │ └── pouyaheydari │ └── appupdater │ └── core │ ├── model │ └── Theme.kt │ └── utils │ └── Constants.kt ├── directdownload ├── .gitignore ├── build.gradle.kts ├── gradle.properties └── src │ ├── main │ ├── AndroidManifest.xml │ ├── kotlin │ │ └── com │ │ │ └── pouyaheydari │ │ │ └── appupdater │ │ │ └── directdownload │ │ │ ├── data │ │ │ ├── DirectDownloadListItem.kt │ │ │ └── UpdateInProgressRepositoryImpl.kt │ │ │ ├── domain │ │ │ ├── DownloadState.kt │ │ │ ├── GetDownloadStateUseCase.kt │ │ │ ├── GetRequestIdUseCase.kt │ │ │ ├── SetDownloadStateUseCase.kt │ │ │ ├── SetRequestIdUseCase.kt │ │ │ └── UpdateInProgressRepository.kt │ │ │ ├── receiver │ │ │ └── DownloadFinishedReceiver.kt │ │ │ └── utils │ │ │ ├── donwloadapk │ │ │ ├── APKDownloadManager.kt │ │ │ ├── APKFileProvider.kt │ │ │ ├── APKFileProviderImpl.kt │ │ │ ├── DownloadAPKHelper.kt │ │ │ └── DownloadManagerRequestCreator.kt │ │ │ ├── installapk │ │ │ ├── APKIntentFactory.kt │ │ │ ├── FileUriProvider.kt │ │ │ ├── InstallAPKIntent.kt │ │ │ ├── InstallAPKIntentForM.kt │ │ │ ├── InstallAPKIntentForNToO.kt │ │ │ ├── InstallAPKIntentForPAndAbove.kt │ │ │ └── InstallApkUtil.kt │ │ │ └── permission │ │ │ ├── DownloadAPKPermission.kt │ │ │ ├── DownloadAPKPermissionFactory.kt │ │ │ ├── DownloadAPKPermissionForOAndBellow.kt │ │ │ └── DownloadAPKPermissionForPAndAbove.kt │ └── res │ │ ├── values │ │ └── strings.xml │ │ └── xml │ │ └── provider_paths.xml │ └── test │ └── kotlin │ └── com │ └── pouyaheydari │ └── appupdater │ └── directdownload │ ├── data │ └── UpdateInProgressRepositoryImplTest.kt │ ├── domain │ ├── GetDownloadStateUseCaseTest.kt │ ├── GetRequestIdUseCaseTest.kt │ ├── SetDownloadStateUseCaseTest.kt │ └── SetRequestIdUseCaseTest.kt │ └── utils │ ├── donwloadapk │ ├── APKDownloadManagerTest.kt │ ├── APKFileProviderImplTest.kt │ ├── DownloadAPKHelperKtTest.kt │ └── DownloadManagerRequestCreatorTest.kt │ ├── installapk │ ├── APKIntentFactoryTest.kt │ ├── FileUriProviderTest.kt │ ├── InstallAPKIntentForMTest.kt │ ├── InstallAPKIntentForNToOTest.kt │ ├── InstallAPKIntentForPAndAboveTest.kt │ └── InstallApkUtilKtTest.kt │ └── permission │ ├── DownloadAPKPermissionFactoryTest.kt │ ├── DownloadAPKPermissionForOAndBellowTest.kt │ ├── DownloadAPKPermissionForPAndAboveTest.kt │ └── DownloadAPKPermissionForPAndAboveUnknownSourceMethodTest.kt ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── jitpack.yml ├── pics ├── header.png └── icon.png ├── renovate.json ├── settings.gradle.kts └── store ├── .gitignore ├── build.gradle.kts ├── gradle.properties └── src ├── androidTest └── java │ └── com │ └── pouyaheydari │ └── appupdater │ └── store │ └── domain │ ├── StoreManagerTest.kt │ └── stores │ ├── AmazonAppStoreTest.kt │ ├── AptoideTest.kt │ ├── CafeBazaarStoreTest.kt │ ├── FDroidTest.kt │ ├── GooglePlayStoreTest.kt │ ├── HuaweiAppGalleryTest.kt │ ├── LenovoAppCenterTest.kt │ ├── MiGetAppStoreTest.kt │ ├── MyketStoreTest.kt │ ├── NineAppsTest.kt │ ├── OneStoreAppMarketTest.kt │ ├── OppoAppMarketTest.kt │ ├── SamsungGalaxyStoreTest.kt │ ├── TencentStoreTest.kt │ ├── VAppStoreTest.kt │ └── ZTEAppCenterTest.kt ├── main ├── java │ └── com │ │ └── pouyaheydari │ │ └── appupdater │ │ └── store │ │ └── domain │ │ ├── AppStoreCallback.kt │ │ ├── StoreFactory.kt │ │ ├── StoreIntentBuilder.kt │ │ ├── StoreListItem.kt │ │ ├── StoreManager.kt │ │ └── stores │ │ ├── AmazonAppStore.kt │ │ ├── AppStore.kt │ │ ├── AppStoreType.kt │ │ ├── Aptoide.kt │ │ ├── CafeBazaarStore.kt │ │ ├── FDroid.kt │ │ ├── GooglePlayStore.kt │ │ ├── HuaweiAppGallery.kt │ │ ├── LenovoAppCenter.kt │ │ ├── MiGetAppStore.kt │ │ ├── MyketStore.kt │ │ ├── NineApps.kt │ │ ├── OneStoreAppMarket.kt │ │ ├── OppoAppMarket.kt │ │ ├── SamsungGalaxyStore.kt │ │ ├── TencentAppStore.kt │ │ ├── VAppStore.kt │ │ └── ZTEAppCenter.kt └── res │ ├── drawable-nodpi │ ├── appupdater_ic_amazon_app_store.png │ ├── appupdater_ic_bazar.png │ ├── appupdater_ic_cloud.png │ ├── appupdater_ic_fdroid.png │ ├── appupdater_ic_galaxy_store.png │ ├── appupdater_ic_get_app_store.png │ ├── appupdater_ic_lenovo_app_center.png │ ├── appupdater_ic_nine_apps.png │ ├── appupdater_ic_one_store.png │ ├── appupdater_ic_oppo_app_market.png │ ├── appupdater_ic_tencent_app_store.png │ ├── appupdater_ic_v_app_store.png │ └── appupdater_ic_zte_app_center.png │ └── drawable │ ├── appupdater_ic_app_gallery.xml │ ├── appupdater_ic_aptoide.xml │ ├── appupdater_ic_google_play.xml │ └── appupdater_ic_myket.xml └── test └── java └── com └── pouyaheydari └── appupdater └── store └── domain ├── StoreFactoryTest.kt └── StoreIntentBuilderTest.kt /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | charset = utf-8 3 | end_of_line = lf 4 | insert_final_newline = true 5 | indent_style = space 6 | indent_size = 4 7 | 8 | [*.{kt,kts}] 9 | max_line_length = 180 10 | ktlint_function_naming_ignore_when_annotated_with = Composable 11 | ktlint_standard_string-template-indent = disabled 12 | ktlint_standard_multiline-expression-wrapping = disabled 13 | ktlint_standard_blank-line-before-declaration = disabled 14 | ktlint_standard_function-signature = disabled 15 | ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = unset 16 | ktlint_function_signature_body_expression_wrapping = default 17 | ktlint_standard_backing-property-naming = disabled 18 | -------------------------------------------------------------------------------- /.github/workflows/android.yml: -------------------------------------------------------------------------------- 1 | name: Android CI 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v4 16 | - name: set up JDK 17 17 | uses: actions/setup-java@v4 18 | with: 19 | java-version: '17' 20 | distribution: 'temurin' 21 | cache: gradle 22 | 23 | - name: Grant execute permission for gradlew 24 | run: chmod +x gradlew 25 | - name: Run tests 26 | run: ./gradlew testDebugUnitTest 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | *.jks 11 | *.apk 12 | 13 | # Gradle files 14 | .gradle/ 15 | build/ 16 | 17 | # Local configuration file (sdk path, etc) 18 | local.properties 19 | 20 | # Log/OS Files 21 | *.log 22 | 23 | # Android Studio generated files and folders 24 | captures/ 25 | .externalNativeBuild/ 26 | .cxx/ 27 | output.json 28 | 29 | # IntelliJ 30 | .idea/ 31 | misc.xml 32 | deploymentTargetDropDown.xml 33 | render.experimental.xml 34 | 35 | # Keystore files 36 | *.keystore 37 | 38 | # Google Services (e.g. APIs or Firebase) 39 | google-services.json 40 | 41 | # Android Profiling 42 | *.hprof 43 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(libs.plugins.androidApplication) 3 | alias(libs.plugins.jetbrainsKotlinAndroid) 4 | alias(libs.plugins.compose.compiler) 5 | } 6 | 7 | android { 8 | compileSdk = libs.versions.compileSdkVersion.get().toInt() 9 | defaultConfig { 10 | applicationId = "com.pouyaheydari.appupdater.demo" 11 | minSdk = libs.versions.minSdkVersion.get().toInt() 12 | targetSdk = libs.versions.targetSdkVersion.get().toInt() 13 | versionCode = libs.versions.appVersion.get().toInt() 14 | versionName = libs.versions.appVersion.get() 15 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 16 | vectorDrawables { 17 | useSupportLibrary = true 18 | } 19 | } 20 | namespace = "com.pouyaheydari.appupdater.demo" 21 | 22 | compileOptions { 23 | sourceCompatibility = JavaVersion.VERSION_17 24 | targetCompatibility = JavaVersion.VERSION_17 25 | } 26 | 27 | kotlinOptions { 28 | jvmTarget = JavaVersion.VERSION_17.toString() 29 | } 30 | buildFeatures { 31 | compose = true 32 | } 33 | packaging { 34 | resources { 35 | excludes += "/META-INF/{AL2.0,LGPL2.1}" 36 | } 37 | } 38 | } 39 | 40 | dependencies { 41 | 42 | // library dependency 43 | implementation(projects.appupdater) 44 | implementation(projects.compose) 45 | 46 | // support dependency 47 | implementation(libs.androidx.appcompat) 48 | implementation(libs.androidx.constraintLayout) 49 | 50 | // compose 51 | val composeBom = platform(libs.androidx.compose.bom) 52 | implementation(composeBom) 53 | implementation(libs.androidx.compose.foundation) 54 | implementation(libs.androidx.compose.ui.tooling) 55 | implementation(libs.androidx.compose.ui.tooling.preview) 56 | implementation(libs.androidx.compose.activity) 57 | implementation(libs.androidx.compose.material) 58 | 59 | // testing dependency 60 | testImplementation(libs.junit4) 61 | androidTestImplementation(libs.androidx.test.junit) 62 | androidTestImplementation(libs.androidx.test.rules) 63 | androidTestImplementation(libs.androidx.test.ui.espresso.core) 64 | androidTestImplementation(composeBom) 65 | } 66 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/assets/fonts/vazir.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeyPouya/AndroidAppUpdater/d7da0e6440876887bfdc6c1b613964f5ccd78df6/app/src/main/assets/fonts/vazir.ttf -------------------------------------------------------------------------------- /app/src/main/java/com/pouyaheydari/appupdater/demo/ui/compose/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package com.pouyaheydari.appupdater.demo.ui.compose.theme 2 | 3 | import androidx.compose.foundation.isSystemInDarkTheme 4 | import androidx.compose.material3.MaterialTheme 5 | import androidx.compose.material3.darkColorScheme 6 | import androidx.compose.material3.lightColorScheme 7 | import androidx.compose.runtime.Composable 8 | import androidx.compose.ui.graphics.Color 9 | 10 | private val DarkColorScheme = darkColorScheme( 11 | primary = Color.Black, 12 | ) 13 | 14 | private val LightColorScheme = lightColorScheme( 15 | primary = Color.Black, 16 | ) 17 | 18 | @Composable 19 | fun AndroidAppUpdaterTheme( 20 | darkTheme: Boolean = isSystemInDarkTheme(), 21 | content: @Composable () -> Unit, 22 | ) { 23 | val colorScheme = if (darkTheme) DarkColorScheme else LightColorScheme 24 | MaterialTheme(colorScheme = colorScheme, typography = Typography, content = content) 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/pouyaheydari/appupdater/demo/ui/compose/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package com.pouyaheydari.appupdater.demo.ui.compose.theme 2 | 3 | import androidx.compose.material3.Typography 4 | import androidx.compose.ui.text.TextStyle 5 | import androidx.compose.ui.text.font.FontFamily 6 | import androidx.compose.ui.text.font.FontWeight 7 | import androidx.compose.ui.unit.sp 8 | 9 | internal val Typography = Typography( 10 | bodyLarge = TextStyle( 11 | fontFamily = FontFamily.Default, 12 | fontWeight = FontWeight.Normal, 13 | fontSize = 16.sp, 14 | lineHeight = 24.sp, 15 | letterSpacing = 0.5.sp, 16 | ), 17 | ) 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/pouyaheydari/appupdater/demo/utils/Constants.kt: -------------------------------------------------------------------------------- 1 | package com.pouyaheydari.appupdater.demo.utils 2 | 3 | internal const val APK_URL = "https://cafebazaar.ir/download/bazaar.apk" 4 | internal const val SAMPLE_PACKAGE_NAME = "com.tencent.mm" 5 | internal const val FDROID_SAMPLE_PACKAGE_NAME = "de.storchp.fdroidbuildstatus" 6 | internal const val GET_APP_SAMPLE_PACKAGE_NAME = "com.opera.browser" 7 | internal const val ONE_STORE_SAMPLE_PACKAGE_NAME = "com.kakao.talk" 8 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 |