Android 11 support

This commit is contained in:
Matéo Duparc 2023-02-01 19:08:14 +01:00
parent 25dbcca854
commit d2f11c85d1
Signed by: hardcoresushi
GPG Key ID: AFE384344A45E13A
6 changed files with 99 additions and 40 deletions

View File

@ -27,7 +27,7 @@ android {
applicationId "sushi.hardcore.droidfs"
minSdkVersion 21
//noinspection ExpiredTargetSdkVersion
targetSdkVersion 29
targetSdkVersion 30
versionCode 29
versionName "2.0.0-alpha2"
@ -88,18 +88,18 @@ dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation 'androidx.core:core-ktx:1.9.0'
implementation "androidx.appcompat:appcompat:1.5.1"
implementation "androidx.appcompat:appcompat:1.6.0"
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1"
implementation "androidx.sqlite:sqlite-ktx:2.2.0"
implementation "androidx.sqlite:sqlite-ktx:2.3.0"
implementation "androidx.preference:preference-ktx:1.2.0"
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
implementation 'com.google.android.material:material:1.6.1'
implementation 'com.google.android.material:material:1.8.0'
implementation "com.github.bumptech.glide:glide:4.13.2"
implementation "androidx.biometric:biometric-ktx:1.2.0-alpha05"
def exoplayer_version = "2.18.1"
def exoplayer_version = "2.18.2"
implementation "com.google.android.exoplayer:exoplayer-core:$exoplayer_version"
implementation "com.google.android.exoplayer:exoplayer-ui:$exoplayer_version"

View File

@ -7,6 +7,7 @@
android:name="${applicationId}.WRITE_TEMPORARY_STORAGE"
android:protectionLevel="signature" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.CAMERA" />

View File

@ -2,11 +2,14 @@ package sushi.hardcore.droidfs.add_volume
import android.Manifest
import android.annotation.SuppressLint
import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.provider.Settings
import android.text.Editable
import android.text.TextWatcher
import android.view.LayoutInflater
@ -77,6 +80,16 @@ class SelectPathFragment: Fragment() {
return binding.root
}
private fun requestManageAllFiles(): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (!Environment.isExternalStorageManager()) {
startActivity(Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION, Uri.parse("package:"+requireContext().packageName)))
return true
}
}
return false
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
sharedPrefs = PreferenceManager.getDefaultSharedPreferences(requireContext())
originalRememberVolume = sharedPrefs.getBoolean(ConstValues.REMEMBER_VOLUME_KEY, true)
@ -98,26 +111,30 @@ class SelectPathFragment: Fragment() {
refreshStatus(binding.editVolumeName.text)
}
binding.buttonPickDirectory.setOnClickListener {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(
requireContext(),
Manifest.permission.READ_EXTERNAL_STORAGE
) +
ContextCompat.checkSelfPermission(
requireContext(),
Manifest.permission.WRITE_EXTERNAL_STORAGE
) == PackageManager.PERMISSION_GRANTED
)
launchPickDirectory()
else
askStoragePermissions.launch(
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
if (!requestManageAllFiles()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (
ContextCompat.checkSelfPermission(
requireContext(),
Manifest.permission.READ_EXTERNAL_STORAGE
) + ContextCompat.checkSelfPermission(
requireContext(),
Manifest.permission.WRITE_EXTERNAL_STORAGE
) == PackageManager.PERMISSION_GRANTED
) {
launchPickDirectory()
} else {
askStoragePermissions.launch(
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
)
)
} else
launchPickDirectory()
}
} else {
launchPickDirectory()
}
}
}
binding.editVolumeName.addTextChangedListener(object: TextWatcher {
override fun afterTextChanged(s: Editable?) {}

View File

@ -3,9 +3,11 @@ package sushi.hardcore.droidfs.util
import android.content.ActivityNotFoundException
import android.content.Context
import android.net.Uri
import android.os.Environment
import android.os.storage.StorageManager
import android.provider.DocumentsContract
import android.provider.OpenableColumns
import android.util.Log
import androidx.activity.result.ActivityResultLauncher
import androidx.core.content.ContextCompat
import sushi.hardcore.droidfs.R
@ -18,6 +20,7 @@ import kotlin.math.pow
object PathUtils {
const val SEPARATOR = '/'
const val PATH_RESOLVER_TAG = "PATH RESOLVER"
fun getParentPath(path: String): String {
val strippedPath = if (path.endsWith(SEPARATOR)) {
@ -95,11 +98,44 @@ object PathUtils {
return "Android/data/${context.packageName}/"
}
private fun getExternalStoragePath(context: Context): List<String> {
private fun getExternalStoragePath(context: Context, name: String): String? {
for (dir in ContextCompat.getExternalFilesDirs(context, null)) {
Log.d(PATH_RESOLVER_TAG, "External dir: $dir")
if (Environment.isExternalStorageRemovable(dir)) {
Log.d(PATH_RESOLVER_TAG, "isExternalStorageRemovable")
val path = dir.path.split("/Android")[0]
if (File(path).name == name) {
return path
}
}
}
Log.d(PATH_RESOLVER_TAG, "getExternalFilesDirs failed")
try {
val process = ProcessBuilder("mount").redirectErrorStream(true).start().apply { waitFor() }
process.inputStream.readBytes().decodeToString().split("\n").forEach { line ->
if (line.startsWith("/dev/block/vold")) {
Log.d(PATH_RESOLVER_TAG, "mount: $line")
val fields = line.split(" ")
if (fields.size >= 3) {
val path = fields[2]
if (File(path).name == name) {
return path
}
}
}
}
} catch (e: Exception) {
e.printStackTrace()
}
Log.d(PATH_RESOLVER_TAG, "mount processing failed")
return null
}
private fun getExternalStoragesPaths(context: Context): List<String> {
val externalPaths: MutableList<String> = ArrayList()
ContextCompat.getExternalFilesDirs(context, null).forEach {
val rootPath = it.path.substring(0, it.path.indexOf(getPackageDataFolder(context)+"files"))
if (!rootPath.endsWith("/0/")){ //not primary storage
if (Environment.isExternalStorageRemovable(it)) {
val rootPath = it.path.substring(0, it.path.indexOf(getPackageDataFolder(context)+"files"))
externalPaths.add(rootPath)
}
}
@ -107,7 +143,7 @@ object PathUtils {
}
fun isPathOnExternalStorage(path: String, context: Context): Boolean {
getExternalStoragePath(context).forEach {
getExternalStoragesPaths(context).forEach {
if (path.startsWith(it)){
return true
}
@ -116,18 +152,23 @@ object PathUtils {
}
private const val PRIMARY_VOLUME_NAME = "primary"
fun getFullPathFromTreeUri(treeUri: Uri?, context: Context): String? {
if (treeUri == null) return null
fun getFullPathFromTreeUri(treeUri: Uri, context: Context): String? {
if ("content".equals(treeUri.scheme, ignoreCase = true)) {
val vId = getVolumeIdFromTreeUri(treeUri)
var volumePath = getVolumePath(vId, context) ?: return null
if (volumePath.endsWith(File.separator))
volumePath = volumePath.substring(0, volumePath.length - 1)
var documentPath = getDocumentPathFromTreeUri(treeUri)
if (documentPath!!.endsWith(File.separator))
documentPath = documentPath.substring(0, documentPath.length - 1)
Log.d(PATH_RESOLVER_TAG, "Volume Id: $vId")
var volumePath = getVolumePath(vId ?: return null, context)
Log.d(PATH_RESOLVER_TAG, "Volume Path: $volumePath")
if (volumePath == null) {
volumePath = if (vId == "primary") {
Environment.getExternalStorageDirectory().path
} else {
getExternalStoragePath(context, vId) ?: "/storage/$vId"
}
}
val documentPath = getDocumentPathFromTreeUri(treeUri)!!
Log.d(PATH_RESOLVER_TAG, "Document Path: $documentPath")
return if (documentPath.isNotEmpty()) {
pathJoin(volumePath, documentPath)
pathJoin(volumePath!!, documentPath)
} else volumePath
} else if ("file".equals(treeUri.scheme, ignoreCase = true)) {
return treeUri.path
@ -135,7 +176,7 @@ object PathUtils {
return null
}
private fun getVolumePath(volumeId: String?, context: Context): String? {
private fun getVolumePath(volumeId: String, context: Context): String? {
return try {
val mStorageManager = context.getSystemService(Context.STORAGE_SERVICE) as StorageManager
val storageVolumeClazz = Class.forName("android.os.storage.StorageVolume")

View File

@ -1,11 +1,11 @@
buildscript {
ext.kotlin_version = "1.7.10"
ext.kotlin_version = "1.7.21"
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.3.0'
classpath 'com.android.tools.build:gradle:7.4.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}

View File

@ -1,6 +1,6 @@
#Wed Sep 01 11:25:55 UTC 2021
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME