Skip to content

Commit 8faf9ef

Browse files
committed
Merge branch 'master' into allowed-app-dirs-external-sdcards
2 parents 5731347 + 265a932 commit 8faf9ef

14 files changed

+591
-581
lines changed

.asf.yaml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,32 @@
1515
# specific language governing permissions and limitations
1616
# under the License.
1717

18+
github:
19+
description: Apache Cordova File Plugin
20+
homepage: https://cordova.apache.org/
21+
22+
labels:
23+
- android
24+
- cordova
25+
- hacktoberfest
26+
- ios
27+
- java
28+
- javascript
29+
- library
30+
- mobile
31+
- nodejs
32+
- objective-c
33+
34+
features:
35+
wiki: false
36+
issues: true
37+
projects: true
38+
39+
enabled_merge_buttons:
40+
squash: true
41+
merge: false
42+
rebase: false
43+
1844
notifications:
1945
commits: commits@cordova.apache.org
2046
issues: issues@cordova.apache.org

README.md

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,17 +95,17 @@ Each URL is in the form _file:///path/to/spot/_, and can be converted to a
9595
in here. (_iOS_, _Android_, _BlackBerry 10_, _OSX_, _windows_)
9696

9797
* `cordova.file.externalApplicationStorageDirectory` - Application space on
98-
external storage. (_Android_)
98+
external storage. (_Android_). See [Quirks](#androids-external-storage-quirks).
9999

100100
* `cordova.file.externalDataDirectory` - Where to put app-specific data files on
101-
external storage. (_Android_)
101+
external storage. (_Android_). See [Quirks](#androids-external-storage-quirks).
102102

103103
* `cordova.file.externalCacheDirectory` - Application cache on external storage.
104-
(_Android_)
104+
(_Android_). See [Quirks](#androids-external-storage-quirks).
105105

106106
* `cordova.file.externalMediaDirectory` - Application media directory on external storage. (_Android_)
107107

108-
* `cordova.file.externalRootDirectory` - External storage (SD card) root. (_Android_, _BlackBerry 10_)
108+
* `cordova.file.externalRootDirectory` - External storage (SD card) root. (_Android_, _BlackBerry 10_). See [Quirks](#androids-external-storage-quirks).
109109

110110
* `cordova.file.tempDirectory` - Temp directory that the OS can clear at will. Do not
111111
rely on the OS to clear this directory; your app should always remove files as
@@ -194,6 +194,26 @@ the `cordova.file.*` properties map to physical paths on a real device.
194194
**Note**: If external storage can't be mounted, the `cordova.file.external*`
195195
properties are `null`.
196196

197+
#### Android's External Storage Quirks
198+
199+
With the introduction of [Scoped Storage](https://source.android.com/docs/core/storage/scoped) access to External Storage is unreliable or limited via File APIs.
200+
Scoped Storage was introduced in API 29. While existing apps may have the ability to opt out, this option is not available for new apps. On Android API 30 and later, Scoped Storage is fully enforced.
201+
202+
Additionally, Direct File Access **is not** supported on API 29. This means this plugin **cannot** access external storage mediums on API 29 devices.
203+
204+
API 30 introduced [FUSE](https://source.android.com/docs/core/storage/scoped) which allowed limited access to external storage using File APIs, allowing this plugin to
205+
partially work again.
206+
207+
Limited access includes but isn't limited to:
208+
- Read only access with appropriate `READ_EXTERNAL` or [READ_MEDIA_*](https://developer.android.com/training/data-storage/shared/media#access-other-apps-files) permissions.
209+
- Read only access is limited to media files, but not documents.
210+
- Writes are limited to only files owned by your app. Modifying files owned by a third-party app (including an image file created via the camera plugin for example) is not possible via the File API.
211+
- Not all paths in external storage is writable.
212+
213+
These limitations only applies to external filesystems (e.g. `cordova.file.external*` paths). Internal filesystems such as `cordova.file.dataDirectory` path are not imposed by these limitations.
214+
215+
If interfacing with the external file system is a requirement for your application, consider using a [MediaStore](https://www.npmjs.com/search?q=ecosystem%3Acordova%20storage%20access%20framework) plugin instead.
216+
197217
### OS X File System Layout
198218

199219
| Device Path | `cordova.file.*` | `iosExtraFileSystems` | r/w? | OS clears | private |

RELEASENOTES.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,18 @@
2020
-->
2121
# Release Notes
2222

23+
### 8.0.1 (Oct 27, 2023)
24+
25+
**Fixes:**
26+
27+
* [GH-608](https://github.com/apache/cordova-plugin-file/pull/608) fix(android): `hasWritePermission` for SDK 33
28+
29+
**Others:**
30+
31+
* [GH-609](https://github.com/apache/cordova-plugin-file/pull/609) chore: update asf config
32+
* [GH-607](https://github.com/apache/cordova-plugin-file/pull/607) refactor(android): various cleanup
33+
* [GH-593](https://github.com/apache/cordova-plugin-file/pull/593) doc(android): expanded on external filesystems limitations
34+
2335
### 8.0.0 (Jul 07, 2023)
2436

2537
* [GH-534](https://github.com/apache/cordova-plugin-file/pull/534) fix(android): `FileError` on a content `resolveLocalFileSystemURL`

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "cordova-plugin-file",
3-
"version": "8.0.1-dev",
3+
"version": "8.0.2-dev",
44
"description": "Cordova File Plugin",
55
"types": "./types/index.d.ts",
66
"cordova": {

plugin.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
2222
xmlns:android="http://schemas.android.com/apk/res/android"
2323
id="cordova-plugin-file"
24-
version="8.0.1-dev">
24+
version="8.0.2-dev">
2525
<name>File</name>
2626
<description>Cordova File Plugin</description>
2727
<license>Apache 2.0</license>

src/android/AssetFilesystem.java

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ Licensed to the Apache Software Foundation (ASF) under one
2424
import org.apache.cordova.CordovaPreferences;
2525
import org.apache.cordova.CordovaResourceApi;
2626
import org.apache.cordova.LOG;
27-
import org.json.JSONArray;
2827
import org.json.JSONException;
2928
import org.json.JSONObject;
3029

@@ -137,7 +136,7 @@ private long getAssetSize(String assetPath) throws FileNotFoundException {
137136
public AssetFilesystem(AssetManager assetManager, CordovaResourceApi resourceApi, CordovaPreferences preferences) {
138137
super(Uri.parse("file:///android_asset/"), "assets", resourceApi, preferences);
139138
this.assetManager = assetManager;
140-
}
139+
}
141140

142141
@Override
143142
public Uri toNativeUri(LocalFilesystemURL inputURL) {
@@ -204,7 +203,7 @@ public LocalFilesystemURL[] listChildren(LocalFilesystemURL inputURL) throws Fil
204203
entries[i] = localUrlforFullPath(new File(inputURL.path, files[i]).getPath());
205204
}
206205
return entries;
207-
}
206+
}
208207

209208
@Override
210209
public JSONObject getFileForLocalURL(LocalFilesystemURL inputURL,
@@ -241,25 +240,25 @@ public JSONObject getFileForLocalURL(LocalFilesystemURL inputURL,
241240
}
242241

243242
@Override
244-
public JSONObject getFileMetadataForLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException {
243+
public JSONObject getFileMetadataForLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException {
245244
JSONObject metadata = new JSONObject();
246245
long size = inputURL.isDirectory ? 0 : getAssetSize(inputURL.path);
247246
try {
248-
metadata.put("size", size);
249-
metadata.put("type", inputURL.isDirectory ? "text/directory" : resourceApi.getMimeType(toNativeUri(inputURL)));
250-
metadata.put("name", new File(inputURL.path).getName());
251-
metadata.put("fullPath", inputURL.path);
252-
metadata.put("lastModifiedDate", 0);
247+
metadata.put("size", size);
248+
metadata.put("type", inputURL.isDirectory ? "text/directory" : resourceApi.getMimeType(toNativeUri(inputURL)));
249+
metadata.put("name", new File(inputURL.path).getName());
250+
metadata.put("fullPath", inputURL.path);
251+
metadata.put("lastModifiedDate", 0);
253252
} catch (JSONException e) {
254253
return null;
255254
}
256255
return metadata;
257-
}
256+
}
258257

259-
@Override
260-
public boolean canRemoveFileAtLocalURL(LocalFilesystemURL inputURL) {
261-
return false;
262-
}
258+
@Override
259+
public boolean canRemoveFileAtLocalURL(LocalFilesystemURL inputURL) {
260+
return false;
261+
}
263262

264263
@Override
265264
long writeToFileAtURL(LocalFilesystemURL inputURL, String data, int offset, boolean isBinary) throws NoModificationAllowedException, IOException {

src/android/ContentFilesystem.java

Lines changed: 56 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ public class ContentFilesystem extends Filesystem {
3838

3939
private final Context context;
4040

41-
public ContentFilesystem(Context context, CordovaResourceApi resourceApi, CordovaPreferences preferences) {
42-
super(Uri.parse("content://"), "content", resourceApi, preferences);
41+
public ContentFilesystem(Context context, CordovaResourceApi resourceApi, CordovaPreferences preferences) {
42+
super(Uri.parse("content://"), "content", resourceApi, preferences);
4343
this.context = context;
44-
}
44+
}
4545

4646
@Override
4747
public Uri toNativeUri(LocalFilesystemURL inputURL) {
@@ -84,41 +84,41 @@ public LocalFilesystemURL toLocalUri(Uri inputURL) {
8484
}
8585

8686
@Override
87-
public JSONObject getFileForLocalURL(LocalFilesystemURL inputURL,
88-
String fileName, JSONObject options, boolean directory) throws IOException, TypeMismatchException, JSONException {
87+
public JSONObject getFileForLocalURL(LocalFilesystemURL inputURL,
88+
String fileName, JSONObject options, boolean directory) throws IOException, TypeMismatchException, JSONException {
8989
throw new UnsupportedOperationException("getFile() not supported for content:. Use resolveLocalFileSystemURL instead.");
90-
}
90+
}
9191

92-
@Override
93-
public boolean removeFileAtLocalURL(LocalFilesystemURL inputURL)
94-
throws NoModificationAllowedException {
92+
@Override
93+
public boolean removeFileAtLocalURL(LocalFilesystemURL inputURL)
94+
throws NoModificationAllowedException {
9595
Uri contentUri = toNativeUri(inputURL);
96-
try {
96+
try {
9797
context.getContentResolver().delete(contentUri, null, null);
98-
} catch (UnsupportedOperationException t) {
99-
// Was seeing this on the File mobile-spec tests on 4.0.3 x86 emulator.
100-
// The ContentResolver applies only when the file was registered in the
101-
// first case, which is generally only the case with images.
98+
} catch (UnsupportedOperationException t) {
99+
// Was seeing this on the File mobile-spec tests on 4.0.3 x86 emulator.
100+
// The ContentResolver applies only when the file was registered in the
101+
// first case, which is generally only the case with images.
102102
NoModificationAllowedException nmae = new NoModificationAllowedException("Deleting not supported for content uri: " + contentUri);
103103
nmae.initCause(t);
104104
throw nmae;
105-
}
105+
}
106106
return true;
107-
}
107+
}
108108

109-
@Override
110-
public boolean recursiveRemoveFileAtLocalURL(LocalFilesystemURL inputURL)
111-
throws NoModificationAllowedException {
112-
throw new NoModificationAllowedException("Cannot remove content url");
113-
}
109+
@Override
110+
public boolean recursiveRemoveFileAtLocalURL(LocalFilesystemURL inputURL)
111+
throws NoModificationAllowedException {
112+
throw new NoModificationAllowedException("Cannot remove content url");
113+
}
114114

115115
@Override
116116
public LocalFilesystemURL[] listChildren(LocalFilesystemURL inputURL) throws FileNotFoundException {
117117
throw new UnsupportedOperationException("readEntriesAtLocalURL() not supported for content:. Use resolveLocalFileSystemURL instead.");
118118
}
119119

120-
@Override
121-
public JSONObject getFileMetadataForLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException {
120+
@Override
121+
public JSONObject getFileMetadataForLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException {
122122
long size = -1;
123123
long lastModified = 0;
124124
Uri nativeUri = toNativeUri(inputURL);
@@ -143,55 +143,55 @@ public JSONObject getFileMetadataForLocalURL(LocalFilesystemURL inputURL) throws
143143
fnfe.initCause(e);
144144
throw fnfe;
145145
} finally {
146-
if (cursor != null)
147-
cursor.close();
146+
if (cursor != null)
147+
cursor.close();
148148
}
149149

150150
JSONObject metadata = new JSONObject();
151151
try {
152-
metadata.put("size", size);
153-
metadata.put("type", mimeType);
154-
metadata.put("name", name);
155-
metadata.put("fullPath", inputURL.path);
156-
metadata.put("lastModifiedDate", lastModified);
152+
metadata.put("size", size);
153+
metadata.put("type", mimeType);
154+
metadata.put("name", name);
155+
metadata.put("fullPath", inputURL.path);
156+
metadata.put("lastModifiedDate", lastModified);
157157
} catch (JSONException e) {
158-
return null;
158+
return null;
159159
}
160160
return metadata;
161-
}
161+
}
162162

163-
@Override
164-
public long writeToFileAtURL(LocalFilesystemURL inputURL, String data,
165-
int offset, boolean isBinary) throws NoModificationAllowedException {
163+
@Override
164+
public long writeToFileAtURL(LocalFilesystemURL inputURL, String data,
165+
int offset, boolean isBinary) throws NoModificationAllowedException {
166166
throw new NoModificationAllowedException("Couldn't write to file given its content URI");
167167
}
168-
@Override
169-
public long truncateFileAtURL(LocalFilesystemURL inputURL, long size)
170-
throws NoModificationAllowedException {
168+
@Override
169+
public long truncateFileAtURL(LocalFilesystemURL inputURL, long size)
170+
throws NoModificationAllowedException {
171171
throw new NoModificationAllowedException("Couldn't truncate file given its content URI");
172-
}
172+
}
173173

174-
protected Cursor openCursorForURL(Uri nativeUri) {
174+
protected Cursor openCursorForURL(Uri nativeUri) {
175175
ContentResolver contentResolver = context.getContentResolver();
176176
try {
177177
return contentResolver.query(nativeUri, null, null, null, null);
178178
} catch (UnsupportedOperationException e) {
179179
return null;
180180
}
181-
}
181+
}
182182

183-
private Long resourceSizeForCursor(Cursor cursor) {
183+
private Long resourceSizeForCursor(Cursor cursor) {
184184
int columnIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
185185
if (columnIndex != -1) {
186186
String sizeStr = cursor.getString(columnIndex);
187187
if (sizeStr != null) {
188-
return Long.parseLong(sizeStr);
188+
return Long.parseLong(sizeStr);
189189
}
190190
}
191191
return null;
192-
}
193-
194-
protected Long lastModifiedDateForCursor(Cursor cursor) {
192+
}
193+
194+
protected Long lastModifiedDateForCursor(Cursor cursor) {
195195
int columnIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DATE_MODIFIED);
196196
if (columnIndex == -1) {
197197
columnIndex = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_LAST_MODIFIED);
@@ -203,22 +203,22 @@ protected Long lastModifiedDateForCursor(Cursor cursor) {
203203
}
204204
}
205205
return null;
206-
}
206+
}
207207

208208
@Override
209209
public String filesystemPathForURL(LocalFilesystemURL url) {
210210
File f = resourceApi.mapUriToFile(toNativeUri(url));
211211
return f == null ? null : f.getAbsolutePath();
212212
}
213213

214-
@Override
215-
public LocalFilesystemURL URLforFilesystemPath(String path) {
216-
// Returns null as we don't support reverse mapping back to content:// URLs
217-
return null;
218-
}
214+
@Override
215+
public LocalFilesystemURL URLforFilesystemPath(String path) {
216+
// Returns null as we don't support reverse mapping back to content:// URLs
217+
return null;
218+
}
219219

220-
@Override
221-
public boolean canRemoveFileAtLocalURL(LocalFilesystemURL inputURL) {
222-
return true;
223-
}
220+
@Override
221+
public boolean canRemoveFileAtLocalURL(LocalFilesystemURL inputURL) {
222+
return true;
223+
}
224224
}

0 commit comments

Comments
 (0)