compose: about: Initial migration

Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
Aayush Gupta
2025-09-18 15:12:20 +05:30
parent 620ca9cc48
commit 8212566dbf
17 changed files with 407 additions and 470 deletions

View File

@@ -12,7 +12,10 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
@@ -20,11 +23,16 @@ import androidx.compose.material3.VerticalDivider
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import coil3.compose.AsyncImage
import coil3.request.ImageRequest
import coil3.request.crossfade
import com.aurora.store.R
import com.aurora.store.data.model.Link
@@ -40,13 +48,19 @@ fun LinkComposable(modifier: Modifier = Modifier, link: Link, onClick: () -> Uni
modifier = modifier
.fillMaxWidth()
.clickable(onClick = onClick)
.padding(dimensionResource(R.dimen.padding_small)),
.padding(dimensionResource(R.dimen.padding_medium)),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium))
) {
Icon(
painter = painterResource(link.icon),
contentDescription = null
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data(link.icon)
.build(),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier
.requiredSize(dimensionResource(R.dimen.icon_size_default))
.clip(CircleShape)
)
VerticalDivider(
modifier = Modifier
@@ -64,7 +78,7 @@ fun LinkComposable(modifier: Modifier = Modifier, link: Link, onClick: () -> Uni
Text(
text = link.subtitle,
style = MaterialTheme.typography.bodySmall,
maxLines = 1,
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
}

View File

@@ -20,6 +20,7 @@ import androidx.navigation3.ui.NavDisplay
import androidx.navigation3.ui.rememberSceneSetupNavEntryDecorator
import com.aurora.store.MainActivity
import com.aurora.store.R
import com.aurora.store.compose.ui.about.AboutScreen
import com.aurora.store.compose.ui.accounts.AccountsScreen
import com.aurora.store.compose.ui.blacklist.BlacklistScreen
import com.aurora.store.compose.ui.details.AppDetailsScreen
@@ -109,6 +110,10 @@ fun NavDisplay(startDestination: NavKey) {
onNavigateToSplash = { activity?.startActivity(splashIntent) }
)
}
entry<Screen.About> {
AboutScreen(onNavigateUp = { onNavigateUp() })
}
}
)
}

View File

@@ -44,4 +44,7 @@ sealed class Screen : NavKey, Parcelable {
@Serializable
data object Accounts : Screen()
@Serializable
data object About : Screen()
}

View File

@@ -0,0 +1,40 @@
/*
* SPDX-FileCopyrightText: 2025 The Calyx Institute
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package com.aurora.store.compose.ui.about
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.aurora.store.R
/**
* Dialog for displaying information about Aurora Store
* @param onDismiss Callback on dismiss
*/
@Composable
fun AboutDialog(onDismiss: () -> Unit = {}) {
AlertDialog(
title = { Text(text = stringResource(R.string.about_aurora_store_title)) },
text = {
Text(text = stringResource(R.string.about_aurora_store_summary))
},
onDismissRequest = onDismiss,
confirmButton = {
TextButton(onClick = onDismiss) {
Text(text = stringResource(android.R.string.ok))
}
}
)
}
@Preview
@Composable
private fun AboutDialogPreview() {
AboutDialog()
}

View File

