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

Ask for the MANAGE_EXTERNAL_STORAGE permission to existing users

When the old READ_EXTERNAL_STORAGE permission has already been granted
by the user on a previous version of the app, some media may be missing
while they haven't granted the new MANAGE_EXTERNAL_STORAGE permission.
If we detect that the user is in that state, we show a snackbar and let
them grant the new permission.

(cherry picked from commit 207b8435)
parent a1fa4a84
No related branches found
No related tags found
1 merge request!1508Fix the video information legend attribute and crash
......@@ -501,6 +501,9 @@
<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="partial_content">VLC cannot access all of your files</string>
<string name="partial_content_description">Starting with Android 11, applications cannot access all of the device\'s files without a special permission.\n\nIn order to allow VLC for Android to access all your files, including files that are not considered as media by the system, please grant this new permission to VLC.</string>
<string name="never_ask_again">Never ask again</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>
......
......@@ -128,6 +128,7 @@ const val AUDIO_STOP_AFTER = "audio_stop_after"
const val LAST_LOCK_ORIENTATION = "last_lock_orientation"
const val INITIAL_PERMISSION_ASKED = "initial_permission_asked"
const val PERMISSION_NEVER_ASK = "permission_never_ask"
class DeviceInfo(context: Context) {
val pm = context.packageManager
......
<?xml version="1.0" encoding="utf-8"?>
<!--
~ *************************************************************************
~ dialog_all_access.xml
~ **************************************************************************
~ Copyright © 2021 VLC authors and VideoLAN
~ Author: Nicolas POMEPUY
~ This program 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 2 of the License, or
~ (at your option) any later version.
~
~ This program 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.
~
~ You should have received a copy of the GNU General Public License
~ along with this program; if not, write to the Free Software
~ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
~ ***************************************************************************
~
~
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/title"
style="@style/Theme.VLC.BottomSheetTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:text="@string/partial_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:text="@string/partial_content_description"
android:textColor="?attr/font_default"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/title" />
<CheckBox
android:id="@+id/never_ask_again"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="@string/never_ask_again"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/description" />
<Button
android:id="@+id/grant_all_access_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginEnd="16dp"
android:text="@string/permission_ask_again"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/never_ask_again" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
......@@ -53,6 +53,7 @@ import org.videolan.vlc.extensions.ExtensionsManager
import org.videolan.vlc.gui.audio.AudioBrowserFragment
import org.videolan.vlc.gui.browser.BaseBrowserFragment
import org.videolan.vlc.gui.browser.ExtensionBrowser
import org.videolan.vlc.gui.dialogs.AllAccessPermissionDialog
import org.videolan.vlc.gui.helpers.INavigator
import org.videolan.vlc.gui.helpers.Navigator
import org.videolan.vlc.gui.helpers.UiTools
......@@ -96,6 +97,12 @@ class MainActivity : ContentActivity(),
mediaLibrary = Medialibrary.getInstance()
VLCBilling.getInstance(application).retrieveSkus()
//Only the partial permission is granted for Android 11+
if (!settings.getBoolean(PERMISSION_NEVER_ASK, false) && Permissions.canReadStorage(this) && !Permissions.hasAllAccess(this)) {
UiTools.snackerMessageInfinite(this, getString(R.string.partial_content))?.setAction(R.string.more) {
AllAccessPermissionDialog.newInstance().show(supportFragmentManager, AllAccessPermissionDialog::class.simpleName)
}?.show()
}
}
......
/**
* **************************************************************************
* AllAccessPermissionDialog.kt
* ****************************************************************************
* Copyright © 2015 VLC authors and VideoLAN
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
* ***************************************************************************
*/
package org.videolan.vlc.gui.dialogs
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.CheckBox
import android.widget.TextView
import androidx.lifecycle.lifecycleScope
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.ObsoleteCoroutinesApi
import kotlinx.coroutines.launch
import org.videolan.tools.PERMISSION_NEVER_ASK
import org.videolan.tools.Settings
import org.videolan.tools.putSingle
import org.videolan.vlc.R
import org.videolan.vlc.gui.helpers.hf.StoragePermissionsDelegate.Companion.getStoragePermission
@ObsoleteCoroutinesApi
@ExperimentalCoroutinesApi
class AllAccessPermissionDialog : VLCBottomSheetDialogFragment() {
private lateinit var titleView:TextView
private lateinit var grantAllAccessButton:Button
private lateinit var neverAskAgain:CheckBox
companion object {
/**
* Create a new AllAccessPermissionDialog
*/
fun newInstance(): AllAccessPermissionDialog {
return AllAccessPermissionDialog()
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.dialog_all_access, container)
titleView = view.findViewById(R.id.title)
grantAllAccessButton = view.findViewById(R.id.grant_all_access_button)
neverAskAgain = view.findViewById(R.id.never_ask_again)
grantAllAccessButton.setOnClickListener {
lifecycleScope.launch { requireActivity().getStoragePermission() }
dismiss()
}
neverAskAgain.setOnCheckedChangeListener { _, isChecked ->
Settings.getInstance(requireActivity()).putSingle(PERMISSION_NEVER_ASK, isChecked)
}
return view
}
override fun getDefaultState(): Int {
return STATE_EXPANDED
}
override fun initialFocusedView(): View = titleView
override fun needToManageOrientation(): Boolean {
return true
}
}
\ No newline at end of file
......@@ -79,7 +79,7 @@ class StoragePermissionsDelegate : BaseHeadlessFragment() {
firstRun = intent.getBooleanExtra(EXTRA_FIRST_RUN, false)
}
write = arguments?.getBoolean(WRITE_ACCESS) ?: false
if (AndroidUtil.isMarshMallowOrLater && !canReadStorage(requireContext())) {
if (AndroidUtil.isMarshMallowOrLater && (!canReadStorage(requireContext()) || !Permissions.hasAllAccess(requireContext()))) {
if (shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE) && !model.permissionRationaleShown) {
Permissions.showStoragePermissionDialog(requireActivity(), false)
model.permissionRationaleShown = true
......
......@@ -30,6 +30,7 @@ import android.app.Dialog
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
......@@ -49,7 +50,9 @@ 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.isCallable
import org.videolan.tools.putSingle
import org.videolan.vlc.BuildConfig
import org.videolan.vlc.R
import org.videolan.vlc.gui.helpers.hf.StoragePermissionsDelegate.Companion.askStoragePermission
import org.videolan.vlc.gui.helpers.hf.WriteExternalDelegate
......@@ -89,6 +92,14 @@ object Permissions {
Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED || isExternalStorageManager()
}
/**
* Check if the app has a complete access to the files especially on Android 11
*
* @param context: the context to check with
* @return true if the app has been granted the whole permissions including [Manifest.permission.MANAGE_EXTERNAL_STORAGE]
*/
fun hasAllAccess(context: Context) = !Intent(android.provider.Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION, Uri.parse("package:${BuildConfig.APP_ID}")).isCallable(context) || isExternalStorageManager()
@JvmOverloads
fun canWriteStorage(context: Context = AppContextProvider.appContext): Boolean {
return ContextCompat.checkSelfPermission(context,
......
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