diff --git a/build.gradle b/build.gradle
index 4c8edca..8427e50 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,12 +1,12 @@
buildscript {
ext.versions = [
- android: '3.4.1',
- kotlin: '1.3.72'
+ android: '7.0.2',
+ kotlin: '1.5.30'
]
repositories {
+ mavenCentral()
google()
- jcenter()
}
dependencies {
@@ -19,14 +19,13 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
android {
- compileSdkVersion 29
- buildToolsVersion '29.0.3'
+ compileSdkVersion 31
defaultConfig {
archivesBaseName = 'foxy-droid'
applicationId 'nya.kitsunyan.foxydroid'
minSdkVersion 21
- targetSdkVersion 29
+ targetSdkVersion 31
versionCode 4
versionName '1.3'
@@ -111,18 +110,21 @@ android {
}
repositories {
+ mavenCentral()
google()
- jcenter()
+ maven { url 'https://jitpack.io' }
}
dependencies {
- implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:' + versions.kotlin
- implementation 'androidx.fragment:fragment:1.2.5'
+ implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:' + versions.kotlin
+ implementation 'androidx.fragment:fragment-ktx:1.3.6'
implementation 'androidx.viewpager2:viewpager2:1.0.0'
implementation 'androidx.vectordrawable:vectordrawable:1.1.0'
- implementation 'com.squareup.okhttp3:okhttp:4.7.2'
- implementation 'io.reactivex.rxjava3:rxjava:3.0.4'
+ implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.2'
+ implementation 'io.reactivex.rxjava3:rxjava:3.1.1'
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
- implementation 'com.fasterxml.jackson.core:jackson-core:2.11.0'
- implementation 'com.squareup.picasso:picasso:2.71828'
+ implementation 'io.reactivex.rxjava3:rxkotlin:3.0.1'
+ implementation 'com.fasterxml.jackson.core:jackson-core:2.12.5'
+ implementation 'com.squareup.picasso:picasso:2.8'
+ implementation 'com.github.topjohnwu.libsu:core:3.1.2'
}
diff --git a/gradle.properties b/gradle.properties
index 5bac8ac..e5635fe 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1 +1,4 @@
android.useAndroidX=true
+android.enableJetifier=true
+org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+org.gradle.parallel=true
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 558870d..892ac8c 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,6 @@
+#Wed Sep 15 23:07:46 CEST 2021
distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-bin.zip
-zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index 11237ad..cdf1c4d 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -9,6 +9,9 @@
+
+ android:name=".MainApplication$BootReceiver"
+ android:exported="false">
@@ -31,7 +35,8 @@
+ android:windowSoftInputMode="adjustResize"
+ android:exported="true">
diff --git a/src/main/kotlin/nya/kitsunyan/foxydroid/MainApplication.kt b/src/main/kotlin/nya/kitsunyan/foxydroid/MainApplication.kt
index 4a2de84..ef71399 100644
--- a/src/main/kotlin/nya/kitsunyan/foxydroid/MainApplication.kt
+++ b/src/main/kotlin/nya/kitsunyan/foxydroid/MainApplication.kt
@@ -9,31 +9,25 @@ import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
-import android.content.pm.PackageInfo
import com.squareup.picasso.OkHttp3Downloader
import com.squareup.picasso.Picasso
import nya.kitsunyan.foxydroid.content.Cache
import nya.kitsunyan.foxydroid.content.Preferences
import nya.kitsunyan.foxydroid.content.ProductPreferences
import nya.kitsunyan.foxydroid.database.Database
-import nya.kitsunyan.foxydroid.entity.InstalledItem
import nya.kitsunyan.foxydroid.index.RepositoryUpdater
import nya.kitsunyan.foxydroid.network.Downloader
import nya.kitsunyan.foxydroid.network.PicassoDownloader
import nya.kitsunyan.foxydroid.service.Connection
import nya.kitsunyan.foxydroid.service.SyncService
import nya.kitsunyan.foxydroid.utility.Utils
+import nya.kitsunyan.foxydroid.utility.Utils.toInstalledItem
import nya.kitsunyan.foxydroid.utility.extension.android.*
import java.net.InetSocketAddress
import java.net.Proxy
@Suppress("unused")
class MainApplication: Application() {
- private fun PackageInfo.toInstalledItem(): InstalledItem {
- val signatureString = singleSignature?.let(Utils::calculateHash).orEmpty()
- return InstalledItem(packageName, versionName.orEmpty(), versionCodeCompat, signatureString)
- }
-
override fun attachBaseContext(base: Context) {
super.attachBaseContext(Utils.configureLocale(base))
}
diff --git a/src/main/kotlin/nya/kitsunyan/foxydroid/content/Preferences.kt b/src/main/kotlin/nya/kitsunyan/foxydroid/content/Preferences.kt
index 470fe68..dc5b1ce 100644
--- a/src/main/kotlin/nya/kitsunyan/foxydroid/content/Preferences.kt
+++ b/src/main/kotlin/nya/kitsunyan/foxydroid/content/Preferences.kt
@@ -15,7 +15,7 @@ object Preferences {
private val subject = PublishSubject.create>()
- private val keys = sequenceOf(Key.AutoSync, Key.IncompatibleVersions, Key.ProxyHost, Key.ProxyPort, Key.ProxyType,
+ private val keys = sequenceOf(Key.AutoSync, Key.IncompatibleVersions, Key.RootInstallation, Key.ProxyHost, Key.ProxyPort, Key.ProxyType,
Key.SortOrder, Key.Theme, Key.UpdateNotify, Key.UpdateUnstable).map { Pair(it.name, it) }.toMap()
fun init(context: Context) {
@@ -82,6 +82,7 @@ object Preferences {
sealed class Key(val name: String, val default: Value) {
object AutoSync: Key("auto_sync", Value.EnumerationValue(Preferences.AutoSync.Wifi))
object IncompatibleVersions: Key("incompatible_versions", Value.BooleanValue(false))
+ object RootInstallation : Key("root_installation",Value.BooleanValue(false))
object ProxyHost: Key("proxy_host", Value.StringValue("localhost"))
object ProxyPort: Key("proxy_port", Value.IntValue(9050))
object ProxyType: Key("proxy_type", Value.EnumerationValue(Preferences.ProxyType.Direct))
diff --git a/src/main/kotlin/nya/kitsunyan/foxydroid/database/Database.kt b/src/main/kotlin/nya/kitsunyan/foxydroid/database/Database.kt
index 7b7a24d..ab61b31 100644
--- a/src/main/kotlin/nya/kitsunyan/foxydroid/database/Database.kt
+++ b/src/main/kotlin/nya/kitsunyan/foxydroid/database/Database.kt
@@ -17,12 +17,13 @@ import nya.kitsunyan.foxydroid.utility.extension.android.*
import nya.kitsunyan.foxydroid.utility.extension.json.*
import java.io.ByteArrayOutputStream
+// TODO migrate to Room
object Database {
fun init(context: Context): Boolean {
val helper = Helper(context)
db = helper.writableDatabase
if (helper.created) {
- for (repository in Repository.defaultRepositories) {
+ for (repository in Repository.defaultRepositories.sortedBy { it.name }) {
RepositoryAdapter.put(repository)
}
}
diff --git a/src/main/kotlin/nya/kitsunyan/foxydroid/entity/Product.kt b/src/main/kotlin/nya/kitsunyan/foxydroid/entity/Product.kt
index 133e9b5..4e8b0d7 100644
--- a/src/main/kotlin/nya/kitsunyan/foxydroid/entity/Product.kt
+++ b/src/main/kotlin/nya/kitsunyan/foxydroid/entity/Product.kt
@@ -129,7 +129,7 @@ data class Product(val repositoryId: Long, val packageName: String, val name: St
companion object {
fun findSuggested(products: List, installedItem: InstalledItem?, extract: (T) -> Product): T? {
- return products.maxWith(compareBy({ extract(it).compatible &&
+ return products.maxWithOrNull(compareBy({ extract(it).compatible &&
(installedItem == null || installedItem.signature in extract(it).signatures) }, { extract(it).versionCode }))
}
diff --git a/src/main/kotlin/nya/kitsunyan/foxydroid/entity/Repository.kt b/src/main/kotlin/nya/kitsunyan/foxydroid/entity/Repository.kt
index 6debcf0..0a5feb1 100644
--- a/src/main/kotlin/nya/kitsunyan/foxydroid/entity/Repository.kt
+++ b/src/main/kotlin/nya/kitsunyan/foxydroid/entity/Repository.kt
@@ -107,12 +107,104 @@ data class Repository(val id: Long, val address: String, val mirrors: List getString(R.string.dark)
}
}
+ addSwitch(Preferences.Key.RootInstallation, getString(R.string.root_installation),
+ getString(R.string.root_installation_summary))
addSwitch(Preferences.Key.IncompatibleVersions, getString(R.string.incompatible_versions),
getString(R.string.incompatible_versions_summary))
}
@@ -110,6 +113,7 @@ class PreferencesFragment: ScreenFragment() {
preferences[Preferences.Key.ProxyHost]?.setEnabled(enabled)
preferences[Preferences.Key.ProxyPort]?.setEnabled(enabled)
}
+ preferences[Preferences.Key.RootInstallation]?.setEnabled(Shell.getShell().isRoot)
if (key == Preferences.Key.Theme) {
requireActivity().recreate()
}
diff --git a/src/main/kotlin/nya/kitsunyan/foxydroid/screen/ProductFragment.kt b/src/main/kotlin/nya/kitsunyan/foxydroid/screen/ProductFragment.kt
index deb28c9..f359437 100644
--- a/src/main/kotlin/nya/kitsunyan/foxydroid/screen/ProductFragment.kt
+++ b/src/main/kotlin/nya/kitsunyan/foxydroid/screen/ProductFragment.kt
@@ -34,6 +34,7 @@ import nya.kitsunyan.foxydroid.service.Connection
import nya.kitsunyan.foxydroid.service.DownloadService
import nya.kitsunyan.foxydroid.utility.RxUtils
import nya.kitsunyan.foxydroid.utility.Utils
+import nya.kitsunyan.foxydroid.utility.Utils.startPackageInstaller
import nya.kitsunyan.foxydroid.utility.extension.android.*
import nya.kitsunyan.foxydroid.widget.DividerItemDecoration
@@ -55,7 +56,7 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
private enum class Action(val id: Int, val adapterAction: ProductAdapter.Action, val iconResId: Int) {
INSTALL(1, ProductAdapter.Action.INSTALL, R.drawable.ic_archive),
- UPDATE(2, ProductAdapter.Action.UPDATE, R.drawable.ic_archive),
+ UPDATE(2, ProductAdapter.Action.UPDATE, R.drawable.ic_update),
LAUNCH(3, ProductAdapter.Action.LAUNCH, R.drawable.ic_launch),
DETAILS(4, ProductAdapter.Action.DETAILS, R.drawable.ic_tune),
UNINSTALL(5, ProductAdapter.Action.UNINSTALL, R.drawable.ic_delete)
@@ -332,23 +333,7 @@ class ProductFragment(): ScreenFragment(), ProductAdapter.Callbacks {
ProductAdapter.Action.INSTALL,
ProductAdapter.Action.UPDATE -> {
val installedItem = installed?.installedItem
- val productRepository = Product.findSuggested(products, installedItem) { it.first }
- val compatibleReleases = productRepository?.first?.selectedReleases.orEmpty()
- .filter { installedItem == null || installedItem.signature == it.signature }
- val release = if (compatibleReleases.size >= 2) {
- compatibleReleases
- .filter { it.platforms.contains(Android.primaryPlatform) }
- .minBy { it.platforms.size }
- ?: compatibleReleases.minBy { it.platforms.size }
- ?: compatibleReleases.firstOrNull()
- } else {
- compatibleReleases.firstOrNull()
- }
- val binder = downloadConnection.binder
- if (productRepository != null && release != null && binder != null) {
- binder.enqueue(packageName, productRepository.first.name, productRepository.second, release)
- }
- Unit
+ Utils.startInstallUpdateAction(packageName, installedItem, products, downloadConnection)
}
ProductAdapter.Action.LAUNCH -> {
val launcherActivities = installed?.launcherActivities.orEmpty()
diff --git a/src/main/kotlin/nya/kitsunyan/foxydroid/screen/ProductsFragment.kt b/src/main/kotlin/nya/kitsunyan/foxydroid/screen/ProductsFragment.kt
index 7af8a19..1cca4ac 100644
--- a/src/main/kotlin/nya/kitsunyan/foxydroid/screen/ProductsFragment.kt
+++ b/src/main/kotlin/nya/kitsunyan/foxydroid/screen/ProductsFragment.kt
@@ -30,10 +30,10 @@ class ProductsFragment(): ScreenFragment(), CursorOwner.Callback {
private const val STATE_LAYOUT_MANAGER = "layoutManager"
}
- enum class Source(val titleResId: Int, val sections: Boolean, val order: Boolean) {
- AVAILABLE(R.string.available, true, true),
- INSTALLED(R.string.installed, false, false),
- UPDATES(R.string.updates, false, false)
+ enum class Source(val titleResId: Int, val sections: Boolean, val order: Boolean, val updates: Boolean) {
+ AVAILABLE(R.string.available, true, true, false),
+ INSTALLED(R.string.installed, false, false, false),
+ UPDATES(R.string.updates, false, false, true)
}
constructor(source: Source): this() {
diff --git a/src/main/kotlin/nya/kitsunyan/foxydroid/screen/ScreenActivity.kt b/src/main/kotlin/nya/kitsunyan/foxydroid/screen/ScreenActivity.kt
index 2466e1c..b75d00e 100644
--- a/src/main/kotlin/nya/kitsunyan/foxydroid/screen/ScreenActivity.kt
+++ b/src/main/kotlin/nya/kitsunyan/foxydroid/screen/ScreenActivity.kt
@@ -2,7 +2,6 @@ package nya.kitsunyan.foxydroid.screen
import android.content.Context
import android.content.Intent
-import android.net.Uri
import android.os.Bundle
import android.os.Parcel
import android.view.View
@@ -13,12 +12,11 @@ import android.widget.Toolbar
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import nya.kitsunyan.foxydroid.R
-import nya.kitsunyan.foxydroid.content.Cache
import nya.kitsunyan.foxydroid.content.Preferences
import nya.kitsunyan.foxydroid.database.CursorOwner
import nya.kitsunyan.foxydroid.utility.KParcelable
import nya.kitsunyan.foxydroid.utility.Utils
-import nya.kitsunyan.foxydroid.utility.extension.android.*
+import nya.kitsunyan.foxydroid.utility.Utils.startPackageInstaller
import nya.kitsunyan.foxydroid.utility.extension.resources.*
import nya.kitsunyan.foxydroid.utility.extension.text.*
@@ -204,11 +202,7 @@ abstract class ScreenActivity: FragmentActivity() {
is SpecialIntent.Install -> {
val packageName = specialIntent.packageName
if (!packageName.isNullOrEmpty()) {
- val fragment = currentFragment
- if (fragment !is ProductFragment || fragment.packageName != packageName) {
- pushFragment(ProductFragment(packageName))
- }
- specialIntent.cacheFileName?.let(::startPackageInstaller)
+ specialIntent.cacheFileName?.let { startPackageInstaller(it) }
}
Unit
}
@@ -229,18 +223,6 @@ abstract class ScreenActivity: FragmentActivity() {
}
}
- internal fun startPackageInstaller(cacheFileName: String) {
- val (uri, flags) = if (Android.sdk(24)) {
- Pair(Cache.getReleaseUri(this, cacheFileName), Intent.FLAG_GRANT_READ_URI_PERMISSION)
- } else {
- Pair(Uri.fromFile(Cache.getReleaseFile(this, cacheFileName)), 0)
- }
- // TODO Handle deprecation
- @Suppress("DEPRECATION")
- startActivity(Intent(Intent.ACTION_INSTALL_PACKAGE)
- .setDataAndType(uri, "application/vnd.android.package-archive").setFlags(flags))
- }
-
internal fun navigateProduct(packageName: String) = pushFragment(ProductFragment(packageName))
internal fun navigateRepositories() = pushFragment(RepositoriesFragment())
internal fun navigatePreferences() = pushFragment(PreferencesFragment())
diff --git a/src/main/kotlin/nya/kitsunyan/foxydroid/screen/TabsFragment.kt b/src/main/kotlin/nya/kitsunyan/foxydroid/screen/TabsFragment.kt
index 61a2b6a..f973d77 100644
--- a/src/main/kotlin/nya/kitsunyan/foxydroid/screen/TabsFragment.kt
+++ b/src/main/kotlin/nya/kitsunyan/foxydroid/screen/TabsFragment.kt
@@ -1,7 +1,9 @@
package nya.kitsunyan.foxydroid.screen
import android.animation.ValueAnimator
+import android.app.AlertDialog
import android.content.Context
+import android.content.DialogInterface
import android.content.res.ColorStateList
import android.graphics.Canvas
import android.graphics.ColorFilter
@@ -16,13 +18,9 @@ import android.view.View
import android.view.ViewGroup
import android.view.animation.AccelerateInterpolator
import android.view.animation.DecelerateInterpolator
-import android.widget.FrameLayout
-import android.widget.ImageView
-import android.widget.LinearLayout
-import android.widget.SearchView
-import android.widget.TextView
-import android.widget.Toolbar
+import android.widget.*
import androidx.fragment.app.Fragment
+import androidx.fragment.app.findFragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.adapter.FragmentStateAdapter
@@ -33,6 +31,7 @@ import io.reactivex.rxjava3.disposables.Disposable
import io.reactivex.rxjava3.schedulers.Schedulers
import nya.kitsunyan.foxydroid.R
import nya.kitsunyan.foxydroid.content.Preferences
+import nya.kitsunyan.foxydroid.database.CursorOwner
import nya.kitsunyan.foxydroid.database.Database
import nya.kitsunyan.foxydroid.entity.ProductItem
import nya.kitsunyan.foxydroid.service.Connection
@@ -61,6 +60,7 @@ class TabsFragment: ScreenFragment() {
val sectionChange = view.findViewById(R.id.section_change)!!
val sectionName = view.findViewById(R.id.section_name)!!
val sectionIcon = view.findViewById(R.id.section_icon)!!
+ val updateAll = view.findViewById