13 Commits

Author SHA1 Message Date
renovate[bot] 7f5d27f656 Add renovate.json 2026-04-15 21:35:13 +00:00
Michatec 7b561fe428 Merge branch 'master' of https://github.com/Michatec/michas-droid 2026-04-09 14:27:07 +02:00
Michatec 688d03ba8b - Migrate build system to Kotlin DSL (build.gradle.kts, settings.gradle.kts)
- Upgrade `compileSdk` and `targetSdk` to 36
- Add German (`de`) localization and string resources
- Add Michachatz F-Droid repository to default list
- Update dependencies and plugins in `libs.versions.toml`
- Remove manual signing configuration from README.md
- Refactor code to use non-deprecated `alpha` property and context-based string retrieval
- Clean up `buildConfig` and locale filtering logic
2026-04-09 13:34:45 +02:00
Michachatz fb06922278 Remove version badge from README
Removed a version badge from the README.
2026-03-30 00:22:31 +02:00
Michachatz 821d847ce2 Update license link in README.md 2026-03-30 00:22:19 +02:00
Michachatz 5a76773ae3 Enhance README with project badges
Added badges for version, downloads, issues, contributors, and license.
2026-03-29 23:55:39 +02:00
Michachatz f303844e28 Add warning about Google identity requirements
Added a warning about upcoming Google requirements for apps on certified Android devices.
2026-03-25 07:37:58 +01:00
Michatec 9e32fe3871 - Move FreeDroidWarn initialization from MainApplication to ScreenActivity 2026-03-24 17:43:21 +01:00
Michatec 6b2ac6d740 - Removed old changelogs
- Removed launcher SVG and build script
2026-03-24 17:33:47 +01:00
Michatec 2c9af08e8c - Refactor code to use idiomatic Kotlin functions like isNullOrEmpty(), associateBy, and appendRange
- Increase `DiffUtil` threshold in `CursorRecyclerAdapter` to 500 and optimize equality checks
- Simplify locale configuration by removing legacy Android SDK version checks
- Remove unnecessary `@SuppressLint` annotations and `@Volatile` modifiers
- Improve performance of collection operations in `ProductFragment` and `Preferences` using `associateBy` and `firstOrNull`
- Suppress deprecation warnings for `getOpacity` in `DrawableWrapper` and `TabsFragment`
- Clean up service binding logic in `Connection.kt` with better type casting
2026-03-24 17:31:51 +01:00
Michatec bdd204d9de - Migrate dependencies to Version Catalog (libs.versions.toml)
- Update Kotlin to 2.3.20 and Jackson Core to 2.21.2
- Update Gradle Wrapper to 9.4.1
- Bump version code to 17 and version name to 1.7
- Remove unused `percentTranslationY` property from `FragmentLinearLayout`
- Clean up semicolons in `MainApplication.kt`
- Update `.gitignore` to include `gradle-daemon-jvm.properties`
2026-03-24 13:07:06 +01:00
Michachatz 112d4bdb62 Integrate FreeDroidWarn for upgrade warnings 2026-03-24 08:43:37 +01:00
Michachatz ebb54fd19b Add JitPack repository and FreeDroidWarn dependency 2026-03-24 08:25:38 +01:00
31 changed files with 371 additions and 296 deletions
+3 -2
View File
@@ -1,7 +1,7 @@
/*
!/.gitignore
!/build.gradle
!/settings.gradle
!/build.gradle.kts
!/settings.gradle.kts
!/COPYING
!/extra
!/gradle
@@ -12,3 +12,4 @@
!/proguard.pro
!/README.md
!/src
/gradle/gradle-daemon-jvm.properties
+13 -9
View File
@@ -1,9 +1,22 @@
# Michas Droid
<p align="center">
<a href="https://github.com/michatec/michas-droid/releases"><img src="https://img.shields.io/github/downloads/michatec/michas-droid/total.svg?style=flat-square" /></a>
<a href="https://github.com/michatec/michas-droid/issues"><img src="https://img.shields.io/github/issues-raw/michatec/michas-droid.svg?style=flat-square&label=issues" /></a>
<a href="https://github.com/michatec/michas-droid/graphs/contributors"><img src="https://img.shields.io/github/contributors/michatec/michas-droid?style=flat-square" /></a>
<a href="https://github.com/michatec/michas-droid/blob/master/COPYING"><img src="https://img.shields.io/github/license/michatec/michas-droid?style=flat-square" /></a>
</p>
Yet another F-Droid client.
[![Release](https://img.shields.io/github/v/release/michatec/michas-droid)](https://github.com/michatec/michas-droid/releases/latest)
## Warning ⚠️
Google has announced that, starting in 2026/2027, all apps on certified Android devices will require the developer to submit personal identity details directly to Google.
Since the developers of this app do not agree to this requirement, this app will no longer work on certified Android devices after that time.
## Description
Unofficial F-Droid client in the style of the classic one.
@@ -34,15 +47,6 @@ or sharing local repositories nearby.
Specify your Android SDK path either using the `ANDROID_HOME` environment variable, or by filling out the `sdk.dir`
property in `local.properties`.
Signing can be done automatically using `keystore.properties` as follows:
```properties
store.file=/path/to/keystore
store.password=key-store-password
key.alias=key-alias
key.password=key-password
```
Run `./gradlew assembleRelease` to build the package, which can be installed using the Android package manager.
## License
-110
View File
@@ -1,110 +0,0 @@
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:9.1.0'
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:2.3.10'
}
}
apply plugin: 'com.android.application'
android {
namespace 'com.michatec.store'
compileSdk 36
defaultConfig {
applicationId 'com.michatec.store'
minSdk 30
targetSdk 36
versionCode 16
versionName '1.6'
def languages = [ 'en' ]
buildConfigField 'String[]', 'LANGUAGES', '{ "' + languages.join('", "') + '" }'
resConfigs languages
}
buildFeatures {
buildConfig = true
}
sourceSets {
main {
java.srcDirs += 'src/main/kotlin'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
flavorDimensions "distributor"
productFlavors {
michas {
dimension "distributor"
buildConfigField "boolean", "FLAVOR_FDROID", "false"
}
fdroid {
dimension "distributor"
buildConfigField "boolean", "FLAVOR_FDROID", "true"
}
}
buildTypes {
debug {
minifyEnabled false
shrinkResources false
}
release {
minifyEnabled true
shrinkResources false
}
}
def keystorePropertiesFile = rootProject.file('keystore.properties')
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
enableV2Signing false
}
}
}
}
}
repositories {
google()
mavenCentral()
}
dependencies {
implementation 'org.jetbrains.kotlin:kotlin-stdlib:2.3.10'
implementation 'androidx.fragment:fragment-ktx:1.8.9'
implementation 'androidx.viewpager2:viewpager2:1.1.0'
implementation 'androidx.vectordrawable:vectordrawable:1.2.0'
implementation 'com.squareup.okhttp3:okhttp:5.3.2'
implementation 'io.reactivex.rxjava3:rxjava:3.1.12'
implementation 'io.reactivex.rxjava3:rxandroid:3.0.2'
implementation 'com.fasterxml.jackson.core:jackson-core:2.21.1'
implementation 'com.squareup.picasso:picasso:2.71828'
}
+80
View File
@@ -0,0 +1,80 @@
plugins {
alias(libs.plugins.android.application)
}
android {
namespace = "com.michatec.store"
compileSdk = 36
defaultConfig {
applicationId = "com.michatec.store"
minSdk = 30
targetSdk = 36
versionCode = 18
versionName = "1.8"
val languages = listOf("en", "de")
buildConfigField("String[]", "LANGUAGES", "{ \"${languages.joinToString("\", \"")}\" }")
@Suppress("UnstableApiUsage")
androidResources {
localeFilters += languages
}
}
buildFeatures {
buildConfig = true
}
sourceSets {
getByName("main") {
kotlin.directories += "src/main/kotlin"
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
flavorDimensions += "distributor"
productFlavors {
create("michas") {
dimension = "distributor"
buildConfigField("boolean", "FLAVOR_FDROID", "false")
}
create("fdroid") {
dimension = "distributor"
buildConfigField("boolean", "FLAVOR_FDROID", "true")
}
}
buildTypes {
getByName("debug") {
isMinifyEnabled = false
isShrinkResources = false
}
getByName("release") {
isMinifyEnabled = true
isShrinkResources = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard.pro"
)
}
}
}
dependencies {
implementation(libs.kotlin.stdlib)
implementation(libs.core.ktx)
implementation(libs.fragment.ktx)
implementation(libs.viewpager2)
implementation(libs.vectordrawable)
implementation(libs.okhttp)
implementation(libs.rxjava)
implementation(libs.rxandroid)
implementation(libs.jackson.core)
implementation(libs.picasso)
implementation(libs.freedroidwarn)
}
-27
View File
@@ -1,27 +0,0 @@
#!/bin/bash
set -e
cd "`dirname "$0"`"
dimensions=(mdpi:1 hdpi:1.5 xhdpi:2 xxhdpi:3 xxxhdpi:4)
res='../src/main/res'
cp 'launcher.svg' 'launcher-foreground.svg'
inkscape --select circle --verb EditDelete --verb=FileSave --verb=FileQuit \
'launcher-foreground.svg'
for dimension in ${dimensions[@]}; do
resource="${dimension%:*}"
scale="${dimension#*:}"
mkdir -p "$res/mipmap-$resource" "$res/drawable-$resource"
size="`bc <<< "48 * $scale"`"
inkscape 'launcher.svg' -a 15:15:93:93 -w "$size" -h "$size" \
-e "$res/mipmap-$resource/ic_launcher.png"
optipng "$res/mipmap-$resource/ic_launcher.png"
size="`bc <<< "108 * $scale"`"
inkscape 'launcher-foreground.svg' -w "$size" -h "$size" \
-e "$res/drawable-$resource/ic_launcher_foreground.png"
optipng "$res/drawable-$resource/ic_launcher_foreground.png"
done
rm 'launcher-foreground.svg'
-87
View File
@@ -1,87 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://www.w3.org/2000/svg" style="enable-background:new" id="svg3" version="1.1" viewBox="0 0 108 108"
height="108" width="108" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://purl.org/dc/elements/1.1/">
<metadata id="metadata55">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>-</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs id="defs17">
<clipPath id="paper-clip" clipPathUnits="userSpaceOnUse">
<path id="paper-clip-path" d="m 67.759585,28.469613 -18.355472,18.146485 -21.453125,-4.732422 9.087891,24.064453 25.894536,12.974609 12.37695,-23.86914 -1.93359,-0.66211 z" />
</clipPath>
<filter id="paper-inner-shadow">
<feFlood id="feFlood6" result="flood" flood-color="rgb(0,0,0)" flood-opacity="0.2" />
<feComposite id="feComposite8" result="composite1" operator="in" in2="SourceGraphic" in="flood" />
<feGaussianBlur id="feGaussianBlur10" result="blur" stdDeviation="2" in="composite1" />
<feOffset id="feOffset12" result="offset" dy="0" dx="0" />
<feComposite id="feComposite14" result="composite2" operator="over" in2="offset" in="SourceGraphic" />
</filter>
<filter id="paper-edge-1" style="color-interpolation-filters:sRGB">
<feFlood id="feFlood958" result="flood" flood-color="rgb(0,0,0)" flood-opacity="0.2" />
<feComposite id="feComposite960" result="composite1" operator="out" in2="SourceGraphic"
in="flood" />
<feGaussianBlur id="feGaussianBlur962" result="blur" stdDeviation="0" in="composite1" />
<feOffset id="feOffset964" result="offset" dy="0.2" dx="0.2" />
<feComposite id="feComposite966" result="composite2" operator="atop" in2="SourceGraphic"
in="offset" />
</filter>
<filter id="paper-edge-2" style="color-interpolation-filters:sRGB">
<feFlood id="feFlood1388" result="flood" flood-color="rgb(255,255,255)" flood-opacity="0.2" />
<feComposite id="feComposite1390" result="composite1" operator="out" in2="SourceGraphic"
in="flood" />
<feGaussianBlur id="feGaussianBlur1392" result="blur" stdDeviation="0" in="composite1" />
<feOffset id="feOffset1394" result="offset" dy="-0.1" dx="0.3" />
<feComposite id="feComposite1396" result="composite2" operator="atop" in2="SourceGraphic"
in="offset" />
</filter>
<filter id="paper-shadow" style="color-interpolation-filters:sRGB">
<feFlood id="feFlood1506" result="flood" flood-color="rgb(0,0,0)" flood-opacity="0.4" />
<feComposite id="feComposite1508" result="composite1" operator="in" in2="SourceGraphic"
in="flood" />
<feGaussianBlur id="feGaussianBlur1510" result="blur" stdDeviation="2" in="composite1" />
<feOffset id="feOffset1512" result="offset" dy="0" dx="0" />
<feComposite id="feComposite1514" result="composite2" operator="over" in2="offset" in="SourceGraphic" />
</filter>
<filter id="circle-shadow" style="color-interpolation-filters:sRGB">
<feFlood id="feFlood1626" result="flood" flood-color="rgb(255,255,255)" flood-opacity="0.2" />
<feComposite id="feComposite1628" result="composite1" operator="out" in2="SourceGraphic"
in="flood" />
<feGaussianBlur id="feGaussianBlur1630" result="blur" stdDeviation="0" in="composite1" />
<feOffset id="feOffset1632" result="offset" dy="0.5" dx="0" />
<feComposite id="feComposite1634" result="fbSourceGraphic" operator="atop" in2="SourceGraphic"
in="offset" />
<feFlood in="fbSourceGraphic" result="flood" flood-color="rgb(0,0,0)" flood-opacity="0.2"
id="feFlood1640" />
<feComposite result="composite1" operator="out" in="flood" id="feComposite1642" in2="fbSourceGraphic" />
<feGaussianBlur result="blur" stdDeviation="0" in="composite1" id="feGaussianBlur1644" />
<feOffset result="offset" dy="-0.5" dx="0" id="feOffset1646" />
<feComposite result="fbSourceGraphic" operator="atop" in="offset" id="feComposite1648"
in2="fbSourceGraphic" />
<feFlood in="fbSourceGraphic" result="flood" flood-color="rgb(0,0,0)" flood-opacity="0.2"
id="feFlood1664" />
<feComposite result="composite1" operator="in" in="flood" id="feComposite1666" in2="fbSourceGraphic" />
<feGaussianBlur result="blur" stdDeviation="1" in="composite1" id="feGaussianBlur1668" />
<feOffset result="offset" dy="1" dx="0" id="feOffset1670" />
<feComposite result="composite2" operator="over" in="fbSourceGraphic" id="feComposite1672"
in2="offset" />
</filter>
</defs>
<circle r="36" cy="54" cx="54" id="circle" style="fill:#262c38;filter:url(#circle-shadow)" />
<g style="filter:url(#paper-shadow)" id="paper-group">
<path d="m 67.75836,28.470764 -23.382292,23.115602 -0.0887,1.254642 29.189963,2.017946 z"
style="fill:#1976d2" id="paper-4" />
<path style="fill:#47a2fc;filter:url(#paper-inner-shadow)" d="m 27.949219,41.884766 9.08789,24.064453 25.894532,12.974609 12.376953,-23.86914 -22.298828,-7.638672 v -0.002 z"
clip-path="url(#paper-clip)" id="paper-3" />
<path d="m 53.009473,47.414648 -15.970174,18.53113 25.894116,12.97696 z" style="fill:#1976d2;filter:url(#paper-edge-1)"
id="paper-2" />
<path style="fill:#47a2fc;filter:url(#paper-edge-2)" d="m 53.009766,47.414016 9.923828,31.503906 12.375,-23.865234 z"
id="paper-1" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 6.0 KiB

+29
View File
@@ -0,0 +1,29 @@
[versions]
core-ktx = "1.18.0"
fragment-ktx = "1.8.9"
freedroidwarn = "V1.10"
gradle = "9.1.0"
jackson-core = "2.21.2"
kotlin = "2.3.20"
okhttp = "5.3.2"
picasso = "2.71828"
rxandroid = "3.0.2"
rxjava = "3.1.12"
vectordrawable = "1.2.0"
viewpager2 = "1.1.0"
[libraries]
core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" }
fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "fragment-ktx" }
freedroidwarn = { group = "com.github.woheller69", name = "FreeDroidWarn", version.ref = "freedroidwarn" }
jackson-core = { group = "com.fasterxml.jackson.core", name = "jackson-core", version.ref = "jackson-core" }
kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlin" }
okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" }
picasso = { group = "com.squareup.picasso", name = "picasso", version.ref = "picasso" }
rxandroid = { group = "io.reactivex.rxjava3", name = "rxandroid", version.ref = "rxandroid" }
rxjava = { group = "io.reactivex.rxjava3", name = "rxjava", version.ref = "rxjava" }
vectordrawable = { group = "androidx.vectordrawable", name = "vectordrawable", version.ref = "vectordrawable" }
viewpager2 = { group = "androidx.viewpager2", name = "viewpager2", version.ref = "viewpager2" }
[plugins]
android-application = { id = "com.android.application", version.ref = "gradle" }
+1 -1
View File
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-3
View File
@@ -1,3 +0,0 @@
* Allow to sort by date added and last update
* Display more compatibility information for packages
* Drop AppCompat and reduce APK file size
-3
View File
@@ -1,3 +0,0 @@
* Persist sorting order across application restarts
* Display Open Collective donation links
* Rework text strings
-4
View File
@@ -1,4 +0,0 @@
* Full text search
* Search results ranking
* Allow to view packages per repository
* Automatic day/night theme for Android 10
+6
View File
@@ -0,0 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended"
]
}
-5
View File
@@ -1,5 +0,0 @@
plugins {
id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0'
}
rootProject.name = "michas-droid"
include ':'
+24
View File
@@ -0,0 +1,24 @@
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
}
@Suppress("UnstableApiUsage")
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
}
rootProject.name = "michas-droid"
include(":")
@@ -16,10 +16,12 @@ object Preferences {
private val subject = PublishSubject.create<Key<*>>()
private val keys = sequenceOf(Key.AutoSync, Key.IncompatibleVersions, Key.ProxyHost, Key.ProxyPort, Key.ProxyType,
Key.SortOrder, Key.Theme, Key.UpdateNotify, Key.UpdateUnstable).map { Pair(it.name, it) }.toMap()
private val keys = sequenceOf(
Key.AutoSync, Key.IncompatibleVersions, Key.ProxyHost, Key.ProxyPort, Key.ProxyType,
Key.SortOrder, Key.Theme, Key.UpdateNotify, Key.UpdateUnstable
).associateBy { it.name }
private val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, keyString ->
private val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, keyString ->
keys[keyString]?.let(subject::onNext)
}
@@ -89,6 +89,12 @@ data class Repository(
"Michachatz official repository. Everything in this repository is always built from the source code.",
21, true, "3546DCBDD900F280EE2161CC163C1156BE2C2F3EB810415115039E0C7D3242C0", ""
),
if (isFdroid) null else defaultRepository(
"https://repo.dgplayser.duckdns.org/fdroid/archive",
"Michachatz F-Droid Repo",
"Michachatz official repository. Everything in this repository is always built from the source code.",
21, true, "3546DCBDD900F280EE2161CC163C1156BE2C2F3EB810415115039E0C7D3242C0 ", ""
),
defaultRepository(
"https://f-droid.org/repo",
"F-Droid",
@@ -39,7 +39,7 @@ open class DrawableWrapper(val drawable: Drawable): Drawable() {
}
override fun getAlpha(): Int {
return DrawableCompat.getAlpha(drawable)
return drawable.alpha
}
override fun setAlpha(alpha: Int) {
@@ -74,5 +74,6 @@ open class DrawableWrapper(val drawable: Drawable): Drawable() {
DrawableCompat.setHotspotBounds(drawable, left, top, right, bottom)
}
@Suppress("DEPRECATION", "OVERRIDE_DEPRECATION")
override fun getOpacity(): Int = drawable.opacity
}
@@ -260,6 +260,6 @@ class IndexHandler(private val repositoryId: Long, private val callback: Callbac
override fun characters(ch: CharArray, start: Int, length: Int) {
super.characters(ch, start, length)
contentBuilder.append(ch, start, length)
contentBuilder.appendRange(ch, start, start + length)
}
}
@@ -35,6 +35,7 @@ import android.widget.ProgressBar
import android.widget.Switch
import android.widget.TextView
import android.widget.Toast
import androidx.core.content.ContextCompat.getString
import androidx.core.graphics.ColorUtils
import androidx.core.text.HtmlCompat
import androidx.core.text.util.LinkifyCompat
@@ -362,10 +363,10 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
}
}
@SuppressLint("ClickableViewAccessibility")
private open class OverlappingViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
init {
// Block touch events if touched above negative margin
@SuppressLint("ClickableViewAccessibility")
itemView.setOnTouchListener { v, event ->
val top = (v.layoutParams as ViewGroup.MarginLayoutParams).topMargin
event.action == MotionEvent.ACTION_DOWN && top < 0 && event.y < -top
@@ -384,8 +385,7 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
init {
val margin = measurement.invalidate(itemView.resources) {
@SuppressLint("SetTextI18n")
text.text = "measure"
text.text = itemView.context.getString(R.string.measure)
link.visibility = View.GONE
measurement.measure(itemView)
((itemView.measuredHeight - icon.measuredHeight) / 2f).roundToInt()
@@ -407,8 +407,7 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
init {
val margin = measurement.invalidate(itemView.resources) {
@SuppressLint("SetTextI18n")
text.text = "measure"
text.text = itemView.context.getString(R.string.measure)
measurement.measure(itemView)
((itemView.measuredHeight - icon.measuredHeight) / 2f).roundToInt()
}
@@ -148,8 +148,8 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
.flatMapSingle { products -> RxUtils
.querySingle { signal -> Database.RepositoryAdapter.getAll(signal) }
.map { result ->
result.asSequence().map { Pair(it.id, it) }.toMap()
.let { map -> products.mapNotNull { product -> map[product.repositoryId]?.let { Pair(product, it) } } } } }
result.associateBy { it.id }
.let { map -> products.mapNotNull { product -> map[product.repositoryId]?.let { Pair(product, it) } } } } }
.flatMapSingle { products -> RxUtils
.querySingle { signal -> Nullable(Database.InstalledAdapter.get(packageName, signal)) }
.map { result -> Pair(products, result) } }
@@ -396,9 +396,9 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
override fun onScreenshotClick(screenshot: Product.Screenshot) {
val pair = products.asSequence()
.map { it -> Pair(it.second, it.first.screenshots.find { it === screenshot }?.identifier) }
.filter { it.second != null }.firstOrNull()
if (pair != null) {
.map { it -> Pair(it.second, it.first.screenshots.find { it === screenshot }?.identifier) }
.firstOrNull { it.second != null }
if (pair != null) {
val (repository, identifier) = pair
if (identifier != null) {
ScreenshotsFragment(packageName, repository.id, identifier).show(childFragmentManager)
@@ -420,8 +420,9 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
MessageDialog(MessageDialog.Message.ReleaseSignatureMismatch).show(childFragmentManager)
}
else -> {
val productRepository = products.asSequence().filter { it -> it.first.releases.any { it === release } }.firstOrNull()
if (productRepository != null) {
val productRepository =
products.firstOrNull { it -> it.first.releases.any { it === release } }
if (productRepository != null) {
downloadConnection.binder?.enqueue(packageName, productRepository.first.name,
productRepository.second, release)
}
@@ -196,6 +196,12 @@ class ProductsAdapter(private val onClick: (ProductItem) -> Unit):
}
}
override fun areContentsTheSame(oldCursor: Cursor, newCursor: Cursor): Boolean {
val oldItem = Database.ProductAdapter.transformItem(oldCursor)
val newItem = Database.ProductAdapter.transformItem(newCursor)
return oldItem == newItem
}
@SuppressLint("NotifyDataSetChanged")
override fun onCursorChanged(oldCursor: Cursor?, newCursor: Cursor?) {
val oldSize = oldCursor?.count ?: 0
@@ -14,6 +14,7 @@ import androidx.core.os.BundleCompat
import androidx.core.view.WindowCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import com.michatec.store.BuildConfig
import com.michatec.store.R
import com.michatec.store.content.Cache
import com.michatec.store.content.Preferences
@@ -23,6 +24,7 @@ import com.michatec.store.utility.Utils
import com.michatec.store.utility.extension.android.*
import com.michatec.store.utility.extension.resources.*
import com.michatec.store.utility.extension.text.*
import org.woheller69.freeDroidWarn.FreeDroidWarn
abstract class ScreenActivity: FragmentActivity() {
companion object {
@@ -74,6 +76,8 @@ abstract class ScreenActivity: FragmentActivity() {
setTheme(Preferences[Preferences.Key.Theme].getResId(resources.configuration))
super.onCreate(savedInstanceState)
FreeDroidWarn.showWarningOnUpgrade(this, BuildConfig.VERSION_CODE)
WindowCompat.setDecorFitsSystemWindows(window, false)
addContentView(FrameLayout(this).apply { id = R.id.main_content },
ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT))
@@ -561,6 +561,7 @@ class TabsFragment: ScreenFragment() {
override fun setAlpha(alpha: Int) = Unit
override fun setColorFilter(colorFilter: ColorFilter?) = Unit
@Deprecated("Deprecated in Java")
override fun getOpacity(): Int = PixelFormat.TRANSLUCENT
}
@@ -19,11 +19,11 @@ class Connection<B: IBinder, S: ConnectionService<B>>(private val serviceClass:
}
}
@Suppress("UNCHECKED_CAST")
override fun onServiceConnected(componentName: ComponentName, binder: IBinder) {
@Suppress("UNCHECKED_CAST")
binder as B
this.binder = binder
onBind?.invoke(this, binder)
val b = binder as B
this.binder = b
onBind?.invoke(this, b)
}
override fun onServiceDisconnected(componentName: ComponentName) {
@@ -331,7 +331,7 @@ class SyncService: ConnectionService<SyncService.Binder>() {
currentTask = null
handleNextTask(false)
val blocked = updateNotificationBlockerFragment?.get()?.isAdded == true
if (!blocked && result != null && result.isNotEmpty()) {
if (!blocked && !result.isNullOrEmpty()) {
displayUpdatesNotification(result)
}
}
@@ -12,7 +12,7 @@ import okhttp3.Response
object RxUtils {
private class ManagedDisposable(private val cancel: () -> Unit): Disposable {
@Volatile var disposed = false
var disposed = false
override fun isDisposed(): Boolean = disposed
override fun dispose() {
@@ -1,7 +1,6 @@
package com.michatec.store.utility
import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.Signature
import android.content.res.Configuration
@@ -67,23 +66,18 @@ object Utils {
}
}
@SuppressLint("SuspiciousIndentation")
fun configureLocale(context: Context): Context {
val supportedLanguages = BuildConfig.LANGUAGES.toSet()
val configuration = context.resources.configuration
val currentLocales = if (Android.sdk(24)) {
val localesList = configuration.locales
(0 until localesList.size()).map(localesList::get)
} else {
@Suppress("DEPRECATION")
listOf(configuration.locale)
}
val localesList = configuration.locales
val currentLocales = (0 until localesList.size()).map(localesList::get)
val compatibleLocales = currentLocales
.filter { it.language in supportedLanguages }
.let { it.ifEmpty { listOf(Locale.US) } }
Locale.setDefault(compatibleLocales.first())
val newConfiguration = Configuration(configuration)
newConfiguration.setLocales(LocaleList(*compatibleLocales.toTypedArray()))
val newConfiguration = Configuration(configuration).apply {
setLocales(LocaleList(*compatibleLocales.toTypedArray()))
}
return context.createConfigurationContext(newConfiguration)
}
@@ -33,9 +33,8 @@ abstract class CursorRecyclerAdapter<VT: Enum<VT>, VH: RecyclerView.ViewHolder>:
return
}
// Further reduced threshold to 100 for DiffUtil to avoid any noticeable frame drops on the main thread.
// JSON parsing and DB access during diffing are slow.
if (oldSize > 100 || newSize > 100) {
// Increased threshold for DiffUtil and optimized callback
if (oldSize > 500 || newSize > 500) {
notifyDataSetChanged()
return
}
@@ -60,7 +59,6 @@ abstract class CursorRecyclerAdapter<VT: Enum<VT>, VH: RecyclerView.ViewHolder>:
})
diffResult.dispatchUpdatesTo(this)
} catch (_: Exception) {
// Fallback in case of cursor issues during diffing
notifyDataSetChanged()
}
}
@@ -12,11 +12,4 @@ class FragmentLinearLayout: LinearLayout {
init {
fitsSystemWindows = true
}
@Suppress("unused")
var percentTranslationY: Float
get() = height.let { if (it > 0) translationY / it else 0f }
set(value) {
translationY = value * height
}
}
+164
View File
@@ -0,0 +1,164 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="action_failed">Aktion fehlgeschlagen</string>
<string name="add_repository">Repository hinzufügen</string>
<string name="address">Adresse</string>
<string name="all_applications">Alle Anwendungen</string>
<string name="all_applications_up_to_date">Alle Anwendungen sind aktuell</string>
<string name="already_exists">Existiert bereits</string>
<string name="always">Immer</string>
<string name="anti_features">Anti-Funktionen</string>
<string name="application">Anwendung</string>
<string name="application_not_found">Anwendung nicht gefunden</string>
<string name="author_email">E-Mail des Autors</string>
<string name="author_website">Website des Autors</string>
<string name="available">Verfügbar</string>
<string name="bug_tracker">Fehlerverfolgung</string>
<string name="cancel">Abbrechen</string>
<string name="cant_edit_sync_DESC">Repository kann nicht bearbeitet werden, da es gerade synchronisiert wird.</string>
<string name="changelog">Änderungsprotokoll</string>
<string name="changes">Änderungen</string>
<string name="checking_repository">Repository wird überprüft</string>
<string name="compiled_for_debugging">Für Debugging kompiliert</string>
<string name="confirmation">Bestätigung</string>
<string name="connecting">Verbinden</string>
<string name="contains_non_free_media">Enthält nicht-freie Medien</string>
<string name="could_not_download_FORMAT">Konnte %s nicht herunterladen</string>
<string name="could_not_sync_FORMAT">Konnte %s nicht synchronisieren</string>
<string name="could_not_validate_FORMAT">Konnte %s nicht validieren</string>
<string name="dark">Dunkel</string>
<string name="date_added">Hinzugefügt am</string>
<string name="delete">Löschen</string>
<string name="delete_repository_DESC">Möchtest du das Repository wirklich löschen?</string>
<string name="description">Beschreibung</string>
<string name="details">Details</string>
<string name="donate">Spenden</string>
<string name="downloaded_FORMAT">Heruntergeladen: %s</string>
<string name="downloading">Wird heruntergeladen</string>
<string name="downloading_FORMAT">Lade %s herunter</string>
<string name="edit_repository">Repository bearbeiten</string>
<string name="file_format_error_DESC">Ungültiges Dateiformat.</string>
<string name="fingerprint">Fingerabdruck</string>
<string name="has_advertising">Enthält Werbung</string>
<string name="has_non_free_dependencies">Hat nicht-freie Abhängigkeiten</string>
<string name="has_security_vulnerabilities">Hat Sicherheitslücken</string>
<string name="http_error_DESC">Ungültige Serverantwort.</string>
<string name="http_proxy">HTTP-Proxy</string>
<string name="ignore_all_updates">Alle Updates ignorieren</string>
<string name="ignore_this_update">Dieses Update ignorieren</string>
<string name="incompatible_api_DESC_FORMAT">Dein %1$s (API-Version %2$d) wird nicht unterstützt. %3$s</string>
<string name="incompatible_api_max_DESC_FORMAT">Maximale API-Version ist %d.</string>
<string name="incompatible_api_min_DESC_FORMAT">Minimale API-Version ist %d.</string>
<string name="incompatible_features_DESC">Fehlende Funktionen.</string>
<string name="incompatible_older_DESC">Diese Version ist älter als die auf deinem Gerät installierte. Bitte deinstalliere diese zuerst.</string>
<string name="incompatible_platforms_DESC_FORMAT">Deine %1$s-Plattform wird nicht unterstützt. Unterstützte Plattformen: %2$s.</string>
<string name="incompatible_signature_DESC">Diese Version wurde mit einem anderen Zertifikat signiert als die auf deinem Gerät installierte. Bitte deinstalliere diese zuerst.</string>
<string name="incompatible_version">Inkompatible Version</string>
<string name="incompatible_versions">Inkompatible Versionen</string>
<string name="incompatible_versions_summary">Zeige Versionen, die nicht mit dem Gerät kompatibel sind</string>
<string name="incompatible_with_FORMAT">Nicht kompatibel mit %s</string>
<string name="install">Installieren</string>
<string name="installed">Installiert</string>
<string name="integrity_check_error_DESC">Integrität konnte nicht überprüft werden.</string>
<string name="invalid_address">Ungültige Adresse</string>
<string name="invalid_fingerprint_format">Ungültiges Fingerabdruckformat</string>
<string name="invalid_metadata_error_DESC">Ungültige Metadaten.</string>
<string name="invalid_permissions_error_DESC">Ungültige Berechtigungen.</string>
<string name="invalid_signature_error_DESC">Ungültige Signatur.</string>
<string name="invalid_username_format">Ungültiges Benutzername-Format</string>
<string name="last_update">Letztes Update</string>
<string name="launch">Starten</string>
<string name="license">Lizenz</string>
<string name="license_FORMAT">Lizenz %s</string>
<string name="light">Hell</string>
<string name="link_copied_to_clipboard">Link in die Zwischenablage kopiert</string>
<string name="links">Links</string>
<string name="merging_FORMAT">Füge %s zusammen</string>
<string name="name">Name</string>
<string name="network_error_DESC">Netzwerkfehler.</string>
<string name="never">Nie</string>
<string name="new_updates_available">Neue Updates verfügbar</string>
<plurals name="new_updates_DESC_FORMAT">
<item quantity="one">%d neues Update.</item>
<item quantity="other">%d neue Updates.</item>
</plurals>
<string name="no_applications_available">Keine Anwendungen verfügbar</string>
<string name="no_applications_installed">Keine Anwendungen installiert</string>
<string name="no_description_available_DESC">Keine Beschreibung verfügbar.</string>
<string name="no_matching_applications_found">Keine passenden Anwendungen gefunden</string>
<string name="no_proxy">Kein Proxy</string>
<string name="notify_about_updates">Über Updates benachrichtigen</string>
<string name="notify_about_updates_summary">Benachrichtigung anzeigen, wenn Updates verfügbar sind</string>
<string name="number_of_applications">Anzahl der Anwendungen</string>
<string name="ok">OK</string>
<string name="only_compatible_with_FORMAT">Nur kompatibel mit %s</string>
<string name="only_on_wifi">Nur über WLAN</string>
<string name="open_DESC_FORMAT">%s öffnen?</string>
<string name="other">Andere</string>
<string name="parsing_index_error_DESC">Indexdatei konnte nicht verarbeitet werden.</string>
<string name="password">Passwort</string>
<string name="password_missing">Passwort fehlt</string>
<string name="permissions">Berechtigungen</string>
<string name="plus_more_FORMAT">+%d mehr</string>
<string name="preferences">Einstellungen</string>
<string name="processing_FORMAT">Verarbeite %1$s</string>
<string name="project_website">Projekt-Website</string>
<string name="promotes_non_free_network_services">Fördert nicht-freie Netzwerkdienste</string>
<string name="promotes_non_free_software">Fördert nicht-freie Software</string>
<string name="provided_by_FORMAT">Bereitgestellt von %s</string>
<string name="proxy">Proxy</string>
<string name="proxy_host">Proxy-Host</string>
<string name="proxy_port">Proxy-Port</string>
<string name="proxy_type">Proxy-Typ</string>
<string name="repositories">Repositories</string>
<string name="repository">Repository</string>
<string name="repository_not_used_DESC">Dieses Repository wurde noch nicht verwendet. Du musst es aktivieren, um die Anwendungen zu sehen.</string>
<string name="repository_unsigned_DESC">Nicht signiert. Die Anwendungsliste konnte nicht verifiziert werden. Sei vorsichtig beim Herunterladen.</string>
<string name="requires_FORMAT">Benötigt %s</string>
<string name="save">Speichern</string>
<string name="saving_details">Speichere Details</string>
<string name="screenshots">Screenshots</string>
<string name="search">Suchen</string>
<string name="select_mirror">Mirror auswählen</string>
<string name="show_more">Mehr anzeigen</string>
<string name="show_older_versions">Ältere Versionen anzeigen</string>
<string name="signature_FORMAT">Signatur %s</string>
<string name="signed_using_unsafe_algorithm">Mit unsicherem Algorithmus signiert</string>
<string name="skip">Überspringen</string>
<string name="socks_proxy">SOCKS-Proxy</string>
<string name="sorting_order">Sortierreihenfolge</string>
<string name="source_code">Quellcode</string>
<string name="source_code_no_longer_available">Quellcode nicht mehr verfügbar</string>
<string name="suggested">Empfohlen</string>
<string name="sync_repositories">Repositories synchronisieren</string>
<string name="sync_repositories_automatically">Repositories automatisch synchronisieren</string>
<string name="syncing">Synchronisiere</string>
<string name="syncing_FORMAT">Synchronisiere %s</string>
<string name="system">System</string>
<string name="tap_to_install_DESC">Tippen zum Installieren.</string>
<string name="theme">Design</string>
<string name="tracks_or_reports_your_activity">Verfolgt oder meldet deine Aktivitäten</string>
<string name="uninstall">Deinstallieren</string>
<string name="unknown">Unbekannt</string>
<string name="unknown_error_DESC">Unbekannter Fehler.</string>
<string name="unknown_FORMAT">Unbekannt: %s</string>
<string name="unsigned">Nicht signiert</string>
<string name="unstable_updates">Instabile Updates</string>
<string name="unstable_updates_summary">Instabile Versionen vorschlagen</string>
<string name="unverified">Nicht verifiziert</string>
<string name="update">Aktualisieren</string>
<string name="updates">Updates</string>
<string name="upstream_source_code_is_not_free">Quellcode ist nicht frei verfügbar</string>
<string name="username">Benutzername</string>
<string name="username_missing">Benutzername fehlt</string>
<string name="validation_index_error_DESC">Index konnte nicht validiert werden.</string>
<string name="version_FORMAT">Version %s</string>
<string name="versions">Versionen</string>
<string name="waiting_to_start_download">Warte auf Start des Downloads</string>
<string name="website">Website</string>
<string name="address_redirect_FORMAT">Die Repository-Adresse wurde zu %s weitergeleitet. Möchtest du diese verwenden?</string>
<string name="credits">Credits an Michatec</string>
<string name="credits_to_fork">Dies ist ein Fork von github.com/kitsunyan/foxy-droid.</string>
</resources>
+1
View File
@@ -166,5 +166,6 @@
<string name="address_redirect_FORMAT">The repository address was redirected to %s. Do you want to use it instead?</string>
<string name="credits">Credits to Michatec</string>
<string name="credits_to_fork">This is a fork from github.com/kitsunyan/foxy-droid.</string>
<string name="measure" translatable="false">measure</string>
</resources>