Commit 7d6ac863 authored by zhouteng's avatar zhouteng

修复android平台视频分享到微信失败的问题

parent 401f3580
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"
android:authorities="com.zt.shareextend.fileprovider" android:authorities="${applicationId}.fileprovider"
android:exported="false" android:exported="false"
android:grantUriPermissions="true"> android:grantUriPermissions="true">
<meta-data <meta-data
......
package com.zt.shareextend; package com.zt.shareextend;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.common.PluginRegistry.Registrar;
import android.Manifest; import android.Manifest;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.os.Environment; import android.os.Environment;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import java.io.File; import java.io.File;
import java.util.Map; import java.util.Map;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.common.PluginRegistry.Registrar;
/** /**
* Plugin method host for presenting a share sheet via Intent * Plugin method host for presenting a share sheet via Intent
*/ */
public class ShareExtendPlugin implements MethodChannel.MethodCallHandler, PluginRegistry.RequestPermissionsResultListener { public class ShareExtendPlugin implements MethodChannel.MethodCallHandler, PluginRegistry.RequestPermissionsResultListener {
/// the authorities for FileProvider /// the authorities for FileProvider
private static final String authorities = "com.zt.shareextend.fileprovider";
private static final int CODE_ASK_PERMISSION = 100; private static final int CODE_ASK_PERMISSION = 100;
private static final String CHANNEL = "share_extend"; private static final String CHANNEL = "share_extend";
...@@ -84,12 +78,14 @@ public class ShareExtendPlugin implements MethodChannel.MethodCallHandler, Plugi ...@@ -84,12 +78,14 @@ public class ShareExtendPlugin implements MethodChannel.MethodCallHandler, Plugi
} }
File f = new File(text); File f = new File(text);
Uri uri = getUriForFile(mRegistrar.context(), f); Uri uri = ShareUtils.getUriForFile(mRegistrar.context(), f);
if ("file".equals(type)) {
shareIntent.setType("application/*");
}
if ("image".equals(type)) { if ("image".equals(type)) {
shareIntent.setType("image/*"); shareIntent.setType("image/*");
} else if ("video".equals(type)) {
shareIntent.setType("video/*");
} else {
shareIntent.setType("application/*");
} }
shareIntent.putExtra(Intent.EXTRA_STREAM, uri); shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
} }
...@@ -127,12 +123,4 @@ public class ShareExtendPlugin implements MethodChannel.MethodCallHandler, Plugi ...@@ -127,12 +123,4 @@ public class ShareExtendPlugin implements MethodChannel.MethodCallHandler, Plugi
} }
return false; return false;
} }
private static Uri getUriForFile(Context context, File file) {
if (Build.VERSION.SDK_INT >= 24) {
return FileProvider.getUriForFile(context, authorities, file);
} else {
return Uri.fromFile(file);
}
}
} }
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.provider.MediaStore;
import android.text.TextUtils;
import java.io.File;
import androidx.core.content.FileProvider;
public class ShareUtils {
/// get the uri for file
public static Uri getUriForFile(Context context, File file) {
String authorities = context.getPackageName() + ".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);
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);
}
}
}
}
return uri;
}
/**
* 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
*/
public 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;
}
}
}
}
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; };
9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; };
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
...@@ -176,7 +175,7 @@ ...@@ -176,7 +175,7 @@
97C146E61CF9000F007C117D /* Project object */ = { 97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastUpgradeCheck = 0910; LastUpgradeCheck = 1010;
ORGANIZATIONNAME = "The Chromium Authors"; ORGANIZATIONNAME = "The Chromium Authors";
TargetAttributes = { TargetAttributes = {
97C146ED1CF9000F007C117D = { 97C146ED1CF9000F007C117D = {
...@@ -209,7 +208,6 @@ ...@@ -209,7 +208,6 @@
files = ( files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */, 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
...@@ -332,12 +330,14 @@ ...@@ -332,12 +330,14 @@
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES; CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
...@@ -386,12 +386,14 @@ ...@@ -386,12 +386,14 @@
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES; CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
......
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "0910" LastUpgradeVersion = "1010"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
...@@ -26,7 +26,6 @@ ...@@ -26,7 +26,6 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES">
<Testables> <Testables>
</Testables> </Testables>
...@@ -46,7 +45,6 @@ ...@@ -46,7 +45,6 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0" launchStyle = "0"
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO" ignoresPersistentStateOnLaunch = "NO"
......
...@@ -25,31 +25,42 @@ class _MyAppState extends State<MyApp> { ...@@ -25,31 +25,42 @@ class _MyAppState extends State<MyApp> {
appBar: new AppBar( appBar: new AppBar(
title: const Text('Plugin example app'), title: const Text('Plugin example app'),
), ),
body: new Center( body: Container(
child: new Center(
child: new Column( child: new Column(
children: <Widget>[ children: <Widget>[
new RaisedButton( new RaisedButton(
onPressed: () { onPressed: () {
ShareExtend.share("share text", "text"); ShareExtend.share("share text", "text");
}, },
child: new Text("share text"), child: new Text("share text"),
),
new RaisedButton(
onPressed: () async {
File f = await ImagePicker.pickImage(
source: ImageSource.gallery);
ShareExtend.share(f.path, "image");
},
child: new Text("share image"),
),
new RaisedButton(
onPressed: () async {
File f = await ImagePicker.pickVideo(
source: ImageSource.gallery);
ShareExtend.share(f.path, "video");
},
child: new Text("share video"),
),
new RaisedButton(
onPressed: () {
_shareApplicationDocumentsFile();
},
child: new Text("share file"),
),
],
), ),
new RaisedButton( ),
onPressed: () async { ),
File f =
await ImagePicker.pickImage(source: ImageSource.gallery);
ShareExtend.share(f.path, "image");
},
child: new Text("share image"),
),
new RaisedButton(
onPressed: () {
_shareApplicationDocumentsFile();
},
child: new Text("share file"),
),
],
)),
), ),
); );
} }
......
#import "ShareExtendPlugin.h" #import "ShareExtendPlugin.h"
@implementation FLTShareExtendPlugin @implementation FLTShareExtendPlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
FlutterMethodChannel* shareChannel = [FlutterMethodChannel
methodChannelWithName:@"share_extend"
binaryMessenger:[registrar messenger]];
[shareChannel setMethodCallHandler:^(FlutterMethodCall *call, FlutterResult result) {
if ([@"share" isEqualToString:call.method]) {
NSDictionary *arguments = [call arguments];
NSString *shareText = arguments[@"text"];
NSString *shareType = arguments[@"type"];
if (shareText.length == 0) {
result(
[FlutterError errorWithCode:@"error" message:@"Non-empty text expected" details:nil]);
return;
}
NSNumber *originX = arguments[@"originX"];
NSNumber *originY = arguments[@"originY"];
NSNumber *originWidth = arguments[@"originWidth"];
NSNumber *originHeight = arguments[@"originHeight"];
CGRect originRect; + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
if (originX != nil && originY != nil && originWidth != nil && originHeight != nil) { FlutterMethodChannel* shareChannel = [FlutterMethodChannel
originRect = CGRectMake([originX doubleValue], [originY doubleValue], methodChannelWithName:@"share_extend"
[originWidth doubleValue], [originHeight doubleValue]); binaryMessenger:[registrar messenger]];
}
[shareChannel setMethodCallHandler:^(FlutterMethodCall *call, FlutterResult result) {
if ([shareType isEqualToString:@"text"]) { if ([@"share" isEqualToString:call.method]) {
[self share:shareText NSDictionary *arguments = [call arguments];
withController:[UIApplication sharedApplication].keyWindow.rootViewController NSString *shareText = arguments[@"text"];
atSource:originRect]; NSString *shareType = arguments[@"type"];
result(nil);
} else if([shareType isEqualToString:@"file"]) { if (shareText.length == 0) {
NSURL *url = [NSURL fileURLWithPath:shareText]; result(
[self share:url [FlutterError errorWithCode:@"error" message:@"Non-empty text expected" details:nil]);
withController:[UIApplication sharedApplication].keyWindow.rootViewController return;
atSource:originRect]; }
result(nil);
} else if ([shareType isEqualToString:@"image"]) { NSNumber *originX = arguments[@"originX"];
UIImage *image = [UIImage imageWithContentsOfFile:shareText]; NSNumber *originY = arguments[@"originY"];
[self share:image NSNumber *originWidth = arguments[@"originWidth"];
withController:[UIApplication sharedApplication].keyWindow.rootViewController NSNumber *originHeight = arguments[@"originHeight"];
atSource:originRect];
CGRect originRect;
if (originX != nil && originY != nil && originWidth != nil && originHeight != nil) {
originRect = CGRectMake([originX doubleValue], [originY doubleValue],
[originWidth doubleValue], [originHeight doubleValue]);
}
if ([shareType isEqualToString:@"text"]) {
[self share:shareText atSource:originRect];
result(nil);
} else if ([shareType isEqualToString:@"image"]) {
UIImage *image = [UIImage imageWithContentsOfFile:shareText];
[self share:image atSource:originRect];
} else {
NSURL *url = [NSURL fileURLWithPath:shareText];
[self share:url atSource:originRect];
result(nil);
}
} else {
result(FlutterMethodNotImplemented);
} }
} else { }];
result(FlutterMethodNotImplemented);
}
}];
} }
+ (void)share:(id)sharedItems + (void)share:(id)sharedItems atSource:(CGRect)origin {
withController:(UIViewController *)controller UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:@[sharedItems] applicationActivities:nil];
atSource:(CGRect)origin {
UIActivityViewController *activityViewController = UIViewController *controller =[UIApplication sharedApplication].keyWindow.rootViewController;
[[UIActivityViewController alloc] initWithActivityItems:@[ sharedItems ]
applicationActivities:nil]; activityViewController.popoverPresentationController.sourceView = controller.view;
activityViewController.popoverPresentationController.sourceView = controller.view; if (!CGRectIsEmpty(origin)) {
if (!CGRectIsEmpty(origin)) { activityViewController.popoverPresentationController.sourceRect = origin;
activityViewController.popoverPresentationController.sourceRect = origin; }
} [controller presentViewController:activityViewController animated:YES completion:nil];
[controller presentViewController:activityViewController animated:YES completion:nil];
} }
@end @end
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