compose: Replicate BlacklistFragment in compose
Introduce a new activity to host composables as View system behaves quite bad when composable are used inside it. Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
@@ -26,6 +26,7 @@ plugins {
|
|||||||
alias(libs.plugins.jetbrains.kotlin.android)
|
alias(libs.plugins.jetbrains.kotlin.android)
|
||||||
alias(libs.plugins.jetbrains.kotlin.compose)
|
alias(libs.plugins.jetbrains.kotlin.compose)
|
||||||
alias(libs.plugins.jetbrains.kotlin.parcelize)
|
alias(libs.plugins.jetbrains.kotlin.parcelize)
|
||||||
|
alias(libs.plugins.jetbrains.kotlin.serialization)
|
||||||
alias(libs.plugins.google.ksp)
|
alias(libs.plugins.google.ksp)
|
||||||
alias(libs.plugins.androidx.navigation)
|
alias(libs.plugins.androidx.navigation)
|
||||||
alias(libs.plugins.ktlint)
|
alias(libs.plugins.ktlint)
|
||||||
@@ -147,15 +148,19 @@ dependencies {
|
|||||||
implementation(libs.androidx.browser)
|
implementation(libs.androidx.browser)
|
||||||
implementation(libs.androidx.lifecycle.viewmodel.ktx)
|
implementation(libs.androidx.lifecycle.viewmodel.ktx)
|
||||||
implementation(libs.androidx.lifecycle.process)
|
implementation(libs.androidx.lifecycle.process)
|
||||||
implementation(libs.androidx.navigation.fragment.ktx)
|
|
||||||
implementation(libs.androidx.navigation.ui.ktx)
|
|
||||||
implementation(libs.androidx.preference.ktx)
|
implementation(libs.androidx.preference.ktx)
|
||||||
implementation(libs.androidx.swiperefreshlayout)
|
implementation(libs.androidx.swiperefreshlayout)
|
||||||
implementation(libs.androidx.viewpager2)
|
implementation(libs.androidx.viewpager2)
|
||||||
implementation(libs.androidx.work.runtime.ktx)
|
implementation(libs.androidx.work.runtime.ktx)
|
||||||
|
|
||||||
|
implementation(libs.androidx.navigation.compose)
|
||||||
|
implementation(libs.kotlinx.serialization.json)
|
||||||
|
implementation(libs.androidx.navigation.fragment.ktx)
|
||||||
|
implementation(libs.androidx.navigation.ui.ktx)
|
||||||
|
|
||||||
implementation(libs.androidx.activity.compose)
|
implementation(libs.androidx.activity.compose)
|
||||||
implementation(platform(libs.androidx.compose.bom))
|
implementation(platform(libs.androidx.compose.bom))
|
||||||
|
|
||||||
implementation(libs.androidx.material3)
|
implementation(libs.androidx.material3)
|
||||||
implementation(libs.androidx.ui)
|
implementation(libs.androidx.ui)
|
||||||
implementation(libs.androidx.ui.graphics)
|
implementation(libs.androidx.ui.graphics)
|
||||||
@@ -205,6 +210,7 @@ dependencies {
|
|||||||
//Hilt
|
//Hilt
|
||||||
ksp(libs.hilt.android.compiler)
|
ksp(libs.hilt.android.compiler)
|
||||||
ksp(libs.hilt.androidx.compiler)
|
ksp(libs.hilt.androidx.compiler)
|
||||||
|
implementation(libs.androidx.hilt.navigation)
|
||||||
implementation(libs.hilt.android.core)
|
implementation(libs.hilt.android.core)
|
||||||
implementation(libs.hilt.androidx.work)
|
implementation(libs.hilt.androidx.work)
|
||||||
|
|
||||||
|
|||||||
@@ -104,6 +104,11 @@
|
|||||||
<nav-graph android:value="@navigation/mobile_navigation" />
|
<nav-graph android:value="@navigation/mobile_navigation" />
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<!-- Activity to host composable screens -->
|
||||||
|
<activity
|
||||||
|
android:name=".ComposeActivity"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
<!-- Notification Action (Download Complete) -->
|
<!-- Notification Action (Download Complete) -->
|
||||||
<activity
|
<activity
|
||||||
android:name=".data.activity.InstallActivity"
|
android:name=".data.activity.InstallActivity"
|
||||||
|
|||||||
@@ -42,7 +42,9 @@ import androidx.core.app.ActivityOptionsCompat
|
|||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import com.aurora.Constants
|
import com.aurora.Constants
|
||||||
import com.aurora.gplayapi.data.models.App
|
import com.aurora.gplayapi.data.models.App
|
||||||
|
import com.aurora.store.ComposeActivity
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
|
import com.aurora.store.compose.navigation.Screen
|
||||||
|
|
||||||
private const val TAG = "Context"
|
private const val TAG = "Context"
|
||||||
|
|
||||||
@@ -153,3 +155,10 @@ fun Context.isDomainVerified(domain: String): Boolean {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Context.navigate(screen: Screen) {
|
||||||
|
val intent = Intent(this, ComposeActivity::class.java).apply {
|
||||||
|
putExtra(Screen.PARCEL_KEY, screen)
|
||||||
|
}
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
|
|||||||
40
app/src/main/java/com/aurora/store/ComposeActivity.kt
Normal file
40
app/src/main/java/com/aurora/store/ComposeActivity.kt
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.activity.compose.setContent
|
||||||
|
import androidx.activity.enableEdgeToEdge
|
||||||
|
import androidx.core.content.IntentCompat
|
||||||
|
import androidx.navigation.compose.rememberNavController
|
||||||
|
import com.aurora.store.compose.navigation.NavGraph
|
||||||
|
import com.aurora.store.compose.navigation.Screen
|
||||||
|
import com.aurora.store.compose.theme.AuroraTheme
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class ComposeActivity : ComponentActivity() {
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
enableEdgeToEdge()
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
// TODO: Change startDestination logic to mirror MainActivity
|
||||||
|
val startDestination = IntentCompat.getParcelableExtra(
|
||||||
|
intent,
|
||||||
|
Screen.PARCEL_KEY,
|
||||||
|
Screen::class.java
|
||||||
|
) ?: Screen.Blacklist
|
||||||
|
|
||||||
|
setContent {
|
||||||
|
AuroraTheme {
|
||||||
|
val navController = rememberNavController()
|
||||||
|
NavGraph(navHostController = navController, startDestination = startDestination)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,6 +41,7 @@ import com.aurora.store.R
|
|||||||
* @param versionName versionName of the package
|
* @param versionName versionName of the package
|
||||||
* @param versionCode versionCode of the package
|
* @param versionCode versionCode of the package
|
||||||
* @param isChecked Whether the app is blacklisted
|
* @param isChecked Whether the app is blacklisted
|
||||||
|
* @param isEnabled Whether this app is allowed to be blacklisted
|
||||||
* @param onClick Callback when the composable is clicked
|
* @param onClick Callback when the composable is clicked
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
@@ -51,12 +52,13 @@ fun BlackListComposable(
|
|||||||
versionName: String,
|
versionName: String,
|
||||||
versionCode: Long,
|
versionCode: Long,
|
||||||
isChecked: Boolean = false,
|
isChecked: Boolean = false,
|
||||||
|
isEnabled: Boolean = true,
|
||||||
onClick: () -> Unit = {}
|
onClick: () -> Unit = {}
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable { onClick() }
|
.clickable(enabled = isEnabled) { onClick() }
|
||||||
.padding(
|
.padding(
|
||||||
horizontal = dimensionResource(R.dimen.padding_medium),
|
horizontal = dimensionResource(R.dimen.padding_medium),
|
||||||
vertical = dimensionResource(R.dimen.padding_xsmall)
|
vertical = dimensionResource(R.dimen.padding_xsmall)
|
||||||
@@ -87,11 +89,13 @@ fun BlackListComposable(
|
|||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.version, versionName, versionCode),
|
text = stringResource(R.string.version, versionName, versionCode),
|
||||||
style = MaterialTheme.typography.bodySmall.copy(fontSize = 10.sp)
|
style = MaterialTheme.typography.bodySmall.copy(fontSize = 10.sp),
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Checkbox(checked = isChecked, onCheckedChange = { onClick() })
|
Checkbox(checked = isChecked, enabled = isEnabled, onCheckedChange = { onClick() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,6 +108,7 @@ private fun BlackListComposablePreview() {
|
|||||||
packageName = BuildConfig.APPLICATION_ID,
|
packageName = BuildConfig.APPLICATION_ID,
|
||||||
versionName = BuildConfig.VERSION_NAME,
|
versionName = BuildConfig.VERSION_NAME,
|
||||||
versionCode = BuildConfig.VERSION_CODE.toLong(),
|
versionCode = BuildConfig.VERSION_CODE.toLong(),
|
||||||
isChecked = true
|
isChecked = true,
|
||||||
|
isEnabled = false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,9 @@ fun InstalledAppComposable(
|
|||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.version, versionName, versionCode),
|
text = stringResource(R.string.version, versionName, versionCode),
|
||||||
style = MaterialTheme.typography.bodySmall.copy(fontSize = 10.sp)
|
style = MaterialTheme.typography.bodySmall.copy(fontSize = 10.sp),
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.compose.composables
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.compose.foundation.layout.RowScope
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
|
import androidx.compose.material.icons.filled.Clear
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextField
|
||||||
|
import androidx.compose.material3.TextFieldDefaults
|
||||||
|
import androidx.compose.material3.TopAppBar
|
||||||
|
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.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A top app bar composable to be used with Scaffold in different Screen
|
||||||
|
* @param searchHint Hint to show to the user in search bar
|
||||||
|
* @param onNavigateUp Action when user clicks the navigation icon
|
||||||
|
* @param onSearch Callback for a search
|
||||||
|
* @param actions Actions to display on the top app bar (for e.g. menu)
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
fun SearchAppBarComposable(
|
||||||
|
@StringRes searchHint: Int? = null,
|
||||||
|
onNavigateUp: () -> Unit,
|
||||||
|
onSearch: (query: String) -> Unit,
|
||||||
|
actions: @Composable (RowScope.() -> Unit) = {}
|
||||||
|
) {
|
||||||
|
val focusManager = LocalFocusManager.current
|
||||||
|
var query by rememberSaveable { mutableStateOf("") }
|
||||||
|
|
||||||
|
TopAppBar(
|
||||||
|
title = {
|
||||||
|
TextField(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
value = query,
|
||||||
|
onValueChange = { newQuery ->
|
||||||
|
query = newQuery.trim()
|
||||||
|
onSearch(query)
|
||||||
|
},
|
||||||
|
maxLines = 1,
|
||||||
|
placeholder = { if (searchHint != null) Text(text = stringResource(searchHint)) },
|
||||||
|
colors = TextFieldDefaults.colors(
|
||||||
|
focusedContainerColor = Color.Transparent,
|
||||||
|
focusedIndicatorColor = Color.Transparent,
|
||||||
|
unfocusedContainerColor = Color.Transparent,
|
||||||
|
unfocusedIndicatorColor = Color.Transparent
|
||||||
|
),
|
||||||
|
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
|
||||||
|
keyboardActions = KeyboardActions(onSearch = { focusManager.clearFocus() })
|
||||||
|
)
|
||||||
|
},
|
||||||
|
navigationIcon = {
|
||||||
|
IconButton(onClick = onNavigateUp) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions = {
|
||||||
|
if (query.isNotBlank()) {
|
||||||
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
query = ""
|
||||||
|
onSearch(query)
|
||||||
|
focusManager.clearFocus(force = true)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Icon(imageVector = Icons.Default.Clear, contentDescription = null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
actions()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.compose.composables
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.compose.foundation.layout.RowScope
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TopAppBar
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A top app bar composable to be used with Scaffold in different Screen
|
||||||
|
* @param title Title of the screen
|
||||||
|
* @param onNavigateUp Action when user clicks the navigation icon
|
||||||
|
* @param actions Actions to display on the top app bar (for e.g. menu)
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
fun TopAppBarComposable(
|
||||||
|
@StringRes title: Int? = null,
|
||||||
|
onNavigateUp: () -> Unit,
|
||||||
|
actions: @Composable (RowScope.() -> Unit) = {}
|
||||||
|
) {
|
||||||
|
TopAppBar(
|
||||||
|
title = {
|
||||||
|
if (title != null) Text(text = stringResource(title))
|
||||||
|
},
|
||||||
|
navigationIcon = {
|
||||||
|
IconButton(onClick = onNavigateUp) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions = actions
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.compose.composables
|
||||||
|
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import com.aurora.store.R
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A transparent icon for occupying space
|
||||||
|
*
|
||||||
|
* This is useful for occupying spaces in composable where alignment is not respected such as
|
||||||
|
* DropDownMenu.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun TransparentIconComposable() {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(R.drawable.ic_transparent),
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(showBackground = true)
|
||||||
|
@Composable
|
||||||
|
private fun TransparentIconComposablePreview() {
|
||||||
|
TransparentIconComposable()
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.compose.menu
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.MoreVert
|
||||||
|
import androidx.compose.material3.DropdownMenu
|
||||||
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import com.aurora.store.R
|
||||||
|
import com.aurora.store.compose.composables.TransparentIconComposable
|
||||||
|
import com.aurora.store.compose.menu.items.BlacklistMenuItem
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Menu for the blacklist screen
|
||||||
|
* @param onMenuItemClicked Callback when a menu item has been clicked
|
||||||
|
* @see BlacklistMenuItem
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun BlacklistMenu(onMenuItemClicked: (blacklistMenuItem: BlacklistMenuItem) -> Unit = {}) {
|
||||||
|
var expanded by remember { mutableStateOf(false) }
|
||||||
|
fun onClick(blacklistMenuItem: BlacklistMenuItem) {
|
||||||
|
onMenuItemClicked(blacklistMenuItem)
|
||||||
|
expanded = false
|
||||||
|
}
|
||||||
|
|
||||||
|
Box {
|
||||||
|
IconButton(onClick = { expanded = true }) {
|
||||||
|
Icon(Icons.Default.MoreVert, contentDescription = stringResource(R.string.menu))
|
||||||
|
}
|
||||||
|
DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = { Text(text = stringResource(R.string.action_select_all)) },
|
||||||
|
onClick = { onClick(BlacklistMenuItem.SELECT_ALL) },
|
||||||
|
leadingIcon = {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(R.drawable.ic_select_all),
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = { Text(text = stringResource(R.string.action_remove_all)) },
|
||||||
|
onClick = { onClick(BlacklistMenuItem.REMOVE_ALL) },
|
||||||
|
leadingIcon = { TransparentIconComposable() }
|
||||||
|
)
|
||||||
|
HorizontalDivider()
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = { Text(text = stringResource(R.string.action_import)) },
|
||||||
|
onClick = { onClick(BlacklistMenuItem.IMPORT) },
|
||||||
|
leadingIcon = {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(R.drawable.ic_file_import),
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = { Text(text = stringResource(R.string.action_export)) },
|
||||||
|
onClick = { onClick(BlacklistMenuItem.EXPORT) },
|
||||||
|
leadingIcon = {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(R.drawable.ic_file_export),
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(showBackground = true)
|
||||||
|
@Composable
|
||||||
|
private fun BlacklistMenuPreview() {
|
||||||
|
BlacklistMenu()
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.compose.menu.items
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Valid menu items for the purpose of handling clicks
|
||||||
|
*/
|
||||||
|
enum class BlacklistMenuItem {
|
||||||
|
SELECT_ALL,
|
||||||
|
REMOVE_ALL,
|
||||||
|
IMPORT,
|
||||||
|
EXPORT
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.compose.navigation
|
||||||
|
|
||||||
|
import androidx.activity.compose.LocalActivity
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
|
import androidx.navigation.compose.NavHost
|
||||||
|
import androidx.navigation.compose.composable
|
||||||
|
import com.aurora.store.compose.ui.commons.BlacklistScreen
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigation graph for compose screens
|
||||||
|
* @param navHostController [NavHostController] to navigate with compose
|
||||||
|
* @param startDestination Starting destination for the activity/app
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun NavGraph(navHostController: NavHostController, startDestination: Screen) {
|
||||||
|
// TODO: Drop this logic once everything is in compose
|
||||||
|
val activity = LocalActivity.current
|
||||||
|
fun onNavigateUp() {
|
||||||
|
if (navHostController.previousBackStackEntry != null) {
|
||||||
|
navHostController.navigateUp()
|
||||||
|
} else {
|
||||||
|
activity?.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NavHost(navController = navHostController, startDestination = startDestination) {
|
||||||
|
composable<Screen.Blacklist> {
|
||||||
|
BlacklistScreen(onNavigateUp = { onNavigateUp() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.compose.navigation
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import com.aurora.store.R
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destination (Screen) for navigation in compose
|
||||||
|
* @param label Label of the screen
|
||||||
|
* @param icon Optional icon for the screen; Must not be null if screen is a top-level destination
|
||||||
|
*/
|
||||||
|
@Parcelize
|
||||||
|
@Serializable
|
||||||
|
sealed class Screen(@StringRes val label: Int, @DrawableRes val icon: Int? = null): Parcelable {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val PARCEL_KEY = "SCREEN"
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data object Blacklist : Screen(label = R.string.title_blacklist_manager)
|
||||||
|
}
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.compose.ui.commons
|
||||||
|
|
||||||
|
import android.content.pm.PackageInfo
|
||||||
|
import android.net.Uri
|
||||||
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.dimensionResource
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.core.content.pm.PackageInfoCompat
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import com.aurora.Constants
|
||||||
|
import com.aurora.extensions.toast
|
||||||
|
import com.aurora.store.R
|
||||||
|
import com.aurora.store.compose.composables.BlackListComposable
|
||||||
|
import com.aurora.store.compose.composables.SearchAppBarComposable
|
||||||
|
import com.aurora.store.compose.menu.BlacklistMenu
|
||||||
|
import com.aurora.store.compose.menu.items.BlacklistMenuItem
|
||||||
|
import com.aurora.store.util.PackageUtil
|
||||||
|
import com.aurora.store.viewmodel.all.BlacklistViewModel
|
||||||
|
import java.util.Calendar
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BlacklistScreen(onNavigateUp: () -> Unit, viewModel: BlacklistViewModel = hiltViewModel()) {
|
||||||
|
val ctx = LocalContext.current
|
||||||
|
val packages by viewModel.filteredPackages.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
|
ScreenContent(
|
||||||
|
packages = packages,
|
||||||
|
onNavigateUp = onNavigateUp,
|
||||||
|
isPackageBlacklisted = { pkgName -> pkgName in viewModel.blacklist },
|
||||||
|
isPackageFiltered = { pkgInfo -> viewModel.isFiltered(pkgInfo) },
|
||||||
|
onBlacklistImport = { uri ->
|
||||||
|
viewModel.importBlacklist(ctx, uri)
|
||||||
|
ctx.toast(R.string.toast_black_import_success)
|
||||||
|
},
|
||||||
|
onBlacklistExport = { uri ->
|
||||||
|
viewModel.exportBlacklist(ctx, uri)
|
||||||
|
ctx.toast(R.string.toast_black_export_success)
|
||||||
|
},
|
||||||
|
onBlacklist = { packageName -> viewModel.blacklist(packageName) },
|
||||||
|
onBlacklistAll = { viewModel.blacklistAll() },
|
||||||
|
onWhitelist = { packageName -> viewModel.whitelist(packageName) },
|
||||||
|
onWhitelistAll = { viewModel.whitelistAll() },
|
||||||
|
onSearch = { query -> viewModel.search(query) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ScreenContent(
|
||||||
|
packages: List<PackageInfo>? = null,
|
||||||
|
onNavigateUp: () -> Unit = {},
|
||||||
|
isPackageBlacklisted: (packageName: String) -> Boolean = { false },
|
||||||
|
isPackageFiltered: (packageInfo: PackageInfo) -> Boolean = { false },
|
||||||
|
onBlacklistImport: (uri: Uri) -> Unit = {},
|
||||||
|
onBlacklistExport: (uri: Uri) -> Unit = {},
|
||||||
|
onBlacklist: (packageName: String) -> Unit = {},
|
||||||
|
onBlacklistAll: () -> Unit = {},
|
||||||
|
onWhitelist: (packageName: String) -> Unit = {},
|
||||||
|
onWhitelistAll: () -> Unit = {},
|
||||||
|
onSearch: (query: String) -> Unit = {}
|
||||||
|
) {
|
||||||
|
|
||||||
|
val ctx = LocalContext.current
|
||||||
|
val docImportLauncher = rememberLauncherForActivityResult(
|
||||||
|
contract = ActivityResultContracts.OpenDocument(),
|
||||||
|
onResult = {
|
||||||
|
if (it != null) onBlacklistImport(it) else ctx.toast(R.string.toast_black_import_failed)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
val docExportLauncher = rememberLauncherForActivityResult(
|
||||||
|
contract = ActivityResultContracts.CreateDocument(Constants.JSON_MIME_TYPE),
|
||||||
|
onResult = {
|
||||||
|
if (it != null) onBlacklistExport(it) else ctx.toast(R.string.toast_black_export_failed)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SetupMenu() {
|
||||||
|
BlacklistMenu { blacklistMenuItem ->
|
||||||
|
when (blacklistMenuItem) {
|
||||||
|
BlacklistMenuItem.SELECT_ALL -> onBlacklistAll()
|
||||||
|
BlacklistMenuItem.REMOVE_ALL -> onWhitelistAll()
|
||||||
|
BlacklistMenuItem.IMPORT -> {
|
||||||
|
docImportLauncher.launch(arrayOf(Constants.JSON_MIME_TYPE))
|
||||||
|
}
|
||||||
|
|
||||||
|
BlacklistMenuItem.EXPORT -> {
|
||||||
|
docExportLauncher.launch(
|
||||||
|
"aurora_store_apps_${Calendar.getInstance().time.time}.json"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
SearchAppBarComposable(
|
||||||
|
onNavigateUp = onNavigateUp,
|
||||||
|
actions = { if (!packages.isNullOrEmpty()) SetupMenu() },
|
||||||
|
searchHint = R.string.search_hint,
|
||||||
|
onSearch = { query -> onSearch(query) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { paddingValues ->
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(paddingValues),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xxsmall))
|
||||||
|
) {
|
||||||
|
items(items = packages ?: emptyList(), key = { p -> p.packageName.hashCode() }) { pkg ->
|
||||||
|
val isBlacklisted = isPackageBlacklisted(pkg.packageName)
|
||||||
|
val isFiltered = isPackageFiltered(pkg)
|
||||||
|
BlackListComposable(
|
||||||
|
icon = PackageUtil.getIconForPackage(ctx, pkg.packageName)!!,
|
||||||
|
displayName = pkg.applicationInfo!!.loadLabel(ctx.packageManager).toString(),
|
||||||
|
packageName = pkg.packageName,
|
||||||
|
versionName = pkg.versionName!!,
|
||||||
|
versionCode = PackageInfoCompat.getLongVersionCode(pkg),
|
||||||
|
isChecked = isBlacklisted || isFiltered,
|
||||||
|
isEnabled = !isFiltered,
|
||||||
|
onClick = {
|
||||||
|
if (isBlacklisted) {
|
||||||
|
onWhitelist(pkg.packageName)
|
||||||
|
} else {
|
||||||
|
onBlacklist(pkg.packageName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun BlacklistScreenPreview() {
|
||||||
|
ScreenContent()
|
||||||
|
}
|
||||||
@@ -113,6 +113,13 @@ class UpdateHelper @Inject constructor(
|
|||||||
updateDao.delete(packageName)
|
updateDao.delete(packageName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all updates from the database
|
||||||
|
*/
|
||||||
|
suspend fun deleteAllUpdates() {
|
||||||
|
updateDao.deleteAll()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancels the automated updates check
|
* Cancels the automated updates check
|
||||||
* @see [UpdateWorker]
|
* @see [UpdateWorker]
|
||||||
|
|||||||
@@ -1,179 +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.commons
|
|
||||||
|
|
||||||
import android.content.pm.PackageInfo
|
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.text.Editable
|
|
||||||
import android.text.TextWatcher
|
|
||||||
import android.view.View
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.fragment.app.viewModels
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.navigation.fragment.findNavController
|
|
||||||
import com.aurora.Constants
|
|
||||||
import com.aurora.extensions.toast
|
|
||||||
import com.aurora.store.AuroraApp
|
|
||||||
import com.aurora.store.R
|
|
||||||
import com.aurora.store.data.event.BusEvent
|
|
||||||
import com.aurora.store.databinding.FragmentGenericWithSearchBinding
|
|
||||||
import com.aurora.store.view.epoxy.views.BlackListViewModel_
|
|
||||||
import com.aurora.store.view.epoxy.views.shimmer.AppListViewShimmerModel_
|
|
||||||
import com.aurora.store.viewmodel.all.BlacklistViewModel
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import java.util.Calendar
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
|
||||||
class BlacklistFragment : BaseFragment<FragmentGenericWithSearchBinding>() {
|
|
||||||
|
|
||||||
private val viewModel: BlacklistViewModel by viewModels()
|
|
||||||
|
|
||||||
private val startForDocumentImport =
|
|
||||||
registerForActivityResult(ActivityResultContracts.OpenDocument()) {
|
|
||||||
if (it != null) importBlacklist(it) else toast(R.string.toast_black_import_failed)
|
|
||||||
}
|
|
||||||
private val startForDocumentExport =
|
|
||||||
registerForActivityResult(ActivityResultContracts.CreateDocument(Constants.JSON_MIME_TYPE)) {
|
|
||||||
if (it != null) exportBlacklist(it) else toast(R.string.toast_black_export_failed)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewCreated(view, savedInstanceState)
|
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
|
||||||
viewModel.packages.collect {
|
|
||||||
updateController(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Toolbar
|
|
||||||
binding.toolbar.apply {
|
|
||||||
inflateMenu(R.menu.menu_blacklist)
|
|
||||||
setNavigationOnClickListener {
|
|
||||||
viewModel.blacklistProvider.blacklist = viewModel.selected
|
|
||||||
findNavController().navigateUp()
|
|
||||||
}
|
|
||||||
setOnMenuItemClickListener {
|
|
||||||
when (it.itemId) {
|
|
||||||
R.id.action_import -> {
|
|
||||||
startForDocumentImport.launch(arrayOf(Constants.JSON_MIME_TYPE))
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.action_export -> {
|
|
||||||
startForDocumentExport.launch(
|
|
||||||
"aurora_store_apps_${Calendar.getInstance().time.time}.json"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.action_select_all -> {
|
|
||||||
viewModel.selectAll()
|
|
||||||
binding.recycler.requestModelBuild()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.action_remove_all -> {
|
|
||||||
viewModel.removeAll()
|
|
||||||
binding.recycler.requestModelBuild()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.searchBar.addTextChangedListener(object : TextWatcher {
|
|
||||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
|
||||||
if (s.isNullOrEmpty()) {
|
|
||||||
updateController(viewModel.packages.value)
|
|
||||||
} else {
|
|
||||||
val filteredPackages = viewModel.packages.value?.filter {
|
|
||||||
it.applicationInfo!!.loadLabel(requireContext().packageManager)
|
|
||||||
.contains(s, true) || it.packageName.contains(s, true)
|
|
||||||
}
|
|
||||||
updateController(filteredPackages)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun afterTextChanged(s: Editable?) {}
|
|
||||||
override fun beforeTextChanged(
|
|
||||||
s: CharSequence?,
|
|
||||||
start: Int,
|
|
||||||
count: Int,
|
|
||||||
after: Int
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPause() {
|
|
||||||
super.onPause()
|
|
||||||
viewModel.blacklistProvider.blacklist = viewModel.selected
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateController(packages: List<PackageInfo>?) {
|
|
||||||
binding.recycler.withModels {
|
|
||||||
setFilterDuplicates(true)
|
|
||||||
if (packages == null) {
|
|
||||||
for (i in 1..10) {
|
|
||||||
add(
|
|
||||||
AppListViewShimmerModel_()
|
|
||||||
.id(i)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
packages
|
|
||||||
.sortedByDescending { app ->
|
|
||||||
viewModel.blacklistProvider.isBlacklisted(app.packageName)
|
|
||||||
}
|
|
||||||
.forEach {
|
|
||||||
add(
|
|
||||||
BlackListViewModel_()
|
|
||||||
.id(it.packageName.hashCode())
|
|
||||||
.packageInfo(it)
|
|
||||||
.markChecked(viewModel.selected.contains(it.packageName))
|
|
||||||
.checked { _, isChecked ->
|
|
||||||
if (isChecked) {
|
|
||||||
viewModel.selected.add(it.packageName)
|
|
||||||
AuroraApp.events.send(BusEvent.Blacklisted(it.packageName))
|
|
||||||
} else {
|
|
||||||
viewModel.selected.remove(it.packageName)
|
|
||||||
}
|
|
||||||
|
|
||||||
requestModelBuild()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun importBlacklist(uri: Uri) {
|
|
||||||
viewModel.importBlacklist(requireContext(), uri)
|
|
||||||
binding.recycler.requestModelBuild()
|
|
||||||
toast(R.string.toast_black_import_success)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun exportBlacklist(uri: Uri) {
|
|
||||||
viewModel.exportBlacklist(requireContext(), uri)
|
|
||||||
toast(R.string.toast_black_export_success)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -57,9 +57,11 @@ import com.aurora.Constants
|
|||||||
import com.aurora.Constants.URL_TOS
|
import com.aurora.Constants.URL_TOS
|
||||||
import com.aurora.extensions.browse
|
import com.aurora.extensions.browse
|
||||||
import com.aurora.extensions.getStyledAttributeColor
|
import com.aurora.extensions.getStyledAttributeColor
|
||||||
|
import com.aurora.extensions.navigate
|
||||||
import com.aurora.extensions.setAppTheme
|
import com.aurora.extensions.setAppTheme
|
||||||
import com.aurora.store.MR
|
import com.aurora.store.MR
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
|
import com.aurora.store.compose.navigation.Screen
|
||||||
import com.aurora.store.util.Preferences
|
import com.aurora.store.util.Preferences
|
||||||
import com.aurora.store.compose.theme.AuroraTheme
|
import com.aurora.store.compose.theme.AuroraTheme
|
||||||
import com.aurora.store.viewmodel.commons.MoreViewModel
|
import com.aurora.store.viewmodel.commons.MoreViewModel
|
||||||
@@ -76,12 +78,23 @@ class MoreDialogFragment : DialogFragment() {
|
|||||||
private var secondaryColor: Color = Color.White
|
private var secondaryColor: Color = Color.White
|
||||||
private var onSecondaryColor: Color = Color.Black
|
private var onSecondaryColor: Color = Color.Black
|
||||||
|
|
||||||
private data class Option(
|
private abstract class Option(
|
||||||
@StringRes val title: Int,
|
@StringRes open val title: Int,
|
||||||
@DrawableRes val icon: Int,
|
@DrawableRes open val icon: Int,
|
||||||
@IdRes val destinationID: Int
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private data class ViewOption(
|
||||||
|
override val title: Int,
|
||||||
|
override val icon: Int,
|
||||||
|
@IdRes val destinationID: Int
|
||||||
|
) : Option(title, icon)
|
||||||
|
|
||||||
|
private data class ComposeOption(
|
||||||
|
override val title: Int,
|
||||||
|
override val icon: Int,
|
||||||
|
val screen: Screen
|
||||||
|
) : Option(title, icon)
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
return MaterialAlertDialogBuilder(requireContext())
|
return MaterialAlertDialogBuilder(requireContext())
|
||||||
.setView(customDialogView(requireContext()))
|
.setView(customDialogView(requireContext()))
|
||||||
@@ -134,7 +147,16 @@ class MoreDialogFragment : DialogFragment() {
|
|||||||
OptionItem(
|
OptionItem(
|
||||||
option = option,
|
option = option,
|
||||||
tintColor = onPrimaryColor,
|
tintColor = onPrimaryColor,
|
||||||
textColor = onSecondaryColor
|
textColor = onSecondaryColor,
|
||||||
|
onClick = {
|
||||||
|
when (option) {
|
||||||
|
is ViewOption -> {
|
||||||
|
findNavController().navigate(option.destinationID)
|
||||||
|
}
|
||||||
|
|
||||||
|
is ComposeOption -> context.navigate(option.screen)
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,7 +164,16 @@ class MoreDialogFragment : DialogFragment() {
|
|||||||
OptionItem(
|
OptionItem(
|
||||||
option = option,
|
option = option,
|
||||||
tintColor = onPrimaryColor,
|
tintColor = onPrimaryColor,
|
||||||
textColor = onPrimaryColor
|
textColor = onPrimaryColor,
|
||||||
|
onClick = {
|
||||||
|
when (option) {
|
||||||
|
is ViewOption -> {
|
||||||
|
findNavController().navigate(option.destinationID)
|
||||||
|
}
|
||||||
|
|
||||||
|
is ComposeOption -> context.navigate(option.screen)
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Footer(onPrimaryColor)
|
Footer(onPrimaryColor)
|
||||||
@@ -310,12 +341,13 @@ class MoreDialogFragment : DialogFragment() {
|
|||||||
private fun OptionItem(
|
private fun OptionItem(
|
||||||
option: Option,
|
option: Option,
|
||||||
tintColor: Color = Color.Blue,
|
tintColor: Color = Color.Blue,
|
||||||
textColor: Color = Color.Black
|
textColor: Color = Color.Black,
|
||||||
|
onClick: () -> Unit
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable { findNavController().navigate(option.destinationID) }
|
.clickable { onClick() }
|
||||||
.padding(12.dp),
|
.padding(12.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.Start)
|
horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.Start)
|
||||||
@@ -391,22 +423,22 @@ class MoreDialogFragment : DialogFragment() {
|
|||||||
|
|
||||||
private fun getOptions(): List<Option> {
|
private fun getOptions(): List<Option> {
|
||||||
return listOf(
|
return listOf(
|
||||||
Option(
|
ViewOption(
|
||||||
title = R.string.title_apps_games,
|
title = R.string.title_apps_games,
|
||||||
icon = R.drawable.ic_apps,
|
icon = R.drawable.ic_apps,
|
||||||
destinationID = R.id.appsGamesFragment
|
destinationID = R.id.appsGamesFragment
|
||||||
),
|
),
|
||||||
Option(
|
ComposeOption(
|
||||||
title = R.string.title_blacklist_manager,
|
title = R.string.title_blacklist_manager,
|
||||||
icon = R.drawable.ic_blacklist,
|
icon = R.drawable.ic_blacklist,
|
||||||
destinationID = R.id.blacklistFragment
|
screen = Screen.Blacklist
|
||||||
),
|
),
|
||||||
Option(
|
ViewOption(
|
||||||
title = R.string.title_favourites_manager,
|
title = R.string.title_favourites_manager,
|
||||||
icon = R.drawable.ic_favorite_unchecked,
|
icon = R.drawable.ic_favorite_unchecked,
|
||||||
destinationID = R.id.favouriteFragment
|
destinationID = R.id.favouriteFragment
|
||||||
),
|
),
|
||||||
Option(
|
ViewOption(
|
||||||
title = R.string.title_spoof_manager,
|
title = R.string.title_spoof_manager,
|
||||||
icon = R.drawable.ic_spoof,
|
icon = R.drawable.ic_spoof,
|
||||||
destinationID = R.id.spoofFragment
|
destinationID = R.id.spoofFragment
|
||||||
@@ -416,12 +448,12 @@ class MoreDialogFragment : DialogFragment() {
|
|||||||
|
|
||||||
private fun getExtraOptions(): List<Option> {
|
private fun getExtraOptions(): List<Option> {
|
||||||
return listOf(
|
return listOf(
|
||||||
Option(
|
ViewOption(
|
||||||
title = R.string.title_settings,
|
title = R.string.title_settings,
|
||||||
icon = R.drawable.ic_menu_settings,
|
icon = R.drawable.ic_menu_settings,
|
||||||
destinationID = R.id.settingsFragment
|
destinationID = R.id.settingsFragment
|
||||||
),
|
),
|
||||||
Option(
|
ViewOption(
|
||||||
title = R.string.title_about,
|
title = R.string.title_about,
|
||||||
icon = R.drawable.ic_menu_about,
|
icon = R.drawable.ic_menu_about,
|
||||||
destinationID = R.id.aboutFragment
|
destinationID = R.id.aboutFragment
|
||||||
|
|||||||
@@ -37,9 +37,11 @@ import androidx.lifecycle.lifecycleScope
|
|||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import com.aurora.extensions.hide
|
import com.aurora.extensions.hide
|
||||||
import com.aurora.extensions.isMAndAbove
|
import com.aurora.extensions.isMAndAbove
|
||||||
|
import com.aurora.extensions.navigate
|
||||||
import com.aurora.extensions.show
|
import com.aurora.extensions.show
|
||||||
import com.aurora.gplayapi.helpers.AuthHelper
|
import com.aurora.gplayapi.helpers.AuthHelper
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
|
import com.aurora.store.compose.navigation.Screen
|
||||||
import com.aurora.store.data.model.AuthState
|
import com.aurora.store.data.model.AuthState
|
||||||
import com.aurora.store.databinding.FragmentSplashBinding
|
import com.aurora.store.databinding.FragmentSplashBinding
|
||||||
import com.aurora.store.util.CertUtil.GOOGLE_ACCOUNT_TYPE
|
import com.aurora.store.util.CertUtil.GOOGLE_ACCOUNT_TYPE
|
||||||
@@ -93,7 +95,7 @@ class SplashFragment : BaseFragment<FragmentSplashBinding>() {
|
|||||||
setOnMenuItemClickListener {
|
setOnMenuItemClickListener {
|
||||||
when (it.itemId) {
|
when (it.itemId) {
|
||||||
R.id.menu_blacklist_manager -> {
|
R.id.menu_blacklist_manager -> {
|
||||||
findNavController().navigate(R.id.blacklistFragment)
|
requireContext().navigate(Screen.Blacklist)
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.menu_spoof_manager -> {
|
R.id.menu_spoof_manager -> {
|
||||||
|
|||||||
@@ -23,10 +23,16 @@ import android.content.Context
|
|||||||
import android.content.pm.PackageInfo
|
import android.content.pm.PackageInfo
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import androidx.compose.runtime.mutableStateListOf
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.aurora.store.AuroraApp
|
||||||
|
import com.aurora.store.data.event.BusEvent
|
||||||
|
import com.aurora.store.data.helper.UpdateHelper
|
||||||
import com.aurora.store.data.providers.BlacklistProvider
|
import com.aurora.store.data.providers.BlacklistProvider
|
||||||
|
import com.aurora.store.util.CertUtil
|
||||||
import com.aurora.store.util.PackageUtil
|
import com.aurora.store.util.PackageUtil
|
||||||
|
import com.aurora.store.util.Preferences
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.google.gson.reflect.TypeToken
|
import com.google.gson.reflect.TypeToken
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
@@ -39,38 +45,87 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class BlacklistViewModel @Inject constructor(
|
class BlacklistViewModel @Inject constructor(
|
||||||
val blacklistProvider: BlacklistProvider,
|
private val gson: Gson,
|
||||||
val gson: Gson,
|
private val updateHelper: UpdateHelper,
|
||||||
|
private val blacklistProvider: BlacklistProvider,
|
||||||
@ApplicationContext private val context: Context
|
@ApplicationContext private val context: Context
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val TAG = BlacklistViewModel::class.java.simpleName
|
private val TAG = BlacklistViewModel::class.java.simpleName
|
||||||
|
|
||||||
private val _packages = MutableStateFlow<List<PackageInfo>?>(null)
|
private val isAuroraOnlyFilterEnabled =
|
||||||
val packages = _packages.asStateFlow()
|
Preferences.getBoolean(context, Preferences.PREFERENCE_FILTER_AURORA_ONLY, false)
|
||||||
|
private val isFDroidFilterEnabled =
|
||||||
|
Preferences.getBoolean(context, Preferences.PREFERENCE_FILTER_FDROID, true)
|
||||||
|
private val isExtendedUpdateEnabled =
|
||||||
|
Preferences.getBoolean(context, Preferences.PREFERENCE_UPDATES_EXTENDED)
|
||||||
|
|
||||||
var selected: MutableSet<String> = blacklistProvider.blacklist
|
private val _packages = MutableStateFlow<List<PackageInfo>?>(null)
|
||||||
|
private val _filteredPackages = MutableStateFlow<List<PackageInfo>?>(null)
|
||||||
|
val filteredPackages = _filteredPackages.asStateFlow()
|
||||||
|
|
||||||
|
val blacklist = mutableStateListOf<String>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
blacklist.addAll(blacklistProvider.blacklist)
|
||||||
fetchApps()
|
fetchApps()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fetchApps() {
|
private fun fetchApps() {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
_packages.value = PackageUtil.getAllValidPackages(context)
|
_packages.value = PackageUtil.getAllValidPackages(context).also { pkgList ->
|
||||||
|
_filteredPackages.value = pkgList
|
||||||
|
}
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
Log.e(TAG, "Failed to fetch apps", exception)
|
Log.e(TAG, "Failed to fetch apps", exception)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun selectAll() {
|
fun search(query: String) {
|
||||||
selected.addAll(packages.value?.map { it.packageName } ?: emptyList())
|
if (query.isNotBlank()) {
|
||||||
|
_filteredPackages.value = _packages.value!!
|
||||||
|
.filter { it.applicationInfo!!.loadLabel(context.packageManager)
|
||||||
|
.contains(query, true) || it.packageName.contains(query, true)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_filteredPackages.value = _packages.value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeAll() {
|
fun isFiltered(packageInfo: PackageInfo): Boolean {
|
||||||
selected.clear()
|
return when {
|
||||||
|
!isExtendedUpdateEnabled && !packageInfo.applicationInfo!!.enabled -> true
|
||||||
|
isAuroraOnlyFilterEnabled -> !CertUtil.isAuroraStoreApp(context, packageInfo.packageName)
|
||||||
|
isFDroidFilterEnabled -> CertUtil.isFDroidApp(context, packageInfo.packageName)
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun blacklist(packageName: String) {
|
||||||
|
blacklist.add(packageName)
|
||||||
|
blacklistProvider.blacklist(packageName)
|
||||||
|
AuroraApp.events.send(BusEvent.Blacklisted(packageName))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun blacklistAll() {
|
||||||
|
blacklistProvider.blacklist = _packages.value!!.map { it.packageName }.toMutableSet()
|
||||||
|
blacklist.apply {
|
||||||
|
clear()
|
||||||
|
addAll(blacklistProvider.blacklist)
|
||||||
|
}
|
||||||
|
viewModelScope.launch { updateHelper.deleteAllUpdates() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun whitelist(packageName: String) {
|
||||||
|
blacklist.remove(packageName)
|
||||||
|
blacklistProvider.whitelist(packageName)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun whitelistAll() {
|
||||||
|
blacklist.clear()
|
||||||
|
blacklistProvider.blacklist = mutableSetOf()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun importBlacklist(context: Context, uri: Uri) {
|
fun importBlacklist(context: Context, uri: Uri) {
|
||||||
@@ -82,10 +137,13 @@ class BlacklistViewModel @Inject constructor(
|
|||||||
object : TypeToken<MutableSet<String?>?>() {}.type
|
object : TypeToken<MutableSet<String?>?>() {}.type
|
||||||
)
|
)
|
||||||
|
|
||||||
val knownSet = blacklistProvider.blacklist
|
val validImportedSet = importedSet
|
||||||
knownSet.addAll(importedSet)
|
.filter { pkgName -> _packages.value!!.any { it.packageName == pkgName } }
|
||||||
|
blacklistProvider.blacklist.addAll(validImportedSet)
|
||||||
selected = knownSet
|
blacklist.apply {
|
||||||
|
clear()
|
||||||
|
addAll(blacklistProvider.blacklist)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
Log.e(TAG, "Failed to import blacklist", exception)
|
Log.e(TAG, "Failed to import blacklist", exception)
|
||||||
|
|||||||
15
app/src/main/res/drawable/ic_file_export.xml
Normal file
15
app/src/main/res/drawable/ic_file_export.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ SPDX-FileCopyrightText: Material Design Authors / Google LLC
|
||||||
|
~ SPDX-License-Identifier: Apache-2.0
|
||||||
|
-->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M440,760L520,760L520,593L584,657L640,600L480,440L320,600L377,656L440,593L440,760ZM240,880Q207,880 183.5,856.5Q160,833 160,800L160,160Q160,127 183.5,103.5Q207,80 240,80L560,80L800,320L800,800Q800,833 776.5,856.5Q753,880 720,880L240,880ZM520,360L520,160L240,160Q240,160 240,160Q240,160 240,160L240,800Q240,800 240,800Q240,800 240,800L720,800Q720,800 720,800Q720,800 720,800L720,360L520,360ZM240,160L240,160L240,360L240,360L240,160L240,360L240,360L240,800Q240,800 240,800Q240,800 240,800L240,800Q240,800 240,800Q240,800 240,800L240,160Q240,160 240,160Q240,160 240,160Z"/>
|
||||||
|
</vector>
|
||||||
15
app/src/main/res/drawable/ic_file_import.xml
Normal file
15
app/src/main/res/drawable/ic_file_import.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ SPDX-FileCopyrightText: Material Design Authors / Google LLC
|
||||||
|
~ SPDX-License-Identifier: Apache-2.0
|
||||||
|
-->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M720,840L880,680L824,624L760,688L760,521L680,521L680,688L616,624L560,680L720,840ZM560,960L560,880L880,880L880,960L560,960ZM240,800Q207,800 183.5,776.5Q160,753 160,720L160,160Q160,127 183.5,103.5Q207,80 240,80L520,80L760,320L760,441L680,441L680,360L480,360L480,160L240,160Q240,160 240,160Q240,160 240,160L240,720Q240,720 240,720Q240,720 240,720L480,720L480,800L240,800ZM240,720L240,441L240,441L240,360L240,160L240,160Q240,160 240,160Q240,160 240,160L240,720Q240,720 240,720Q240,720 240,720Z"/>
|
||||||
|
</vector>
|
||||||
15
app/src/main/res/drawable/ic_select_all.xml
Normal file
15
app/src/main/res/drawable/ic_select_all.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ SPDX-FileCopyrightText: Material Design Authors / Google LLC
|
||||||
|
~ SPDX-License-Identifier: Apache-2.0
|
||||||
|
-->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M280,680L280,280L680,280L680,680L280,680ZM360,600L600,600L600,360L360,360L360,600ZM200,760L200,840Q167,840 143.5,816.5Q120,793 120,760L200,760ZM120,680L120,600L200,600L200,680L120,680ZM120,520L120,440L200,440L200,520L120,520ZM120,360L120,280L200,280L200,360L120,360ZM200,200L120,200Q120,167 143.5,143.5Q167,120 200,120L200,200ZM280,840L280,760L360,760L360,840L280,840ZM280,200L280,120L360,120L360,200L280,200ZM440,840L440,760L520,760L520,840L440,840ZM440,200L440,120L520,120L520,200L440,200ZM600,840L600,760L680,760L680,840L600,840ZM600,200L600,120L680,120L680,200L600,200ZM760,840L760,760L840,760Q840,793 816.5,816.5Q793,840 760,840ZM760,680L760,600L840,600L840,680L760,680ZM760,520L760,440L840,440L840,520L760,520ZM760,360L760,280L840,280L840,360L760,360ZM760,200L760,120Q793,120 816.5,143.5Q840,167 840,200L760,200Z"/>
|
||||||
|
</vector>
|
||||||
12
app/src/main/res/drawable/ic_transparent.xml
Normal file
12
app/src/main/res/drawable/ic_transparent.xml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||||
|
~ SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
-->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
</vector>
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
|
||||||
~ 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/>.
|
|
||||||
~
|
|
||||||
-->
|
|
||||||
|
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<item
|
|
||||||
android:id="@+id/action_select_all"
|
|
||||||
android:title="@string/action_select_all" />
|
|
||||||
<item
|
|
||||||
android:id="@+id/action_remove_all"
|
|
||||||
android:title="@string/action_remove_all" />
|
|
||||||
<item
|
|
||||||
android:id="@+id/action_import"
|
|
||||||
android:title="@string/action_import" />
|
|
||||||
<item
|
|
||||||
android:id="@+id/action_export"
|
|
||||||
android:title="@string/action_export" />
|
|
||||||
</menu>
|
|
||||||
@@ -82,11 +82,6 @@
|
|||||||
android:name="com.aurora.store.view.ui.commons.FavouriteFragment"
|
android:name="com.aurora.store.view.ui.commons.FavouriteFragment"
|
||||||
android:label="@string/title_favourites_manager"
|
android:label="@string/title_favourites_manager"
|
||||||
tools:layout="@layout/fragment_generic_with_toolbar" />
|
tools:layout="@layout/fragment_generic_with_toolbar" />
|
||||||
<fragment
|
|
||||||
android:id="@+id/blacklistFragment"
|
|
||||||
android:name="com.aurora.store.view.ui.commons.BlacklistFragment"
|
|
||||||
android:label="@string/title_blacklist_manager"
|
|
||||||
tools:layout="@layout/fragment_generic_with_toolbar" />
|
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/accountFragment"
|
android:id="@+id/accountFragment"
|
||||||
android:name="com.aurora.store.view.ui.account.AccountFragment"
|
android:name="com.aurora.store.view.ui.account.AccountFragment"
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ plugins {
|
|||||||
alias(libs.plugins.jetbrains.kotlin.android) apply false
|
alias(libs.plugins.jetbrains.kotlin.android) apply false
|
||||||
alias(libs.plugins.jetbrains.kotlin.compose) apply false
|
alias(libs.plugins.jetbrains.kotlin.compose) apply false
|
||||||
alias(libs.plugins.jetbrains.kotlin.parcelize) apply false
|
alias(libs.plugins.jetbrains.kotlin.parcelize) apply false
|
||||||
|
alias(libs.plugins.jetbrains.kotlin.serialization) apply false
|
||||||
alias(libs.plugins.google.ksp) apply false
|
alias(libs.plugins.google.ksp) apply false
|
||||||
alias(libs.plugins.androidx.navigation) apply false
|
alias(libs.plugins.androidx.navigation) apply false
|
||||||
alias(libs.plugins.ktlint) apply false
|
alias(libs.plugins.ktlint) apply false
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
[versions]
|
[versions]
|
||||||
activityCompose = "1.10.0"
|
activityCompose = "1.10.0"
|
||||||
androidGradlePlugin = "8.8.0"
|
androidGradlePlugin = "8.8.0"
|
||||||
|
androidx-hilt = "1.2.0"
|
||||||
browserVersion = "1.8.0"
|
browserVersion = "1.8.0"
|
||||||
coilVersion = "3.1.0"
|
coilVersion = "3.1.0"
|
||||||
composeBom = "2025.01.01"
|
composeBom = "2025.01.01"
|
||||||
@@ -29,6 +30,7 @@ protobufJavaliteVersion = "4.29.3"
|
|||||||
rikkaHiddenVersion = "4.3.3"
|
rikkaHiddenVersion = "4.3.3"
|
||||||
rikkaToolsVersion = "4.4.0"
|
rikkaToolsVersion = "4.4.0"
|
||||||
roomVersion = "2.6.1"
|
roomVersion = "2.6.1"
|
||||||
|
serialization = "1.8.0"
|
||||||
shimmerVersion = "0.5.0"
|
shimmerVersion = "0.5.0"
|
||||||
shizukuVersion = "13.1.5"
|
shizukuVersion = "13.1.5"
|
||||||
swiperefreshlayoutVersion = "1.1.0"
|
swiperefreshlayoutVersion = "1.1.0"
|
||||||
@@ -44,10 +46,12 @@ androidx-browser = { module = "androidx.browser:browser", version.ref = "browser
|
|||||||
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
|
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
|
||||||
androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "coreVersion" }
|
androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "coreVersion" }
|
||||||
androidx-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "espressoVersion" }
|
androidx-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "espressoVersion" }
|
||||||
|
androidx-hilt-navigation = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "androidx-hilt" }
|
||||||
androidx-junit = { module = "androidx.test.ext:junit-ktx", version.ref = "junitVersion" }
|
androidx-junit = { module = "androidx.test.ext:junit-ktx", version.ref = "junitVersion" }
|
||||||
androidx-lifecycle-process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "lifeVersion" }
|
androidx-lifecycle-process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "lifeVersion" }
|
||||||
androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifeVersion" }
|
androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifeVersion" }
|
||||||
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
|
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
|
||||||
|
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navVersion" }
|
||||||
androidx-navigation-fragment-ktx = { module = "androidx.navigation:navigation-fragment-ktx", version.ref = "navVersion" }
|
androidx-navigation-fragment-ktx = { module = "androidx.navigation:navigation-fragment-ktx", version.ref = "navVersion" }
|
||||||
androidx-navigation-ui-ktx = { module = "androidx.navigation:navigation-ui-ktx", version.ref = "navVersion" }
|
androidx-navigation-ui-ktx = { module = "androidx.navigation:navigation-ui-ktx", version.ref = "navVersion" }
|
||||||
androidx-preference-ktx = { module = "androidx.preference:preference-ktx", version.ref = "preferenceVersion" }
|
androidx-preference-ktx = { module = "androidx.preference:preference-ktx", version.ref = "preferenceVersion" }
|
||||||
@@ -79,6 +83,7 @@ hilt-android-testing = { module = "com.google.dagger:hilt-android-testing", vers
|
|||||||
hilt-androidx-compiler = { module = "androidx.hilt:hilt-compiler", version.ref = "hiltWorkVersion" }
|
hilt-androidx-compiler = { module = "androidx.hilt:hilt-compiler", version.ref = "hiltWorkVersion" }
|
||||||
hilt-androidx-work = { module = "androidx.hilt:hilt-work", version.ref = "hiltWorkVersion" }
|
hilt-androidx-work = { module = "androidx.hilt:hilt-work", version.ref = "hiltWorkVersion" }
|
||||||
junit = { module = "junit:junit", version.ref = "junit" }
|
junit = { module = "junit:junit", version.ref = "junit" }
|
||||||
|
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization" }
|
||||||
lsposed-hiddenapibypass = { module = "org.lsposed.hiddenapibypass:hiddenapibypass", version.ref = "hiddenapibypassVersion" }
|
lsposed-hiddenapibypass = { module = "org.lsposed.hiddenapibypass:hiddenapibypass", version.ref = "hiddenapibypassVersion" }
|
||||||
process-phoenix = { module = "com.jakewharton:process-phoenix", version.ref = "processPhoenix" }
|
process-phoenix = { module = "com.jakewharton:process-phoenix", version.ref = "processPhoenix" }
|
||||||
rikka-hidden-stub = { module = "dev.rikka.hidden:stub", version.ref = "rikkaHiddenVersion" }
|
rikka-hidden-stub = { module = "dev.rikka.hidden:stub", version.ref = "rikkaHiddenVersion" }
|
||||||
@@ -97,4 +102,5 @@ ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlintVersion" }
|
|||||||
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlinVersion" }
|
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlinVersion" }
|
||||||
jetbrains-kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlinVersion" }
|
jetbrains-kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlinVersion" }
|
||||||
jetbrains-kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlinVersion" }
|
jetbrains-kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlinVersion" }
|
||||||
|
jetbrains-kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlinVersion" }
|
||||||
rikka-tools-refine-plugin = { id = "dev.rikka.tools.refine", version.ref = "rikkaToolsVersion" }
|
rikka-tools-refine-plugin = { id = "dev.rikka.tools.refine", version.ref = "rikkaToolsVersion" }
|
||||||
|
|||||||
Reference in New Issue
Block a user