Skip to content
This repository was archived by the owner on Mar 18, 2023. It is now read-only.

Commit 40239d6

Browse files
committed
Resolution for issue where lacking permissions causes the file chooser to hang the first time it is run
1 parent ebeba64 commit 40239d6

File tree

11 files changed

+139
-41
lines changed

11 files changed

+139
-41
lines changed

AndroidFilePickerLightLibrary/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ android {
3737
minSdkVersion 26
3838
targetSdkVersion 30
3939
compileSdkVersion 31
40-
versionCode 20
41-
versionName "1.1.9"
40+
versionCode 21
41+
versionName "1.2.0"
4242
buildToolsVersion "30.0.1"
4343

4444
}

AndroidFilePickerLightLibrary/src/main/AndroidManifest.xml

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,7 @@
2222
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:required="true" />
2323
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:required="true" />
2424
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" android:required="true" />
25-
<!--<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" android:required="false" />-->
26-
<!--<uses-permission android:name="android.permission.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION" android:required="false" />-->
27-
<uses-permission android:name="android.permission.INTERNET" android:required="false" />
25+
<uses-permission android:name="android.permission.INTERNET" android:required="true" />
2826

2927
<application
3028
android:launchMode="singleTop"
@@ -36,9 +34,9 @@
3634
android:usesCleartextTraffic="false"
3735
android:installLocation="preferExternal"
3836
android:requestLegacyExternalStorage="true"
37+
android:preserveLegacyExternalStorage="true"
3938
android:largeHeap="true"
4039
>
41-
<!--android:preserveLegacyExternalStorage="true" : Causes problems with minSDK < 29 ... -->
4240

4341
<activity
4442
android:name=".FileChooserActivity"
@@ -52,13 +50,19 @@
5250
android:grantUriPermissions="true"
5351
android:noHistory="true"
5452
android:excludeFromRecents="true"
53+
android:launchMode="singleTop"
54+
android:autoRemoveFromRecents="true"
55+
android:finishOnTaskLaunch="true"
56+
android:immersive="true"
57+
android:persistableMode="persistNever"
58+
android:resizeableActivity="false"
5559
>
5660

5761
<intent-filter android:priority="1000">
5862
<action android:name="android.intent.action.ACTION_PICK_ACTIVITY" />
59-
<category android:name="android.intent.category.HOME" />
60-
<category android:name="android.intent.category.DEFAULT" />
61-
<category android:name="android.intent.category.LAUNCHER" />
63+
<!--<category android:name="android.intent.category.HOME" />-->
64+
<!--<category android:name="android.intent.category.DEFAULT" />-->
65+
<!--<category android:name="android.intent.category.LAUNCHER" />-->
6266
</intent-filter>
6367

6468
</activity>

AndroidFilePickerLightLibrary/src/main/java/com/maxieds/androidfilepickerlightlibrary/FileChooserActivity.java

Lines changed: 80 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ This program (the AndroidFilePickerLight library) is free software written by
2020
import android.app.Activity;
2121
import android.content.Intent;
2222
import android.content.pm.ActivityInfo;
23+
import android.content.pm.PackageManager;
24+
import android.os.Build;
2325
import android.os.Bundle;
2426
import android.os.Handler;
2527
import android.util.Log;
@@ -32,6 +34,7 @@ This program (the AndroidFilePickerLight library) is free software written by
3234

3335
import androidx.appcompat.app.AppCompatActivity;
3436
import androidx.appcompat.widget.Toolbar;
37+
import androidx.core.app.ActivityCompat;
3538

3639
import java.util.ArrayList;
3740
import java.util.Arrays;
@@ -62,6 +65,18 @@ public class FileChooserActivity extends AppCompatActivity implements EasyPermis
6265

6366
private PrefetchFilesUpdater prefetchFilesUpdaterInst;
6467

