Browse Source

Get write storage access on Oreo

(cherry picked from commit 0b7880a425)
2.5.x
Geoffrey Métais 8 years ago
parent
commit
f52fadfa83
  1. 6
      vlc-android/src/org/videolan/vlc/gui/ContentActivity.java
  2. 22
      vlc-android/src/org/videolan/vlc/gui/audio/AudioBrowserFragment.java
  3. 21
      vlc-android/src/org/videolan/vlc/gui/browser/MediaBrowserFragment.java
  4. 44
      vlc-android/src/org/videolan/vlc/gui/helpers/hf/StoragePermissionsDelegate.java
  5. 9
      vlc-android/src/org/videolan/vlc/gui/video/VideoGridFragment.java
  6. 71
      vlc-android/src/org/videolan/vlc/util/Permissions.java

6
vlc-android/src/org/videolan/vlc/gui/ContentActivity.java

@ -186,4 +186,10 @@ public class ContentActivity extends AudioPlayerContainerActivity implements Sea
((Filterable) current).restoreList(); ((Filterable) current).restoreList();
} }
} }
public Runnable deleteAction;
public void onWriteAccessGranted() {
if (deleteAction != null) mActivityHandler.postDelayed(deleteAction, 500);
deleteAction = null;
}
} }

22
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); final MediaLibraryItem mediaItem = adapter.getItem(position);
if (id == R.id.audio_list_browser_delete) { 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 previous = position > 0 ? adapter.getItem(position-1) : null;
final MediaLibraryItem next = position < adapter.getItemCount()-1 ? adapter.getItem(position+1) : null; final MediaLibraryItem next = position < adapter.getItemCount()-1 ? adapter.getItem(position+1) : null;
String message; final String message;
Runnable action; final Runnable action;
final MediaLibraryItem separator = previous != null && previous.getItemType() == MediaLibraryItem.TYPE_DUMMY && final MediaLibraryItem separator = previous != null && previous.getItemType() == MediaLibraryItem.TYPE_DUMMY &&
(next == null || next.getItemType() == MediaLibraryItem.TYPE_DUMMY) ? previous : null; (next == null || next.getItemType() == MediaLibraryItem.TYPE_DUMMY) ? previous : null;
adapter.remove(mediaLibraryItem); adapter.remove(mediaItem);
if (separator != null) if (separator != null) adapter.remove(separator);
adapter.remove(separator);
if (mode == MODE_PLAYLIST) { if (mode == MODE_PLAYLIST) {
message = getString(R.string.playlist_deleted); message = getString(R.string.playlist_deleted);
action = new Runnable() { action = new Runnable() {
@Override @Override
public void run() { public void run() {
deletePlaylist((Playlist) mediaLibraryItem); deletePlaylist((Playlist) mediaItem);
} }
}; };
} else if (mode == MODE_SONG) { } else if (mode == MODE_SONG) {
@ -265,15 +263,21 @@ public class AudioBrowserFragment extends BaseAudioBrowser implements SwipeRefre
public void run() { public void run() {
if (separator != null) if (separator != null)
adapter.addItem(position-1, separator); adapter.addItem(position-1, separator);
adapter.addItem(position, mediaLibraryItem); adapter.addItem(position, mediaItem);
} }
}; };
action = new Runnable() { action = new Runnable() {
@Override @Override
public void run() { 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 } else
return false; return false;
UiTools.snackerWithCancel(getView(), message, action); UiTools.snackerWithCancel(getView(), message, action);

21
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.MediaParsingService;
import org.videolan.vlc.R; import org.videolan.vlc.R;
import org.videolan.vlc.VLCApplication; import org.videolan.vlc.VLCApplication;
import org.videolan.vlc.gui.ContentActivity;
import org.videolan.vlc.gui.InfoActivity; import org.videolan.vlc.gui.InfoActivity;
import org.videolan.vlc.gui.PlaybackServiceFragment; import org.videolan.vlc.gui.PlaybackServiceFragment;
import org.videolan.vlc.gui.helpers.UiTools; import org.videolan.vlc.gui.helpers.UiTools;
import org.videolan.vlc.gui.view.ContextMenuRecyclerView; import org.videolan.vlc.gui.view.ContextMenuRecyclerView;
import org.videolan.vlc.gui.view.SwipeRefreshLayout; import org.videolan.vlc.gui.view.SwipeRefreshLayout;
import org.videolan.vlc.util.AndroidDevices;
import org.videolan.vlc.util.FileUtils; import org.videolan.vlc.util.FileUtils;
import org.videolan.vlc.util.Permissions;
import java.util.LinkedList; import java.util.LinkedList;
@ -166,10 +169,8 @@ public abstract class MediaBrowserFragment extends PlaybackServiceFragment imple
@Override @Override
public boolean onContextItemSelected(MenuItem menu) { public boolean onContextItemSelected(MenuItem menu) {
if(!getUserVisibleHint()) if(!getUserVisibleHint()) return false;
return false; final ContextMenuRecyclerView.RecyclerContextMenuInfo info = (ContextMenuRecyclerView.RecyclerContextMenuInfo) menu.getMenuInfo();
ContextMenuRecyclerView.RecyclerContextMenuInfo info = (ContextMenuRecyclerView.RecyclerContextMenuInfo) menu.getMenuInfo();
return info != null && handleContextItemSelected(menu, info.position); return info != null && handleContextItemSelected(menu, info.position);
} }
@ -197,8 +198,7 @@ public abstract class MediaBrowserFragment extends PlaybackServiceFragment imple
return; return;
} }
if (mService != null) if (mService != null)
for (String path : mediaPaths) for (String path : mediaPaths) mService.removeLocation(path);
mService.removeLocation(path);
if (refresh) onRefresh(); 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) { private void onDeleteFailed(MediaWrapper media) {
final View v = getView(); final View v = getView();
if (v != null && isAdded()) UiTools.snacker(v, getString(R.string.msg_delete_failed, media.getTitle())); if (v != null && isAdded()) UiTools.snacker(v, getString(R.string.msg_delete_failed, media.getTitle()));

44
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.MediaParsingService;
import org.videolan.vlc.StartActivity; import org.videolan.vlc.StartActivity;
import org.videolan.vlc.VLCApplication; import org.videolan.vlc.VLCApplication;
import org.videolan.vlc.gui.ContentActivity;
import org.videolan.vlc.util.Permissions; import org.videolan.vlc.util.Permissions;
import static org.videolan.vlc.util.Permissions.PERMISSION_STORAGE_TAG;
import static org.videolan.vlc.util.Permissions.canReadStorage; import static org.videolan.vlc.util.Permissions.canReadStorage;
@TargetApi(Build.VERSION_CODES.LOLLIPOP) @TargetApi(Build.VERSION_CODES.LOLLIPOP)
@ -54,7 +54,7 @@ public class StoragePermissionsDelegate extends BaseHeadlessFragment {
public final static String TAG = "VLC/StorageHF"; public final static String TAG = "VLC/StorageHF";
private boolean mFirstRun, mUpgrade; private boolean mFirstRun, mUpgrade, mWrite;
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -65,23 +65,29 @@ public class StoragePermissionsDelegate extends BaseHeadlessFragment {
intent.removeExtra(StartActivity.EXTRA_UPGRADE); intent.removeExtra(StartActivity.EXTRA_UPGRADE);
intent.removeExtra(StartActivity.EXTRA_FIRST_RUN); intent.removeExtra(StartActivity.EXTRA_FIRST_RUN);
} }
mWrite = getArguments().getBoolean("write");
if (AndroidUtil.isMarshMallowOrLater && !canReadStorage(getActivity())) { if (AndroidUtil.isMarshMallowOrLater && !canReadStorage(getActivity())) {
if (shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE)) if (shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE))
Permissions.showStoragePermissionDialog(mActivity, false); Permissions.showStoragePermissionDialog(mActivity, false, false);
else 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() { private void requestStorageAccess(boolean write) {
requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, requestPermissions(new String[]{write ? Manifest.permission.WRITE_EXTERNAL_STORAGE : Manifest.permission.READ_EXTERNAL_STORAGE},
Permissions.PERMISSION_STORAGE_TAG); write ? Permissions.PERMISSION_WRITE_STORAGE_TAG : Permissions.PERMISSION_STORAGE_TAG);
} }
@Override @Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
switch (requestCode) { switch (requestCode) {
case PERMISSION_STORAGE_TAG: case Permissions.PERMISSION_STORAGE_TAG:
// If request is cancelled, the result arrays are empty. // If request is cancelled, the result arrays are empty.
final Context ctx = VLCApplication.getAppContext(); final Context ctx = VLCApplication.getAppContext();
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
@ -95,21 +101,29 @@ public class StoragePermissionsDelegate extends BaseHeadlessFragment {
} }
exit(); exit();
} else if (mActivity != null) { } else if (mActivity != null) {
Permissions.showStoragePermissionDialog(mActivity, false); Permissions.showStoragePermissionDialog(mActivity, false, mWrite);
if (!shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE)) if (!shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE))
exit(); exit();
} }
break; 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; if (activity.isFinishing()) return;
final FragmentManager fm = activity.getSupportFragmentManager(); final FragmentManager fm = activity.getSupportFragmentManager();
final Fragment fragment = fm.findFragmentByTag(TAG); Fragment fragment = fm.findFragmentByTag(TAG);
if (fragment == null) if (fragment == null) {
fm.beginTransaction().add(new StoragePermissionsDelegate(), TAG).commitAllowingStateLoss(); final Bundle args = new Bundle();
else args.putBoolean("write", write);
((StoragePermissionsDelegate)fragment).requestStorageAccess(); fragment = new StoragePermissionsDelegate();
fragment.setArguments(args);
fm.beginTransaction().add(fragment, TAG).commitAllowingStateLoss();
} else
((StoragePermissionsDelegate)fragment).requestStorageAccess(write);
} }
} }

