2020-06-02 15:35:36 +02:00
package org.moire.ultrasonic.util ;
import android.Manifest ;
import android.content.Context ;
import android.content.DialogInterface ;
import android.content.Intent ;
import android.net.Uri ;
2020-08-18 21:24:24 +02:00
import android.os.Handler ;
import android.os.Looper ;
2020-09-30 14:47:59 +02:00
import timber.log.Timber ;
2020-06-02 15:35:36 +02:00
import androidx.appcompat.app.AlertDialog ;
import androidx.core.content.PermissionChecker ;
import com.karumi.dexter.Dexter ;
import com.karumi.dexter.MultiplePermissionsReport ;
import com.karumi.dexter.PermissionToken ;
import com.karumi.dexter.listener.DexterError ;
import com.karumi.dexter.listener.PermissionRequest ;
import com.karumi.dexter.listener.PermissionRequestErrorListener ;
import com.karumi.dexter.listener.multi.MultiplePermissionsListener ;
import org.moire.ultrasonic.R ;
import org.moire.ultrasonic.activity.MainActivity ;
import java.util.List ;
import static androidx.core.content.PermissionChecker.PERMISSION_DENIED ;
/ * *
* Contains static functions for Permission handling
* /
public class PermissionUtil {
public interface PermissionRequestFinishedCallback {
2020-08-18 21:24:24 +02:00
void onPermissionRequestFinished ( boolean hasPermission ) ;
2020-06-02 15:35:36 +02:00
}
/ * *
* This function can be used to handle file access permission failures .
*
* It will check if the failure is because the necessary permissions aren ' t available ,
* and it will request them , if necessary .
*
* @param context context for the operation
* @param callback callback function to execute after the permission request is finished
* /
public static void handlePermissionFailed ( final Context context , final PermissionRequestFinishedCallback callback ) {
2020-06-17 18:14:34 +02:00
String currentCachePath = Util . getPreferences ( context ) . getString ( Constants . PREFERENCES_KEY_CACHE_LOCATION , FileUtil . getDefaultMusicDirectory ( context ) . getPath ( ) ) ;
String defaultCachePath = FileUtil . getDefaultMusicDirectory ( context ) . getPath ( ) ;
2020-06-02 15:35:36 +02:00
// Ultrasonic can do nothing about this error when the Music Directory is already set to the default.
if ( currentCachePath . compareTo ( defaultCachePath ) = = 0 ) return ;
// We must get the context of the Main Activity for the dialogs, as this function may be called from a background thread where displaying dialogs is not available
2020-08-18 21:24:24 +02:00
final Context mainContext = MainActivity . getInstance ( ) ;
2020-06-02 15:35:36 +02:00
if ( ( PermissionChecker . checkSelfPermission ( context , Manifest . permission . WRITE_EXTERNAL_STORAGE ) = = PERMISSION_DENIED ) | |
( PermissionChecker . checkSelfPermission ( context , Manifest . permission . READ_EXTERNAL_STORAGE ) = = PERMISSION_DENIED ) ) {
// While we request permission, the Music Directory is temporarily reset to its default location
2020-06-17 18:14:34 +02:00
setCacheLocation ( context , FileUtil . getDefaultMusicDirectory ( context ) . getPath ( ) ) ;
2020-08-18 21:24:24 +02:00
requestFailedPermission ( mainContext , currentCachePath , callback ) ;
2020-06-02 15:35:36 +02:00
} else {
2020-06-17 18:14:34 +02:00
setCacheLocation ( context , FileUtil . getDefaultMusicDirectory ( context ) . getPath ( ) ) ;
2020-08-18 21:24:24 +02:00
new Handler ( Looper . getMainLooper ( ) ) . post ( new Runnable ( ) {
@Override
public void run ( ) {
showWarning ( mainContext , context . getString ( R . string . permissions_message_box_title ) , context . getString ( R . string . permissions_access_error ) , null ) ;
}
} ) ;
callback . onPermissionRequestFinished ( false ) ;
2020-06-02 15:35:36 +02:00
}
}
2020-08-18 21:24:24 +02:00
/ * *
* This function requests permission to access the filesystem .
* It can be used to request the permission initially , e . g . when the user decides to use a non - default folder for the cache
* @param context context for the operation
* @param callback callback function to execute after the permission request is finished
* /
public static void requestInitialPermission ( final Context context , final PermissionRequestFinishedCallback callback ) {
Dexter . withContext ( context )
. withPermissions (
Manifest . permission . WRITE_EXTERNAL_STORAGE ,
Manifest . permission . READ_EXTERNAL_STORAGE )
. withListener ( new MultiplePermissionsListener ( ) {
@Override
public void onPermissionsChecked ( MultiplePermissionsReport report ) {
if ( report . areAllPermissionsGranted ( ) ) {
2020-09-30 14:47:59 +02:00
Timber . i ( " Permission granted to read / write external storage " ) ;
2020-08-18 21:24:24 +02:00
if ( callback ! = null ) callback . onPermissionRequestFinished ( true ) ;
return ;
}
if ( report . isAnyPermissionPermanentlyDenied ( ) ) {
2020-09-30 14:47:59 +02:00
Timber . i ( " Found permanently denied permission to read / write external storage, offering settings " ) ;
2020-08-18 21:24:24 +02:00
showSettingsDialog ( context ) ;
if ( callback ! = null ) callback . onPermissionRequestFinished ( false ) ;
return ;
}
2020-09-30 14:47:59 +02:00
Timber . i ( " At least one permission is missing to read / write external storage " ) ;
2020-08-18 21:24:24 +02:00
showWarning ( context , context . getString ( R . string . permissions_message_box_title ) ,
context . getString ( R . string . permissions_rationale_description_initial ) , null ) ;
if ( callback ! = null ) callback . onPermissionRequestFinished ( false ) ;
}
@Override
public void onPermissionRationaleShouldBeShown ( List < PermissionRequest > permissions , PermissionToken token ) {
showWarning ( context , context . getString ( R . string . permissions_rationale_title ) ,
context . getString ( R . string . permissions_rationale_description_initial ) , token ) ;
}
} ) . withErrorListener ( new PermissionRequestErrorListener ( ) {
@Override
public void onError ( DexterError error ) {
2020-09-30 14:47:59 +02:00
Timber . e ( " An error has occurred during checking permissions with Dexter: %s " , error . toString ( ) ) ;
2020-08-18 21:24:24 +02:00
}
} )
. check ( ) ;
}
2020-06-02 15:35:36 +02:00
private static void setCacheLocation ( Context context , String cacheLocation ) {
Util . getPreferences ( context ) . edit ( )
. putString ( Constants . PREFERENCES_KEY_CACHE_LOCATION , cacheLocation )
. apply ( ) ;
}
2020-08-18 21:24:24 +02:00
private static void requestFailedPermission ( final Context context , final String cacheLocation , final PermissionRequestFinishedCallback callback ) {
2020-06-02 15:35:36 +02:00
Dexter . withContext ( context )
. withPermissions (
Manifest . permission . WRITE_EXTERNAL_STORAGE ,
Manifest . permission . READ_EXTERNAL_STORAGE )
. withListener ( new MultiplePermissionsListener ( ) {
@Override
public void onPermissionsChecked ( MultiplePermissionsReport report ) {
if ( report . areAllPermissionsGranted ( ) ) {
2020-09-30 14:47:59 +02:00
Timber . i ( " Permission granted to use cache directory %s " , cacheLocation ) ;
2020-06-02 15:35:36 +02:00
setCacheLocation ( context , cacheLocation ) ;
2020-08-18 21:24:24 +02:00
if ( callback ! = null ) callback . onPermissionRequestFinished ( true ) ;
2020-06-02 15:35:36 +02:00
return ;
}
if ( report . isAnyPermissionPermanentlyDenied ( ) ) {
2020-09-30 14:47:59 +02:00
Timber . i ( " Found permanently denied permission to use cache directory %s, offering settings " , cacheLocation ) ;
2020-06-02 15:35:36 +02:00
showSettingsDialog ( context ) ;
2020-08-18 21:24:24 +02:00
if ( callback ! = null ) callback . onPermissionRequestFinished ( false ) ;
2020-06-02 15:35:36 +02:00
return ;
}
2020-09-30 14:47:59 +02:00
Timber . i ( " At least one permission is missing to use directory %s " , cacheLocation ) ;
2020-06-17 18:14:34 +02:00
setCacheLocation ( context , FileUtil . getDefaultMusicDirectory ( context ) . getPath ( ) ) ;
2020-06-02 15:35:36 +02:00
showWarning ( context , context . getString ( R . string . permissions_message_box_title ) ,
context . getString ( R . string . permissions_permission_missing ) , null ) ;
2020-08-18 21:24:24 +02:00
if ( callback ! = null ) callback . onPermissionRequestFinished ( false ) ;
2020-06-02 15:35:36 +02:00
}
@Override
public void onPermissionRationaleShouldBeShown ( List < PermissionRequest > permissions , PermissionToken token ) {
showWarning ( context , context . getString ( R . string . permissions_rationale_title ) ,
2020-08-18 21:24:24 +02:00
context . getString ( R . string . permissions_rationale_description_failed ) , token ) ;
2020-06-02 15:35:36 +02:00
}
} ) . withErrorListener ( new PermissionRequestErrorListener ( ) {
@Override
public void onError ( DexterError error ) {
2020-09-30 14:47:59 +02:00
Timber . e ( " An error has occurred during checking permissions with Dexter: %s " , error . toString ( ) ) ;
2020-06-02 15:35:36 +02:00
}
} )
. check ( ) ;
}
private static void showSettingsDialog ( final Context context ) {
AlertDialog . Builder builder = new AlertDialog . Builder ( context , R . style . Theme_AppCompat_Dialog ) ;
builder . setIcon ( android . R . drawable . ic_dialog_alert ) ;
builder . setTitle ( context . getString ( R . string . permissions_permanent_denial_title ) ) ;
builder . setMessage ( context . getString ( R . string . permissions_permanent_denial_description ) ) ;
builder . setPositiveButton ( context . getString ( R . string . permissions_open_settings ) , new DialogInterface . OnClickListener ( ) {
@Override
public void onClick ( DialogInterface dialog , int which ) {
dialog . cancel ( ) ;
openSettings ( context ) ;
}
} ) ;
builder . setNegativeButton ( context . getString ( R . string . common_cancel ) , new DialogInterface . OnClickListener ( ) {
@Override
public void onClick ( DialogInterface dialog , int which ) {
2020-06-17 18:14:34 +02:00
setCacheLocation ( context , FileUtil . getDefaultMusicDirectory ( context ) . getPath ( ) ) ;
2020-06-02 15:35:36 +02:00
dialog . cancel ( ) ;
}
} ) ;
builder . show ( ) ;
}
private static void openSettings ( Context context ) {
Intent i = new Intent ( android . provider . Settings . ACTION_APPLICATION_DETAILS_SETTINGS ) ;
i . addCategory ( Intent . CATEGORY_DEFAULT ) ;
i . setData ( Uri . parse ( " package: " + context . getPackageName ( ) ) ) ;
context . startActivity ( i ) ;
}
private static void showWarning ( Context context , String title , String text , final PermissionToken token ) {
AlertDialog . Builder builder = new AlertDialog . Builder ( context , R . style . Theme_AppCompat_Dialog ) ;
builder . setIcon ( android . R . drawable . ic_dialog_alert ) ;
builder . setTitle ( title ) ;
builder . setMessage ( text ) ;
builder . setPositiveButton ( context . getString ( R . string . common_ok ) , new DialogInterface . OnClickListener ( ) {
@Override
public void onClick ( DialogInterface dialog , int which ) {
dialog . cancel ( ) ;
if ( token ! = null ) token . continuePermissionRequest ( ) ;
}
} ) ;
builder . show ( ) ;
}
}