diff --git a/Readme.md b/Readme.md
index 754a7d7..954ad32 100644
--- a/Readme.md
+++ b/Readme.md
@@ -43,7 +43,7 @@ Add it in your root build.gradle at the end of repositories:
Step 2. Add the dependency
dependencies {
- implementation 'com.github.TutorialsAndroid:FilePicker:v8.0.19'
+ implementation 'com.github.TutorialsAndroid:FilePicker:v9.0.0'
}
### Usage
@@ -84,7 +84,7 @@ Step 2. Add the dependency
3. Next create an instance of `FilePickerDialog`, and pass `Context` and `DialogProperties` references as parameters. Optional: You can change the title of dialog. Default is current directory name. Set the positive button string. Default is Select. Set the negative button string. Defalut is Cancel.
```java
- FilePickerDialog dialog = new FilePickerDialog(MainActivity.this,properties);
+ FilePickerDialog dialog = new FilePickerDialog(MainActivity.this, MainActivity.this,properties);
dialog.setTitle("Select a File");
```
@@ -125,6 +125,22 @@ Marshmallow and above requests for the permission on runtime. You should overrid
}
```
+### Android13 and Above Instructions:
+If your app targets Android 13 or higher and needs to access media files that other apps have created, you must request one or more of the following granular media permissions instead of the ```READ_EXTERNAL_STORAGE permission```:
+As of Android 13 and above you can only browse and select Images,Videos and Audio files only. This library is still in development and I'm looking for contributors to make this library more better
+```
+ Type of media | Permission to request
+
+ Images and photos | READ_MEDIA_IMAGES
+ Videos | READ_MEDIA_VIDEO
+ Audio files | READ_MEDIA_AUDIO
+```
+Before you access another app's media files, verify that the user has granted the appropriate granular media permissions to your app.
+
+If you request both the ```READ_MEDIA_IMAGES``` permission and the ```READ_MEDIA_VIDEO``` permission at the same time, only one system permission dialog appears.
+
+If your app was previously granted the ```READ_EXTERNAL_STORAGE``` permission, then any requested ```READ_MEDIA_*``` permissions are granted automatically when upgrading.
+
That's It. You are good to proceed further.
diff --git a/build.gradle b/build.gradle
index ce64ae9..86e00d2 100644
--- a/build.gradle
+++ b/build.gradle
@@ -6,7 +6,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:7.1.2'
+ classpath 'com.android.tools.build:gradle:8.1.1'
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
// NOTE: Do not place your application dependencies here; they belong
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index a3fd49d..2c4d430 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
\ No newline at end of file
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
\ No newline at end of file
diff --git a/library/build.gradle b/library/build.gradle
index 8f577d4..8f56977 100644
--- a/library/build.gradle
+++ b/library/build.gradle
@@ -1,14 +1,12 @@
apply plugin: 'com.android.library'
-
-//apply plugin: 'com.github.dcendents.android-maven'
group='com.github.TutorialsAndroid'
android {
- compileSdkVersion 31
+ compileSdk 34
defaultConfig {
minSdkVersion 19
- targetSdkVersion 31
+ targetSdkVersion 34
}
buildTypes {
release {
@@ -16,7 +14,8 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
- lintOptions {
+ namespace 'com.developer.filepicker'
+ lint {
abortOnError false
}
}
diff --git a/library/src/main/AndroidManifest.xml b/library/src/main/AndroidManifest.xml
index b2bfa48..3ec3900 100644
--- a/library/src/main/AndroidManifest.xml
+++ b/library/src/main/AndroidManifest.xml
@@ -1,6 +1,11 @@
-
-
+ xmlns:android="http://schemas.android.com/apk/res/android">
+
+
+
+
+
+
\ No newline at end of file
diff --git a/library/src/main/java/com/developer/filepicker/utils/Utility.java b/library/src/main/java/com/developer/filepicker/utils/Utility.java
index 3cfedf2..1105139 100644
--- a/library/src/main/java/com/developer/filepicker/utils/Utility.java
+++ b/library/src/main/java/com/developer/filepicker/utils/Utility.java
@@ -1,7 +1,10 @@
package com.developer.filepicker.utils;
+import android.Manifest;
+import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.os.Build;
import com.developer.filepicker.model.FileListItem;
@@ -15,6 +18,8 @@
*/
public class Utility {
+ private static final int REQUEST_MEDIA_PERMISSIONS = 456;
+
public static boolean checkStorageAccessPermissions(Context context) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
String permission = "android.permission.READ_EXTERNAL_STORAGE";
@@ -26,6 +31,23 @@ public static boolean checkStorageAccessPermissions(Context context) {
}
}
+ @android.annotation.TargetApi(Build.VERSION_CODES.TIRAMISU)
+ public static boolean checkMediaAccessPermissions(Context context) {//, Activity activity) {
+ String audioPermission = Manifest.permission.READ_MEDIA_AUDIO;
+ String imagesPermission = Manifest.permission.READ_MEDIA_IMAGES;
+ String videoPermission = Manifest.permission.READ_MEDIA_VIDEO;
+ // Check for permissions and request them if needed
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ // You have the permissions, you can proceed with your media file operations.
+ // You don't have the permissions. Request them.
+ // activity.requestPermissions(new String[]{audioPermission, imagesPermission, videoPermission}, REQUEST_MEDIA_PERMISSIONS);
+ return context.checkSelfPermission(audioPermission) == PackageManager.PERMISSION_GRANTED &&
+ context.checkSelfPermission(imagesPermission) == PackageManager.PERMISSION_GRANTED &&
+ context.checkSelfPermission(videoPermission) == PackageManager.PERMISSION_GRANTED;
+ }
+ return false;
+ }
+
public static ArrayList
prepareFileListEntries(ArrayList internalList, File inter,
ExtensionFilter filter, boolean show_hidden_files) {
diff --git a/library/src/main/java/com/developer/filepicker/view/FilePickerDialog.java b/library/src/main/java/com/developer/filepicker/view/FilePickerDialog.java
index 38064ed..719776e 100644
--- a/library/src/main/java/com/developer/filepicker/view/FilePickerDialog.java
+++ b/library/src/main/java/com/developer/filepicker/view/FilePickerDialog.java
@@ -7,6 +7,7 @@
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
+import android.util.Log;
import android.view.View;
import android.view.Window;
import android.widget.AdapterView;
@@ -38,7 +39,10 @@
@SuppressWarnings("unused")
public class FilePickerDialog extends Dialog implements AdapterView.OnItemClickListener {
+ private static final String TAG = FilePickerDialog.class.getSimpleName();
+
private final Context context;
+ private Activity activity;
private ListView listView;
private TextView dname, dir_path, title;
private DialogProperties properties;
@@ -53,6 +57,7 @@ public class FilePickerDialog extends Dialog implements AdapterView.OnItemClickL
public static final int EXTERNAL_READ_PERMISSION_GRANT = 112;
+ @Deprecated
public FilePickerDialog(Context context) {
super(context);
this.context = context;
@@ -61,6 +66,16 @@ public FilePickerDialog(Context context) {
internalList = new ArrayList<>();
}
+ public FilePickerDialog(Activity activity, Context context) {
+ super(context);
+ this.activity = activity;
+ this.context = context;
+ properties = new DialogProperties();
+ filter = new ExtensionFilter(properties);
+ internalList = new ArrayList<>();
+ }
+
+ @Deprecated
public FilePickerDialog(Context context, DialogProperties properties, int themeResId) {
super(context, themeResId);
this.context = context;
@@ -69,6 +84,16 @@ public FilePickerDialog(Context context, DialogProperties properties, int themeR
internalList = new ArrayList<>();
}
+ public FilePickerDialog(Activity activity, Context context, DialogProperties properties, int themeResId) {
+ super(context, themeResId);
+ this.activity = activity;
+ this.context = context;
+ this.properties = properties;
+ filter = new ExtensionFilter(properties);
+ internalList = new ArrayList<>();
+ }
+
+ @Deprecated
public FilePickerDialog(Context context, DialogProperties properties) {
super(context);
this.context = context;
@@ -77,6 +102,15 @@ public FilePickerDialog(Context context, DialogProperties properties) {
internalList = new ArrayList<>();
}
+ public FilePickerDialog(Activity activity, Context context, DialogProperties properties) {
+ super(context);
+ this.activity = activity;
+ this.context = context;
+ this.properties = properties;
+ filter = new ExtensionFilter(properties);
+ internalList = new ArrayList<>();
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -197,33 +231,50 @@ protected void onStart() {
positiveBtnNameStr
);
select.setText(positiveBtnNameStr);
- if (Utility.checkStorageAccessPermissions(context)) {
- File currLoc;
- internalList.clear();
- if (properties.offset.isDirectory() && validateOffsetPath()) {
- currLoc = new File(properties.offset.getAbsolutePath());
- FileListItem parent = new FileListItem();
- parent.setFilename(context.getString(R.string.label_parent_dir));
- parent.setDirectory(true);
- parent.setLocation(Objects.requireNonNull(currLoc.getParentFile())
- .getAbsolutePath());
- parent.setTime(currLoc.lastModified());
- internalList.add(parent);
- } else if (properties.root.exists() && properties.root.isDirectory()) {
- currLoc = new File(properties.root.getAbsolutePath());
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ if (Utility.checkMediaAccessPermissions(context)) {
+ //Permission granted...
+ dir();
} else {
- currLoc = new File(properties.error_dir.getAbsolutePath());
+ //Permissions are not granted...
+ Log.d(TAG, "Permissions are not granted");
+ }
+ } else {
+ if (Utility.checkStorageAccessPermissions(context)) {
+ Log.d(TAG, "Permission granted");
+ dir();
+ } else {
+ Log.d(TAG, "Permission not granted");
}
- dname.setText(currLoc.getName());
- dir_path.setText(currLoc.getAbsolutePath());
- setTitle();
- internalList = Utility.prepareFileListEntries(internalList, currLoc, filter,
- properties.show_hidden_files);
- mFileListAdapter.notifyDataSetChanged();
- listView.setOnItemClickListener(this);
}
}
+ private void dir() {
+ File currLoc;
+ internalList.clear();
+ if (properties.offset.isDirectory() && validateOffsetPath()) {
+ currLoc = new File(properties.offset.getAbsolutePath());
+ FileListItem parent = new FileListItem();
+ parent.setFilename(context.getString(R.string.label_parent_dir));
+ parent.setDirectory(true);
+ parent.setLocation(Objects.requireNonNull(currLoc.getParentFile())
+ .getAbsolutePath());
+ parent.setTime(currLoc.lastModified());
+ internalList.add(parent);
+ } else if (properties.root.exists() && properties.root.isDirectory()) {
+ currLoc = new File(properties.root.getAbsolutePath());
+ } else {
+ currLoc = new File(properties.error_dir.getAbsolutePath());
+ }
+ dname.setText(currLoc.getName());
+ dir_path.setText(currLoc.getAbsolutePath());
+ setTitle();
+ internalList = Utility.prepareFileListEntries(internalList, currLoc, filter,
+ properties.show_hidden_files);
+ mFileListAdapter.notifyDataSetChanged();
+ listView.setOnItemClickListener(this);
+ }
+
private boolean validateOffsetPath() {
String offset_path = properties.offset.getAbsolutePath();
String root_path = properties.root.getAbsolutePath();
@@ -393,24 +444,49 @@ public void markFiles(List paths) {
@Override
public void show() {
- if (!Utility.checkStorageAccessPermissions(context)) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- ((Activity) context).requestPermissions(new String[]{Manifest.permission
- .READ_EXTERNAL_STORAGE}, EXTERNAL_READ_PERMISSION_GRANT);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ if (Utility.checkMediaAccessPermissions(context)) {
+ //Permission granted...
+ super.show();
+ positiveBtnNameStr = positiveBtnNameStr == null ?
+ context.getResources().getString(R.string.choose_button_label) : positiveBtnNameStr;
+ select.setText(positiveBtnNameStr);
+ int size = MarkedItemList.getFileCount();
+ if (size == 0) {
+ select.setText(positiveBtnNameStr);
+ } else {
+ String button_label = positiveBtnNameStr + " (" + size + ") ";
+ select.setText(button_label);
+ }
+ } else {
+ //Permissions are not granted...
+ Log.d(TAG, "Permissions are not granted");
}
} else {
- super.show();
- positiveBtnNameStr = positiveBtnNameStr == null ?
- context.getResources().getString(R.string.choose_button_label) : positiveBtnNameStr;
- select.setText(positiveBtnNameStr);
- int size = MarkedItemList.getFileCount();
- if (size == 0) {
- select.setText(positiveBtnNameStr);
+ if (!Utility.checkStorageAccessPermissions(context)) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ ((Activity) context).requestPermissions(new String[]{Manifest.permission
+ .READ_EXTERNAL_STORAGE}, EXTERNAL_READ_PERMISSION_GRANT);
+ }
} else {
- String button_label = positiveBtnNameStr + " (" + size + ") ";
- select.setText(button_label);
+ super.show();
+ positiveBtnNameStr = positiveBtnNameStr == null ?
+ context.getResources().getString(R.string.choose_button_label) : positiveBtnNameStr;
+ select.setText(positiveBtnNameStr);
+ int size = MarkedItemList.getFileCount();
+ if (size == 0) {
+ select.setText(positiveBtnNameStr);
+ } else {
+ String button_label = positiveBtnNameStr + " (" + size + ") ";
+ select.setText(button_label);
+ }
}
}
+ if (!Utility.checkStorageAccessPermissions(context)) {
+
+ } else {
+
+ }
}
@Override
diff --git a/sample/build.gradle b/sample/build.gradle
index 147f0a0..23e7354 100644
--- a/sample/build.gradle
+++ b/sample/build.gradle
@@ -1,14 +1,14 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 31
+ compileSdk 34
defaultConfig {
applicationId "com.developer.filepicker.file"
minSdkVersion 19
- targetSdkVersion 31
- versionCode 7
- versionName "8.0.20"
+ targetSdkVersion 34
+ versionCode 8
+ versionName "9.0.0"
}
buildTypes {
release {
@@ -16,6 +16,7 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
+ namespace 'com.developer.filepicker.file'
}
dependencies {
@@ -25,6 +26,6 @@ dependencies {
implementation project(':library')
//AndroidX Libraries
- implementation 'androidx.appcompat:appcompat:1.4.1'
- implementation 'androidx.recyclerview:recyclerview:1.2.1'
+ implementation 'androidx.appcompat:appcompat:1.6.1'
+ implementation 'androidx.recyclerview:recyclerview:1.3.1'
}
diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml
index ae32442..8a1092a 100644
--- a/sample/src/main/AndroidManifest.xml
+++ b/sample/src/main/AndroidManifest.xml
@@ -1,10 +1,15 @@
+ xmlns:tools="http://schemas.android.com/tools">
-
-
+
+
+
+
+
+
listItem;
private FileListAdapter mFileListAdapter;
+ private static final int REQUEST_STORAGE_PERMISSIONS = 123;
+ private String readPermission = android.Manifest.permission.READ_EXTERNAL_STORAGE;
+ private String writePermission = android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
+
+ private static final int REQUEST_MEDIA_PERMISSIONS = 456;
+ private String audioPermission = android.Manifest.permission.READ_MEDIA_AUDIO;
+ private String imagesPermission = android.Manifest.permission.READ_MEDIA_IMAGES;
+ private String videoPermission = android.Manifest.permission.READ_MEDIA_VIDEO;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -47,7 +61,7 @@ protected void onCreate(Bundle savedInstanceState) {
final DialogProperties properties = new DialogProperties();
//Instantiate FilePickerDialog with Context and DialogProperties.
- dialog = new FilePickerDialog(MainActivity.this, properties);
+ dialog = new FilePickerDialog(MainActivity.this, MainActivity.this, properties);
dialog.setTitle("Select a File");
dialog.setPositiveBtnName("Select");
dialog.setNegativeBtnName("Cancel");
@@ -163,8 +177,30 @@ public void onClick(View view) {
showDialog.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- //Showing dialog when Show Dialog button is clicked.
- dialog.show();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ // Check for permissions and request them if needed
+ if (ContextCompat.checkSelfPermission(MainActivity.this, audioPermission) == PackageManager.PERMISSION_GRANTED &&
+ ContextCompat.checkSelfPermission(MainActivity.this, imagesPermission) == PackageManager.PERMISSION_GRANTED &&
+ ContextCompat.checkSelfPermission(MainActivity.this, videoPermission) == PackageManager.PERMISSION_GRANTED) {
+ // You have the permissions, you can proceed with your media file operations.
+ //Showing dialog when Show Dialog button is clicked.
+ dialog.show();
+ } else {
+ // You don't have the permissions. Request them.
+ ActivityCompat.requestPermissions(MainActivity.this, new String[]{audioPermission, imagesPermission, videoPermission}, REQUEST_MEDIA_PERMISSIONS);
+ }
+ } else {
+ // Check for permissions and request them if needed
+ if (ContextCompat.checkSelfPermission(MainActivity.this, readPermission) == PackageManager.PERMISSION_GRANTED &&
+ ContextCompat.checkSelfPermission(MainActivity.this, writePermission) == PackageManager.PERMISSION_GRANTED) {
+ // You have the permissions, you can proceed with your file operations.
+ //Showing dialog when Show Dialog button is clicked.
+ dialog.show();
+ } else {
+ // You don't have the permissions. Request them.
+ ActivityCompat.requestPermissions(MainActivity.this, new String[]{readPermission, writePermission}, REQUEST_STORAGE_PERMISSIONS);
+ }
+ }
}
});
@@ -197,19 +233,83 @@ private int countCommas(String fextension) {
}
//Add this method to show Dialog when the required permission has been granted to the app.
+// @Override
+// public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+// super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+// if (requestCode == FilePickerDialog.EXTERNAL_READ_PERMISSION_GRANT) {
+// if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+// if (dialog != null) {
+// //Show dialog if the read permission has been granted.
+// dialog.show();
+// }
+// } else {
+// //Permission has not been granted. Notify the user.
+// Toast.makeText(MainActivity.this, "Permission is Required for getting list of files", Toast.LENGTH_SHORT).show();
+// }
+// }
+// }
+
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
- if (requestCode == FilePickerDialog.EXTERNAL_READ_PERMISSION_GRANT) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ if (requestCode == REQUEST_STORAGE_PERMISSIONS) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- if (dialog != null) {
- //Show dialog if the read permission has been granted.
- dialog.show();
- }
+ // Permissions were granted. You can proceed with your file operations.
+ //Showing dialog when Show Dialog button is clicked.
+ dialog.show();
+ } else {
+ // Permissions were denied. Show a rationale dialog or inform the user about the importance of these permissions.
+ showRationaleDialog();
+ }
+ }
+
+ if (requestCode == REQUEST_MEDIA_PERMISSIONS) {
+ if (grantResults.length > 0 && areAllPermissionsGranted(grantResults)) {
+ // Permissions were granted. You can proceed with your media file operations.
+ //Showing dialog when Show Dialog button is clicked.
+ dialog.show();
} else {
- //Permission has not been granted. Notify the user.
- Toast.makeText(MainActivity.this, "Permission is Required for getting list of files", Toast.LENGTH_SHORT).show();
+ // Permissions were denied. Show a rationale dialog or inform the user about the importance of these permissions.
+ showRationaleDialog();
+ }
+ }
+ }
+
+ private boolean areAllPermissionsGranted(int[] grantResults) {
+ for (int result : grantResults) {
+ if (result != PackageManager.PERMISSION_GRANTED) {
+ return false;
}
}
+ return true;
+ }
+
+ private void showRationaleDialog() {
+ if (ActivityCompat.shouldShowRequestPermissionRationale(this, readPermission) ||
+ ActivityCompat.shouldShowRequestPermissionRationale(this, writePermission)) {
+ // Show a rationale dialog explaining why the permissions are necessary.
+ new AlertDialog.Builder(this)
+ .setTitle("Permission Needed")
+ .setMessage("This app needs storage permissions to read and write files.")
+ .setPositiveButton("OK", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ // Request permissions when the user clicks OK.
+ ActivityCompat.requestPermissions(MainActivity.this, new String[]{readPermission, writePermission}, REQUEST_STORAGE_PERMISSIONS);
+ }
+ })
+ .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ // Handle the case where the user cancels the permission request.
+ }
+ })
+ .show();
+ } else {
+ // Request permissions directly if no rationale is needed.
+ ActivityCompat.requestPermissions(this, new String[]{readPermission, writePermission}, REQUEST_STORAGE_PERMISSIONS);
+ }
}
public boolean onCreateOptionsMenu(Menu menu) {