mirror of
https://github.com/f-droid/fdroidclient.git
synced 2026-05-19 14:10:38 -04:00
next
This commit is contained in:
1
basic/.gitignore
vendored
Normal file
1
basic/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
66
basic/build.gradle.kts
Normal file
66
basic/build.gradle.kts
Normal file
@@ -0,0 +1,66 @@
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.jetbrains.kotlin.android)
|
||||
alias(libs.plugins.jetbrains.kotlin.parcelize)
|
||||
alias(libs.plugins.jetbrains.compose.compiler)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "org.fdroid.basic"
|
||||
compileSdk = libs.versions.compileSdk.get().toInt()
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "org.fdroid.next"
|
||||
minSdk = 23
|
||||
targetSdk = 36
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(libs.androidx.ui)
|
||||
implementation(libs.androidx.ui.graphics)
|
||||
implementation(libs.androidx.compose.material.icons.extended)
|
||||
implementation(libs.androidx.compose.ui.tooling.preview)
|
||||
implementation(libs.androidx.compose.material3)
|
||||
implementation(libs.androidx.compose.material3.adaptive)
|
||||
implementation(libs.androidx.compose.material3.adaptive.layout)
|
||||
implementation(libs.androidx.compose.material3.adaptive.navigation)
|
||||
implementation(libs.androidx.compose.material3.adaptive.navigation.suite.android)
|
||||
|
||||
testImplementation(libs.junit)
|
||||
|
||||
androidTestImplementation(libs.androidx.test.ext.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||
androidTestImplementation(libs.androidx.ui.test.junit4)
|
||||
debugImplementation(libs.androidx.compose.ui.tooling)
|
||||
debugImplementation(libs.androidx.ui.test.manifest)
|
||||
}
|
||||
21
basic/proguard-rules.pro
vendored
Normal file
21
basic/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
23
basic/src/main/AndroidManifest.xml
Normal file
23
basic/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Fdroidclient">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.Fdroidclient">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
17
basic/src/main/java/org/fdroid/basic/MainActivity.kt
Normal file
17
basic/src/main/java/org/fdroid/basic/MainActivity.kt
Normal file
@@ -0,0 +1,17 @@
|
||||
package org.fdroid.basic
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import org.fdroid.basic.ui.main.Main
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
setContent {
|
||||
Main()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package org.fdroid.basic.ui.icons
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.PathFillType.Companion.NonZero
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.graphics.StrokeCap.Companion.Butt
|
||||
import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.ImageVector.Builder
|
||||
import androidx.compose.ui.graphics.vector.path
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.fdroid.fdroid.ui.theme.FDroidContent
|
||||
|
||||
val PackageVariant: ImageVector
|
||||
get() {
|
||||
if (_PackageVariant != null) {
|
||||
return _PackageVariant!!
|
||||
}
|
||||
_PackageVariant = Builder(
|
||||
name = "packageVariant", defaultWidth = 24.0.dp, defaultHeight
|
||||
= 24.0.dp, viewportWidth = 24.0f, viewportHeight = 24.0f
|
||||
).apply {
|
||||
path(
|
||||
fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
|
||||
strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
|
||||
pathFillType = NonZero
|
||||
) {
|
||||
moveTo(2.0f, 10.96f)
|
||||
curveTo(1.5f, 10.68f, 1.35f, 10.07f, 1.63f, 9.59f)
|
||||
lineTo(3.13f, 7.0f)
|
||||
curveTo(3.24f, 6.8f, 3.41f, 6.66f, 3.6f, 6.58f)
|
||||
lineTo(11.43f, 2.18f)
|
||||
curveTo(11.59f, 2.06f, 11.79f, 2.0f, 12.0f, 2.0f)
|
||||
curveTo(12.21f, 2.0f, 12.41f, 2.06f, 12.57f, 2.18f)
|
||||
lineTo(20.47f, 6.62f)
|
||||
curveTo(20.66f, 6.72f, 20.82f, 6.88f, 20.91f, 7.08f)
|
||||
lineTo(22.36f, 9.6f)
|
||||
curveTo(22.64f, 10.08f, 22.47f, 10.69f, 22.0f, 10.96f)
|
||||
lineTo(21.0f, 11.54f)
|
||||
verticalLineTo(16.5f)
|
||||
curveTo(21.0f, 16.88f, 20.79f, 17.21f, 20.47f, 17.38f)
|
||||
lineTo(12.57f, 21.82f)
|
||||
curveTo(12.41f, 21.94f, 12.21f, 22.0f, 12.0f, 22.0f)
|
||||
curveTo(11.79f, 22.0f, 11.59f, 21.94f, 11.43f, 21.82f)
|
||||
lineTo(3.53f, 17.38f)
|
||||
curveTo(3.21f, 17.21f, 3.0f, 16.88f, 3.0f, 16.5f)
|
||||
verticalLineTo(10.96f)
|
||||
curveTo(2.7f, 11.13f, 2.32f, 11.14f, 2.0f, 10.96f)
|
||||
moveTo(12.0f, 4.15f)
|
||||
verticalLineTo(4.15f)
|
||||
lineTo(12.0f, 10.85f)
|
||||
verticalLineTo(10.85f)
|
||||
lineTo(17.96f, 7.5f)
|
||||
lineTo(12.0f, 4.15f)
|
||||
moveTo(5.0f, 15.91f)
|
||||
lineTo(11.0f, 19.29f)
|
||||
verticalLineTo(12.58f)
|
||||
lineTo(5.0f, 9.21f)
|
||||
verticalLineTo(15.91f)
|
||||
moveTo(19.0f, 15.91f)
|
||||
verticalLineTo(12.69f)
|
||||
lineTo(14.0f, 15.59f)
|
||||
curveTo(13.67f, 15.77f, 13.3f, 15.76f, 13.0f, 15.6f)
|
||||
verticalLineTo(19.29f)
|
||||
lineTo(19.0f, 15.91f)
|
||||
moveTo(13.85f, 13.36f)
|
||||
lineTo(20.13f, 9.73f)
|
||||
lineTo(19.55f, 8.72f)
|
||||
lineTo(13.27f, 12.35f)
|
||||
lineTo(13.85f, 13.36f)
|
||||
close()
|
||||
}
|
||||
}
|
||||
.build()
|
||||
return _PackageVariant!!
|
||||
}
|
||||
|
||||
private var _PackageVariant: ImageVector? = null
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun Preview() {
|
||||
FDroidContent {
|
||||
Box(modifier = Modifier.padding(12.dp)) {
|
||||
Image(imageVector = PackageVariant, contentDescription = "")
|
||||
}
|
||||
}
|
||||
}
|
||||
134
basic/src/main/java/org/fdroid/basic/ui/main/Apps.kt
Normal file
134
basic/src/main/java/org/fdroid/basic/ui/main/Apps.kt
Normal file
@@ -0,0 +1,134 @@
|
||||
package org.fdroid.basic.ui.main
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
|
||||
import androidx.compose.material3.adaptive.layout.AnimatedPane
|
||||
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffold
|
||||
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole.Detail
|
||||
import androidx.compose.material3.adaptive.layout.PaneAdaptedValue
|
||||
import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewScreenSizes
|
||||
import kotlinx.coroutines.launch
|
||||
import org.fdroid.basic.R
|
||||
import org.fdroid.basic.ui.main.apps.AppDetails
|
||||
import org.fdroid.basic.ui.main.apps.AppList
|
||||
import org.fdroid.basic.ui.main.apps.AppNavigationItem
|
||||
import org.fdroid.basic.ui.main.apps.AppsFilter
|
||||
import org.fdroid.basic.ui.main.apps.AppsSearch
|
||||
import org.fdroid.fdroid.ui.theme.FDroidContent
|
||||
|
||||
enum class Sort {
|
||||
NAME,
|
||||
LATEST,
|
||||
}
|
||||
|
||||
const val NUM_ITEMS = 42
|
||||
|
||||
@Composable
|
||||
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
||||
fun Apps(modifier: Modifier) {
|
||||
val navigator = rememberListDetailPaneScaffoldNavigator<AppNavigationItem>()
|
||||
val scope = rememberCoroutineScope()
|
||||
BackHandler(enabled = navigator.canNavigateBack()) {
|
||||
scope.launch {
|
||||
navigator.navigateBack()
|
||||
}
|
||||
}
|
||||
val isDetailVisible = navigator.scaffoldValue[Detail] == PaneAdaptedValue.Expanded
|
||||
ListDetailPaneScaffold(
|
||||
directive = navigator.scaffoldDirective,
|
||||
value = navigator.scaffoldValue,
|
||||
listPane = {
|
||||
AnimatedPane {
|
||||
Column(
|
||||
modifier.fillMaxSize()
|
||||
) {
|
||||
var filterExpanded by rememberSaveable { mutableStateOf(true) }
|
||||
var sortBy by rememberSaveable { mutableStateOf(Sort.NAME) }
|
||||
var onlyInstalledApps by rememberSaveable { mutableStateOf(false) }
|
||||
val addedCategories = remember { mutableStateListOf<String>() }
|
||||
val addedRepos = remember { mutableStateListOf<String>() }
|
||||
val categories = listOf(
|
||||
stringResource(R.string.category_Time),
|
||||
stringResource(R.string.category_Games),
|
||||
stringResource(R.string.category_Money),
|
||||
stringResource(R.string.category_Reading),
|
||||
stringResource(R.string.category_Theming),
|
||||
stringResource(R.string.category_Connectivity),
|
||||
stringResource(R.string.category_Internet),
|
||||
stringResource(R.string.category_Navigation),
|
||||
stringResource(R.string.category_Multimedia),
|
||||
stringResource(R.string.category_Phone_SMS),
|
||||
stringResource(R.string.category_Science_Education),
|
||||
stringResource(R.string.category_Security),
|
||||
stringResource(R.string.category_Sports_Health),
|
||||
stringResource(R.string.category_System),
|
||||
stringResource(R.string.category_Writing),
|
||||
)
|
||||
AppsSearch(
|
||||
onlyInstalledApps = onlyInstalledApps,
|
||||
addedCategories = addedCategories,
|
||||
addedRepos = addedRepos,
|
||||
toggleFilter = { filterExpanded = !filterExpanded },
|
||||
)
|
||||
AppsFilter(
|
||||
filterExpanded = filterExpanded,
|
||||
sortBy = sortBy,
|
||||
onlyInstalledApps = onlyInstalledApps,
|
||||
addedCategories = addedCategories,
|
||||
addedRepos = addedRepos,
|
||||
categories = categories,
|
||||
onSortByChanged = { sortBy = it },
|
||||
toggleOnlyInstalledApps = {
|
||||
onlyInstalledApps = !onlyInstalledApps
|
||||
},
|
||||
)
|
||||
AppList(
|
||||
onlyInstalledApps = onlyInstalledApps,
|
||||
sortBy = sortBy,
|
||||
addedCategories = addedCategories,
|
||||
categories = categories,
|
||||
currentItem = if (isDetailVisible) {
|
||||
navigator.currentDestination?.contentKey
|
||||
} else {
|
||||
null
|
||||
},
|
||||
) {
|
||||
scope.launch { navigator.navigateTo(Detail, it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
detailPane = {
|
||||
AnimatedPane {
|
||||
navigator.currentDestination?.contentKey?.let {
|
||||
AppDetails(
|
||||
appItem = it,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@PreviewScreenSizes
|
||||
@Composable
|
||||
fun AppsPreview() {
|
||||
FDroidContent {
|
||||
Apps(Modifier)
|
||||
}
|
||||
}
|
||||
97
basic/src/main/java/org/fdroid/basic/ui/main/Main.kt
Normal file
97
basic/src/main/java/org/fdroid/basic/ui/main/Main.kt
Normal file
@@ -0,0 +1,97 @@
|
||||
package org.fdroid.basic.ui.main
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Apps
|
||||
import androidx.compose.material.icons.filled.Update
|
||||
import androidx.compose.material3.Badge
|
||||
import androidx.compose.material3.BadgedBox
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
|
||||
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteDefaults
|
||||
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffold
|
||||
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteType
|
||||
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.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewScreenSizes
|
||||
import androidx.window.core.layout.WindowWidthSizeClass
|
||||
import org.fdroid.basic.R
|
||||
import org.fdroid.fdroid.ui.theme.FDroidContent
|
||||
|
||||
enum class AppDestinations(
|
||||
@StringRes val label: Int,
|
||||
val icon: ImageVector,
|
||||
) {
|
||||
APPS(R.string.apps, Icons.Filled.Apps),
|
||||
UPDATES(R.string.updates, Icons.Filled.Update),
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Main() {
|
||||
var currentDestination by rememberSaveable { mutableStateOf(AppDestinations.APPS) }
|
||||
FDroidContent {
|
||||
val adaptiveInfo = currentWindowAdaptiveInfo()
|
||||
val customNavSuiteType = with(adaptiveInfo) {
|
||||
when (windowSizeClass.windowWidthSizeClass) {
|
||||
WindowWidthSizeClass.COMPACT -> NavigationSuiteType.NavigationBar
|
||||
else -> NavigationSuiteType.NavigationRail
|
||||
}
|
||||
}
|
||||
NavigationSuiteScaffold(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
layoutType = customNavSuiteType,
|
||||
navigationSuiteColors = NavigationSuiteDefaults.colors(
|
||||
navigationBarContainerColor = Color.Transparent,
|
||||
),
|
||||
navigationSuiteItems = {
|
||||
AppDestinations.entries.forEach { dest ->
|
||||
item(
|
||||
icon = {
|
||||
BadgedBox(
|
||||
badge = {
|
||||
if (dest == AppDestinations.UPDATES) {
|
||||
Badge {
|
||||
Text(text = "13")
|
||||
}
|
||||
}
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
dest.icon,
|
||||
contentDescription = stringResource(dest.label)
|
||||
)
|
||||
}
|
||||
},
|
||||
label = { Text(stringResource(dest.label)) },
|
||||
selected = dest == currentDestination,
|
||||
onClick = { currentDestination = dest }
|
||||
)
|
||||
}
|
||||
}
|
||||
) {
|
||||
if (currentDestination == AppDestinations.APPS) Apps(Modifier)
|
||||
else Text(
|
||||
text = "TODO",
|
||||
modifier = Modifier.safeDrawingPadding(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@PreviewScreenSizes
|
||||
@Composable
|
||||
fun MainPreview() {
|
||||
Main()
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package org.fdroid.basic.ui.main
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Info
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import org.fdroid.basic.R
|
||||
import org.fdroid.basic.ui.icons.PackageVariant
|
||||
|
||||
@Composable
|
||||
fun MainOverFlowMenu(menuExpanded: Boolean, onDismissRequest: () -> Unit) {
|
||||
DropdownMenu(
|
||||
expanded = menuExpanded,
|
||||
onDismissRequest = onDismissRequest) {
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.app_details_repositories)) },
|
||||
onClick = { },
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
PackageVariant,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.menu_settings)) },
|
||||
onClick = { },
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
Icons.Filled.Settings,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text("About") },
|
||||
onClick = { },
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
Icons.Filled.Info,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package org.fdroid.basic.ui.main.apps
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement.spacedBy
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Android
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.fdroid.fdroid.ui.theme.FDroidContent
|
||||
|
||||
@Composable
|
||||
fun AppDetails(
|
||||
appItem: AppNavigationItem,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.safeDrawingPadding()
|
||||
.padding(16.dp),
|
||||
horizontalArrangement = spacedBy(8.dp),
|
||||
) {
|
||||
Icon(
|
||||
Icons.Filled.Android,
|
||||
tint = MaterialTheme.colorScheme.secondary,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(48.dp),
|
||||
)
|
||||
Column {
|
||||
Text(appItem.name, style = MaterialTheme.typography.headlineMedium)
|
||||
Text(appItem.summary, style = MaterialTheme.typography.bodyMedium)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun AppDetailsPreview() {
|
||||
FDroidContent {
|
||||
val item = AppNavigationItem(
|
||||
packageName = "foo",
|
||||
name = "bar",
|
||||
summary = "This is a nice app!",
|
||||
)
|
||||
AppDetails(item)
|
||||
}
|
||||
}
|
||||
111
basic/src/main/java/org/fdroid/basic/ui/main/apps/AppList.kt
Normal file
111
basic/src/main/java/org/fdroid/basic/ui/main/apps/AppList.kt
Normal file
@@ -0,0 +1,111 @@
|
||||
package org.fdroid.basic.ui.main.apps
|
||||
|
||||
import androidx.compose.animation.ExperimentalSharedTransitionApi
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.systemBars
|
||||
import androidx.compose.foundation.layout.windowInsetsBottomHeight
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.selection.selectable
|
||||
import androidx.compose.foundation.selection.selectableGroup
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Android
|
||||
import androidx.compose.material.icons.filled.NewReleases
|
||||
import androidx.compose.material3.BadgedBox
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.ListItemDefaults
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.fdroid.basic.ui.main.NUM_ITEMS
|
||||
import org.fdroid.basic.ui.main.Sort
|
||||
|
||||
@Composable
|
||||
fun AppList(
|
||||
onlyInstalledApps: Boolean,
|
||||
sortBy: Sort,
|
||||
addedCategories: List<String>,
|
||||
categories: List<String>,
|
||||
currentItem: AppNavigationItem?,
|
||||
onItemClick: (AppNavigationItem) -> Unit,
|
||||
) {
|
||||
LazyColumn(
|
||||
contentPadding = PaddingValues(vertical = 8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier = Modifier.then(
|
||||
if (currentItem == null) Modifier
|
||||
else Modifier.selectableGroup()
|
||||
),
|
||||
) {
|
||||
repeat(NUM_ITEMS) { idx ->
|
||||
if (onlyInstalledApps && idx % 2 > 0) return@repeat
|
||||
val i = if (sortBy == Sort.NAME) idx else NUM_ITEMS - idx
|
||||
val category = categories.getOrElse(i) { categories.random() }
|
||||
if (addedCategories.isNotEmpty() && category !in addedCategories) return@repeat
|
||||
item {
|
||||
val navItem = AppNavigationItem(
|
||||
packageName = "$i",
|
||||
name = "App $i",
|
||||
summary = "Summary of the app • $category",
|
||||
)
|
||||
val isSelected = currentItem?.packageName == navItem.packageName
|
||||
val interactionModifier = if (currentItem == null) {
|
||||
Modifier.clickable(
|
||||
onClick = { onItemClick(navItem) }
|
||||
)
|
||||
} else {
|
||||
Modifier.selectable(
|
||||
selected = isSelected,
|
||||
onClick = { onItemClick(navItem) }
|
||||
)
|
||||
}
|
||||
ListItem(
|
||||
headlineContent = { Text(navItem.name) },
|
||||
supportingContent = { Text(navItem.summary) },
|
||||
leadingContent = {
|
||||
BadgedBox(badge = {
|
||||
if (i <= 3) Icon(
|
||||
imageVector = Icons.Filled.NewReleases,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
contentDescription = null, modifier = Modifier.size(24.dp),
|
||||
)
|
||||
}) {
|
||||
Icon(
|
||||
Icons.Filled.Android,
|
||||
tint = MaterialTheme.colorScheme.secondary,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
},
|
||||
colors = ListItemDefaults.colors(
|
||||
containerColor = if (isSelected) {
|
||||
MaterialTheme.colorScheme.surfaceVariant
|
||||
} else {
|
||||
Color.Transparent
|
||||
}
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = 8.dp,
|
||||
vertical = 4.dp
|
||||
)
|
||||
.then(interactionModifier)
|
||||
)
|
||||
}
|
||||
}
|
||||
item {
|
||||
Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.systemBars))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.fdroid.basic.ui.main.apps
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
class AppNavigationItem(
|
||||
val packageName: String,
|
||||
val name: String,
|
||||
val summary: String,
|
||||
): Parcelable
|
||||
@@ -0,0 +1,90 @@
|
||||
package org.fdroid.basic.ui.main.apps
|
||||
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.text.input.TextFieldState
|
||||
import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Clear
|
||||
import androidx.compose.material.icons.filled.FilterList
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material3.Badge
|
||||
import androidx.compose.material3.BadgedBox
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.SearchBarDefaults
|
||||
import androidx.compose.material3.SearchBarState
|
||||
import androidx.compose.material3.SearchBarValue
|
||||
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.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import kotlinx.coroutines.launch
|
||||
import org.fdroid.basic.ui.main.MainOverFlowMenu
|
||||
|
||||
@Composable
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
fun AppSearchInputField(
|
||||
searchBarState: SearchBarState,
|
||||
textFieldState: TextFieldState,
|
||||
toggleFilter: () -> Unit,
|
||||
showFilterBadge: Boolean
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
var menuExpanded by remember { mutableStateOf(false) }
|
||||
SearchBarDefaults.InputField(
|
||||
modifier = Modifier,
|
||||
searchBarState = searchBarState,
|
||||
textFieldState = textFieldState,
|
||||
onSearch = {
|
||||
scope.launch { searchBarState.animateToCollapsed() }
|
||||
},
|
||||
placeholder = { Text("Search...") },
|
||||
leadingIcon = {
|
||||
if (searchBarState.currentValue == SearchBarValue.Expanded) {
|
||||
IconButton(
|
||||
onClick = { scope.launch { searchBarState.animateToCollapsed() } }
|
||||
) {
|
||||
Icon(Icons.AutoMirrored.Default.ArrowBack, contentDescription = "Back")
|
||||
}
|
||||
} else {
|
||||
Icon(Icons.Default.Search, contentDescription = null)
|
||||
}
|
||||
},
|
||||
trailingIcon = {
|
||||
if (searchBarState.currentValue == SearchBarValue.Expanded) {
|
||||
IconButton(onClick = { textFieldState.setTextAndPlaceCursorAtEnd("") }) {
|
||||
Icon(
|
||||
Icons.Filled.Clear,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
} else Row {
|
||||
IconButton(onClick = toggleFilter) {
|
||||
BadgedBox(badge = {
|
||||
if (showFilterBadge) Badge(containerColor = MaterialTheme.colorScheme.secondary)
|
||||
}) {
|
||||
Icon(
|
||||
Icons.Filled.FilterList,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
IconButton(onClick = { menuExpanded = true }) {
|
||||
Icon(
|
||||
Icons.Default.MoreVert,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
MainOverFlowMenu(menuExpanded) { menuExpanded = false }
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
212
basic/src/main/java/org/fdroid/basic/ui/main/apps/AppsFilter.kt
Normal file
212
basic/src/main/java/org/fdroid/basic/ui/main/apps/AppsFilter.kt
Normal file
@@ -0,0 +1,212 @@
|
||||
package org.fdroid.basic.ui.main.apps
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.Arrangement.spacedBy
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.AccessTime
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.ArrowDropDown
|
||||
import androidx.compose.material.icons.filled.Clear
|
||||
import androidx.compose.material.icons.filled.Done
|
||||
import androidx.compose.material.icons.filled.SortByAlpha
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.FilterChip
|
||||
import androidx.compose.material3.FilterChipDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
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.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.fdroid.basic.R
|
||||
import org.fdroid.basic.ui.main.Sort
|
||||
|
||||
@Composable
|
||||
fun ColumnScope.AppsFilter(
|
||||
filterExpanded: Boolean,
|
||||
sortBy: Sort,
|
||||
onlyInstalledApps: Boolean,
|
||||
addedCategories: MutableList<String>,
|
||||
addedRepos: MutableList<String>,
|
||||
categories: List<String>,
|
||||
onSortByChanged: (Sort) -> Unit,
|
||||
toggleOnlyInstalledApps: () -> Unit,
|
||||
) {
|
||||
AnimatedVisibility(filterExpanded) {
|
||||
FlowRow(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp),
|
||||
horizontalArrangement = spacedBy(16.dp),
|
||||
) {
|
||||
var sortByMenuExpanded by remember { mutableStateOf(false) }
|
||||
var repoMenuExpanded by remember { mutableStateOf(false) }
|
||||
var categoryMenuExpanded by remember { mutableStateOf(false) }
|
||||
addedCategories.forEach { category ->
|
||||
FilterChip(
|
||||
selected = true,
|
||||
trailingIcon = {
|
||||
Icon(
|
||||
Icons.Filled.Clear,
|
||||
null,
|
||||
modifier = Modifier.size(FilterChipDefaults.IconSize)
|
||||
)
|
||||
},
|
||||
label = {
|
||||
Text(category)
|
||||
},
|
||||
onClick = {
|
||||
addedCategories.remove(category)
|
||||
}
|
||||
)
|
||||
}
|
||||
addedRepos.forEach { repo ->
|
||||
FilterChip(
|
||||
selected = true,
|
||||
trailingIcon = {
|
||||
Icon(
|
||||
Icons.Filled.Clear,
|
||||
null,
|
||||
modifier = Modifier.size(FilterChipDefaults.IconSize)
|
||||
)
|
||||
},
|
||||
label = {
|
||||
Text(repo)
|
||||
},
|
||||
onClick = {
|
||||
addedRepos.remove(repo)
|
||||
}
|
||||
)
|
||||
}
|
||||
FilterChip(
|
||||
selected = false,
|
||||
leadingIcon = {
|
||||
val vector = when (sortBy) {
|
||||
Sort.NAME -> Icons.Filled.SortByAlpha
|
||||
Sort.LATEST -> Icons.Filled.AccessTime
|
||||
}
|
||||
Icon(vector, null, modifier = Modifier.size(FilterChipDefaults.IconSize))
|
||||
},
|
||||
trailingIcon = {
|
||||
Icon(Icons.Filled.ArrowDropDown, null)
|
||||
},
|
||||
label = {
|
||||
val s = when (sortBy) {
|
||||
Sort.NAME -> "Sort by name"
|
||||
Sort.LATEST -> "Sort by latest"
|
||||
}
|
||||
Text(s)
|
||||
DropdownMenu(
|
||||
expanded = sortByMenuExpanded,
|
||||
onDismissRequest = { sortByMenuExpanded = false },
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = { Text("Sort by name") },
|
||||
leadingIcon = {
|
||||
Icon(Icons.Filled.SortByAlpha, null)
|
||||
},
|
||||
onClick = {
|
||||
onSortByChanged(Sort.NAME)
|
||||
sortByMenuExpanded = false
|
||||
},
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text("Sort by latest") },
|
||||
leadingIcon = {
|
||||
Icon(Icons.Filled.AccessTime, null)
|
||||
},
|
||||
onClick = {
|
||||
onSortByChanged(Sort.LATEST)
|
||||
sortByMenuExpanded = false
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
onClick = { sortByMenuExpanded = !sortByMenuExpanded },
|
||||
)
|
||||
FilterChip(
|
||||
selected = onlyInstalledApps,
|
||||
leadingIcon = if (onlyInstalledApps) {
|
||||
{
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Done,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(FilterChipDefaults.IconSize),
|
||||
)
|
||||
}
|
||||
} else null,
|
||||
label = { Text(stringResource(R.string.app_installed)) },
|
||||
onClick = toggleOnlyInstalledApps,
|
||||
)
|
||||
FilterChip(
|
||||
selected = false,
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
Icons.Filled.Add,
|
||||
null,
|
||||
modifier = Modifier.size(FilterChipDefaults.IconSize)
|
||||
)
|
||||
},
|
||||
label = {
|
||||
Text("Category")
|
||||
DropdownMenu(
|
||||
expanded = categoryMenuExpanded,
|
||||
onDismissRequest = { categoryMenuExpanded = false },
|
||||
) {
|
||||
categories.forEach { category ->
|
||||
DropdownMenuItem(
|
||||
text = { Text(category) },
|
||||
onClick = {
|
||||
addedCategories.add(category)
|
||||
categoryMenuExpanded = false
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
onClick = { categoryMenuExpanded = !categoryMenuExpanded },
|
||||
)
|
||||
FilterChip(
|
||||
selected = false,
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
Icons.Filled.Add,
|
||||
null,
|
||||
modifier = Modifier.size(FilterChipDefaults.IconSize)
|
||||
)
|
||||
},
|
||||
label = {
|
||||
Text("Repository")
|
||||
DropdownMenu(
|
||||
expanded = repoMenuExpanded,
|
||||
onDismissRequest = { repoMenuExpanded = false },
|
||||
) {
|
||||
val repos = listOf(
|
||||
"F-Droid",
|
||||
"Guardian Project",
|
||||
"IzzyOnDroid",
|
||||
)
|
||||
repos.forEach { category ->
|
||||
DropdownMenuItem(
|
||||
text = { Text(category) },
|
||||
onClick = {
|
||||
addedRepos.add(category)
|
||||
repoMenuExpanded = false
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
onClick = { repoMenuExpanded = !repoMenuExpanded },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package org.fdroid.basic.ui.main.apps
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.systemBars
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.input.rememberTextFieldState
|
||||
import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Star
|
||||
import androidx.compose.material3.ExpandedFullScreenSearchBar
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.ListItemDefaults
|
||||
import androidx.compose.material3.SearchBarDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopSearchBar
|
||||
import androidx.compose.material3.rememberSearchBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
fun AppsSearch(
|
||||
onlyInstalledApps: Boolean,
|
||||
addedCategories: List<String>,
|
||||
addedRepos: List<String>,
|
||||
toggleFilter: () -> Unit,
|
||||
) {
|
||||
val textFieldState = rememberTextFieldState()
|
||||
val scrollBehavior = SearchBarDefaults.enterAlwaysSearchBarScrollBehavior()
|
||||
val searchBarState = rememberSearchBarState()
|
||||
val scope = rememberCoroutineScope()
|
||||
val inputField = @Composable {
|
||||
AppSearchInputField(
|
||||
searchBarState = searchBarState,
|
||||
textFieldState = textFieldState,
|
||||
toggleFilter = toggleFilter,
|
||||
showFilterBadge = addedRepos.isNotEmpty() || addedCategories.isNotEmpty() ||
|
||||
onlyInstalledApps,
|
||||
)
|
||||
}
|
||||
TopSearchBar(
|
||||
modifier = Modifier,
|
||||
state = searchBarState,
|
||||
scrollBehavior = scrollBehavior,
|
||||
windowInsets = WindowInsets.systemBars,
|
||||
inputField = inputField,
|
||||
)
|
||||
ExpandedFullScreenSearchBar(
|
||||
state = searchBarState,
|
||||
inputField = inputField,
|
||||
) {
|
||||
Column(Modifier.verticalScroll(rememberScrollState())) {
|
||||
repeat(4) { idx ->
|
||||
val resultText = "Suggestion $idx"
|
||||
ListItem(headlineContent = { Text(resultText) },
|
||||
supportingContent = { Text("Additional info") },
|
||||
leadingContent = {
|
||||
Icon(
|
||||
Icons.Filled.Star,
|
||||
contentDescription = null
|
||||
)
|
||||
},
|
||||
colors = ListItemDefaults.colors(containerColor = Color.Transparent),
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
textFieldState.setTextAndPlaceCursorAtEnd(resultText)
|
||||
scope.launch { searchBarState.animateToCollapsed() }
|
||||
}
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = 16.dp,
|
||||
vertical = 4.dp
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
basic/src/main/java/org/fdroid/basic/ui/theme/Color.kt
Symbolic link
1
basic/src/main/java/org/fdroid/basic/ui/theme/Color.kt
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../../../../../../app/src/main/java/org/fdroid/fdroid/ui/theme/Color.kt
|
||||
1
basic/src/main/java/org/fdroid/basic/ui/theme/Theme.kt
Symbolic link
1
basic/src/main/java/org/fdroid/basic/ui/theme/Theme.kt
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../../../../../../app/src/main/java/org/fdroid/fdroid/ui/theme/Theme.kt
|
||||
9
basic/src/main/java/org/fdroid/fdroid/Compat.kt
Normal file
9
basic/src/main/java/org/fdroid/fdroid/Compat.kt
Normal file
@@ -0,0 +1,9 @@
|
||||
package org.fdroid.fdroid
|
||||
|
||||
object Preferences {
|
||||
|
||||
fun get(): Preferences = Preferences
|
||||
|
||||
val isPureBlack: Boolean = true
|
||||
|
||||
}
|
||||
30
basic/src/main/res/drawable-v24/ic_launcher_foreground.xml
Normal file
30
basic/src/main/res/drawable-v24/ic_launcher_foreground.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="85.84757"
|
||||
android:endY="92.4963"
|
||||
android:startX="42.9492"
|
||||
android:startY="49.59793"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeColor="#00000000" />
|
||||
</vector>
|
||||
170
basic/src/main/res/drawable/ic_launcher_background.xml
Normal file
170
basic/src/main/res/drawable/ic_launcher_background.xml
Normal file
@@ -0,0 +1,170 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#3DDC84"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
</vector>
|
||||
6
basic/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
6
basic/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
10
basic/src/main/res/values/colors.xml
Normal file
10
basic/src/main/res/values/colors.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="purple_200">#FFBB86FC</color>
|
||||
<color name="purple_500">#FF6200EE</color>
|
||||
<color name="purple_700">#FF3700B3</color>
|
||||
<color name="teal_200">#FF03DAC5</color>
|
||||
<color name="teal_700">#FF018786</color>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
</resources>
|
||||
6
basic/src/main/res/values/strings-next.xml
Normal file
6
basic/src/main/res/values/strings-next.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="app_name">F-Droid Next</string>
|
||||
|
||||
</resources>
|
||||
1
basic/src/main/res/values/strings.xml
Symbolic link
1
basic/src/main/res/values/strings.xml
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../../app/src/main/res/values/strings.xml
|
||||
5
basic/src/main/res/values/themes.xml
Normal file
5
basic/src/main/res/values/themes.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="Theme.Fdroidclient" parent="android:Theme.Material.Light.NoActionBar" />
|
||||
</resources>
|
||||
@@ -82,6 +82,7 @@ kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-t
|
||||
|
||||
androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidxCoreKtx" }
|
||||
androidx-lifecycle-livedata-ktx = { module = "androidx.lifecycle:lifecycle-livedata-ktx", version.ref = "androidxLifecycleLivedataKtx" }
|
||||
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "androidxLifecycleLivedataKtx" }
|
||||
androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "androidxConstraintlayout" }
|
||||
androidx-swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "androidxSwipeRefreshLayout" }
|
||||
androidx-gridlayout = { module = "androidx.gridlayout:gridlayout", version.ref = "androidxGridlayout" }
|
||||
@@ -96,12 +97,19 @@ androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
|
||||
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
|
||||
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
|
||||
|
||||
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "androidxComposeBom" }
|
||||
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom-alpha", version.ref = "androidxComposeBom" } # TODO remove -alpha
|
||||
androidx-compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" }
|
||||
androidx-compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" }
|
||||
androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose" }
|
||||
androidx-compose-material-icons-extended = { module = "androidx.compose.material:material-icons-extended" }
|
||||
androidx-compose-material3 = { module = "androidx.compose.material3:material3" }
|
||||
androidx-compose-material3-adaptive = { module = "androidx.compose.material3.adaptive:adaptive" }
|
||||
androidx-compose-material3-adaptive-navigation = { module = "androidx.compose.material3.adaptive:adaptive-navigation" }
|
||||
androidx-compose-material3-adaptive-layout = { module = "androidx.compose.material3.adaptive:adaptive-layout" }
|
||||
androidx-compose-material3-adaptive-navigation-suite-android = { module = "androidx.compose.material3:material3-adaptive-navigation-suite-android" }
|
||||
|
||||
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
|
||||
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
|
||||
accompanist-drawablepainter = { module = "com.google.accompanist:accompanist-drawablepainter", version.ref = "accompanistDrawablepainter" }
|
||||
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidxActivityCompose" }
|
||||
|
||||
@@ -162,6 +170,8 @@ turbine = { module = "app.cash.turbine:turbine", version.ref = "turbine" }
|
||||
hamcrest = { module = "org.hamcrest:hamcrest", version.ref = "hamcrest" }
|
||||
mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockitoCore" }
|
||||
json = { module = "org.json:json", version.ref = "json" }
|
||||
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
||||
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
|
||||
@@ -170,6 +180,7 @@ android-ksp = { id = "com.google.devtools.ksp", version.ref = "androidKspPlugin"
|
||||
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||
jetbrains-kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
|
||||
jetbrains-kotlin-plugin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
||||
jetbrains-kotlin-parcelize = { id = "kotlin-parcelize" }
|
||||
jetbrains-compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||
jetbrains-dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" }
|
||||
vanniktech-maven-publish = { id = "com.vanniktech.maven.publish", version.ref = "mavenPublish" }
|
||||
|
||||
@@ -12,3 +12,5 @@ include ':libs:sharedTest'
|
||||
include ':libs:download'
|
||||
include ':libs:index'
|
||||
include ':libs:database'
|
||||
|
||||
include ':basic'
|
||||
|
||||
Reference in New Issue
Block a user