Fix files from SD card not being deleted (#48)

This commit is contained in:
Bart Uliasz 2021-09-18 05:29:42 -04:00 committed by GitHub
parent ac0ad8ec64
commit b977cde1af
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 82 additions and 34 deletions

View file

@ -118,6 +118,7 @@
<activity android:name=".activities.DriveModeActivity" /> <activity android:name=".activities.DriveModeActivity" />
<activity android:name=".activities.PermissionActivity" /> <activity android:name=".activities.PermissionActivity" />
<activity android:name=".activities.LockScreenActivity" /> <activity android:name=".activities.LockScreenActivity" />
<activity android:name=".activities.saf.SAFRequestActivity" />
<activity <activity
android:name=".appshortcuts.AppShortcutLauncherActivity" android:name=".appshortcuts.AppShortcutLauncherActivity"

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2021 Bartlomiej Uliasz.
*
* Licensed under the GNU General Public License v3
*
* This is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*/
package io.github.muntashirakon.music.activities.saf
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import io.github.muntashirakon.music.activities.saf.SAFGuideActivity.REQUEST_CODE_SAF_GUIDE
import io.github.muntashirakon.music.util.SAFUtil
/** Created by buliasz on 2021-02-07. */
class SAFRequestActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val intent = Intent(this, SAFGuideActivity::class.java)
startActivityForResult(intent, REQUEST_CODE_SAF_GUIDE)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
super.onActivityResult(requestCode, resultCode, intent)
when (requestCode) {
REQUEST_CODE_SAF_GUIDE -> {
SAFUtil.openTreePicker(this)
}
SAFUtil.REQUEST_SAF_PICK_TREE -> {
if (resultCode == RESULT_OK) {
SAFUtil.saveTreeUri(this, intent)
}
finish()
}
}
}
}

View file