68+
public static final String[] ACTIVITY_REQUIRED_PERMISSIONS = {
69+
"android.permission.READ_EXTERNAL_STORAGE",
70+
"android.permission.WRITE_EXTERNAL_STORAGE",
71+
"android.permission.ACCESS_MEDIA_LOCATION",
72+
"android.permission.INTERNET"
73+
};
74+
75+
public static final String[] ACTIVITY_OPTIONAL_PERMISSIONS = {
76+
//"android.permission.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION",
77+
//"android.permission.MANAGE_EXTERNAL_STORAGE",
78+
};
79+
6580
public void startPrefetchFileUpdatesThread() {
6681
if(prefetchFilesUpdaterInst.isAlive()) {
6782
prefetchFilesUpdaterInst.interrupt();
@@ -96,30 +111,87 @@ private void setUnhandledExceptionHandler() {
96111
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
97112
@Override
98113
public void uncaughtException(Thread paramThread, Throwable paramExcpt) {
99-
getInstance().postSelectedFilesActivityResult((Exception) paramExcpt);
114+
FileChooserException.AndroidFilePickerLightException paramAsRTE = null;
115+
String unhandledExcptMsg = "Unhandled file chooser exception";
116+
if (paramExcpt != null) {
117+
unhandledExcptMsg = String.format(Locale.getDefault(), "%s: %s", unhandledExcptMsg, paramExcpt.getMessage());
118+
paramAsRTE = new FileChooserException.AndroidFilePickerLightException(unhandledExcptMsg);
119+
paramAsRTE.initCause(paramExcpt);
120+
} else {
121+
paramAsRTE = new FileChooserException.AndroidFilePickerLightException(unhandledExcptMsg);
122+
}
123+
getInstance().postSelectedFilesActivityResult((Exception) paramAsRTE);
100124
}
101125
});
102126
}
103127

