Commit 524fd1f6 authored by zhouteng's avatar zhouteng

Merge branch 'dev'

parents 67f7ba7f f5d8bb3e
......@@ -4,13 +4,15 @@ import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
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;
......@@ -23,10 +25,10 @@ public class ShareExtendPlugin implements MethodChannel.MethodCallHandler, Plugi
/// the authorities for FileProvider
private static final int CODE_ASK_PERMISSION = 100;
private static final String CHANNEL = "share_extend";
private static final String CHANNEL = "com.zt.shareextend/share_extend";
private final Registrar mRegistrar;
private String text;
private List<String> list;
private String type;
public static void registerWith(Registrar registrar) {
......@@ -48,40 +50,41 @@ public class ShareExtendPlugin implements MethodChannel.MethodCallHandler, Plugi
throw new IllegalArgumentException("Map argument expected");
}
// Android does not support showing the share sheet at a particular point on screen.
share((String) call.argument("text"), (String) call.argument("type"));
share((List) call.argument("list"), (String) call.argument("type"));
result.success(null);
} else {
result.notImplemented();
}
}
private void share(String text, String type) {
if (text == null || text.isEmpty()) {
throw new IllegalArgumentException("Non-empty text expected");
private void share(List<String> list, String type) {
if (list == null || list.isEmpty()) {
throw new IllegalArgumentException("Non-empty list expected");
}
this.text = text;
this.list = list;
this.type = type;
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
if ("text".equals(type)) {
shareIntent.putExtra(Intent.EXTRA_TEXT, text);
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_TEXT, list.get(0));
shareIntent.setType("text/plain");
} else {
File f = new File(text);
if (!f.exists()) {
throw new IllegalArgumentException("file not exists");
}
if (ShareUtils.shouldRequestPermission(text)) {
if (!checkPermisson()) {
if (ShareUtils.shouldRequestPermission(list)) {
if (!checkPermission()) {
requestPermission();
return;
}
}
ArrayList<Uri> uriList = new ArrayList<>();
for (String path : list) {
File f = new File(path);
Uri uri = ShareUtils.getUriForFile(mRegistrar.activity(), f, type);
uriList.add(uri);
}
if ("image".equals(type)) {
shareIntent.setType("image/*");
......@@ -90,9 +93,18 @@ public class ShareExtendPlugin implements MethodChannel.MethodCallHandler, Plugi
} else {
shareIntent.setType("application/*");
}
shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
if (uriList.size() == 1) {
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM, uriList.get(0));
} else {
shareIntent.setAction(Intent.ACTION_SEND_MULTIPLE);
shareIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uriList);
}
}
startChooserActivity(shareIntent);
}
private void startChooserActivity(Intent shareIntent) {
Intent chooserIntent = Intent.createChooser(shareIntent, null /* dialog title optional */);
if (mRegistrar.activity() != null) {
mRegistrar.activity().startActivity(chooserIntent);
......@@ -102,12 +114,9 @@ public class ShareExtendPlugin implements MethodChannel.MethodCallHandler, Plugi
}
}
private boolean checkPermisson() {
if (ContextCompat.checkSelfPermission(mRegistrar.context(), Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED) {
return true;
}
return false;
private boolean checkPermission() {
return ContextCompat.checkSelfPermission(mRegistrar.context(), Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED;
}
private void requestPermission() {
......@@ -117,7 +126,7 @@ public class ShareExtendPlugin implements MethodChannel.MethodCallHandler, Plugi
@Override
public boolean onRequestPermissionsResult(int requestCode, String[] perms, int[] grantResults) {
if (requestCode == CODE_ASK_PERMISSION && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
share(text, type);
share(list, type);
}
return false;
}
......
......@@ -11,13 +11,14 @@ import android.provider.MediaStore;
import android.text.TextUtils;
import java.io.File;
import java.util.List;
import androidx.core.content.FileProvider;
public class ShareUtils {
/// get the uri for file
public static Uri getUriForFile(Context context, File file, String type) {
static Uri getUriForFile(Context context, File file, String type) {
String authorities = context.getPackageName() + ".shareextend.fileprovider";
......@@ -51,7 +52,16 @@ public class ShareUtils {
return uri;
}
public static boolean shouldRequestPermission(String path) {
static boolean shouldRequestPermission(List<String> pathList) {
for (String path : pathList) {
if (shouldRequestPermission(path)) {
return true;
}
}
return false;
}
static boolean shouldRequestPermission(String path) {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && isPathInExternalStorage(path);
}
......@@ -121,7 +131,7 @@ public class ShareUtils {
* @param audioFile
* @return content Uri
*/
public static Uri getAudioContentUri(Context context, File audioFile) {
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 + "=? ",
......
......@@ -7,6 +7,8 @@
-->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
......
#!/bin/sh
# This is a generated file; do not edit or check into version control.
export "FLUTTER_ROOT=/Users/zhouteng/Android/flutter"
export "FLUTTER_APPLICATION_PATH=/Users/zhouteng/flutter-demos/ShareExtend/example"
export "FLUTTER_TARGET=/Users/zhouteng/flutter-demos/ShareExtend/example/lib/main.dart"
export "FLUTTER_BUILD_DIR=build"
export "SYMROOT=${SOURCE_ROOT}/../build/ios"
export "FLUTTER_FRAMEWORK_DIR=/Users/zhouteng/Android/flutter/bin/cache/artifacts/engine/ios"
export "FLUTTER_BUILD_NAME=1.0.0"
export "FLUTTER_BUILD_NUMBER=1"
export "TRACK_WIDGET_CREATION=true"
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
platform :ios, '9.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
......@@ -27,6 +27,7 @@ def parse_KV_file(file, separator='=')
end
target 'Runner' do
use_frameworks!
# Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
# referring to absolute paths on developers' machines.
system('rm -rf .symlinks')
......
#import "GeneratedPluginRegistrant.h"
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:multi_image_picker/multi_image_picker.dart';
import 'package:share_extend/share_extend.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart';
void main() => runApp(new MyApp());
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => new _MyAppState();
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
......@@ -20,22 +22,22 @@ class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Container(
child: new Center(
child: new Column(
child: Center(
child: Column(
children: <Widget>[
new RaisedButton(
RaisedButton(
onPressed: () {
ShareExtend.share("share text", "text");
},
child: new Text("share text"),
child: Text("share text"),
),
new RaisedButton(
RaisedButton(
onPressed: () async {
File f = await ImagePicker.pickImage(
source: ImageSource.gallery);
......@@ -43,9 +45,9 @@ class _MyAppState extends State<MyApp> {
ShareExtend.share(f.path, "image");
}
},
child: new Text("share image"),
child: Text("share image"),
),
new RaisedButton(
RaisedButton(
onPressed: () async {
File f = await ImagePicker.pickVideo(
source: ImageSource.gallery);
......@@ -53,13 +55,19 @@ class _MyAppState extends State<MyApp> {
ShareExtend.share(f.path, "video");
}
},
child: new Text("share video"),
child: Text("share video"),
),
new RaisedButton(
RaisedButton(
onPressed: () {
_shareApplicationDocumentsFile();
},
child: new Text("share file"),
child: Text("share file"),
),
RaisedButton(
onPressed: () {
_shareMultipleImages();
},
child: Text("share multiple images"),
),
],
),
......@@ -69,10 +77,20 @@ class _MyAppState extends State<MyApp> {
);
}
///share multiple images
_shareMultipleImages() async {
List<Asset> assetList = await MultiImagePicker.pickImages(maxImages: 5);
var imageList = List<String>();
for (var asset in assetList) {
imageList.add(await asset.filePath);
}
ShareExtend.shareMultiple(imageList, "image");
}
///share the documents file
_shareApplicationDocumentsFile() async {
Directory dir = await getApplicationDocumentsDirectory();
File testFile = new File("${dir.path}/flutter/test.txt");
File testFile = File("${dir.path}/flutter/test.txt");
if (!await testFile.exists()) {
await testFile.create(recursive: true);
testFile.writeAsStringSync("test for share documents file");
......
......@@ -19,8 +19,14 @@ dependencies:
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.2
path_provider: ^1.4.0
multi_image_picker: ^4.5.9
# image_picker: ^0.6.1+10
image_picker:
path_provider:
git:
url: git@github.com:miguelpruivo/plugins.git
path: packages/image_picker
ref: image_picker-Fix-#41046
dev_dependencies:
flutter_test:
......@@ -29,6 +35,8 @@ dev_dependencies:
share_extend:
path: ../
# For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec
......
......@@ -4,18 +4,18 @@
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
FlutterMethodChannel* shareChannel = [FlutterMethodChannel
methodChannelWithName:@"share_extend"
methodChannelWithName:@"com.zt.shareextend/share_extend"
binaryMessenger:[registrar messenger]];
[shareChannel setMethodCallHandler:^(FlutterMethodCall *call, FlutterResult result) {
if ([@"share" isEqualToString:call.method]) {
NSDictionary *arguments = [call arguments];
NSString *shareText = arguments[@"text"];
NSArray *array = arguments[@"list"];
NSString *shareType = arguments[@"type"];
if (shareText.length == 0) {
if (array.count == 0) {
result(
[FlutterError errorWithCode:@"error" message:@"Non-empty text expected" details:nil]);
[FlutterError errorWithCode:@"error" message:@"Non-empty list expected" details:nil]);
return;
}
......@@ -31,14 +31,22 @@
}
if ([shareType isEqualToString:@"text"]) {
[self share:shareText atSource:originRect];
[self share:array atSource:originRect];
result(nil);
} else if ([shareType isEqualToString:@"image"]) {
UIImage *image = [UIImage imageWithContentsOfFile:shareText];
[self share:image atSource:originRect];
NSMutableArray * imageArray = [[NSMutableArray alloc] init];
for (NSString * path in array) {
UIImage *image = [UIImage imageWithContentsOfFile:path];
[imageArray addObject:image];
}
[self share:imageArray atSource:originRect];
} else {
NSURL *url = [NSURL fileURLWithPath:shareText];
[self share:url atSource:originRect];
NSMutableArray * urlArray = [[NSMutableArray alloc] init];
for (NSString * path in array) {
NSURL *url = [NSURL fileURLWithPath:path];
[urlArray addObject:url];
}
[self share:urlArray atSource:originRect];
result(nil);
}
} else {
......@@ -47,8 +55,8 @@
}];
}
+ (void)share:(id)sharedItems atSource:(CGRect)origin {
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:@[sharedItems] applicationActivities:nil];
+ (void)share:(NSArray *)sharedItems atSource:(CGRect)origin {
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:sharedItems applicationActivities:nil];
UIViewController *controller =[UIApplication sharedApplication].keyWindow.rootViewController;
......
/// A flutter plugin to share text, image, file with system ui.
/// It is compatible with both andorid and ios.
/// It is compatible with both android and ios.
///
///
/// A open source authorized by zhouteng [https://github.com/zhouteng0217/ShareExtend](https://github.com/zhouteng0217/ShareExtend).
......@@ -11,29 +11,43 @@ import 'dart:ui';
/// Plugin for summoning a platform share sheet.
class ShareExtend {
/// [MethodChannel] used to communicate with the platform side.
static const MethodChannel _channel = const MethodChannel('share_extend');
static const MethodChannel _channel =
const MethodChannel('com.zt.shareextend/share_extend');
static Future<void> shareMultiple(List<String> list, String type,
{Rect sharePositionOrigin}) {
assert(list != null && list.isNotEmpty);
return _shareInner(list, type, sharePositionOrigin: sharePositionOrigin);
}
static Future<void> share(String text, String type,
{Rect sharePositionOrigin}) {
assert(text != null);
assert(text.isNotEmpty);
List<String> list = [text];
return _shareInner(list, type, sharePositionOrigin: sharePositionOrigin);
}
/// method to share with system ui
/// It uses the ACTION_SEND Intent on Android and UIActivityViewController
/// on iOS.
/// type "text", "image" ,"file"
/// [list] can be text or path list
/// [type] "text", "image" ,"file"
/// [sharePositionOrigin] only supports ios
///
static Future<void> share(String text, String type,
static Future<void> _shareInner(List<String> list, String type,
{Rect sharePositionOrigin}) {
assert(text != null);
assert(text.isNotEmpty);
assert(list != null && list.isNotEmpty);
final Map<String, dynamic> params = <String, dynamic>{
'text': text,
'list': list,
'type': type
};
if (sharePositionOrigin != null) {
params['originX'] = sharePositionOrigin.left;
params['originY'] = sharePositionOrigin.top;
params['originWidth'] = sharePositionOrigin.width;
params['originHeight'] = sharePositionOrigin.height;
}
return _channel.invokeMethod('share', params);
}
}
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