From f52fadfa8343988d29dd7b9d46ea45400fd75c8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Geoffrey=20M=C3=A9tais?= Date: Tue, 23 Jan 2018 15:50:59 +0100 Subject: [PATCH] Get write storage access on Oreo (cherry picked from commit 0b7880a4257b08c4c5a0b1a94d9762354705c850) --- .../org/videolan/vlc/gui/ContentActivity.java | 6 ++ .../vlc/gui/audio/AudioBrowserFragment.java | 22 +++--- .../vlc/gui/browser/MediaBrowserFragment.java | 21 ++++-- .../hf/StoragePermissionsDelegate.java | 44 ++++++++---- .../vlc/gui/video/VideoGridFragment.java | 9 ++- .../org/videolan/vlc/util/Permissions.java | 71 +++++++++++-------- 6 files changed, 111 insertions(+), 62 deletions(-) diff --git a/vlc-android/src/org/videolan/vlc/gui/ContentActivity.java b/vlc-android/src/org/videolan/vlc/gui/ContentActivity.java index 5f0e6eab8..3e0bb4074 100644 --- a/vlc-android/src/org/videolan/vlc/gui/ContentActivity.java +++ b/vlc-android/src/org/videolan/vlc/gui/ContentActivity.java @@ -186,4 +186,10 @@ public class ContentActivity extends AudioPlayerContainerActivity implements Sea ((Filterable) current).restoreList(); } } + + public Runnable deleteAction; + public void onWriteAccessGranted() { + if (deleteAction != null) mActivityHandler.postDelayed(deleteAction, 500); + deleteAction = null; + } } diff --git a/vlc-android/src/org/videolan/vlc/gui/audio/AudioBrowserFragment.java b/vlc-android/src/org/videolan/vlc/gui/audio/AudioBrowserFragment.java index 2a354e627..496254994 100644 --- a/vlc-android/src/org/videolan/vlc/gui/audio/AudioBrowserFragment.java +++ b/vlc-android/src/org/videolan/vlc/gui/audio/AudioBrowserFragment.java @@ -239,23 +239,21 @@ public class AudioBrowserFragment extends BaseAudioBrowser implements SwipeRefre final MediaLibraryItem mediaItem = adapter.getItem(position); if (id == R.id.audio_list_browser_delete) { - final MediaLibraryItem mediaLibraryItem = adapter.getItem(position); final MediaLibraryItem previous = position > 0 ? adapter.getItem(position-1) : null; final MediaLibraryItem next = position < adapter.getItemCount()-1 ? adapter.getItem(position+1) : null; - String message; - Runnable action; + final String message; + final Runnable action; final MediaLibraryItem separator = previous != null && previous.getItemType() == MediaLibraryItem.TYPE_DUMMY && (next == null || next.getItemType() == MediaLibraryItem.TYPE_DUMMY) ? previous : null; - adapter.remove(mediaLibraryItem); - if (separator != null) - adapter.remove(separator); + adapter.remove(mediaItem); + if (separator != null) adapter.remove(separator); if (mode == MODE_PLAYLIST) { message = getString(R.string.playlist_deleted); action = new Runnable() { @Override public void run() { - deletePlaylist((Playlist) mediaLibraryItem); + deletePlaylist((Playlist) mediaItem); } }; } else if (mode == MODE_SONG) { @@ -265,15 +263,21 @@ public class AudioBrowserFragment extends BaseAudioBrowser implements SwipeRefre public void run() { if (separator != null) adapter.addItem(position-1, separator); - adapter.addItem(position, mediaLibraryItem); + adapter.addItem(position, mediaItem); } }; action = new Runnable() { @Override public void run() { - deleteMedia(mediaLibraryItem, true, cancel); + deleteMedia(mediaItem, true, cancel); } }; + if (!checkWritePermission((MediaWrapper) mediaItem, new Runnable() { + @Override + public void run() { + UiTools.snackerWithCancel(getView(), message, action, cancel); + } + })) return false; } else return false; UiTools.snackerWithCancel(getView(), message, action); diff --git a/vlc-android/src/org/videolan/vlc/gui/browser/MediaBrowserFragment.java b/vlc-android/src/org/videolan/vlc/gui/browser/MediaBrowserFragment.java index be5087c93..0a776577f 100644 --- a/vlc-android/src/org/videolan/vlc/gui/browser/MediaBrowserFragment.java +++ b/vlc-android/src/org/videolan/vlc/gui/browser/MediaBrowserFragment.java @@ -46,12 +46,15 @@ import org.videolan.medialibrary.media.MediaWrapper; import org.videolan.vlc.MediaParsingService; import org.videolan.vlc.R; import org.videolan.vlc.VLCApplication; +import org.videolan.vlc.gui.ContentActivity; import org.videolan.vlc.gui.InfoActivity; import org.videolan.vlc.gui.PlaybackServiceFragment; import org.videolan.vlc.gui.helpers.UiTools; import org.videolan.vlc.gui.view.ContextMenuRecyclerView; import org.videolan.vlc.gui.view.SwipeRefreshLayout; +import org.videolan.vlc.util.AndroidDevices; import org.videolan.vlc.util.FileUtils; +import org.videolan.vlc.util.Permissions; import java.util.LinkedList; @@ -166,10 +169,8 @@ public abstract class MediaBrowserFragment extends PlaybackServiceFragment imple @Override public boolean onContextItemSelected(MenuItem menu) { - if(!getUserVisibleHint()) - return false; - ContextMenuRecyclerView.RecyclerContextMenuInfo info = (ContextMenuRecyclerView.RecyclerContextMenuInfo) menu.getMenuInfo(); - + if(!getUserVisibleHint()) return false; + final ContextMenuRecyclerView.RecyclerContextMenuInfo info = (ContextMenuRecyclerView.RecyclerContextMenuInfo) menu.getMenuInfo(); return info != null && handleContextItemSelected(menu, info.position); } @@ -197,8 +198,7 @@ public abstract class MediaBrowserFragment extends PlaybackServiceFragment imple return; } if (mService != null) - for (String path : mediaPaths) - mService.removeLocation(path); + for (String path : mediaPaths) mService.removeLocation(path); if (refresh) onRefresh(); } }); @@ -207,6 +207,15 @@ public abstract class MediaBrowserFragment extends PlaybackServiceFragment imple }); } + protected boolean checkWritePermission(MediaWrapper media, Runnable callback) { + if (media.getUri().getPath().startsWith(AndroidDevices.EXTERNAL_PUBLIC_DIRECTORY) && !Permissions.canWriteStorage()) { + final ContentActivity activity = (ContentActivity) getActivity(); + activity.deleteAction = callback; + Permissions.askWriteStoragePermission(getActivity(), false); + return false; + } + return true; + } private void onDeleteFailed(MediaWrapper media) { final View v = getView(); if (v != null && isAdded()) UiTools.snacker(v, getString(R.string.msg_delete_failed, media.getTitle())); diff --git a/vlc-android/src/org/videolan/vlc/gui/helpers/hf/StoragePermissionsDelegate.java b/vlc-android/src/org/videolan/vlc/gui/helpers/hf/StoragePermissionsDelegate.java index 3cf5fef25..e2d558245 100644 --- a/vlc-android/src/org/videolan/vlc/gui/helpers/hf/StoragePermissionsDelegate.java +++ b/vlc-android/src/org/videolan/vlc/gui/helpers/hf/StoragePermissionsDelegate.java @@ -40,9 +40,9 @@ import org.videolan.libvlc.util.AndroidUtil; import org.videolan.vlc.MediaParsingService; import org.videolan.vlc.StartActivity; import org.videolan.vlc.VLCApplication; +import org.videolan.vlc.gui.ContentActivity; import org.videolan.vlc.util.Permissions; -import static org.videolan.vlc.util.Permissions.PERMISSION_STORAGE_TAG; import static org.videolan.vlc.util.Permissions.canReadStorage; @TargetApi(Build.VERSION_CODES.LOLLIPOP) @@ -54,7 +54,7 @@ public class StoragePermissionsDelegate extends BaseHeadlessFragment { public final static String TAG = "VLC/StorageHF"; - private boolean mFirstRun, mUpgrade; + private boolean mFirstRun, mUpgrade, mWrite; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -65,23 +65,29 @@ public class StoragePermissionsDelegate extends BaseHeadlessFragment { intent.removeExtra(StartActivity.EXTRA_UPGRADE); intent.removeExtra(StartActivity.EXTRA_FIRST_RUN); } + mWrite = getArguments().getBoolean("write"); if (AndroidUtil.isMarshMallowOrLater && !canReadStorage(getActivity())) { if (shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE)) - Permissions.showStoragePermissionDialog(mActivity, false); + Permissions.showStoragePermissionDialog(mActivity, false, false); else - requestStorageAccess(); + requestStorageAccess(false); + } else if (mWrite) { + if (shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE)) + Permissions.showStoragePermissionDialog(mActivity, false, true); + else + requestStorageAccess(true); } } - private void requestStorageAccess() { - requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, - Permissions.PERMISSION_STORAGE_TAG); + private void requestStorageAccess(boolean write) { + requestPermissions(new String[]{write ? Manifest.permission.WRITE_EXTERNAL_STORAGE : Manifest.permission.READ_EXTERNAL_STORAGE}, + write ? Permissions.PERMISSION_WRITE_STORAGE_TAG : Permissions.PERMISSION_STORAGE_TAG); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { switch (requestCode) { - case PERMISSION_STORAGE_TAG: + case Permissions.PERMISSION_STORAGE_TAG: // If request is cancelled, the result arrays are empty. final Context ctx = VLCApplication.getAppContext(); if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { @@ -95,21 +101,29 @@ public class StoragePermissionsDelegate extends BaseHeadlessFragment { } exit(); } else if (mActivity != null) { - Permissions.showStoragePermissionDialog(mActivity, false); + Permissions.showStoragePermissionDialog(mActivity, false, mWrite); if (!shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE)) exit(); } break; + case Permissions.PERMISSION_WRITE_STORAGE_TAG: + if (mActivity instanceof ContentActivity) ((ContentActivity) mActivity).onWriteAccessGranted(); + break; + } } - public static void askStoragePermission(@NonNull FragmentActivity activity) { + public static void askStoragePermission(@NonNull FragmentActivity activity, boolean write) { if (activity.isFinishing()) return; final FragmentManager fm = activity.getSupportFragmentManager(); - final Fragment fragment = fm.findFragmentByTag(TAG); - if (fragment == null) - fm.beginTransaction().add(new StoragePermissionsDelegate(), TAG).commitAllowingStateLoss(); - else - ((StoragePermissionsDelegate)fragment).requestStorageAccess(); + Fragment fragment = fm.findFragmentByTag(TAG); + if (fragment == null) { + final Bundle args = new Bundle(); + args.putBoolean("write", write); + fragment = new StoragePermissionsDelegate(); + fragment.setArguments(args); + fm.beginTransaction().add(fragment, TAG).commitAllowingStateLoss(); + } else + ((StoragePermissionsDelegate)fragment).requestStorageAccess(write); } } diff --git a/vlc-android/src/org/videolan/vlc/gui/video/VideoGridFragment.java b/vlc-android/src/org/videolan/vlc/gui/video/VideoGridFragment.java index d4f734646..6d639ae03 100644 --- a/vlc-android/src/org/videolan/vlc/gui/video/VideoGridFragment.java +++ b/vlc-android/src/org/videolan/vlc/gui/video/VideoGridFragment.java @@ -253,8 +253,7 @@ public class VideoGridFragment extends SortableFragment implem } protected boolean handleContextItemSelected(MenuItem menu, final int position) { - if (position >= mAdapter.getItemCount()) - return false; + if (position >= mAdapter.getItemCount()) return false; final MediaWrapper media = mAdapter.getItem(position); if (media == null) return false; @@ -292,6 +291,12 @@ public class VideoGridFragment extends SortableFragment implem } private void removeVideo(final MediaWrapper media) { + if (!checkWritePermission(media, new Runnable() { + @Override + public void run() { + removeVideo(media); + } + })) return; final int position = mAdapter.remove(media); final View view = getView(); if (position != -1 && view != null) { diff --git a/vlc-android/src/org/videolan/vlc/util/Permissions.java b/vlc-android/src/org/videolan/vlc/util/Permissions.java index c3a8e3f8c..3f1d157f6 100644 --- a/vlc-android/src/org/videolan/vlc/util/Permissions.java +++ b/vlc-android/src/org/videolan/vlc/util/Permissions.java @@ -51,6 +51,7 @@ public class Permissions { public static final int PERMISSION_STORAGE_TAG = 255; public static final int PERMISSION_SETTINGS_TAG = 254; + public static final int PERMISSION_WRITE_STORAGE_TAG = 253; public static final int PERMISSION_SYSTEM_RINGTONE = 42; @@ -84,18 +85,35 @@ public class Permissions { Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; } + public static boolean canWriteStorage() { + return canWriteStorage(VLCApplication.getAppContext()); + } + + public static boolean canWriteStorage(Context context) { + return ContextCompat.checkSelfPermission(context, + Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; + } + public static boolean checkReadStoragePermission(FragmentActivity activity, boolean exit) { if (AndroidUtil.isMarshMallowOrLater && !canReadStorage(activity)) { if (ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.READ_EXTERNAL_STORAGE)) { - showStoragePermissionDialog(activity, exit); + showStoragePermissionDialog(activity, exit, false); } else - requestStoragePermission(activity); + requestStoragePermission(activity, false); return false; } return true; } + public static void askWriteStoragePermission(FragmentActivity activity, boolean exit) { + if (ActivityCompat.shouldShowRequestPermissionRationale(activity, + Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + showStoragePermissionDialog(activity, exit, true); + } else + requestStoragePermission(activity, true); + } + public static void checkDrawOverlaysPermission(FragmentActivity activity) { if (AndroidUtil.isMarshMallowOrLater && !canDrawOverlays(activity)) { showSettingsPermissionDialog(activity, PERMISSION_SYSTEM_DRAW_OVRLAYS); @@ -103,29 +121,23 @@ public class Permissions { } public static void checkWriteSettingsPermission(FragmentActivity activity, int mode) { - if (!canWriteSettings(activity)) { - showSettingsPermissionDialog(activity, mode); - } + if (!canWriteSettings(activity)) showSettingsPermissionDialog(activity, mode); } private static Dialog sAlertDialog; - public static void showSettingsPermissionDialog(final FragmentActivity activity, int mode) { - if (activity.isFinishing() || (sAlertDialog != null && sAlertDialog.isShowing())) - return; + private static void showSettingsPermissionDialog(final FragmentActivity activity, int mode) { + if (activity.isFinishing() || (sAlertDialog != null && sAlertDialog.isShowing())) return; sAlertDialog = createSettingsDialogCompat(activity, mode); } - public static void showStoragePermissionDialog(final FragmentActivity activity, boolean exit) { - if (activity.isFinishing() || (sAlertDialog != null && sAlertDialog.isShowing())) - return; - if (activity instanceof AppCompatActivity) - sAlertDialog = createDialogCompat(activity, exit); - else - sAlertDialog = createDialog(activity, exit); + public static void showStoragePermissionDialog(final FragmentActivity activity, boolean exit, boolean write) { + if (activity.isFinishing() || (sAlertDialog != null && sAlertDialog.isShowing())) return; + if (activity instanceof AppCompatActivity) sAlertDialog = createDialogCompat(activity, exit, write); + else sAlertDialog = createDialog(activity, exit, write); } - private static Dialog createDialog(final FragmentActivity activity, boolean exit) { + private static Dialog createDialog(final FragmentActivity activity, boolean exit, boolean write) { android.app.AlertDialog.Builder dialogBuilder = new android.app.AlertDialog.Builder(activity) .setTitle(activity.getString(R.string.allow_storage_access_title)) .setMessage(activity.getString(R.string.allow_storage_access_description)) @@ -133,9 +145,9 @@ public class Permissions { .setPositiveButton(activity.getString(R.string.permission_ask_again), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int whichButton) { - SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(activity); + final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(activity); if (!settings.getBoolean("user_declined_storage_access", false)) - requestStoragePermission(activity); + requestStoragePermission(activity, false); else { final Intent i = new Intent("android.settings.APPLICATION_DETAILS_SETTINGS"); i.addCategory(Intent.CATEGORY_DEFAULT); @@ -145,9 +157,9 @@ public class Permissions { activity.startActivity(i); } catch (Exception ignored) {} } - SharedPreferences.Editor editor = settings.edit(); - editor.putBoolean("user_declined_storage_access", true); - editor.apply(); + settings.edit() + .putBoolean("user_declined_storage_access", true) + .apply(); } }); if (exit) { @@ -162,7 +174,7 @@ public class Permissions { return dialogBuilder.show(); } - private static Dialog createDialogCompat(final FragmentActivity activity, boolean exit) { + private static Dialog createDialogCompat(final FragmentActivity activity, boolean exit, boolean write) { AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(activity) .setTitle(activity.getString(R.string.allow_storage_access_title)) .setMessage(activity.getString(R.string.allow_storage_access_description)) @@ -170,9 +182,9 @@ public class Permissions { .setPositiveButton(activity.getString(R.string.permission_ask_again), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int whichButton) { - SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(activity); + final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(activity); if (!settings.getBoolean("user_declined_storage_access", false)) - requestStoragePermission(activity); + requestStoragePermission(activity, false); else { Intent i = new Intent(); i.setAction("android.settings.APPLICATION_DETAILS_SETTINGS"); @@ -183,9 +195,9 @@ public class Permissions { activity.startActivity(i); } catch (Exception ignored) {} } - SharedPreferences.Editor editor = settings.edit(); - editor.putBoolean("user_declined_storage_access", true); - editor.apply(); + settings.edit() + .putBoolean("user_declined_storage_access", true) + .apply(); } }); if (exit) { @@ -240,8 +252,7 @@ public class Permissions { return dialogBuilder.show(); } - private static void requestStoragePermission(FragmentActivity activity) { - if (activity != null) - StoragePermissionsDelegate.askStoragePermission(activity); + private static void requestStoragePermission(FragmentActivity activity, boolean write) { + if (activity != null) StoragePermissionsDelegate.askStoragePermission(activity, write); } }