Commit 5b4c9914 authored by zhouteng's avatar zhouteng

Merge branch 'dev'

parents 6f87f891 2904f3db
......@@ -8,9 +8,6 @@
android:authorities="${applicationId}.shareextend.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/share_extend_provider_path" />
</provider>
</application>
......
package com.zt.shareextend;
import android.text.TextUtils;
import android.webkit.MimeTypeMap;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
/**
* Created by zhouteng on 2020-03-29
*/
public class MimeUtils {
private static final String DEFAULT_MINE_TYPE = "application/octet-stream";
private static final Map<String, String> extensionToMimeTypeMap = new HashMap<>();
static {
add("epub", "application/epub+zip");
add("ogx", "application/ogg");
add("odp", "application/vnd.oasis.opendocument.presentation");
add("otp", "application/vnd.oasis.opendocument.presentation-template");
add("yt", "application/vnd.youtube.yt");
add("hwp", "application/x-hwp");
add("3gpp", "video/3gpp");
add("3gp", "video/3gpp");
add("3gpp2", "video/3gpp2");
add("3g2", "video/3gpp2");
add("oga", "audio/ogg");
add("ogg", "audio/ogg");
add("spx", "audio/ogg");
add("dng", "image/x-adobe-dng");
add("cr2", "image/x-canon-cr2");
add("raf", "image/x-fuji-raf");
add("nef", "image/x-nikon-nef");
add("nrw", "image/x-nikon-nrw");
add("orf", "image/x-olympus-orf");
add("rw2", "image/x-panasonic-rw2");
add("pef", "image/x-pentax-pef");
add("srw", "image/x-samsung-srw");
add("arw", "image/x-sony-arw");
add("ogv", "video/ogg");
add("tgz", "application/x-gtar-compressed");
add("taz", "application/x-gtar-compressed");
add("csv", "text/csv");
add("gz", "application/gzip");
add("cab", "application/vnd.ms-cab-compressed");
add("7z", "application/x-7z-compressed");
add("bz", "application/x-bzip");
add("bz2", "application/x-bzip2");
add("z", "application/x-compress");
add("jar", "application/x-java-archive");
add("lzma", "application/x-lzma");
add("xz", "application/x-xz");
add("m3u", "audio/x-mpegurl");
add("m3u8", "audio/x-mpegurl");
add("p7b", "application/x-pkcs7-certificates");
add("spc", "application/x-pkcs7-certificates");
add("p7c", "application/pkcs7-mime");
add("p7s", "application/pkcs7-signature");
add("es", "application/ecmascript");
add("js", "application/javascript");
add("json", "application/json");
add("ts", "application/typescript");
add("perl", "text/x-perl");
add("pl", "text/x-perl");
add("pm", "text/x-perl");
add("py", "text/x-python");
add("py3", "text/x-python");
add("py3x", "text/x-python");
add("pyx", "text/x-python");
add("wsgi", "text/x-python");
add("rb", "text/ruby");
add("sh", "application/x-sh");
add("yaml", "text/x-yaml");
add("yml", "text/x-yaml");
add("asm", "text/x-asm");
add("s", "text/x-asm");
add("cs", "text/x-csharp");
add("azw", "application/vnd.amazon.ebook");
add("ibooks", "application/x-ibooks+zip");
add("mobi", "application/x-mobipocket-ebook");
add("woff", "font/woff");
add("woff2", "font/woff2");
add("msg", "application/vnd.ms-outlook");
add("eml", "message/rfc822");
add("eot", "application/vnd.ms-fontobject");
add("ttf", "font/ttf");
add("otf", "font/otf");
add("ttc", "font/collection");
add("markdown", "text/markdown");
add("md", "text/markdown");
add("mkd", "text/markdown");
add("conf", "text/plain");
add("ini", "text/plain");
add("list", "text/plain");
add("log", "text/plain");
add("prop", "text/plain");
add("properties", "text/plain");
add("rc", "text/plain");
add("flv", "video/x-flv");
}
private static void add(String extension, String mimeType) {
if (!extensionToMimeTypeMap.containsKey(extension)) {
extensionToMimeTypeMap.put(extension, mimeType);
}
}
public static String getMineType(File file) {
final int lastDot = file.getName().lastIndexOf('.');
if (lastDot < 0) {
return DEFAULT_MINE_TYPE;
}
final String extension = file.getName().substring(lastDot + 1).toLowerCase();
String mineType = extensionToMimeTypeMap.get(extension);
if (!TextUtils.isEmpty(mineType)) {
return mineType;
}
mineType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
if (!TextUtils.isEmpty(mineType)) {
return mineType;
}
return DEFAULT_MINE_TYPE;
}
}
......@@ -86,7 +86,7 @@ public class ShareExtendPlugin implements MethodChannel.MethodCallHandler, Plugi
ArrayList<Uri> uriList = new ArrayList<>();
for (String path : list) {
File f = new File(path);
Uri uri = ShareUtils.getUriForFile(mRegistrar.activity(), f, type);
Uri uri = ShareUtils.getUriForFile(mRegistrar.activity(), f);
uriList.add(uri);
}
......
package com.zt.shareextend;
import androidx.core.content.FileProvider;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.ProviderInfo;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
public class ShareExtendProvider extends FileProvider {
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.io.File;
import java.io.FileNotFoundException;
public class ShareExtendProvider extends ContentProvider {
private static final String[] COLUMNS = {
OpenableColumns.DISPLAY_NAME,
OpenableColumns.SIZE,
MediaStore.MediaColumns.DATA
};
@Override
public void attachInfo(Context context, ProviderInfo info) {
super.attachInfo(context, info);
// Sanity check our security
if (info.exported) {
throw new SecurityException("Provider must not be exported");
}
if (!info.grantUriPermissions) {
throw new SecurityException("Provider must grant uri permissions");
}
}
@Override
public boolean onCreate() {
return false;
}
@Nullable
@Override
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException {
final File file = getFileForUri(uri);
final int fileMode = modeToMode(mode);
return ParcelFileDescriptor.open(file, fileMode);
}
private static int modeToMode(String mode) {
int modeBits;
if ("r".equals(mode)) {
modeBits = ParcelFileDescriptor.MODE_READ_ONLY;
} else if ("w".equals(mode) || "wt".equals(mode)) {
modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
| ParcelFileDescriptor.MODE_CREATE
| ParcelFileDescriptor.MODE_TRUNCATE;
} else if ("wa".equals(mode)) {
modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
| ParcelFileDescriptor.MODE_CREATE
| ParcelFileDescriptor.MODE_APPEND;
} else if ("rw".equals(mode)) {
modeBits = ParcelFileDescriptor.MODE_READ_WRITE
| ParcelFileDescriptor.MODE_CREATE;
} else if ("rwt".equals(mode)) {
modeBits = ParcelFileDescriptor.MODE_READ_WRITE
| ParcelFileDescriptor.MODE_CREATE
| ParcelFileDescriptor.MODE_TRUNCATE;
} else {
throw new IllegalArgumentException("Invalid mode: " + mode);
}
return modeBits;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
File file = getFileForUri(uri);
if (projection == null) {
projection = COLUMNS;
}
String[] cols = new String[projection.length];
Object[] values = new Object[projection.length];
int i = 0;
for (String col : projection) {
if (OpenableColumns.DISPLAY_NAME.equals(col)) {
cols[i] = OpenableColumns.DISPLAY_NAME;
values[i++] = file.getName();
} else if (OpenableColumns.SIZE.equals(col)) {
cols[i] = OpenableColumns.SIZE;
values[i++] = file.length();
} else if (MediaStore.MediaColumns.DATA.equals(col)) {
cols[i] = MediaStore.MediaColumns.DATA;
values[i++] = file.getAbsolutePath();
}
}
cols = copyOf(cols, i);
values = copyOf(values, i);
final MatrixCursor cursor = new MatrixCursor(cols, 1);
cursor.addRow(values);
return cursor;
}
private static String[] copyOf(String[] original, int newLength) {
final String[] result = new String[newLength];
System.arraycopy(original, 0, result, 0, newLength);
return result;
}
private static Object[] copyOf(Object[] original, int newLength) {
final Object[] result = new Object[newLength];
System.arraycopy(original, 0, result, 0, newLength);
return result;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return MimeUtils.getMineType(getFileForUri(uri));
}
public static Uri getUriForPath(String authority, String path) {
return new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(authority)
.path(Uri.encode(path))
.build();
}
private File getFileForUri(Uri uri) {
String path = Uri.decode(uri.getPath()).substring(1);
return new File(path);
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
throw new UnsupportedOperationException("No external inserts");
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
throw new UnsupportedOperationException("No external deletes");
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
throw new UnsupportedOperationException("No external updates");
}
}
package com.zt.shareextend;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.MediaStore;
import android.text.TextUtils;
import java.io.File;
import java.util.List;
import androidx.core.content.FileProvider;
public class ShareUtils {
class ShareUtils {
/// get the uri for file
static Uri getUriForFile(Context context, File file, String type) {
static Uri getUriForFile(Context context, File file) {
String authorities = context.getPackageName() + ".shareextend.fileprovider";
Uri uri;
// 低版本直接用 Uri.fromFile
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
uri = Uri.fromFile(file);
} else {
// 使用 FileProvider 会在某些 app 下不支持(在使用FileProvider 方式情况下QQ不能支持图片、视频分享,微信不支持视频分享)
uri = FileProvider.getUriForFile(context, authorities, file);
if (!isPathInExternalStorage(file.getAbsolutePath()) || "file".equals(type)) {
return uri;
}
ContentResolver cR = context.getContentResolver();
if (uri != null && !TextUtils.isEmpty(uri.toString())) {
String fileType = cR.getType(uri);
// 使用 MediaStore 的 content:// 而不是自己 FileProvider 提供的uri,不然有些app无法适配
if (!TextUtils.isEmpty(fileType)) {
if (fileType.contains("video/")) {
uri = getVideoContentUri(context, file);
} else if (fileType.contains("image/")) {
uri = getImageContentUri(context, file);
} else if (fileType.contains("audio/")) {
uri = getAudioContentUri(context, file);
}
}
}
uri = ShareExtendProvider.getUriForPath(authorities, file.getAbsolutePath());
}
return uri;
}
static boolean shouldRequestPermission(List<String> pathList) {
for (String path : pathList) {
if (shouldRequestPermission(path)) {
......@@ -61,7 +36,7 @@ public class ShareUtils {
return false;
}
static boolean shouldRequestPermission(String path) {
private static boolean shouldRequestPermission(String path) {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && isPathInExternalStorage(path);
}
......@@ -69,85 +44,4 @@ public class ShareUtils {
File storagePath = Environment.getExternalStorageDirectory();
return path.startsWith(storagePath.getAbsolutePath());
}
/**
* Gets the content:// URI from the given corresponding path to a file
*
* @param context
* @param imageFile
* @return content Uri
*/
public static Uri getImageContentUri(Context context, File imageFile) {
String filePath = imageFile.getAbsolutePath();
Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
new String[]{MediaStore.Images.Media._ID}, MediaStore.Images.Media.DATA + "=? ",
new String[]{filePath}, null);
if (cursor != null && cursor.moveToFirst()) {
int id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID));
Uri baseUri = Uri.parse("content://media/external/images/media");
return Uri.withAppendedPath(baseUri, "" + id);
} else {
if (imageFile.exists()) {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DATA, filePath);
return context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
} else {
return null;
}
}
}
/**
* Gets the content:// URI from the given corresponding path to a file
*
* @param context
* @param videoFile
* @return content Uri
*/
public static Uri getVideoContentUri(Context context, File videoFile) {
String filePath = videoFile.getAbsolutePath();
Cursor cursor = context.getContentResolver().query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
new String[]{MediaStore.Video.Media._ID}, MediaStore.Video.Media.DATA + "=? ",
new String[]{filePath}, null);
if (cursor != null && cursor.moveToFirst()) {
int id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID));
Uri baseUri = Uri.parse("content://media/external/video/media");
return Uri.withAppendedPath(baseUri, "" + id);
} else {
if (videoFile.exists()) {
ContentValues values = new ContentValues();
values.put(MediaStore.Video.Media.DATA, filePath);
return context.getContentResolver().insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values);
} else {
return null;
}
}
}
/**
* Gets the content:// URI from the given corresponding path to a file
*
* @param context
* @param audioFile
* @return content Uri
*/
private static Uri getAudioContentUri(Context context, File audioFile) {
String filePath = audioFile.getAbsolutePath();
Cursor cursor = context.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
new String[]{MediaStore.Audio.Media._ID}, MediaStore.Audio.Media.DATA + "=? ",
new String[]{filePath}, null);
if (cursor != null && cursor.moveToFirst()) {
int id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID));
Uri baseUri = Uri.parse("content://media/external/audio/media");
return Uri.withAppendedPath(baseUri, "" + id);
} else {
if (audioFile.exists()) {
ContentValues values = new ContentValues();
values.put(MediaStore.Audio.Media.DATA, filePath);
return context.getContentResolver().insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values);
} else {
return null;
}
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<resources>
<paths>
<root-path
name="root_path"
path="/" />
</paths>
</resources>
\ No newline at end of file
......@@ -6,6 +6,7 @@
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
......
......@@ -96,7 +96,9 @@ class _MyAppState extends State<MyApp> {
}
Future<String> _writeByteToImageFile(ByteData byteData) async {
Directory dir = await getApplicationDocumentsDirectory();
Directory dir = Platform.isAndroid
? await getExternalStorageDirectory()
: await getApplicationDocumentsDirectory();
File imageFile = new File(
"${dir.path}/flutter/${DateTime.now().millisecondsSinceEpoch}.png");
imageFile.createSync(recursive: true);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment