Skip to content
Snippets Groups Projects
Commit a1fa4a84 authored by Nicolas Pomepuy's avatar Nicolas Pomepuy
Browse files

Ask for the MANAGE_EXTERNAL_STORAGE permission on first launch

(cherry picked from commit 62897067)
parent 40ef97bc
No related branches found
No related tags found
1 merge request!1508Fix the video information legend attribute and crash
......@@ -4,6 +4,8 @@ import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.os.Environment
import android.view.View
import androidx.core.content.ContextCompat
import org.videolan.libvlc.util.AndroidUtil
......@@ -65,9 +67,14 @@ fun MediaLibraryItem.getTracksCount() = when (itemType) {
fun canReadStorage(context: Context): Boolean {
return !AndroidUtil.isMarshMallowOrLater || ContextCompat.checkSelfPermission(context,
Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED || isExternalStorageManager()
}
/**
* Check if the app has the [Manifest.permission.MANAGE_EXTERNAL_STORAGE] granted
*/
fun isExternalStorageManager(): Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && Environment.isExternalStorageManager()
@JvmOverloads
fun canWriteStorage(context: Context = AppContextProvider.appContext): Boolean {
return ContextCompat.checkSelfPermission(context,
......
......@@ -499,6 +499,8 @@
<string name="widget_default_text" translatable="false">VLC mini player</string>
<string name="allow_storage_access_title">Allow VLC to access video and audio files</string>
<string name="allow_storage_access_description">VLC needs you to grant this permission to access the media files on this device.</string>
<string name="allow_storage_manager_title">Allow VLC all file access</string>
<string name="allow_storage_manager_description">VLC needs you to grant this permission to access all your files on this device.\nGrant permission?</string>
<string name="allow_settings_access_ringtone_title">Allow VLC to set the ringtone</string>
<string name="allow_settings_access_ringtone_description">VLC needs you to grant this permission to set up this song as your ringtone.</string>
<string name="allow_settings_access_brightness_title">Allow VLC to change brightness mode</string>
......
package org.videolan.tools
import android.annotation.SuppressLint
import android.app.Activity
import android.app.ActivityManager
import android.app.ActivityManager.RunningAppProcessInfo
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.content.res.Resources
import android.net.ConnectivityManager
import android.net.Uri
......@@ -151,3 +155,15 @@ fun Uri?.removeQuery(): Uri? {
}
return null
}
/**
* Checks if the intent is callable
*
* @param context: the context to use to test the intent
* @return true if the intent is callable
*/
fun Intent.isCallable(context: Context): Boolean {
val list: List<ResolveInfo> = context.packageManager.queryIntentActivities(this,
PackageManager.MATCH_DEFAULT_ONLY)
return list.isNotEmpty()
}
\ No newline at end of file
......@@ -27,6 +27,7 @@ package org.videolan.vlc.gui.helpers.hf
import android.Manifest
import android.annotation.TargetApi
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
......@@ -43,12 +44,14 @@ import org.videolan.libvlc.util.AndroidUtil
import org.videolan.resources.AndroidDevices
import org.videolan.resources.EXTRA_FIRST_RUN
import org.videolan.resources.EXTRA_UPGRADE
import org.videolan.resources.util.isExternalStorageManager
import org.videolan.resources.util.startMedialibrary
import org.videolan.tools.INITIAL_PERMISSION_ASKED
import org.videolan.tools.Settings
import org.videolan.tools.isCallable
import org.videolan.tools.putSingle
import org.videolan.vlc.BuildConfig
import org.videolan.vlc.gui.onboarding.ONBOARDING_DONE_KEY
import org.videolan.vlc.gui.onboarding.OnboardingActivity
import org.videolan.vlc.util.FileUtils
import org.videolan.vlc.util.Permissions
import org.videolan.vlc.util.Permissions.canReadStorage
......@@ -104,10 +107,10 @@ class StoragePermissionsDelegate : BaseHeadlessFragment() {
return@registerForActivityResult
}
when (askedPermission) {
Permissions.PERMISSION_STORAGE_TAG -> {
Permissions.PERMISSION_STORAGE_TAG, Permissions.MANAGE_EXTERNAL_STORAGE -> {
// If request is cancelled, the result arrays are empty.
if(activity == null) return@registerForActivityResult
if (isGranted) {
if (isGranted || isExternalStorageManager()) {
storageAccessGranted.value = true
model.deferredGrant.complete(true)
exit()
......@@ -125,6 +128,22 @@ class StoragePermissionsDelegate : BaseHeadlessFragment() {
}
private fun requestStorageAccess(write: Boolean) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val uri = Uri.parse("package:${BuildConfig.APP_ID}")
val intent = Intent(android.provider.Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION, uri)
if (intent.isCallable(requireActivity())) {
Permissions.showExternalPermissionDialog(requireActivity()) { asked ->
if (asked) {
val code = android.provider.Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION
askedPermission = Permissions.MANAGE_EXTERNAL_STORAGE
timeAsked = System.currentTimeMillis()
activityResultLauncher.launch(code)
startActivity(intent)
}
}
return
}
}
val code = if (write) Manifest.permission.WRITE_EXTERNAL_STORAGE else Manifest.permission.READ_EXTERNAL_STORAGE
askedPermission = if (write) Permissions.PERMISSION_WRITE_STORAGE_TAG else Permissions.PERMISSION_STORAGE_TAG
timeAsked = System.currentTimeMillis()
......
......@@ -47,6 +47,7 @@ import org.videolan.libvlc.util.AndroidUtil
import org.videolan.medialibrary.interfaces.media.MediaWrapper
import org.videolan.resources.AndroidDevices
import org.videolan.resources.AppContextProvider
import org.videolan.resources.util.isExternalStorageManager
import org.videolan.tools.Settings
import org.videolan.tools.putSingle
import org.videolan.vlc.R
......@@ -60,6 +61,7 @@ object Permissions {
const val PERMISSION_STORAGE_TAG = 255
const val PERMISSION_SETTINGS_TAG = 254
const val PERMISSION_WRITE_STORAGE_TAG = 253
const val MANAGE_EXTERNAL_STORAGE = 256
const val PERMISSION_SYSTEM_RINGTONE = 42
......@@ -84,7 +86,7 @@ object Permissions {
fun canReadStorage(context: Context): Boolean {
return !AndroidUtil.isMarshMallowOrLater || ContextCompat.checkSelfPermission(context,
Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED || isExternalStorageManager()
}
@JvmOverloads
......@@ -136,6 +138,17 @@ object Permissions {
createDialog(activity, exit)
}
/**
* Display a dialog asking for the [Manifest.permission.MANAGE_EXTERNAL_STORAGE] permission if needed
*
* @param activity: the activity used to trigger the dialog
* @param listener: the listener for the permission result
*/
fun showExternalPermissionDialog(activity: FragmentActivity, listener: (boolean: Boolean) -> Unit) {
if (activity.isFinishing || sAlertDialog != null && sAlertDialog!!.isShowing) return
sAlertDialog = createExternalManagerDialog(activity, listener)
}
private fun createDialog(activity: FragmentActivity, exit: Boolean): Dialog {
val dialogBuilder = android.app.AlertDialog.Builder(activity)
.setTitle(activity.getString(R.string.allow_storage_access_title))
......@@ -153,6 +166,32 @@ object Permissions {
return dialogBuilder.show()
}
/**
* Display a dialog asking for the [Manifest.permission.MANAGE_EXTERNAL_STORAGE] permission
*
* @param activity: the activity used to trigger the dialog
* @param listener: the listener for the permission result
*/
private fun createExternalManagerDialog(activity: FragmentActivity, listener: (boolean: Boolean) -> Unit): Dialog {
val dialogBuilder = android.app.AlertDialog.Builder(activity)
.setTitle(activity.getString(R.string.allow_storage_manager_title))
.setMessage(activity.getString(R.string.allow_storage_manager_description))
.setIcon(R.drawable.ic_warning)
.setPositiveButton(activity.getString(R.string.ok)) { _, _ ->
listener.invoke(true)
}.setNegativeButton(activity.getString(R.string.cancel)) { _, _ -> listener.invoke(false) }
.setCancelable(false)
return dialogBuilder.show().apply {
if (activity is AppCompatActivity) activity.lifecycle.addObserver(object : LifecycleObserver {
@Suppress("unused")
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun clear() {
dismiss()
}
})
}
}
private fun createDialogCompat(activity: FragmentActivity, exit: Boolean): Dialog {
val dialogBuilder = AlertDialog.Builder(activity)
.setTitle(activity.getString(R.string.allow_storage_access_title))
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment