Skip to content

Commit 437a681

Browse files
WIP
1 parent 775b55f commit 437a681

File tree

9 files changed

+239
-549
lines changed

9 files changed

+239
-549
lines changed

app/build.gradle.kts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import com.android.build.gradle.internal.tasks.factory.dependsOn
33
plugins {
44
id("com.android.application")
55
id("org.jetbrains.kotlin.android")
6+
id("org.jetbrains.kotlin.plugin.compose") version "2.1.20"
67
}
78

89
kotlin {
@@ -46,6 +47,7 @@ android {
4647

4748
buildFeatures {
4849
buildConfig = true
50+
compose = true
4951
viewBinding = true
5052
}
5153

@@ -109,6 +111,7 @@ android {
109111

110112
dependencies {
111113
// AndroidX
114+
implementation("androidx.activity:activity-compose:1.10.1")
112115
implementation("androidx.appcompat:appcompat:1.7.0")
113116
implementation("androidx.constraintlayout:constraintlayout:2.2.1")
114117
implementation("androidx.core:core-ktx:1.16.0")
@@ -119,6 +122,14 @@ dependencies {
119122
implementation("com.google.android.material:material:1.12.0")
120123
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5")
121124

125+
// Compose
126+
val composeBom = platform("androidx.compose:compose-bom:2025.05.01")
127+
implementation(composeBom)
128+
testImplementation(composeBom)
129+
androidTestImplementation(composeBom)
130+
implementation("androidx.compose.foundation:foundation")
131+
implementation("androidx.compose.material3:material3")
132+
122133
// Third-party
123134
implementation("com.journeyapps:zxing-android-embedded:4.3.0@aar")
124135
implementation("com.github.yalantis:ucrop:2.2.10")
Lines changed: 99 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -1,149 +1,124 @@
11
package protect.card_locker
22

3+
import android.app.Activity
4+
import android.content.Context
35
import android.os.Bundle
46
import android.text.Spanned
5-
import android.view.MenuItem
6-
import android.view.View
77
import android.widget.ScrollView
88
import android.widget.TextView
9-
10-
import androidx.annotation.StringRes
11-
import androidx.core.view.isVisible
9+
import androidx.activity.ComponentActivity
10+
import androidx.activity.compose.setContent
11+
import androidx.compose.foundation.layout.Column
12+
import androidx.compose.foundation.layout.padding
13+
import androidx.compose.foundation.rememberScrollState
14+
import androidx.compose.foundation.verticalScroll
15+
import androidx.compose.material3.ExperimentalMaterial3Api
16+
17+
import androidx.compose.material3.Scaffold
18+
import androidx.compose.ui.Modifier
19+
import androidx.compose.ui.res.stringResource
1220

1321
import com.google.android.material.dialog.MaterialAlertDialogBuilder
22+
import protect.card_locker.compose.CatimaAboutSection
23+
import protect.card_locker.compose.CatimaTopAppBar
24+
import protect.card_locker.compose.theme.CatimaTheme
1425

15-
import protect.card_locker.databinding.AboutActivityBinding
1626

17-
class AboutActivity : CatimaAppCompatActivity() {
18-
private companion object {
27+
class AboutActivity : ComponentActivity() {
28+
internal companion object {
1929
private const val TAG = "Catima"
20-
}
21-
22-
private lateinit var binding: AboutActivityBinding
23-
private lateinit var content: AboutContent
24-
25-
override fun onCreate(savedInstanceState: Bundle?) {
26-
super.onCreate(savedInstanceState)
27-
binding = AboutActivityBinding.inflate(layoutInflater)
28-
content = AboutContent(this)
29-
title = content.pageTitle
30-
setContentView(binding.root)
31-
setSupportActionBar(binding.toolbar)
32-
enableToolbarBackButton()
33-
34-
binding.apply {
35-
creditsSub.text = content.copyrightShort
36-
versionHistorySub.text = content.versionHistory
37-
38-
versionHistory.tag = "https://catima.app/changelog/"
39-
translate.tag = "https://hosted.weblate.org/engage/catima/"
40-
license.tag = "https://github.com/CatimaLoyalty/Android/blob/main/LICENSE"
41-
repo.tag = "https://github.com/CatimaLoyalty/Android/"
42-
privacy.tag = "https://catima.app/privacy-policy/"
43-
reportError.tag = "https://github.com/CatimaLoyalty/Android/issues"
44-
rate.tag = "https://play.google.com/store/apps/details?id=me.hackerchick.catima"
45-
donate.tag = "https://catima.app/donate"
46-
47-
// Hide Google Play rate button if not on Google Play
48-
rate.isVisible = BuildConfig.showRateOnGooglePlay
49-
// Hide donate button on Google Play (Google Play doesn't allow donation links)
50-
donate.isVisible = BuildConfig.showDonate
51-
}
52-
53-
bindClickListeners()
54-
}
55-
56-
override fun onOptionsItemSelected(item: MenuItem): Boolean {
57-
return when (item.itemId) {
58-
android.R.id.home -> {
59-
finish()
60-
true
30+
fun showHTML(context: Context, title: String, text: Spanned, activity: Activity, url: String?) {
31+
val dialogContentPadding = context.resources.getDimensionPixelSize(R.dimen.alert_dialog_content_padding)
32+
val textView = TextView(context).apply {
33+
setText(text)
34+
Utils.makeTextViewLinksClickable(this, text)
6135
}
6236

63-
else -> super.onOptionsItemSelected(item)
64-
}
65-
}
37+
val scrollView = ScrollView(context).apply {
38+
addView(textView)
39+
setPadding(dialogContentPadding, dialogContentPadding / 2, dialogContentPadding, 0)
40+
}
6641

67-
override fun onDestroy() {
68-
super.onDestroy()
69-
content.destroy()
70-
clearClickListeners()
71-
}
42+
MaterialAlertDialogBuilder(context).apply {
43+
setTitle(title)
44+
setView(scrollView)
45+
setPositiveButton(R.string.ok, null)
7246

73-
private fun bindClickListeners() {
74-
binding.apply {
75-
versionHistory.setOnClickListener { showHistory(it) }
76-
translate.setOnClickListener { openExternalBrowser(it) }
77-
license.setOnClickListener { showLicense(it) }
78-
repo.setOnClickListener { openExternalBrowser(it) }
79-
privacy.setOnClickListener { showPrivacy(it) }
80-
reportError.setOnClickListener { openExternalBrowser(it) }
81-
rate.setOnClickListener { openExternalBrowser(it) }
82-
donate.setOnClickListener { openExternalBrowser(it) }
83-
credits.setOnClickListener { showCredits() }
84-
}
85-
}
47+
// Add View online button if an URL is given
48+
url?.let {
49+
setNeutralButton(R.string.view_online) { _, _ -> OpenWebLinkHandler().openBrowser(activity, url) }
50+
}
8651

87-
private fun clearClickListeners() {
88-
binding.apply {
89-
versionHistory.setOnClickListener(null)
90-
translate.setOnClickListener(null)
91-
license.setOnClickListener(null)
92-
repo.setOnClickListener(null)
93-
privacy.setOnClickListener(null)
94-
reportError.setOnClickListener(null)
95-
rate.setOnClickListener(null)
96-
donate.setOnClickListener(null)
97-
credits.setOnClickListener(null)
52+
show()
53+
}
9854
}
9955
}
10056

101-
private fun showCredits() {
102-
showHTML(R.string.credits, content.contributorInfo, null)
103-
}
104-
105-
private fun showHistory(view: View) {
106-
showHTML(R.string.version_history, content.historyInfo, view)
107-
}
108-
109-
private fun showLicense(view: View) {
110-
showHTML(R.string.license, content.licenseInfo, view)
111-
}
112-
113-
private fun showPrivacy(view: View) {
114-
showHTML(R.string.privacy_policy, content.privacyInfo, view)
115-
}
116-
117-
private fun showHTML(@StringRes title: Int, text: Spanned, view: View?) {
118-
val dialogContentPadding = resources.getDimensionPixelSize(R.dimen.alert_dialog_content_padding)
119-
val textView = TextView(this).apply {
120-
setText(text)
121-
Utils.makeTextViewLinksClickable(this, text)
122-
}
123-
124-
val scrollView = ScrollView(this).apply {
125-
addView(textView)
126-
setPadding(dialogContentPadding, dialogContentPadding / 2, dialogContentPadding, 0)
127-
}
57+
private lateinit var content: AboutContent
12858

129-
MaterialAlertDialogBuilder(this).apply {
130-
setTitle(title)
131-
setView(scrollView)
132-
setPositiveButton(R.string.ok, null)
59+
@OptIn(ExperimentalMaterial3Api::class)
60+
override fun onCreate(savedInstanceState: Bundle?) {
61+
super.onCreate(savedInstanceState)
62+
content = AboutContent(this)
63+
title = content.pageTitle
13364

134-
// Add View online button if an URL is linked to this view
135-
view?.tag?.let {
136-
setNeutralButton(R.string.view_online) { _, _ -> openExternalBrowser(view) }
65+
setContent {
66+
CatimaTheme {
67+
Scaffold(
68+
topBar = { CatimaTopAppBar(title.toString(), onBackPressedDispatcher) }
69+
) { innerPadding ->
70+
Column(modifier = Modifier.padding(innerPadding).verticalScroll(rememberScrollState())) {
71+
CatimaAboutSection(
72+
stringResource(R.string.version_history),
73+
content.versionHistory,
74+
onClickUrl = "https://catima.app/changelog/",
75+
onClickDialogText = content.historyInfo
76+
)
77+
CatimaAboutSection(
78+
stringResource(R.string.credits),
79+
content.copyrightShort,
80+
onClickDialogText = content.contributorInfo
81+
)
82+
CatimaAboutSection(
83+
stringResource(R.string.help_translate_this_app),
84+
stringResource(R.string.translate_platform),
85+
onClickUrl = "https://hosted.weblate.org/engage/catima/"
86+
)
87+
CatimaAboutSection(
88+
stringResource(R.string.license),
89+
stringResource(R.string.app_license),
90+
onClickUrl = "https://github.com/CatimaLoyalty/Android/blob/main/LICENSE",
91+
onClickDialogText = content.licenseInfo
92+
)
93+
CatimaAboutSection(
94+
stringResource(R.string.source_repository),
95+
stringResource(R.string.on_github),
96+
onClickUrl = "https://github.com/CatimaLoyalty/Android/"
97+
)
98+
CatimaAboutSection(
99+
stringResource(R.string.privacy_policy),
100+
stringResource(R.string.and_data_usage),
101+
onClickUrl = "https://catima.app/privacy-policy/",
102+
onClickDialogText = content.privacyInfo
103+
)
104+
CatimaAboutSection(
105+
stringResource(R.string.donate),
106+
"",
107+
onClickUrl = "https://catima.app/donate"
108+
)
109+
CatimaAboutSection(
110+
stringResource(R.string.rate_this_app),
111+
stringResource(R.string.on_google_play),
112+
onClickUrl = "https://play.google.com/store/apps/details?id=me.hackerchick.catima"
113+
)
114+
CatimaAboutSection(
115+
stringResource(R.string.report_error),
116+
stringResource(R.string.on_github),
117+
onClickUrl = "https://github.com/CatimaLoyalty/Android/issues"
118+
)
119+
}
120+
}
137121
}
138-
139-
show()
140-
}
141-
}
142-
143-
private fun openExternalBrowser(view: View) {
144-
val tag = view.tag
145-
if (tag is String && tag.startsWith("https://")) {
146-
OpenWebLinkHandler().openBrowser(this, tag)
147122
}
148123
}
149124
}

app/src/main/java/protect/card_locker/OpenWebLinkHandler.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
package protect.card_locker;
22

3+
import android.app.Activity;
34
import android.content.ActivityNotFoundException;
45
import android.content.Intent;
56
import android.net.Uri;
67
import android.util.Log;
78
import android.widget.Toast;
89

9-
import androidx.appcompat.app.AppCompatActivity;
10-
1110
public class OpenWebLinkHandler {
1211

1312
private static final String TAG = "Catima";
1413

15-
public void openBrowser(AppCompatActivity activity, String url) {
14+
public void openBrowser(Activity activity, String url) {
1615
if (url == null) {
1716
return;
1817
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package protect.card_locker.compose
2+
3+
import android.text.Spanned
4+
import androidx.activity.compose.LocalActivity
5+
import androidx.compose.foundation.clickable
6+
import androidx.compose.foundation.layout.Column
7+
import androidx.compose.foundation.layout.Row
8+
import androidx.compose.foundation.layout.padding
9+
import androidx.compose.material3.MaterialTheme
10+
import androidx.compose.material3.Text
11+
import androidx.compose.runtime.Composable
12+
import androidx.compose.ui.Alignment
13+
import androidx.compose.ui.Modifier
14+
import androidx.compose.ui.platform.LocalContext
15+
import androidx.compose.ui.unit.dp
16+
import protect.card_locker.AboutActivity
17+
import protect.card_locker.OpenWebLinkHandler
18+
19+
@Composable
20+
fun CatimaAboutSection(title: String, message: String, onClickUrl: String? = null, onClickDialogText: Spanned? = null) {
21+
val context = LocalContext.current
22+
val activity = LocalActivity.current
23+
24+
Column(
25+
modifier = Modifier
26+
.padding(8.dp)
27+
.clickable {
28+
if (onClickDialogText != null) {
29+
AboutActivity.showHTML(context, title, onClickDialogText, activity!!, onClickUrl)
30+
} else if (onClickUrl != null) {
31+
OpenWebLinkHandler().openBrowser(activity, onClickUrl)
32+
}
33+
}
34+
) {
35+
Row {
36+
Column(modifier = Modifier.weight(1F)) {
37+
Text(
38+
text = title,
39+
style = MaterialTheme.typography.titleLarge
40+
)
41+
Text(text = message)
42+
}
43+
Text(modifier = Modifier.align(Alignment.CenterVertically),
44+
text = ">",
45+
style = MaterialTheme.typography.titleMedium
46+
)
47+
}
48+
}
49+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package protect.card_locker.compose
2+
3+
import androidx.activity.OnBackPressedDispatcher
4+
import androidx.compose.material.icons.Icons
5+
import androidx.compose.material.icons.automirrored.filled.ArrowBack
6+
import androidx.compose.material3.ExperimentalMaterial3Api
7+
import androidx.compose.material3.Icon
8+
import androidx.compose.material3.IconButton
9+
import androidx.compose.material3.Text
10+
import androidx.compose.material3.TopAppBar
11+
import androidx.compose.runtime.Composable
12+
import androidx.compose.ui.res.stringResource
13+
import protect.card_locker.R
14+
15+
@OptIn(ExperimentalMaterial3Api::class)
16+
@Composable
17+
fun CatimaTopAppBar(title: String, onBackPressedDispatcher: OnBackPressedDispatcher?) {
18+
TopAppBar(
19+
title = {
20+
Text(text = title)
21+
},
22+
navigationIcon = { if (onBackPressedDispatcher != null) {
23+
IconButton(onClick = { onBackPressedDispatcher.onBackPressed() }) {
24+
Icon(
25+
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
26+
contentDescription = stringResource(R.string.back)
27+
)
28+
}
29+
} else null }
30+
)
31+
}

0 commit comments

Comments
 (0)