@@ -0,0 +1,172 @@
/*
* SPDX-FileCopyrightText: 2025 The Calyx Institute
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package com.aurora.store.compose.ui.about
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringArrayResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import coil3.compose.AsyncImage
import coil3.request.ImageRequest
import com.aurora.extensions.browse
import com.aurora.store.BuildConfig.VERSION_CODE
import com.aurora.store.BuildConfig.VERSION_NAME
import com.aurora.store.R
import com.aurora.store.compose.composables.LinkComposable
import com.aurora.store.compose.composables.TopAppBarComposable
import com.aurora.store.compose.ui.accounts.LogoutDialog
import com.aurora.store.data.model.Link
import com.aurora.store.data.providers.AccountProvider
@Composable
fun AboutScreen(onNavigateUp: () -> Unit) {
var shouldShowAboutDialog by rememberSaveable { mutableStateOf(false) }
if (shouldShowAboutDialog) {
AboutDialog(onDismiss = { shouldShowAboutDialog = false })
}
ScreenContent(
onNavigateUp = onNavigateUp,
onAboutAurora = { shouldShowAboutDialog = true }
)
}
@Composable
private fun ScreenContent(onNavigateUp: () -> Unit = {}, onAboutAurora: () -> Unit = {}) {
val context = LocalContext.current
val linkURLS = stringArrayResource(R.array.link_urls)
val linkTitles = stringArrayResource(R.array.link_titles)
val linkSummary = stringArrayResource(R.array.link_subtitle)
val linkIcons = intArrayOf(
R.drawable.ic_about,
R.drawable.ic_help,
R.drawable.ic_xda,
R.drawable.ic_telegram,
R.drawable.ic_gitlab,
R.drawable.ic_fdroid,
R.drawable.ic_bitcoin_btc,
R.drawable.ic_bitcoin_bch,
R.drawable.ic_ethereum_eth,
R.drawable.ic_bhim,
R.drawable.ic_paypal,
R.drawable.ic_libera_pay,
)
val links = linkURLS.mapIndexed { index, url ->
Link(
id = index,
title = linkTitles[index],
subtitle = linkSummary[index],
url = url,
icon = linkIcons[index]
)
}
Scaffold(
topBar = {
TopAppBarComposable(
title = stringResource(R.string.title_about),
onNavigateUp = onNavigateUp
)
}
) { paddingValues ->
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues),
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xxsmall))
) {
stickyHeader {
Surface(modifier = Modifier.fillMaxWidth()) {
BrandHeader()
}
}
items(items = links, key = { item -> item.id }) { link ->
LinkComposable(
link = link,
onClick = {
when (link.id) {
0 -> onAboutAurora()
else -> context.browse(link.url)
}
}
)
}
}
}
}
@Composable
private fun BrandHeader() {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(dimensionResource(R.dimen.margin_medium)),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xxsmall))
) {
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data(R.mipmap.ic_launcher)
.build(),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier
.requiredSize(dimensionResource(R.dimen.icon_size))
.clip(CircleShape)
)
Text(
text = stringResource(R.string.app_name),
style = MaterialTheme.typography.titleLarge,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Text(
text = stringResource(R.string.version, VERSION_NAME, VERSION_CODE),
style = MaterialTheme.typography.bodyMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Text(
text = stringResource(R.string.made_with_love, String(Character.toChars(0x2764))),
style = MaterialTheme.typography.bodyMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
}
@Preview
@Composable
private fun AboutScreenPreview() {
ScreenContent()
}

View File

@@ -15,8 +15,8 @@ import com.aurora.store.R
/**
* Dialog for confirming log out
* @param onConfirm Callback when the token has been entered
* @param onDismiss Callback when the dialog has been dismissed
* @param onConfirm Callback on confirmation
* @param onDismiss Callback on dismiss
*/
@Composable
fun LogoutDialog(onConfirm: () -> Unit = {}, onDismiss: () -> Unit = {}) {

View File

@@ -1,64 +0,0 @@
/*
* Aurora Store
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
*
* Aurora Store is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Aurora Store is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.aurora.store.view.epoxy.views.preference
import android.content.Context
import android.util.AttributeSet
import coil3.load
import com.airbnb.epoxy.CallbackProp
import com.airbnb.epoxy.ModelProp
import com.airbnb.epoxy.ModelView
import com.aurora.extensions.hide
import com.aurora.extensions.show
import com.aurora.store.data.model.Link
import com.aurora.store.databinding.ViewLinkBinding
import com.aurora.store.view.epoxy.views.BaseModel
import com.aurora.store.view.epoxy.views.BaseView
@ModelView(
autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT,
baseModelClass = BaseModel::class
)
class LinkView @JvmOverloads constructor(
context: Context?,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : BaseView<ViewLinkBinding>(context, attrs, defStyleAttr) {
@ModelProp
fun link(link: Link) {
binding.line1.text = link.title
binding.line2.text = link.subtitle
if (link.url.startsWith("http") || link.url.startsWith("upi")) {
binding.line3.hide()
} else {
binding.line3.show()
binding.line3.text = link.url
}
binding.imgIcon.load(link.icon)
}
@CallbackProp
fun click(onClickListener: OnClickListener?) {
binding.root.setOnClickListener(onClickListener)
}
}

View File

@@ -1,114 +0,0 @@
/*
* Aurora Store
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
*
* Aurora Store is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* Aurora Store is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.aurora.store.view.ui.about
import android.os.Bundle
import android.view.View
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import coil3.load
import com.aurora.extensions.browse
import com.aurora.extensions.copyToClipBoard
import com.aurora.store.BuildConfig
import com.aurora.store.R
import com.aurora.store.data.model.Link
import com.aurora.store.databinding.FragmentAboutBinding
import com.aurora.store.view.epoxy.views.preference.LinkViewModel_
import com.aurora.store.view.ui.commons.BaseFragment
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class AboutFragment : BaseFragment<FragmentAboutBinding>() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Toolbar
binding.toolbar.setNavigationOnClickListener { findNavController().navigateUp() }
// About Details
binding.imgIcon.load(R.mipmap.ic_launcher)
binding.line2.text = view.context.getString(
R.string.version,
BuildConfig.VERSION_NAME,
BuildConfig.VERSION_CODE
)
binding.line3.text = getString(R.string.made_with_love, String(Character.toChars(0x2764)))
binding.epoxyRecycler.layoutManager =
LinearLayoutManager(view.context, RecyclerView.VERTICAL, false)
updateController()
}
private fun updateController() {
val linkURLS = resources.getStringArray(R.array.link_urls)
val linkTitles = resources.getStringArray(R.array.link_titles)
val linkSummary = resources.getStringArray(R.array.link_subtitle)
val linkIcons = intArrayOf(
R.drawable.ic_menu_about,
R.drawable.ic_help,
R.drawable.ic_xda,
R.drawable.ic_telegram,
R.drawable.ic_gitlab,
R.drawable.ic_fdroid,
R.drawable.ic_bitcoin_btc,
R.drawable.ic_bitcoin_bch,
R.drawable.ic_ethereum_eth,
R.drawable.ic_bhim,
R.drawable.ic_paypal,
R.drawable.ic_libera_pay,
)
binding.epoxyRecycler.withModels {
for (i in linkURLS.indices) {
val link = Link(
id = i,
title = linkTitles[i],
subtitle = linkSummary[i],
url = linkURLS[i],
icon = linkIcons[i]
)
add(
LinkViewModel_()
.id(i)
.link(link)
.click { _ ->
if (link.id == 0) {
findNavController().navigate(R.id.aboutDialog)
} else {
processUrl(link.url)
}
}
)
}
}
}
private fun processUrl(url: String) {
when {
url.startsWith("http") -> context?.browse(url)
url.startsWith("upi") -> context?.browse(url)
else -> context?.copyToClipBoard(url)
}
}
}

View File

@@ -453,10 +453,10 @@ class MoreDialogFragment : DialogFragment() {
icon = R.drawable.ic_menu_settings,
destinationID = R.id.settingsFragment
),
ViewOption(
ComposeOption(
title = R.string.title_about,
icon = R.drawable.ic_menu_about,
destinationID = R.id.aboutFragment
screen = Screen.About
)
)
}

View File

@@ -92,7 +92,7 @@ abstract class BaseFlavouredSplashFragment : BaseFragment<FragmentSplashBinding>
findNavController().navigate(R.id.settingsFragment)
}
R.id.menu_about -> findNavController().navigate(R.id.aboutFragment)
R.id.menu_about -> requireContext().navigate(Screen.About)
}
true
}