10 Commits

Author SHA1 Message Date
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
25 changed files with 101 additions and 179 deletions
+1
View File
@@ -12,3 +12,4 @@
!/proguard.pro !/proguard.pro
!/README.md !/README.md
!/src !/src
/gradle/gradle-daemon-jvm.properties
+13
View File
@@ -1,9 +1,22 @@
# Michas Droid # 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. Yet another F-Droid client.
[![Release](https://img.shields.io/github/v/release/michatec/michas-droid)](https://github.com/michatec/michas-droid/releases/latest) [![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 ## Description
Unofficial F-Droid client in the style of the classic one. Unofficial F-Droid client in the style of the classic one.
+16 -13
View File
@@ -2,11 +2,12 @@ buildscript {
repositories { repositories {
google() google()
mavenCentral() mavenCentral()
maven { url 'https://jitpack.io' }
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:9.1.0' classpath libs.gradle
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:2.3.10' classpath libs.kotlin.gradle.plugin
} }
} }
@@ -20,8 +21,8 @@ android {
applicationId 'com.michatec.store' applicationId 'com.michatec.store'
minSdk 30 minSdk 30
targetSdk 36 targetSdk 36
versionCode 16 versionCode 17
versionName '1.6' versionName '1.7'
def languages = [ 'en' ] def languages = [ 'en' ]
buildConfigField 'String[]', 'LANGUAGES', '{ "' + languages.join('", "') + '" }' buildConfigField 'String[]', 'LANGUAGES', '{ "' + languages.join('", "') + '" }'
@@ -95,16 +96,18 @@ android {
repositories { repositories {
google() google()
mavenCentral() mavenCentral()
maven { url 'https://jitpack.io' }
} }
dependencies { dependencies {
implementation 'org.jetbrains.kotlin:kotlin-stdlib:2.3.10' implementation libs.kotlin.stdlib
implementation 'androidx.fragment:fragment-ktx:1.8.9' implementation libs.fragment.ktx
implementation 'androidx.viewpager2:viewpager2:1.1.0' implementation libs.viewpager2
implementation 'androidx.vectordrawable:vectordrawable:1.2.0' implementation libs.vectordrawable
implementation 'com.squareup.okhttp3:okhttp:5.3.2' implementation libs.okhttp
implementation 'io.reactivex.rxjava3:rxjava:3.1.12' implementation libs.rxjava
implementation 'io.reactivex.rxjava3:rxandroid:3.0.2' implementation libs.rxandroid
implementation 'com.fasterxml.jackson.core:jackson-core:2.21.1' implementation libs.jackson.core
implementation 'com.squareup.picasso:picasso:2.71828' 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

+30
View File
@@ -0,0 +1,30 @@
[versions]
fragment-ktx = "1.8.9"
freedroidwarn = "V1.10"
gradle = "9.1.0"
gradle-toolchains-foojay-resolver-convention = "1.0.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]
fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "fragment-ktx" }
freedroidwarn = { group = "com.github.woheller69", name = "FreeDroidWarn", version.ref = "freedroidwarn" }
gradle = { group = "com.android.tools.build", name = "gradle", version.ref = "gradle" }
jackson-core = { group = "com.fasterxml.jackson.core", name = "jackson-core", version.ref = "jackson-core" }
kotlin-gradle-plugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" }
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]
foojay = { id = "org.gradle.toolchains.foojay-resolver-convention", version.ref = "gradle-toolchains-foojay-resolver-convention" }
+1 -1
View File
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists 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 zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists 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
+1 -1
View File
@@ -1,5 +1,5 @@
plugins { plugins {
id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' id "org.gradle.toolchains.foojay-resolver-convention" version "1.0.0"
} }
rootProject.name = "michas-droid" rootProject.name = "michas-droid"
include ':' include ':'
@@ -16,10 +16,12 @@ object Preferences {
private val subject = PublishSubject.create<Key<*>>() private val subject = PublishSubject.create<Key<*>>()
private val keys = sequenceOf(Key.AutoSync, Key.IncompatibleVersions, Key.ProxyHost, Key.ProxyPort, Key.ProxyType, private val keys = sequenceOf(
Key.SortOrder, Key.Theme, Key.UpdateNotify, Key.UpdateUnstable).map { Pair(it.name, it) }.toMap() 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) keys[keyString]?.let(subject::onNext)
} }
@@ -74,5 +74,6 @@ open class DrawableWrapper(val drawable: Drawable): Drawable() {
DrawableCompat.setHotspotBounds(drawable, left, top, right, bottom) DrawableCompat.setHotspotBounds(drawable, left, top, right, bottom)
} }
@Suppress("DEPRECATION", "OVERRIDE_DEPRECATION")
override fun getOpacity(): Int = drawable.opacity 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) { override fun characters(ch: CharArray, start: Int, length: Int) {
super.characters(ch, start, length) super.characters(ch, start, length)
contentBuilder.append(ch, start, length) contentBuilder.appendRange(ch, start, start + length)
} }
} }
@@ -362,10 +362,10 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
} }
} }
@SuppressLint("ClickableViewAccessibility")
private open class OverlappingViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) { private open class OverlappingViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
init { init {
// Block touch events if touched above negative margin // Block touch events if touched above negative margin
@SuppressLint("ClickableViewAccessibility")
itemView.setOnTouchListener { v, event -> itemView.setOnTouchListener { v, event ->
val top = (v.layoutParams as ViewGroup.MarginLayoutParams).topMargin val top = (v.layoutParams as ViewGroup.MarginLayoutParams).topMargin
event.action == MotionEvent.ACTION_DOWN && top < 0 && event.y < -top event.action == MotionEvent.ACTION_DOWN && top < 0 && event.y < -top
@@ -373,6 +373,7 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
} }
} }
@SuppressLint("SetTextI18n")
private class LinkViewHolder(itemView: View): OverlappingViewHolder(itemView) { private class LinkViewHolder(itemView: View): OverlappingViewHolder(itemView) {
companion object { companion object {
private val measurement = Measurement<Int>() private val measurement = Measurement<Int>()
@@ -384,7 +385,6 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
init { init {
val margin = measurement.invalidate(itemView.resources) { val margin = measurement.invalidate(itemView.resources) {
@SuppressLint("SetTextI18n")
text.text = "measure" text.text = "measure"
link.visibility = View.GONE link.visibility = View.GONE
measurement.measure(itemView) measurement.measure(itemView)
@@ -397,6 +397,7 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
} }
} }
@SuppressLint("SetTextI18n")
private class PermissionsViewHolder(itemView: View): OverlappingViewHolder(itemView) { private class PermissionsViewHolder(itemView: View): OverlappingViewHolder(itemView) {
companion object { companion object {
private val measurement = Measurement<Int>() private val measurement = Measurement<Int>()
@@ -407,7 +408,6 @@ class ProductAdapter(private val callbacks: Callbacks, private val columns: Int)
init { init {
val margin = measurement.invalidate(itemView.resources) { val margin = measurement.invalidate(itemView.resources) {
@SuppressLint("SetTextI18n")
text.text = "measure" text.text = "measure"
measurement.measure(itemView) measurement.measure(itemView)
((itemView.measuredHeight - icon.measuredHeight) / 2f).roundToInt() ((itemView.measuredHeight - icon.measuredHeight) / 2f).roundToInt()
@@ -148,8 +148,8 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
.flatMapSingle { products -> RxUtils .flatMapSingle { products -> RxUtils
.querySingle { signal -> Database.RepositoryAdapter.getAll(signal) } .querySingle { signal -> Database.RepositoryAdapter.getAll(signal) }
.map { result -> .map { result ->
result.asSequence().map { Pair(it.id, it) }.toMap() result.associateBy { it.id }
.let { map -> products.mapNotNull { product -> map[product.repositoryId]?.let { Pair(product, it) } } } } } .let { map -> products.mapNotNull { product -> map[product.repositoryId]?.let { Pair(product, it) } } } } }
.flatMapSingle { products -> RxUtils .flatMapSingle { products -> RxUtils
.querySingle { signal -> Nullable(Database.InstalledAdapter.get(packageName, signal)) } .querySingle { signal -> Nullable(Database.InstalledAdapter.get(packageName, signal)) }
.map { result -> Pair(products, result) } } .map { result -> Pair(products, result) } }
@@ -396,9 +396,9 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
override fun onScreenshotClick(screenshot: Product.Screenshot) { override fun onScreenshotClick(screenshot: Product.Screenshot) {
val pair = products.asSequence() val pair = products.asSequence()
.map { it -> Pair(it.second, it.first.screenshots.find { it === screenshot }?.identifier) } .map { it -> Pair(it.second, it.first.screenshots.find { it === screenshot }?.identifier) }
.filter { it.second != null }.firstOrNull() .firstOrNull { it.second != null }
if (pair != null) { if (pair != null) {
val (repository, identifier) = pair val (repository, identifier) = pair
if (identifier != null) { if (identifier != null) {
ScreenshotsFragment(packageName, repository.id, identifier).show(childFragmentManager) ScreenshotsFragment(packageName, repository.id, identifier).show(childFragmentManager)
@@ -420,8 +420,9 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
MessageDialog(MessageDialog.Message.ReleaseSignatureMismatch).show(childFragmentManager) MessageDialog(MessageDialog.Message.ReleaseSignatureMismatch).show(childFragmentManager)
} }
else -> { else -> {
val productRepository = products.asSequence().filter { it -> it.first.releases.any { it === release } }.firstOrNull() val productRepository =
if (productRepository != null) { products.firstOrNull { it -> it.first.releases.any { it === release } }
if (productRepository != null) {
downloadConnection.binder?.enqueue(packageName, productRepository.first.name, downloadConnection.binder?.enqueue(packageName, productRepository.first.name,
productRepository.second, release) 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") @SuppressLint("NotifyDataSetChanged")
override fun onCursorChanged(oldCursor: Cursor?, newCursor: Cursor?) { override fun onCursorChanged(oldCursor: Cursor?, newCursor: Cursor?) {
val oldSize = oldCursor?.count ?: 0 val oldSize = oldCursor?.count ?: 0
@@ -14,6 +14,7 @@ import androidx.core.os.BundleCompat
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import com.michatec.store.BuildConfig
import com.michatec.store.R import com.michatec.store.R
import com.michatec.store.content.Cache import com.michatec.store.content.Cache
import com.michatec.store.content.Preferences 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.android.*
import com.michatec.store.utility.extension.resources.* import com.michatec.store.utility.extension.resources.*
import com.michatec.store.utility.extension.text.* import com.michatec.store.utility.extension.text.*
import org.woheller69.freeDroidWarn.FreeDroidWarn
abstract class ScreenActivity: FragmentActivity() { abstract class ScreenActivity: FragmentActivity() {
companion object { companion object {
@@ -74,6 +76,8 @@ abstract class ScreenActivity: FragmentActivity() {
setTheme(Preferences[Preferences.Key.Theme].getResId(resources.configuration)) setTheme(Preferences[Preferences.Key.Theme].getResId(resources.configuration))
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
FreeDroidWarn.showWarningOnUpgrade(this, BuildConfig.VERSION_CODE)
WindowCompat.setDecorFitsSystemWindows(window, false) WindowCompat.setDecorFitsSystemWindows(window, false)
addContentView(FrameLayout(this).apply { id = R.id.main_content }, addContentView(FrameLayout(this).apply { id = R.id.main_content },
ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)) 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 setAlpha(alpha: Int) = Unit
override fun setColorFilter(colorFilter: ColorFilter?) = Unit override fun setColorFilter(colorFilter: ColorFilter?) = Unit
@Deprecated("Deprecated in Java")
override fun getOpacity(): Int = PixelFormat.TRANSLUCENT 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) { override fun onServiceConnected(componentName: ComponentName, binder: IBinder) {
@Suppress("UNCHECKED_CAST") val b = binder as B
binder as B this.binder = b
this.binder = binder onBind?.invoke(this, b)
onBind?.invoke(this, binder)
} }
override fun onServiceDisconnected(componentName: ComponentName) { override fun onServiceDisconnected(componentName: ComponentName) {
@@ -331,7 +331,7 @@ class SyncService: ConnectionService<SyncService.Binder>() {
currentTask = null currentTask = null
handleNextTask(false) handleNextTask(false)
val blocked = updateNotificationBlockerFragment?.get()?.isAdded == true val blocked = updateNotificationBlockerFragment?.get()?.isAdded == true
if (!blocked && result != null && result.isNotEmpty()) { if (!blocked && !result.isNullOrEmpty()) {
displayUpdatesNotification(result) displayUpdatesNotification(result)
} }
} }
@@ -12,7 +12,7 @@ import okhttp3.Response
object RxUtils { object RxUtils {
private class ManagedDisposable(private val cancel: () -> Unit): Disposable { private class ManagedDisposable(private val cancel: () -> Unit): Disposable {
@Volatile var disposed = false var disposed = false
override fun isDisposed(): Boolean = disposed override fun isDisposed(): Boolean = disposed
override fun dispose() { override fun dispose() {
@@ -1,7 +1,6 @@
package com.michatec.store.utility package com.michatec.store.utility
import android.animation.ValueAnimator import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.pm.Signature import android.content.pm.Signature
import android.content.res.Configuration import android.content.res.Configuration
@@ -67,17 +66,11 @@ object Utils {
} }
} }
@SuppressLint("SuspiciousIndentation")
fun configureLocale(context: Context): Context { fun configureLocale(context: Context): Context {
val supportedLanguages = BuildConfig.LANGUAGES.toSet() val supportedLanguages = BuildConfig.LANGUAGES.toSet()
val configuration = context.resources.configuration val configuration = context.resources.configuration
val currentLocales = if (Android.sdk(24)) { val localesList = configuration.locales
val localesList = configuration.locales val currentLocales = (0 until localesList.size()).map(localesList::get)
(0 until localesList.size()).map(localesList::get)
} else {
@Suppress("DEPRECATION")
listOf(configuration.locale)
}
val compatibleLocales = currentLocales val compatibleLocales = currentLocales
.filter { it.language in supportedLanguages } .filter { it.language in supportedLanguages }
.let { it.ifEmpty { listOf(Locale.US) } } .let { it.ifEmpty { listOf(Locale.US) } }
@@ -33,9 +33,8 @@ abstract class CursorRecyclerAdapter<VT: Enum<VT>, VH: RecyclerView.ViewHolder>:
return return
} }
// Further reduced threshold to 100 for DiffUtil to avoid any noticeable frame drops on the main thread. // Increased threshold for DiffUtil and optimized callback
// JSON parsing and DB access during diffing are slow. if (oldSize > 500 || newSize > 500) {
if (oldSize > 100 || newSize > 100) {
notifyDataSetChanged() notifyDataSetChanged()
return return
} }
@@ -60,7 +59,6 @@ abstract class CursorRecyclerAdapter<VT: Enum<VT>, VH: RecyclerView.ViewHolder>:
}) })
diffResult.dispatchUpdatesTo(this) diffResult.dispatchUpdatesTo(this)
} catch (_: Exception) { } catch (_: Exception) {
// Fallback in case of cursor issues during diffing
notifyDataSetChanged() notifyDataSetChanged()
} }
} }
@@ -12,11 +12,4 @@ class FragmentLinearLayout: LinearLayout {
init { init {
fitsSystemWindows = true fitsSystemWindows = true
} }
@Suppress("unused")
var percentTranslationY: Float
get() = height.let { if (it > 0) translationY / it else 0f }
set(value) {
translationY = value * height
}
} }