diff --git a/app/build.gradle b/app/build.gradle
index 44083f1..967a430 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -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"
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 60e02a4..b1c31cd 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -7,6 +7,7 @@
android:name="${applicationId}.WRITE_TEMPORARY_STORAGE"
android:protectionLevel="signature" />
+
diff --git a/app/src/main/java/sushi/hardcore/droidfs/add_volume/SelectPathFragment.kt b/app/src/main/java/sushi/hardcore/droidfs/add_volume/SelectPathFragment.kt
index c9b8fdf..3927f2f 100644
--- a/app/src/main/java/sushi/hardcore/droidfs/add_volume/SelectPathFragment.kt
+++ b/app/src/main/java/sushi/hardcore/droidfs/add_volume/SelectPathFragment.kt
@@ -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?) {}
diff --git a/app/src/main/java/sushi/hardcore/droidfs/util/PathUtils.kt b/app/src/main/java/sushi/hardcore/droidfs/util/PathUtils.kt
index 6b54ad9..27c9936 100644
--- a/app/src/main/java/sushi/hardcore/droidfs/util/PathUtils.kt
+++ b/app/src/main/java/sushi/hardcore/droidfs/util/PathUtils.kt
@@ -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 {
+ 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 {
val externalPaths: MutableList = 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")
diff --git a/build.gradle b/build.gradle
index 53b347b..cd441b6 100644
--- a/build.gradle
+++ b/build.gradle
@@ -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"
}
}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 562920e..5fa549d 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -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