9
vlc-android/src/org/videolan/vlc/gui/video/VideoGridFragment.java

@ -253,8 +253,7 @@ public class VideoGridFragment extends SortableFragment<VideoListAdapter> implem
} }
protected boolean handleContextItemSelected(MenuItem menu, final int position) { protected boolean handleContextItemSelected(MenuItem menu, final int position) {
if (position >= mAdapter.getItemCount()) if (position >= mAdapter.getItemCount()) return false;
return false;
final MediaWrapper media = mAdapter.getItem(position); final MediaWrapper media = mAdapter.getItem(position);
if (media == null) if (media == null)
return false; return false;
@ -292,6 +291,12 @@ public class VideoGridFragment extends SortableFragment<VideoListAdapter> implem
} }
private void removeVideo(final MediaWrapper media) { 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 int position = mAdapter.remove(media);
final View view = getView(); final View view = getView();
if (position != -1 && view != null) { if (position != -1 && view != null) {

71
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_STORAGE_TAG = 255;
public static final int PERMISSION_SETTINGS_TAG = 254; 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; public static final int PERMISSION_SYSTEM_RINGTONE = 42;
@ -84,18 +85,35 @@ public class Permissions {
Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; 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) { public static boolean checkReadStoragePermission(FragmentActivity activity, boolean exit) {
if (AndroidUtil.isMarshMallowOrLater && !canReadStorage(activity)) { if (AndroidUtil.isMarshMallowOrLater && !canReadStorage(activity)) {
if (ActivityCompat.shouldShowRequestPermissionRationale(activity, if (ActivityCompat.shouldShowRequestPermissionRationale(activity,
Manifest.permission.READ_EXTERNAL_STORAGE)) { Manifest.permission.READ_EXTERNAL_STORAGE)) {
showStoragePermissionDialog(activity, exit); showStoragePermissionDialog(activity, exit, false);
} else } else
requestStoragePermission(activity); requestStoragePermission(activity, false);
return false; return false;
} }
return true; 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) { public static void checkDrawOverlaysPermission(FragmentActivity activity) {
if (AndroidUtil.isMarshMallowOrLater && !canDrawOverlays(activity)) { if (AndroidUtil.isMarshMallowOrLater && !canDrawOverlays(activity)) {
showSettingsPermissionDialog(activity, PERMISSION_SYSTEM_DRAW_OVRLAYS); showSettingsPermissionDialog(activity, PERMISSION_SYSTEM_DRAW_OVRLAYS);
@ -103,29 +121,23 @@ public class Permissions {
} }
public static void checkWriteSettingsPermission(FragmentActivity activity, int mode) { public static void checkWriteSettingsPermission(FragmentActivity activity, int mode) {
if (!canWriteSettings(activity)) { if (!canWriteSettings(activity)) showSettingsPermissionDialog(activity, mode);
showSettingsPermissionDialog(activity, mode);
}
} }
private static Dialog sAlertDialog; private static Dialog sAlertDialog;
public static void showSettingsPermissionDialog(final FragmentActivity activity, int mode) { private static void showSettingsPermissionDialog(final FragmentActivity activity, int mode) {
if (activity.isFinishing() || (sAlertDialog != null && sAlertDialog.isShowing())) if (activity.isFinishing() || (sAlertDialog != null && sAlertDialog.isShowing())) return;
return;
sAlertDialog = createSettingsDialogCompat(activity, mode); sAlertDialog = createSettingsDialogCompat(activity, mode);
} }
public static void showStoragePermissionDialog(final FragmentActivity activity, boolean exit) { public static void showStoragePermissionDialog(final FragmentActivity activity, boolean exit, boolean write) {
if (activity.isFinishing() || (sAlertDialog != null && sAlertDialog.isShowing())) if (activity.isFinishing() || (sAlertDialog != null && sAlertDialog.isShowing())) return;
return; if (activity instanceof AppCompatActivity) sAlertDialog = createDialogCompat(activity, exit, write);
if (activity instanceof AppCompatActivity) else sAlertDialog = createDialog(activity, exit, write);
sAlertDialog = createDialogCompat(activity, exit);
else
sAlertDialog = createDialog(activity, exit);
} }
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) android.app.AlertDialog.Builder dialogBuilder = new android.app.AlertDialog.Builder(activity)
.setTitle(activity.getString(R.string.allow_storage_access_title)) .setTitle(activity.getString(R.string.allow_storage_access_title))
.setMessage(activity.getString(R.string.allow_storage_access_description)) .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() { .setPositiveButton(activity.getString(R.string.permission_ask_again), new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int whichButton) { 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)) if (!settings.getBoolean("user_declined_storage_access", false))
requestStoragePermission(activity); requestStoragePermission(activity, false);
else { else {
final Intent i = new Intent("android.settings.APPLICATION_DETAILS_SETTINGS"); final Intent i = new Intent("android.settings.APPLICATION_DETAILS_SETTINGS");
i.addCategory(Intent.CATEGORY_DEFAULT); i.addCategory(Intent.CATEGORY_DEFAULT);
@ -145,9 +157,9 @@ public class Permissions {
activity.startActivity(i); activity.startActivity(i);
} catch (Exception ignored) {} } catch (Exception ignored) {}
} }
SharedPreferences.Editor editor = settings.edit(); settings.edit()
editor.putBoolean("user_declined_storage_access", true); .putBoolean("user_declined_storage_access", true)
editor.apply(); .apply();
} }
}); });
if (exit) { if (exit) {
@ -162,7 +174,7 @@ public class Permissions {
return dialogBuilder.show(); 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) AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(activity)
.setTitle(activity.getString(R.string.allow_storage_access_title)) .setTitle(activity.getString(R.string.allow_storage_access_title))
.setMessage(activity.getString(R.string.allow_storage_access_description)) .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() { .setPositiveButton(activity.getString(R.string.permission_ask_again), new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int whichButton) { 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)) if (!settings.getBoolean("user_declined_storage_access", false))
requestStoragePermission(activity); requestStoragePermission(activity, false);
else { else {
Intent i = new Intent(); Intent i = new Intent();
i.setAction("android.settings.APPLICATION_DETAILS_SETTINGS"); i.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
@ -183,9 +195,9 @@ public class Permissions {
activity.startActivity(i); activity.startActivity(i);
} catch (Exception ignored) {} } catch (Exception ignored) {}
} }
SharedPreferences.Editor editor = settings.edit(); settings.edit()
editor.putBoolean("user_declined_storage_access", true); .putBoolean("user_declined_storage_access", true)
editor.apply(); .apply();
} }
}); });
if (exit) { if (exit) {
@ -240,8 +252,7 @@ public class Permissions {
return dialogBuilder.show(); return dialogBuilder.show();
} }
private static void requestStoragePermission(FragmentActivity activity) { private static void requestStoragePermission(FragmentActivity activity, boolean write) {
if (activity != null) if (activity != null) StoragePermissionsDelegate.askStoragePermission(activity, write);
StoragePermissionsDelegate.askStoragePermission(activity);
} }
} }

Loading…
Cancel
Save