Android 11 support
This commit is contained in:
parent
25dbcca854
commit
d2f11c85d1
@ -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"
|
||||
|
||||
|
@ -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" />
|
||||
|
@ -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,27 +111,31 @@ class SelectPathFragment: Fragment() {
|
||||
refreshStatus(binding.editVolumeName.text)
|
||||
}
|
||||
binding.buttonPickDirectory.setOnClickListener {
|
||||
if (!requestManageAllFiles()) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (ContextCompat.checkSelfPermission(
|
||||
if (
|
||||
ContextCompat.checkSelfPermission(
|
||||
requireContext(),
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
) +
|
||||
ContextCompat.checkSelfPermission(
|
||||
) + ContextCompat.checkSelfPermission(
|
||||
requireContext(),
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
)
|
||||
) {
|
||||
launchPickDirectory()
|
||||
else
|
||||
} else {
|
||||
askStoragePermissions.launch(
|
||||
arrayOf(
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
)
|
||||
)
|
||||
} else
|
||||
}
|
||||
} else {
|
||||
launchPickDirectory()
|
||||
}
|
||||
}
|
||||
}
|
||||
binding.editVolumeName.addTextChangedListener(object: TextWatcher {
|
||||
override fun afterTextChanged(s: Editable?) {}
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
|
@ -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 {
|
||||
if (Environment.isExternalStorageRemovable(it)) {
|
||||
val rootPath = it.path.substring(0, it.path.indexOf(getPackageDataFolder(context)+"files"))
|
||||
if (!rootPath.endsWith("/0/")){ //not primary storage
|
||||
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")
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user