128+
private final long FILE_CHOOSER_ACTIVITY_PERMISSIONS_CHECK_DELAY = 5000;
129+
130+
private void checkRequiredPermissionsForActivityLaunch(long closeActivityDelayTimeout) {
131+
132+
/* A fix to the problem where the activity hangs indefinitely with a blank screen the
133+
* first time an application using the library is run. If we do not have the required
134+
* storage permissions, then abort. The notification asking the user whether to allow
135+
* the storage access permissions will persist so that running the file chooser activity
136+
* the next time will succeed.
137+
*/
138+
String[] requiredPermsList = ACTIVITY_REQUIRED_PERMISSIONS;
139+
String missingPermission = "<Unspecified>";
140+
boolean checkPermsStatusOK = true;
141+
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
142+
for (String permission : requiredPermsList) {
143+
if (ActivityCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
144+
Log.w(LOGTAG, String.format(Locale.getDefault(), "No permission for %s", permission));
145+
checkPermsStatusOK = false;
146+
missingPermission = permission;
147+
break;
148+
}
149+
}
150+
}
151+
if (!checkPermsStatusOK) {
152+
String activityReturnErrorMsg = String.format(Locale.getDefault(), "File chooser activity aborted: Unable to obtain required permission: %s", missingPermission);
153+
final FileChooserException.AndroidFilePickerLightException closeActivityRTEFinal = new FileChooserException.AndroidFilePickerLightException(activityReturnErrorMsg);
154+
Handler closeActivityDelayTimeoutHandler = new Handler();
155+
Runnable closeActivityDelayTimeoutRunner = new Runnable() {
156+
final FileChooserException.AndroidFilePickerLightException rteToPost = closeActivityRTEFinal;
157+
@Override
158+
public void run() {
159+
FileChooserActivity.getInstance().postSelectedFilesActivityResult((Exception) rteToPost);
160+
}
161+
};
162+
Log.e(LOGTAG, "Aborting file chooser activity with denied permission(s) :(");
163+
closeActivityDelayTimeoutHandler.postDelayed(closeActivityDelayTimeoutRunner, closeActivityDelayTimeout);
164+
} else {
165+
Log.i(LOGTAG, "All required permissions for file chooser obtained!");
166+
}
167+
168+
}
169+
104170
@Override
105171
public void onCreate(Bundle lastSettingsBundle) {
106172

107173
super.onCreate(lastSettingsBundle);
174+
175+
RuntimeException closeActivityRTE = null;
176+
boolean checkPermsStatus = true;
177+
try {
178+
PermissionsHandler.obtainRequiredPermissions(this, ACTIVITY_REQUIRED_PERMISSIONS);
179+
PermissionsHandler.requestOptionalPermissions(this, ACTIVITY_OPTIONAL_PERMISSIONS);
180+
} catch (Throwable permsEx) {}
181+
182+
/* Otherwise, continue with initializing the file chooser display: */
108183
setUnhandledExceptionHandler();
109184
staticRunningInst = this;
110185
displayFragmentsInst = new DisplayFragments();
111186
displayFragmentsInst.resetRecyclerViewLayoutContext();
112187
BasicFileProvider.resetBasicFileProviderDefaults();
113188
cwdFolderCtx = null;
114189

115-
FileChooserBuilder fpConfig = FileChooserBuilder.getInstance();
190+
FileChooserBuilder fpConfig = new FileChooserBuilder(this);
116191
if(fpConfig.getExternalFilesProvider() != null) {
117192
BasicFileProvider.setExternalDocumentsProvider(fpConfig.getExternalFilesProvider());
118193
}
119194

120-
PermissionsHandler.obtainRequiredPermissions(this, ACTIVITY_REQUIRED_PERMISSIONS);
121-
PermissionsHandler.requestOptionalPermissions(this, ACTIVITY_OPTIONAL_PERMISSIONS);
122-
123195
setTheme(R.style.LibraryDefaultTheme);
124196
setContentView(R.layout.main_picker_activity_base_layout);
125197
configureInitialMainLayout(fpConfig);
@@ -145,7 +217,7 @@ public void onCreate(Bundle lastSettingsBundle) {
145217
);
146218

147219
long idleTimeout = fpConfig.getIdleTimeout();
148-
if(idleTimeout != FileChooserBuilder.NO_ABORT_TIMEOUT) {
220+
if (idleTimeout != FileChooserBuilder.NO_ABORT_TIMEOUT) {
149221
Handler execIdleTimeoutHandler = new Handler();
150222
Runnable execIdleTimeoutRunner = new Runnable() {
151223
@Override
@@ -155,6 +227,7 @@ public void run() {
155227
};
156228
execIdleTimeoutHandler.postDelayed(execIdleTimeoutRunner, idleTimeout);
157229
}
230+
checkRequiredPermissionsForActivityLaunch(FILE_CHOOSER_ACTIVITY_PERMISSIONS_CHECK_DELAY);
158231

159232
}
160233

@@ -323,18 +396,6 @@ public void onNewIntent(Intent broadcastIntent) {
323396
super.onNewIntent(broadcastIntent);
324397
}
325398

326-
public static final String[] ACTIVITY_REQUIRED_PERMISSIONS = {
327-
"android.permission.READ_EXTERNAL_STORAGE",
328-
"android.permission.WRITE_EXTERNAL_STORAGE",
329-
"android.permission.ACCESS_MEDIA_LOCATION",
330-
};
331-
332-
public static final String[] ACTIVITY_OPTIONAL_PERMISSIONS = {
333-
"android.permission.INTERNET",
334-
//"android.permission.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION",
335-
//"android.permission.MANAGE_EXTERNAL_STORAGE",
336-
};
337-
338399
@Override
339400
public void onRequestPermissionsResult(int requestCode, String[] permsList, int[] grantedPermsList) {
340401
super.onRequestPermissionsResult(requestCode, permsList, grantedPermsList);
@@ -350,7 +411,7 @@ public void onPermissionsDenied(int requestCode, List<String> permsList) {
350411
new AppSettingsDialog.Builder(this).build().show();
351412
}
352413
else if(requestCode == PermissionsHandler.REQUEST_REQUIRED_PERMISSIONS_CODE) {
353-
throw new FileChooserException.PermissionsErrorException();
414+
getInstance().postSelectedFilesActivityResult((Exception) new FileChooserException.PermissionsErrorException());
354415
}
355416
}
356417

AndroidFilePickerLightLibrary/src/main/java/com/maxieds/androidfilepickerlightlibrary/FileChooserBuilder.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,8 @@ public enum SelectionModeType {
205205
SELECT_OMNIVORE
206206
}
207207

208-
private static FileChooserBuilder localActivityBuildeStaticInst;
209-
public static FileChooserBuilder getInstance() { return localActivityBuildeStaticInst; }
208+
private static FileChooserBuilder localActivityBuildStaticInst;
209+
public static FileChooserBuilder getInstance() { return localActivityBuildStaticInst; }
210210

211211
private WeakReference<Activity> activityContextRef;
212212
private FileChooserException.AndroidFilePickerLightException defaultExceptionType;
@@ -235,7 +235,7 @@ public enum SelectionModeType {
235235
public static final int DEFAULT_MAX_SELECTED_FILES = 10;
236236

237237
public FileChooserBuilder(Activity activityContextInst) {
238-
localActivityBuildeStaticInst = this;
238+
localActivityBuildStaticInst = this;
239239
activityContextRef = new WeakReference<Activity>(activityContextInst);
240240
defaultExceptionType = FileChooserException.CommunicateSelectionDataException.getNewInstance();
241241
displayUIConfig = null;

AndroidFilePickerLightLibrary/src/main/java/com/maxieds/androidfilepickerlightlibrary/FileChooserException.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,15 +75,23 @@ public static class AndroidFilePickerLightException extends RuntimeException {
7575
private ExceptionDataFieldFormatter dataItemsFormatter;
7676

7777
/* Constructors -- building a new descriptive exception from the usual causal data: */
78-
public AndroidFilePickerLightException() {
78+
public void initializeExceptionData(String excptErrorMsg) {
7979
isError = false;
8080
errorCode = 0;
81-
errorMsg = "";
81+
errorMsg = excptErrorMsg;
8282
invokingJavaExcpt = null;
8383
defaultDataItemsFmtType = DEFAULT_DATA_ITEMS_TYPE;
8484
dataItemsFormatter = null;
8585
}
8686

87+
public AndroidFilePickerLightException(String excptErrorMsg) {
88+
initializeExceptionData(excptErrorMsg);
89+
}
90+
91+
public AndroidFilePickerLightException() {
92+
initializeExceptionData("<NO-EMSG>");
93+
}
94+
8795
protected static AndroidFilePickerLightException getNewInstance() {
8896
return new AndroidFilePickerLightException();
8997
}

AndroidFilePickerLightLibrary/src/main/java/com/maxieds/androidfilepickerlightlibrary/PermissionsHandler.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ protected static boolean hasAccessPermission(Activity activityCtx, String permNa
3434
public static final int REQUEST_REQUIRED_PERMISSIONS_CODE = 0;
3535
public static final int REQUEST_OPTIONAL_PERMISSIONS_CODE = 1;
3636

37+
public static boolean ABORT_ON_DENIED_PERMISSION = false;
38+
3739
public static boolean obtainRequiredPermissions(Activity activityCtx, String[] permsList) {
3840
if(android.os.Build.VERSION.SDK_INT >= 23) {
3941
activityCtx.requestPermissions(permsList, REQUEST_REQUIRED_PERMISSIONS_CODE);
@@ -47,8 +49,11 @@ public static boolean obtainRequiredPermissions(Activity activityCtx, String[] p
4749
}
4850
for(int pidx = 0; pidx < permsList.length; pidx++) {
4951
if(!hasAccessPermission(activityCtx, permsList[pidx])) {
50-
throw new FileChooserException.PermissionsErrorException();
51-
//return false;
52+
if (ABORT_ON_DENIED_PERMISSION) {
53+
throw new FileChooserException.PermissionsErrorException();
54+
} else {
55+
return false;
56+
}
5257
}
5358
}
5459
return true;

app/debug/app-debug.aab

-7 MB
Binary file not shown.

app/debug/app-debug.apk

100755100644
201 KB
Binary file not shown.
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"version": 1,
2+
"version": 3,
33
"artifactType": {
44
"type": "APK",
55
"kind": "Directory"
@@ -10,11 +10,11 @@
1010
{
1111
"type": "SINGLE",
1212
"filters": [],
13-
"properties": [],
14-
"versionCode": 1,
15-
"versionName": "1",
16-
"enabled": true,
13+
"attributes": [],
14+
"versionCode": 2,
15+
"versionName": "1.0",
1716
"outputFile": "app-debug.apk"
1817
}
19-
]
18+
],
19+
"elementType": "File"
2020
}

app/release/app-release.apk

3.07 MB
Binary file not shown.

0 commit comments

Comments
 (0)