@ -477,8 +477,7 @@ object MusicUtil : KoinComponent {
val id: Int = cursor.getInt(0) val id: Int = cursor.getInt(0)
val name: String = cursor.getString(1) val name: String = cursor.getString(1)
try { // File.delete can throw a security exception try { // File.delete can throw a security exception
val f = File(name) if (SAFUtil.delete(context, name, null)) {
if (f.delete()) {
// Step 3: Remove selected track from the database // Step 3: Remove selected track from the database
context.contentResolver.delete( context.contentResolver.delete(
ContentUris.withAppendedId( ContentUris.withAppendedId(
@ -488,8 +487,6 @@ object MusicUtil : KoinComponent {
) )
deletedCount++ deletedCount++
} else { } else {
// I'm not sure if we'd ever get here (deletion would
// have to fail, but no exception thrown)
Log.e("MusicUtils", "Failed to delete file $name") Log.e("MusicUtils", "Failed to delete file $name")
} }
cursor.moveToNext() cursor.moveToNext()

View file

@ -14,6 +14,7 @@
package io.github.muntashirakon.music.util; package io.github.muntashirakon.music.util;
import android.annotation.SuppressLint;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
@ -30,6 +31,7 @@ import androidx.annotation.Nullable;
import androidx.documentfile.provider.DocumentFile; import androidx.documentfile.provider.DocumentFile;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import io.github.muntashirakon.music.R; import io.github.muntashirakon.music.R;
import io.github.muntashirakon.music.activities.saf.SAFRequestActivity;
import io.github.muntashirakon.music.model.Song; import io.github.muntashirakon.music.model.Song;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@ -50,7 +52,7 @@ public class SAFUtil {
public static final int REQUEST_SAF_PICK_TREE = 43; public static final int REQUEST_SAF_PICK_TREE = 43;
public static boolean isSAFRequired(File file) { public static boolean isSAFRequired(File file) {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && !file.canWrite(); return !file.canWrite();
} }
public static boolean isSAFRequired(String path) { public static boolean isSAFRequired(String path) {
@ -114,21 +116,21 @@ public class SAFUtil {
@TargetApi(Build.VERSION_CODES.KITKAT) @TargetApi(Build.VERSION_CODES.KITKAT)
public static void saveTreeUri(Context context, Intent data) { public static void saveTreeUri(Context context, Intent data) {
Uri uri = data.getData(); Uri uri = data.getData();
context if (uri != null) {
.getContentResolver() context.getContentResolver().takePersistableUriPermission(
.takePersistableUriPermission( uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION); PreferenceUtil.INSTANCE.setSafSdCardUri(uri.toString());
PreferenceUtil.INSTANCE.setSafSdCardUri(uri.toString()); }
} }
@TargetApi(Build.VERSION_CODES.LOLLIPOP) @TargetApi(Build.VERSION_CODES.LOLLIPOP)
public static boolean isTreeUriSaved(Context context) { public static boolean isTreeUriSaved() {
return !TextUtils.isEmpty(PreferenceUtil.INSTANCE.getSafSdCardUri()); return !TextUtils.isEmpty(PreferenceUtil.INSTANCE.getSafSdCardUri());
} }
@TargetApi(Build.VERSION_CODES.LOLLIPOP) @TargetApi(Build.VERSION_CODES.LOLLIPOP)
public static boolean isSDCardAccessGranted(Context context) { public static boolean isSDCardAccessGranted(Context context) {
if (!isTreeUriSaved(context)) return false; if (!isTreeUriSaved()) return false;
String sdcardUri = PreferenceUtil.INSTANCE.getSafSdCardUri(); String sdcardUri = PreferenceUtil.INSTANCE.getSafSdCardUri();
@ -152,6 +154,10 @@ public class SAFUtil {
*/ */
@Nullable @Nullable
public static Uri findDocument(DocumentFile dir, List<String> segments) { public static Uri findDocument(DocumentFile dir, List<String> segments) {
if (dir == null) {
return null;
}
for (DocumentFile file : dir.listFiles()) { for (DocumentFile file : dir.listFiles()) {
int index = segments.indexOf(file.getName()); int index = segments.indexOf(file.getName());
if (index == -1) { if (index == -1) {
@ -196,7 +202,7 @@ public class SAFUtil {
return; return;
} }
if (isTreeUriSaved(context)) { if (isTreeUriSaved()) {
List<String> pathSegments = List<String> pathSegments =
new ArrayList<>(Arrays.asList(audio.getFile().getAbsolutePath().split("/"))); new ArrayList<>(Arrays.asList(audio.getFile().getAbsolutePath().split("/")));
Uri sdcard = Uri.parse(PreferenceUtil.INSTANCE.getSafSdCardUri()); Uri sdcard = Uri.parse(PreferenceUtil.INSTANCE.getSafSdCardUri());
@ -235,6 +241,7 @@ public class SAFUtil {
fos.write(audioContent); fos.write(audioContent);
fos.close(); fos.close();
//noinspection ResultOfMethodCallIgnored
temp.delete(); temp.delete();
} catch (final Exception e) { } catch (final Exception e) {
Log.e(TAG, "writeSAF: Failed to write to file descriptor provided by SAF", e); Log.e(TAG, "writeSAF: Failed to write to file descriptor provided by SAF", e);
@ -245,58 +252,57 @@ public class SAFUtil {
} }
} }
public static void delete(Context context, String path, Uri safUri) { public static boolean delete(Context context, String path, Uri safUri) {
if (isSAFRequired(path)) { if (isSAFRequired(path)) {
deleteSAF(context, path, safUri); return deleteUsingSAF(context, path, safUri);
} else { } else {
try { try {
deleteFile(path); return new File(path).delete();
} catch (NullPointerException e) { } catch (NullPointerException e) {
Log.e("MusicUtils", "Failed to find file " + path); Log.e("MusicUtils", "Failed to find file " + path);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
return false;
} }
public static void deleteFile(String path) { @SuppressLint("StringFormatInvalid")
new File(path).delete();
}
@TargetApi(Build.VERSION_CODES.KITKAT) @TargetApi(Build.VERSION_CODES.KITKAT)
public static void deleteSAF(Context context, String path, Uri safUri) { private static boolean deleteUsingSAF(Context context, String path, Uri safUri) {
Uri uri = null;
if (context == null) { if (context == null) {
Log.e(TAG, "deleteSAF: context == null"); Log.e(TAG, "deleteSAF: context == null");
return; return false;
} }
if (isTreeUriSaved(context)) { if (safUri == null && isTreeUriSaved()) {
List<String> pathSegments = new ArrayList<>(Arrays.asList(path.split("/"))); List<String> pathSegments = new ArrayList<>(Arrays.asList(path.split("/")));
Uri sdcard = Uri.parse(PreferenceUtil.INSTANCE.getSafSdCardUri()); Uri sdcard = Uri.parse(PreferenceUtil.INSTANCE.getSafSdCardUri());
uri = findDocument(DocumentFile.fromTreeUri(context, sdcard), pathSegments); safUri = findDocument(DocumentFile.fromTreeUri(context, sdcard), pathSegments);
} }
if (uri == null) { if (safUri == null) {
uri = safUri; requestSAF(context);
}
if (uri == null) {
Log.e(TAG, "deleteSAF: Can't get SAF URI");
toast(context, context.getString(R.string.saf_error_uri)); toast(context, context.getString(R.string.saf_error_uri));
return; return false;
} }
try { try {
DocumentsContract.deleteDocument(context.getContentResolver(), uri); DocumentsContract.deleteDocument(context.getContentResolver(), safUri);
} catch (final Exception e) { } catch (final Exception e) {
Log.e(TAG, "deleteSAF: Failed to delete a file descriptor provided by SAF", e); Log.e(TAG, "deleteSAF: Failed to delete a file descriptor provided by SAF", e);
toast( toast(
context, context,
String.format(context.getString(R.string.saf_delete_failed), e.getLocalizedMessage())); String.format(context.getString(R.string.saf_delete_failed), e.getLocalizedMessage()));
return false;
} }
return true;
}
private static void requestSAF(Context context) {
Intent intent = new Intent(context, SAFRequestActivity.class);
context.startActivity(intent);
} }
private static void toast(final Context context, final String message) { private static void toast(final Context context, final String message) {