diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 370cb5ab85..c43873232c 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1 +1 @@
-* @Oztechan/core-team
+* @Oztechan/core-team @mustafaozhan
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 7703c71938..45c2895e4d 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -8,7 +8,7 @@ on:
concurrency:
group: ${{ github.ref }}
- cancel-in-progress: true
+ cancel-in-progress: ${{ !contains(github.ref, 'develop')}}
env:
BASE_URL_BACKEND: ${{ secrets.BASE_URL_BACKEND }}
@@ -90,9 +90,12 @@ jobs:
distribution: 'temurin'
- name: Assemble
- uses: gradle/gradle-build-action@v3.1.0
+ uses: gradle/actions/setup-gradle@v3.3.0
with:
arguments: assemble
+ build-scan-publish: true
+ build-scan-terms-of-use-url: "https://gradle.com/help/legal-terms-of-use"
+ build-scan-terms-of-use-agree: "yes"
- name: Upload Android Artifacts
uses: actions/upload-artifact@v4.3.1
@@ -112,7 +115,7 @@ jobs:
run: echo "status=success" >> $GITHUB_OUTPUT
DistributeAndroid:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
needs: [ GradleBuild ]
if: github.event_name == 'push'
outputs:
@@ -123,7 +126,7 @@ jobs:
uses: actions/checkout@v4.1.1
- name: Download Android Artifacts
- uses: actions/download-artifact@v4.1.4
+ uses: actions/download-artifact@v4.1.5
with:
name: androidArtifacts
@@ -212,7 +215,7 @@ jobs:
run: echo "status=success" >> $GITHUB_OUTPUT
DistributeIOS:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
needs: [ XCodeBuild ]
if: github.event_name == 'push'
outputs:
@@ -222,7 +225,7 @@ jobs:
uses: actions/checkout@v4.1.1
- name: Download iOS IPA
- uses: actions/download-artifact@v4.1.4
+ uses: actions/download-artifact@v4.1.5
with:
name: iOSArtifacts
path: ios
@@ -262,9 +265,12 @@ jobs:
distribution: 'temurin'
- name: Run Quality Jobs
- uses: gradle/gradle-build-action@v3.1.0
+ uses: gradle/actions/setup-gradle@v3.3.0
with:
arguments: check koverMergedXmlReport --parallel
+ build-scan-publish: true
+ build-scan-terms-of-use-url: "https://gradle.com/help/legal-terms-of-use"
+ build-scan-terms-of-use-agree: "yes"
- name: Upload Coverage Report
uses: actions/upload-artifact@v4.3.1
@@ -281,7 +287,7 @@ jobs:
run: echo "status=success" >> $GITHUB_OUTPUT
UploadQualityReports:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
needs: [ Quality ]
outputs:
status: ${{ steps.status.outputs.status }}
@@ -290,13 +296,13 @@ jobs:
uses: actions/checkout@v4.1.1
- name: Download Coverage Report
- uses: actions/download-artifact@v4.1.4
+ uses: actions/download-artifact@v4.1.5
with:
name: coverageReport
path: build
- name: Upload coverage to Codecov
- uses: codecov/codecov-action@v4.1.0
+ uses: codecov/codecov-action@v4.3.0
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: build/report.xml
@@ -329,7 +335,7 @@ jobs:
run: echo "status=success" >> $GITHUB_OUTPUT
CodeAnalysis:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
outputs:
status: ${{ steps.status.outputs.status }}
steps:
@@ -346,9 +352,12 @@ jobs:
distribution: 'temurin'
- name: Detekt
- uses: gradle/gradle-build-action@v3.1.0
+ uses: gradle/actions/setup-gradle@v3.3.0
with:
arguments: detektAll
+ build-scan-publish: true
+ build-scan-terms-of-use-url: "https://gradle.com/help/legal-terms-of-use"
+ build-scan-terms-of-use-agree: "yes"
- name: SwiftLint
uses: norio-nomura/action-swiftlint@3.2.1
@@ -366,7 +375,7 @@ jobs:
run: echo "status=success" >> $GITHUB_OUTPUT
Notify:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
needs: [ GradleBuild, XCodeBuild, Quality, CodeAnalysis, DistributeAndroid, DistributeIOS, UploadQualityReports ]
if: always()
steps:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 3e1a458370..0a53f5f4f3 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -54,7 +54,7 @@ env:
jobs:
GenerateGradleArtifacts:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
outputs:
status: ${{ steps.status.outputs.status }}
steps:
@@ -85,9 +85,12 @@ jobs:
distribution: 'temurin'
- name: Generate Artifacts
- uses: gradle/gradle-build-action@v3.1.0
+ uses: gradle/actions/setup-gradle@v3.3.0
with:
arguments: :android:app:bundleRelease :backend:app:jar --parallel
+ build-scan-publish: true
+ build-scan-terms-of-use-url: "https://gradle.com/help/legal-terms-of-use"
+ build-scan-terms-of-use-agree: "yes"
- name: Upload Google App Bundle
uses: actions/upload-artifact@v4.3.1
@@ -112,14 +115,14 @@ jobs:
run: echo "status=success" >> $GITHUB_OUTPUT
UploadToGooglePlay:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
needs: [ GenerateGradleArtifacts ]
outputs:
status: ${{ steps.status.outputs.status }}
steps:
- name: Download App Bundle
- uses: actions/download-artifact@v4.1.4
+ uses: actions/download-artifact@v4.1.5
with:
name: googleBundle
@@ -148,14 +151,14 @@ jobs:
run: echo "status=success" >> $GITHUB_OUTPUT
UploadToHuaweiAppGallery:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
needs: [ GenerateGradleArtifacts ]
outputs:
status: ${{ steps.status.outputs.status }}
steps:
- name: Download App Bundle
- uses: actions/download-artifact@v4.1.4
+ uses: actions/download-artifact@v4.1.5
with:
name: huaweiBundle
@@ -180,14 +183,14 @@ jobs:
run: echo "status=success" >> $GITHUB_OUTPUT
DeployToServer:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
needs: [ GenerateGradleArtifacts ]
outputs:
status: ${{ steps.status.outputs.status }}
steps:
- name: Download Backend Jar
- uses: actions/download-artifact@v4.1.4
+ uses: actions/download-artifact@v4.1.5
with:
name: backendJar
path: artifact
@@ -246,7 +249,7 @@ jobs:
run: echo "status=success" >> $GITHUB_OUTPUT
Notify:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-22.04
needs: [ GenerateGradleArtifacts, UploadToGooglePlay, UploadToHuaweiAppGallery, DeployToServer, UploadToAppStore ]
if: always()
steps:
diff --git a/android/app/src/googleDebug/res/values/strings.xml b/android/app/src/googleDebug/res/values/strings.xml
new file mode 100644
index 0000000000..d6894d603b
--- /dev/null
+++ b/android/app/src/googleDebug/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+
+ CCC_G
+
diff --git a/android/app/src/huaweiDebug/res/values/strings.xml b/android/app/src/huaweiDebug/res/values/strings.xml
new file mode 100644
index 0000000000..8906136e9c
--- /dev/null
+++ b/android/app/src/huaweiDebug/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+
+ CCC_H
+
diff --git a/android/app/src/main/kotlin/com/oztechan/ccc/android/app/Application.kt b/android/app/src/main/kotlin/com/oztechan/ccc/android/app/Application.kt
index 49a085549f..2fb5fa644a 100644
--- a/android/app/src/main/kotlin/com/oztechan/ccc/android/app/Application.kt
+++ b/android/app/src/main/kotlin/com/oztechan/ccc/android/app/Application.kt
@@ -10,10 +10,9 @@ import android.os.StrictMode.ThreadPolicy
import android.os.StrictMode.VmPolicy
import co.touchlab.kermit.Logger
import com.github.submob.logmob.ANRWatchDogHandler
-import com.github.submob.logmob.initCrashlytics
import com.github.submob.logmob.initLogger
+import com.github.submob.logmob.setCrashlyticsCollection
import com.oztechan.ccc.android.app.di.initKoin
-import com.oztechan.ccc.android.core.ad.initAds
import com.oztechan.ccc.client.core.analytics.initAnalytics
class Application : Application() {
@@ -21,8 +20,9 @@ class Application : Application() {
override fun onCreate() {
super.onCreate()
+ setCrashlyticsCollection(!BuildConfig.DEBUG)
+
if (!BuildConfig.DEBUG) {
- initCrashlytics()
initAnalytics(this)
}
@@ -38,8 +38,6 @@ class Application : Application() {
Thread.setDefaultUncaughtExceptionHandler(ANRWatchDogHandler())
}
- initAds(this)
-
initKoin(this)
}
diff --git a/android/core/ad/android-core-ad.gradle.kts b/android/core/ad/android-core-ad.gradle.kts
index c6e48689a4..b6c876cb6d 100644
--- a/android/core/ad/android-core-ad.gradle.kts
+++ b/android/core/ad/android-core-ad.gradle.kts
@@ -72,6 +72,7 @@ dependencies {
libs.android.apply {
google.apply {
DeviceFlavour.GOOGLE.implementation(googleAds)
+ DeviceFlavour.GOOGLE.implementation(ump)
}
huawei.apply {
DeviceFlavour.HUAWEI.implementation(huaweiAds)
diff --git a/android/core/ad/src/google/kotlin/com/oztechan/ccc/android/core/ad/AdManagerImpl.kt b/android/core/ad/src/google/kotlin/com/oztechan/ccc/android/core/ad/AdManagerImpl.kt
index 7b52822cc2..9537b3277a 100644
--- a/android/core/ad/src/google/kotlin/com/oztechan/ccc/android/core/ad/AdManagerImpl.kt
+++ b/android/core/ad/src/google/kotlin/com/oztechan/ccc/android/core/ad/AdManagerImpl.kt
@@ -13,8 +13,17 @@ import com.google.android.gms.ads.interstitial.InterstitialAd
import com.google.android.gms.ads.interstitial.InterstitialAdLoadCallback
import com.google.android.gms.ads.rewarded.RewardedAd
import com.google.android.gms.ads.rewarded.RewardedAdLoadCallback
+import com.google.android.ump.ConsentInformation
+import com.google.android.ump.ConsentRequestParameters
+import com.google.android.ump.UserMessagingPlatform
+import java.util.concurrent.atomic.AtomicBoolean
-internal class AdManagerImpl : AdManager {
+internal class AdManagerImpl(context: Context) : AdManager {
+ // Use an atomic boolean to initialize the Google Mobile Ads SDK and load ads once.
+ private val isMobileAdsInitializeCalled = AtomicBoolean(false)
+
+ private val consentInformation: ConsentInformation =
+ UserMessagingPlatform.getConsentInformation(context)
private val adRequest: AdRequest by lazy {
AdRequest.Builder().build()
@@ -22,8 +31,46 @@ internal class AdManagerImpl : AdManager {
init {
Logger.v { "AdManagerImpl init" }
- MobileAds.setAppVolume(0.0f)
- MobileAds.setAppMuted(true)
+ }
+
+ override fun initAds(activity: Activity) {
+ Logger.v { "AdManagerImpl initAds" }
+ consentInformation.requestConsentInfoUpdate(
+ activity,
+ ConsentRequestParameters.Builder().build(),
+ {
+ UserMessagingPlatform.loadAndShowConsentFormIfRequired(activity) {
+ if (it != null) {
+ Logger.e { "Consent gathering failed: ${it.errorCode}: ${it.message}" }
+ }
+
+ // Consent has been gathered.
+ if (consentInformation.canRequestAds()) {
+ activity.initializeMobileAdsSdk()
+ }
+ }
+ },
+ { Logger.e { "Consent gathering failed: ${it.errorCode}: ${it.message}" } }
+ )
+
+ // Check if you can initialize the Google Mobile Ads SDK in parallel
+ // while checking for new consent information. Consent obtained in
+ // the previous session can be used to request ads.
+ if (consentInformation.canRequestAds()) {
+ activity.initializeMobileAdsSdk()
+ }
+ }
+
+ override fun isPrivacyOptionsRequired() =
+ consentInformation.privacyOptionsRequirementStatus ==
+ ConsentInformation.PrivacyOptionsRequirementStatus.REQUIRED
+
+ override fun showConsentForm(activity: Activity) {
+ UserMessagingPlatform.showPrivacyOptionsForm(activity) {
+ if (it != null) {
+ Logger.e { "Showing consent form failed: ${it.errorCode}: ${it.message}" }
+ }
+ }
}
override fun getBannerAd(
@@ -115,4 +162,17 @@ internal class AdManagerImpl : AdManager {
}
)
}
+
+ private fun Activity.initializeMobileAdsSdk() {
+ Logger.v { "AdManagerImpl initializeMobileAdsSdk" }
+
+ if (isMobileAdsInitializeCalled.getAndSet(true)) {
+ Logger.v { "AdManagerImpl initializeMobileAdsSdk is not called, already called" }
+ return
+ }
+
+ MobileAds.initialize(this)
+ MobileAds.setAppVolume(0.0f)
+ MobileAds.setAppMuted(true)
+ }
}
diff --git a/android/core/ad/src/google/kotlin/com/oztechan/ccc/android/core/ad/Ads.kt b/android/core/ad/src/google/kotlin/com/oztechan/ccc/android/core/ad/Ads.kt
deleted file mode 100644
index d1e3fd55ed..0000000000
--- a/android/core/ad/src/google/kotlin/com/oztechan/ccc/android/core/ad/Ads.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.oztechan.ccc.android.core.ad
-
-import android.content.Context
-import co.touchlab.kermit.Logger
-import com.google.android.gms.ads.MobileAds
-
-fun initAds(context: Context) {
- Logger.v { "Ads initAds" }
- MobileAds.initialize(context)
-}
diff --git a/android/core/ad/src/huawei/kotlin/com/oztechan/ccc/android/core/ad/AdManagerImpl.kt b/android/core/ad/src/huawei/kotlin/com/oztechan/ccc/android/core/ad/AdManagerImpl.kt
index b313dfd167..e3d97bdd18 100644
--- a/android/core/ad/src/huawei/kotlin/com/oztechan/ccc/android/core/ad/AdManagerImpl.kt
+++ b/android/core/ad/src/huawei/kotlin/com/oztechan/ccc/android/core/ad/AdManagerImpl.kt
@@ -22,10 +22,19 @@ internal class AdManagerImpl : AdManager {
init {
Logger.v { "AdManagerImpl init" }
+ }
+
+ override fun initAds(activity: Activity) {
+ Logger.v { "Ads initAds" }
+ HwAds.init(activity)
HwAds.setVideoVolume(0f)
HwAds.setVideoMuted(true)
}
+ override fun isPrivacyOptionsRequired() = false
+
+ override fun showConsentForm(activity: Activity) = Unit
+
override fun getBannerAd(
context: Context,
width: Int,
diff --git a/android/core/ad/src/huawei/kotlin/com/oztechan/ccc/android/core/ad/Ads.kt b/android/core/ad/src/huawei/kotlin/com/oztechan/ccc/android/core/ad/Ads.kt
deleted file mode 100644
index 82efeab88d..0000000000
--- a/android/core/ad/src/huawei/kotlin/com/oztechan/ccc/android/core/ad/Ads.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.oztechan.ccc.android.core.ad
-
-import android.content.Context
-import co.touchlab.kermit.Logger
-import com.huawei.hms.ads.HwAds
-
-fun initAds(context: Context) {
- Logger.v { "Ads initAds" }
- HwAds.init(context)
-}
diff --git a/android/core/ad/src/main/kotlin/com/oztechan/ccc/android/core/ad/AdManager.kt b/android/core/ad/src/main/kotlin/com/oztechan/ccc/android/core/ad/AdManager.kt
index 96769b31f6..074490f01c 100644
--- a/android/core/ad/src/main/kotlin/com/oztechan/ccc/android/core/ad/AdManager.kt
+++ b/android/core/ad/src/main/kotlin/com/oztechan/ccc/android/core/ad/AdManager.kt
@@ -5,6 +5,12 @@ import android.content.Context
interface AdManager {
+ fun initAds(activity: Activity)
+
+ fun isPrivacyOptionsRequired(): Boolean
+
+ fun showConsentForm(activity: Activity)
+
fun getBannerAd(
context: Context,
width: Int,
diff --git a/android/ui/mobile/android-ui-mobile.gradle.kts b/android/ui/mobile/android-ui-mobile.gradle.kts
index 3d1ebfe5a1..7b5f6dc3cd 100644
--- a/android/ui/mobile/android-ui-mobile.gradle.kts
+++ b/android/ui/mobile/android-ui-mobile.gradle.kts
@@ -66,6 +66,10 @@ dependencies {
implementation(koinCompose)
implementation(lifecycleRuntime)
implementation(splashScreen)
+
+ // todo can be removed when SearchView is removed: https://github.com/Oztechan/CCC/issues/3272
+ implementation(appCompat)
+ implementation(appCompatResources)
}
android.google.apply {
diff --git a/android/ui/mobile/src/main/kotlin/com/oztechan/ccc/android/ui/mobile/content/main/MainActivity.kt b/android/ui/mobile/src/main/kotlin/com/oztechan/ccc/android/ui/mobile/content/main/MainActivity.kt
index 37acae6c32..9282e2c1a1 100755
--- a/android/ui/mobile/src/main/kotlin/com/oztechan/ccc/android/ui/mobile/content/main/MainActivity.kt
+++ b/android/ui/mobile/src/main/kotlin/com/oztechan/ccc/android/ui/mobile/content/main/MainActivity.kt
@@ -19,6 +19,7 @@ import com.oztechan.ccc.android.ui.mobile.BuildConfig
import com.oztechan.ccc.android.ui.mobile.R
import com.oztechan.ccc.android.ui.mobile.util.getThemeMode
import com.oztechan.ccc.android.ui.mobile.util.requestAppReview
+import com.oztechan.ccc.android.ui.mobile.util.resolveAndStartIntent
import com.oztechan.ccc.android.ui.mobile.util.showDialog
import com.oztechan.ccc.android.ui.mobile.util.updateBaseContextLocale
import com.oztechan.ccc.client.viewmodel.main.MainEffect
@@ -46,6 +47,7 @@ class MainActivity : BaseActivity() {
super.onCreate(savedInstanceState)
Logger.i { "MainActivity onCreate" }
setContentView(R.layout.activity_main)
+ adManager.initAds(this)
observeStates()
observeEffects()
}
@@ -91,7 +93,7 @@ class MainActivity : BaseActivity() {
positiveButton = R.string.update,
cancelable = isCancelable
) {
- startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(marketLink)))
+ resolveAndStartIntent(Intent(Intent.ACTION_VIEW, Uri.parse(marketLink)))
}
private fun setDestination(fragmentId: Int) = with(getNavigationController()) {
diff --git a/android/ui/mobile/src/main/kotlin/com/oztechan/ccc/android/ui/mobile/content/premium/PremiumBottomSheet.kt b/android/ui/mobile/src/main/kotlin/com/oztechan/ccc/android/ui/mobile/content/premium/PremiumBottomSheet.kt
index 97a6522008..14a03309f2 100644
--- a/android/ui/mobile/src/main/kotlin/com/oztechan/ccc/android/ui/mobile/content/premium/PremiumBottomSheet.kt
+++ b/android/ui/mobile/src/main/kotlin/com/oztechan/ccc/android/ui/mobile/content/premium/PremiumBottomSheet.kt
@@ -16,6 +16,7 @@ import com.oztechan.ccc.android.core.billing.BillingManager
import com.oztechan.ccc.android.ui.mobile.BuildConfig
import com.oztechan.ccc.android.ui.mobile.R
import com.oztechan.ccc.android.ui.mobile.databinding.BottomSheetPremiumBinding
+import com.oztechan.ccc.android.ui.mobile.util.resolveAndStartIntent
import com.oztechan.ccc.android.ui.mobile.util.showDialog
import com.oztechan.ccc.android.ui.mobile.util.showSnack
import com.oztechan.ccc.android.ui.mobile.util.toOldPurchaseList
@@ -155,6 +156,6 @@ class PremiumBottomSheet : BaseVBBottomSheetDialogFragment() {
with(itemDisableAds) {
imgSettingsItem.setBackgroundResource(R.drawable.ic_premium)
settingsItemTitle.text = getString(R.string.settings_item_premium_title)
- settingsItemSubTitle.text = getString(R.string.settings_item_premium_sub_title_no_ads_and_widget)
+ settingsItemSubTitle.text =
+ getString(R.string.settings_item_premium_sub_title_no_ads_and_widget)
}
with(itemPrecision) {
@@ -131,6 +133,12 @@ class SettingsFragment : BaseVBFragment() {
settingsItemTitle.text = getString(R.string.settings_item_on_github_title)
settingsItemSubTitle.text = getString(R.string.settings_item_on_github_sub_title)
}
+ with(itemPrivacySettings) {
+ root.visibleIf(adManager.isPrivacyOptionsRequired())
+ imgSettingsItem.setBackgroundResource(R.drawable.ic_privacy_settings)
+ settingsItemTitle.text = getString(R.string.settings_item_privacy_settings_title)
+ settingsItemSubTitle.text = getString(R.string.settings_item_privacy_settings_sub_title)
+ }
with(itemVersion) {
imgSettingsItem.setBackgroundResource(R.drawable.ic_version)
settingsItemTitle.text = getString(R.string.settings_item_version_title)
@@ -151,8 +159,15 @@ class SettingsFragment : BaseVBFragment() {
itemDisableAds.settingsItemValue.text = when (val state = it.premiumStatus) {
PremiumStatus.NeverActivated -> ""
- is PremiumStatus.Active -> getString(R.string.settings_item_premium_value_will_expire, state.until)
- is PremiumStatus.Expired -> getString(R.string.settings_item_premium_value_expired, state.at)
+ is PremiumStatus.Active -> getString(
+ R.string.settings_item_premium_value_will_expire,
+ state.until
+ )
+
+ is PremiumStatus.Expired -> getString(
+ R.string.settings_item_premium_value_expired,
+ state.at
+ )
}
itemPrecision.settingsItemValue.text = requireContext().getString(
@@ -185,10 +200,17 @@ class SettingsFragment : BaseVBFragment() {
R.string.rate_and_support,
R.string.rate
) {
- startIntent(Intent(Intent.ACTION_VIEW, Uri.parse(viewEffect.marketLink)))
+ requireContext().resolveAndStartIntent(
+ Intent(
+ Intent.ACTION_VIEW,
+ Uri.parse(viewEffect.marketLink)
+ )
+ )
}
- SettingsEffect.OnGitHub -> startIntent(
+ SettingsEffect.PrivacySettings -> adManager.showConsentForm(requireActivity())
+
+ SettingsEffect.OnGitHub -> requireContext().resolveAndStartIntent(
Intent(
Intent.ACTION_VIEW,
Uri.parse(getString(R.string.github_url))
@@ -210,7 +232,12 @@ class SettingsFragment : BaseVBFragment() {
SettingsEffect.OnlyOneTimeSync -> view?.showSnack(R.string.txt_already_synced)
SettingsEffect.AlreadyPremium -> view?.showSnack(R.string.txt_you_already_have_premium)
SettingsEffect.SelectPrecision -> showPrecisionDialog()
- SettingsEffect.OpenWatchers -> startActivity(Intent(context, ComposeMainActivity::class.java))
+ SettingsEffect.OpenWatchers -> startActivity(
+ Intent(
+ context,
+ ComposeMainActivity::class.java
+ )
+ )
}
}.launchIn(viewLifecycleOwner.lifecycleScope)
@@ -226,6 +253,7 @@ class SettingsFragment : BaseVBFragment() {
itemFeedback.root.setOnClickListener { onFeedBackClick() }
itemShare.root.setOnClickListener { onShareClick() }
itemOnGithub.root.setOnClickListener { onOnGitHubClick() }
+ itemPrivacySettings.root.setOnClickListener { onPrivacySettingsClick() }
itemPrecision.root.setOnClickListener { onPrecisionClick() }
}
@@ -258,10 +286,6 @@ class SettingsFragment : BaseVBFragment() {
settingsViewModel.event.onPrecisionSelect(it)
}
- private fun startIntent(intent: Intent) = getBaseActivity()?.packageManager?.let {
- intent.resolveActivity(it)?.let { startActivity(intent) }
- }
-
private fun share(marketLink: String) = Intent(Intent.ACTION_SEND).apply {
type = TEXT_TYPE
putExtra(Intent.EXTRA_TEXT, marketLink)
diff --git a/android/ui/mobile/src/main/kotlin/com/oztechan/ccc/android/ui/mobile/util/IntentUtil.kt b/android/ui/mobile/src/main/kotlin/com/oztechan/ccc/android/ui/mobile/util/IntentUtil.kt
new file mode 100644
index 0000000000..55b62dacca
--- /dev/null
+++ b/android/ui/mobile/src/main/kotlin/com/oztechan/ccc/android/ui/mobile/util/IntentUtil.kt
@@ -0,0 +1,11 @@
+package com.oztechan.ccc.android.ui.mobile.util
+
+import android.content.Context
+import android.content.Intent
+import co.touchlab.kermit.Logger
+
+fun Context.resolveAndStartIntent(intent: Intent) {
+ intent.resolveActivity(packageManager)?.let {
+ startActivity(intent)
+ } ?: Logger.w { "No activity found to handle the intent: $intent" }
+}
diff --git a/android/ui/mobile/src/main/res/drawable/ic_privacy_settings.xml b/android/ui/mobile/src/main/res/drawable/ic_privacy_settings.xml
new file mode 100644
index 0000000000..dca83eb70f
--- /dev/null
+++ b/android/ui/mobile/src/main/res/drawable/ic_privacy_settings.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
diff --git a/android/ui/mobile/src/main/res/layout/fragment_currencies.xml b/android/ui/mobile/src/main/res/layout/fragment_currencies.xml
index b4efa72ec1..b95a692602 100755
--- a/android/ui/mobile/src/main/res/layout/fragment_currencies.xml
+++ b/android/ui/mobile/src/main/res/layout/fragment_currencies.xml
@@ -35,6 +35,7 @@
android:id="@+id/txt_select_currencies"
style="@style/SelectYourCurrenciesStyle"
app:layout_constraintBottom_toTopOf="@+id/ad_view_container"
+ app:layout_constraintEnd_toStartOf="@+id/btn_done"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/recycler_view_currencies" />
diff --git a/android/ui/mobile/src/main/res/layout/fragment_settings.xml b/android/ui/mobile/src/main/res/layout/fragment_settings.xml
index 5a7d905c25..104e76fb56 100644
--- a/android/ui/mobile/src/main/res/layout/fragment_settings.xml
+++ b/android/ui/mobile/src/main/res/layout/fragment_settings.xml
@@ -123,12 +123,20 @@
app:layout_constraintTop_toBottomOf="@+id/item_share" />
+
+
diff --git a/android/ui/mobile/src/main/res/layout/item_currencies.xml b/android/ui/mobile/src/main/res/layout/item_currencies.xml
index 31ab6e79ed..e210d3f676 100755
--- a/android/ui/mobile/src/main/res/layout/item_currencies.xml
+++ b/android/ui/mobile/src/main/res/layout/item_currencies.xml
@@ -4,7 +4,7 @@
+ style="@style/CurrenciesItemLayout">
+ app:layout_constraintStart_toEndOf="@+id/img_settings_item"
+ app:layout_constraintTop_toBottomOf="@+id/settings_item_title" />
- 11sp
+ 11sp
+ 12sp
15sp
17sp
- 19sp
+ 20sp
22sp
25sp
@@ -42,7 +43,7 @@
48dp
200dp
20dp
- 36dp
+ 36dp
256dp
diff --git a/android/ui/mobile/src/main/res/values/styles.xml b/android/ui/mobile/src/main/res/values/styles.xml
index 0abf7fe06d..1226a3bdb0 100755
--- a/android/ui/mobile/src/main/res/values/styles.xml
+++ b/android/ui/mobile/src/main/res/values/styles.xml
@@ -109,7 +109,6 @@
@@ -174,7 +173,7 @@
@@ -251,6 +250,8 @@
- match_parent
- wrap_content
- @dimen/margin_eight
+ - @dimen/margin_two
+ - @dimen/margin_two
@@ -294,7 +295,7 @@
- @dimen/fit
- center
- @style/BaseTextStyle
- - @dimen/margin_eight
+ - @dimen/margin_twelve
- @color/background
@@ -317,6 +318,8 @@
@@ -324,7 +327,7 @@
@@ -337,24 +340,37 @@
- @drawable/ic_search
-
+
+
-
+
+
@@ -426,6 +435,7 @@
- match_parent
- @dimen/height_settings_item_layout
- @drawable/selector_item
+ - @dimen/margin_four
- true
- true
@@ -454,14 +464,13 @@
@@ -487,9 +496,7 @@
- @dimen/margin_two
- @dimen/margin_two
- @dimen/margin_four
- - @dimen/margin_four
- @dimen/margin_eight
- - @dimen/margin_eight
- end
- 1
- @style/TextStyleSmall
@@ -498,7 +505,6 @@