Mon 21 Jul 22:43:21 CEST 2025
This commit is contained in:
parent
0d8d9fe2c8
commit
bb652baebd
|
@ -0,0 +1,216 @@
|
||||||
|
/*
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.cordova.file;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.provider.DocumentsContract;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
import android.provider.OpenableColumns;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import org.apache.cordova.CordovaResourceApi;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
public class ContentFilesystem extends Filesystem {
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
|
||||||
|
public ContentFilesystem(Context context, CordovaResourceApi resourceApi) {
|
||||||
|
super(Uri.parse("content://"), "content", resourceApi);
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Uri toNativeUri(LocalFilesystemURL inputURL) {
|
||||||
|
String authorityAndPath = inputURL.uri.getEncodedPath().substring(this.name.length() + 2);
|
||||||
|
if (authorityAndPath.length() < 2) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String ret = "content://" + authorityAndPath;
|
||||||
|
String query = inputURL.uri.getEncodedQuery();
|
||||||
|
if (query != null) {
|
||||||
|
ret += '?' + query;
|
||||||
|
}
|
||||||
|
String frag = inputURL.uri.getEncodedFragment();
|
||||||
|
if (frag != null) {
|
||||||
|
ret += '#' + frag;
|
||||||
|
}
|
||||||
|
return Uri.parse(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LocalFilesystemURL toLocalUri(Uri inputURL) {
|
||||||
|
if (!"content".equals(inputURL.getScheme())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String subPath = inputURL.getEncodedPath();
|
||||||
|
if (subPath.length() > 0) {
|
||||||
|
subPath = subPath.substring(1);
|
||||||
|
}
|
||||||
|
Uri.Builder b = new Uri.Builder()
|
||||||
|
.scheme(LocalFilesystemURL.FILESYSTEM_PROTOCOL)
|
||||||
|
.authority("localhost")
|
||||||
|
.path(name)
|
||||||
|
.appendPath(inputURL.getAuthority());
|
||||||
|
if (subPath.length() > 0) {
|
||||||
|
b.appendEncodedPath(subPath);
|
||||||
|
}
|
||||||
|
Uri localUri = b.encodedQuery(inputURL.getEncodedQuery())
|
||||||
|
.encodedFragment(inputURL.getEncodedFragment())
|
||||||
|
.build();
|
||||||
|
return LocalFilesystemURL.parse(localUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JSONObject getFileForLocalURL(LocalFilesystemURL inputURL,
|
||||||
|
String fileName, JSONObject options, boolean directory) throws IOException, TypeMismatchException, JSONException {
|
||||||
|
throw new UnsupportedOperationException("getFile() not supported for content:. Use resolveLocalFileSystemURL instead.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeFileAtLocalURL(LocalFilesystemURL inputURL)
|
||||||
|
throws NoModificationAllowedException {
|
||||||
|
Uri contentUri = toNativeUri(inputURL);
|
||||||
|
try {
|
||||||
|
context.getContentResolver().delete(contentUri, null, null);
|
||||||
|
} catch (UnsupportedOperationException t) {
|
||||||
|
// Was seeing this on the File mobile-spec tests on 4.0.3 x86 emulator.
|
||||||
|
// The ContentResolver applies only when the file was registered in the
|
||||||
|
// first case, which is generally only the case with images.
|
||||||
|
throw new NoModificationAllowedException("Deleting not supported for content uri: " + contentUri);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean recursiveRemoveFileAtLocalURL(LocalFilesystemURL inputURL)
|
||||||
|
throws NoModificationAllowedException {
|
||||||
|
throw new NoModificationAllowedException("Cannot remove content url");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LocalFilesystemURL[] listChildren(LocalFilesystemURL inputURL) throws FileNotFoundException {
|
||||||
|
throw new UnsupportedOperationException("readEntriesAtLocalURL() not supported for content:. Use resolveLocalFileSystemURL instead.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JSONObject getFileMetadataForLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException {
|
||||||
|
long size = -1;
|
||||||
|
long lastModified = 0;
|
||||||
|
Uri nativeUri = toNativeUri(inputURL);
|
||||||
|
String mimeType = resourceApi.getMimeType(nativeUri);
|
||||||
|
Cursor cursor = openCursorForURL(nativeUri);
|
||||||
|
try {
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
size = resourceSizeForCursor(cursor);
|
||||||
|
Long modified = lastModifiedDateForCursor(cursor);
|
||||||
|
if (modified != null)
|
||||||
|
lastModified = modified.longValue();
|
||||||
|
} else {
|
||||||
|
// Some content providers don't support cursors at all!
|
||||||
|
CordovaResourceApi.OpenForReadResult offr = resourceApi.openForRead(nativeUri);
|
||||||
|
size = offr.length;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new FileNotFoundException();
|
||||||
|
} finally {
|
||||||
|
if (cursor != null)
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject metadata = new JSONObject();
|
||||||
|
try {
|
||||||
|
metadata.put("size", size);
|
||||||
|
metadata.put("type", mimeType);
|
||||||
|
metadata.put("name", name);
|
||||||
|
metadata.put("fullPath", inputURL.path);
|
||||||
|
metadata.put("lastModifiedDate", lastModified);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long writeToFileAtURL(LocalFilesystemURL inputURL, String data,
|
||||||
|
int offset, boolean isBinary) throws NoModificationAllowedException {
|
||||||
|
throw new NoModificationAllowedException("Couldn't write to file given its content URI");
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public long truncateFileAtURL(LocalFilesystemURL inputURL, long size)
|
||||||
|
throws NoModificationAllowedException {
|
||||||
|
throw new NoModificationAllowedException("Couldn't truncate file given its content URI");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Cursor openCursorForURL(Uri nativeUri) {
|
||||||
|
ContentResolver contentResolver = context.getContentResolver();
|
||||||
|
try {
|
||||||
|
return contentResolver.query(nativeUri, null, null, null, null);
|
||||||
|
} catch (UnsupportedOperationException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long resourceSizeForCursor(Cursor cursor) {
|
||||||
|
int columnIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
|
||||||
|
if (columnIndex != -1) {
|
||||||
|
String sizeStr = cursor.getString(columnIndex);
|
||||||
|
if (sizeStr != null) {
|
||||||
|
return Long.parseLong(sizeStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Long lastModifiedDateForCursor(Cursor cursor) {
|
||||||
|
int columnIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DATE_MODIFIED);
|
||||||
|
if (columnIndex == -1) {
|
||||||
|
columnIndex = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_LAST_MODIFIED);
|
||||||
|
}
|
||||||
|
if (columnIndex != -1) {
|
||||||
|
String dateStr = cursor.getString(columnIndex);
|
||||||
|
if (dateStr != null) {
|
||||||
|
return Long.parseLong(dateStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String filesystemPathForURL(LocalFilesystemURL url) {
|
||||||
|
File f = resourceApi.mapUriToFile(toNativeUri(url));
|
||||||
|
return f == null ? null : f.getAbsolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LocalFilesystemURL URLforFilesystemPath(String path) {
|
||||||
|
// Returns null as we don't support reverse mapping back to content:// URLs
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canRemoveFileAtLocalURL(LocalFilesystemURL inputURL) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user