diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..8b61e2ce --- /dev/null +++ b/build.gradle @@ -0,0 +1,216 @@ +plugins { + id 'com.android.application' version '7.1.3' apply false + id 'com.android.library' version '7.1.3' apply false + id 'org.jetbrains.kotlin.android' version '1.6.10' apply false + id 'org.jetbrains.kotlin.plugin.serialization' version '1.6.10' +} + +apply plugin: 'com.android.application' +apply plugin: 'org.jetbrains.kotlin.android' +apply plugin: "kotlin-kapt" + +android { + + compileSdk = 32 + defaultConfig { + archivesBaseName = 'droidify' + applicationId = 'com.looker.droidify' + minSdk = 23 + targetSdk = 32 + versionCode = 903 + versionName = "0.9.0" + vectorDrawables.useSupportLibrary = true + + javaCompileOptions { + annotationProcessorOptions { + arguments += ["room.schemaLocation": "$projectDir/schemas".toString()] + arguments += ["room.incremental": "true"] + } + } + } + + sourceSets.all { + def javaDir = it.java.srcDirs.find { it.name == 'java' } + it.java.srcDirs += new File(javaDir.parentFile, 'kotlin') + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } + + kotlinOptions { + jvmTarget = '11' + freeCompilerArgs = ['-Xjvm-default=compatibility'] + } + + buildFeatures { + compose true + dataBinding true + } + + composeOptions { + kotlinCompilerExtensionVersion "1.2.0-alpha07" + } + + buildTypes { + debug { + minifyEnabled = false + shrinkResources = false + applicationIdSuffix = ".debug" + versionNameSuffix = "-alpha3" + resValue "string", "application_name", "Neo Store-Debug" + manifestPlaceholders = [ + appIcon : "@mipmap/ic_launcher_debug", + appIconRound: "@mipmap/ic_launcher_round_debug" + ] + } + neo { + minifyEnabled = false + shrinkResources = false + applicationIdSuffix = ".neo" + versionNameSuffix = "-alpha3" + resValue "string", "application_name", "Neo Store-alpha" + manifestPlaceholders = [ + appIcon : "@mipmap/ic_launcher_debug", + appIconRound: "@mipmap/ic_launcher_round_debug" + ] + } + release { + minifyEnabled = true + shrinkResources = true + resValue "string", "application_name", "Neo Store" + manifestPlaceholders = [ + appIcon : "@mipmap/ic_launcher", + appIconRound: "@mipmap/ic_launcher_round" + ] + } + all { + crunchPngs = false + proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard.pro") + } + } + packagingOptions { + jniLibs { + excludes += ['/okhttp3/internal/publicsuffix/*'] + } + resources { + excludes += ['/DebugProbesKt.bin', '/kotlin/**.kotlin_builtins', '/kotlin/**.kotlin_metadata', '/META-INF/**.kotlin_module', '/META-INF/**.pro', '/META-INF/**.version', '/okhttp3/internal/publicsuffix/*'] + } + } + + + def keystorePropertiesFile = rootProject.file('keystore.properties') + lint { + ignore 'InvalidVectorPath' + warning 'InvalidPackage' + } + if (keystorePropertiesFile.exists()) { + def keystoreProperties = new Properties() + keystoreProperties.load(keystorePropertiesFile.newDataInputStream()) + + def signing = [ + storeFile : keystoreProperties['store.file'], + storePassword: keystoreProperties['store.password'], + keyAlias : keystoreProperties['key.alias'], + keyPassword : keystoreProperties['key.password'] + ] + + if (!signing.any { _, v -> v == null }) { + signingConfigs { + primary { + storeFile = file(signing.storeFile) + storePassword = signing.storePassword + keyAlias = signing.keyAlias + keyPassword = signing.keyPassword + v2SigningEnabled = false + } + } + + buildTypes { + debug.signingConfig = signingConfigs.primary + release.signingConfig = signingConfigs.primary + } + } + } +} + +dependencies { + + // Core + implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.10' + implementation 'androidx.core:core-ktx:1.7.0' + implementation 'androidx.appcompat:appcompat:1.4.1' + implementation 'androidx.fragment:fragment-ktx:1.5.0-alpha05' + implementation 'androidx.activity:activity-ktx:1.6.0-alpha01' + implementation "androidx.preference:preference-ktx:1.2.0" + implementation 'androidx.navigation:navigation-fragment-ktx:2.5.0-alpha04' + implementation 'androidx.navigation:navigation-ui-ktx:2.5.0-alpha04' + + // Material3 + implementation 'com.google.android.material:material:1.6.0-beta01' + + // Coil + implementation 'io.coil-kt:coil:2.0.0-rc03' + implementation 'io.coil-kt:coil-compose:2.0.0-rc03' + + // OkHttps + implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.6' + + // RxJava + implementation 'io.reactivex.rxjava3:rxjava:3.1.4' + implementation 'io.reactivex.rxjava3:rxandroid:3.0.0' + + implementation 'me.zhanghai.android.fastscroll:library:1.1.7' + + // LibSu + implementation 'com.github.topjohnwu.libsu:core:3.2.1' + + // JSON + implementation 'com.fasterxml.jackson.core:jackson-core:2.13.2' + implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2" + + // Markdown + implementation 'org.jetbrains:markdown:0.3.1' + + // Coroutines / Lifecycle + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.0-alpha06' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1-native-mt' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1-native-mt' + + // Room + implementation 'androidx.room:room-runtime:2.4.2' + implementation 'androidx.room:room-ktx:2.4.2' + implementation 'androidx.room:room-rxjava3:2.4.2' + kapt 'androidx.room:room-compiler:2.4.2' + + // Compose + implementation "androidx.compose.runtime:runtime:1.2.0-alpha07" + implementation "androidx.compose.ui:ui:1.2.0-alpha07" + implementation "androidx.compose.ui:ui-tooling:1.2.0-alpha07" + implementation "androidx.compose.foundation:foundation:1.2.0-alpha07" + implementation "androidx.compose.runtime:runtime-livedata:1.2.0-alpha07" + implementation "androidx.compose.material3:material3:1.0.0-alpha09" + implementation "androidx.compose.material:material:1.2.0-alpha07" + implementation "androidx.compose.animation:animation:1.2.0-alpha07" + implementation "androidx.compose.material:material-icons-extended:1.2.0-alpha07" + implementation "com.google.android.material:compose-theme-adapter:1.1.6" +} + +// using a task as a preBuild dependency instead of a function that takes some time insures that it runs +task detectAndroidLocals { + Set langsList = new HashSet<>() + + // in /res are (almost) all languages that have a translated string is saved. this is safer and saves some time + fileTree("src/main/res").visit { FileVisitDetails details -> + if (details.file.path.endsWith("strings.xml") + && details.file.canonicalFile.getText('UTF-8').contains("?, repositories: Map, - modifier: Modifier = Modifier.fillMaxSize(), + modifier: Modifier = Modifier, onUserClick: (ProductItem) -> Unit = {}, onFavouriteClick: (ProductItem) -> Unit = {}, onInstallClick: (ProductItem) -> Unit = {} ) { - VerticalItemList(list = productsList, modifier = modifier) { + VerticalItemList(list = productsList, modifier = modifier.fillMaxSize()) { it.toItem().let { item -> ProductsListItem( item, diff --git a/src/main/kotlin/com/looker/droidify/ui/compose/pages/app_detail/components/ReleaseItem.kt b/src/main/kotlin/com/looker/droidify/ui/compose/pages/app_detail/components/ReleaseItem.kt new file mode 100644 index 00000000..3437db22 --- /dev/null +++ b/src/main/kotlin/com/looker/droidify/ui/compose/pages/app_detail/components/ReleaseItem.kt @@ -0,0 +1,117 @@ +package com.looker.droidify.ui.compose.pages.app_detail.components + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.foundation.layout.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.Download +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.looker.droidify.R +import com.looker.droidify.database.entity.Release +import com.looker.droidify.utility.extension.text.formatSize + +@Composable +fun ReleaseItem( + modifier: Modifier = Modifier, + isSuggested: Boolean = false, + isInstalled: Boolean = false, + release: Release, + onDownloadClick: (Release) -> Unit = {} +) { + val highlight by animateDpAsState(targetValue = if (isSuggested or isInstalled) 12.dp else 0.dp) + val currentRelease by remember { mutableStateOf(release) } + Surface( + modifier = modifier.fillMaxWidth(), + shape = MaterialTheme.shapes.large, + tonalElevation = highlight + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Start + ) { + IconButton(onClick = { onDownloadClick(currentRelease) }) { + Icon( + imageVector = Icons.Rounded.Download, + contentDescription = "Download Release" + ) + } + Box { + ReleaseTitleWithBadge( + modifier = Modifier.align(Alignment.CenterStart), + title = release.version, + subtitle = release.targetSdkVersion.toString() + ) { + AnimatedVisibility(visible = isSuggested) { + ReleaseBadge(text = stringResource(id = R.string.suggested)) + } + } + ReleaseItemEndText( + modifier = Modifier.align(Alignment.CenterEnd), + title = release.added.toString(), + subtitle = release.size.formatSize() + ) + } + } + } +} + +@Composable +fun BoxScope.ReleaseTitleWithBadge( + modifier: Modifier = Modifier, + title: String, + subtitle: String, + badges: @Composable RowScope.() -> Unit = {} +) { + Column( + modifier = modifier.matchParentSize(), + verticalArrangement = Arrangement.SpaceEvenly, + horizontalAlignment = Alignment.Start + ) { + Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) { + Text(text = title, style = MaterialTheme.typography.bodyMedium) + badges() + } + Text(text = subtitle, style = MaterialTheme.typography.labelMedium) + } +} + +@Composable +fun BoxScope.ReleaseItemEndText( + modifier: Modifier = Modifier, + title: String, + subtitle: String +) { + Column( + modifier = modifier.matchParentSize(), + verticalArrangement = Arrangement.SpaceEvenly, + horizontalAlignment = Alignment.Start + ) { + Text(text = title, style = MaterialTheme.typography.bodySmall) + Text(text = subtitle, style = MaterialTheme.typography.labelSmall) + } +} + +@Composable +fun ReleaseBadge( + modifier: Modifier = Modifier, + text: String, + color: Color = MaterialTheme.colorScheme.secondaryContainer, + onColor: Color = MaterialTheme.colorScheme.onSecondaryContainer +) { + Surface( + modifier = modifier.padding(8.dp), + color = color, + shape = Shapes.Full + ) { + Text(text = text, color = onColor) + } +} \ No newline at end of file