66
77import android .content .Intent ;
88import io .flutter .plugin .common .MethodCall ;
9+ import android .net .Uri ;
10+ import android .os .Environment ;
11+ import androidx .annotation .NonNull ;
12+ import androidx .core .content .FileProvider ;
913import io .flutter .plugin .common .MethodChannel ;
1014import io .flutter .plugin .common .PluginRegistry .Registrar ;
15+ import java .io .*;
1116import java .util .Map ;
1217
1318/** Plugin method host for presenting a share sheet via Intent */
@@ -29,15 +34,36 @@ private SharePlugin(Registrar registrar) {
2934
3035 @ Override
3136 public void onMethodCall (MethodCall call , MethodChannel .Result result ) {
32- if (call .method .equals ("share" )) {
33- if (!(call .arguments instanceof Map )) {
34- throw new IllegalArgumentException ("Map argument expected" );
35- }
36- // Android does not support showing the share sheet at a particular point on screen.
37- share ((String ) call .argument ("text" ));
38- result .success (null );
39- } else {
40- result .notImplemented ();
37+ switch (call .method ) {
38+ case "share" :
39+ expectMapArguments (call );
40+ // Android does not support showing the share sheet at a particular point on screen.
41+ share ((String ) call .argument ("text" ));
42+ result .success (null );
43+ break ;
44+ case "shareFile" :
45+ expectMapArguments (call );
46+ // Android does not support showing the share sheet at a particular point on screen.
47+ try {
48+ shareFile (
49+ (String ) call .argument ("path" ),
50+ (String ) call .argument ("mimeType" ),
51+ (String ) call .argument ("subject" ),
52+ (String ) call .argument ("text" ));
53+ result .success (null );
54+ } catch (IOException e ) {
55+ result .error (e .getMessage (), null , null );
56+ }
57+ break ;
58+ default :
59+ result .notImplemented ();
60+ break ;
61+ }
62+ }
63+
64+ private void expectMapArguments (MethodCall call ) throws IllegalArgumentException {
65+ if (!(call .arguments instanceof Map )) {
66+ throw new IllegalArgumentException ("Map argument expected" );
4167 }
4268 }
4369
@@ -58,4 +84,96 @@ private void share(String text) {
5884 mRegistrar .context ().startActivity (chooserIntent );
5985 }
6086 }
87+
88+
89+ private void shareFile (String path , String mimeType , String subject , String text )
90+ throws IOException {
91+ if (path == null || path .isEmpty ()) {
92+ throw new IllegalArgumentException ("Non-empty path expected" );
93+ }
94+
95+ File file = new File (path );
96+ clearExternalShareFolder ();
97+ if (!fileIsOnExternal (file )) {
98+ file = copyToExternalShareFolder (file );
99+ }
100+
101+ Uri fileUri =
102+ FileProvider .getUriForFile (
103+ mRegistrar .context (),
104+ mRegistrar .context ().getPackageName () + ".flutter.share_provider" ,
105+ file );
106+
107+ Intent shareIntent = new Intent ();
108+ shareIntent .setAction (Intent .ACTION_SEND );
109+ shareIntent .putExtra (Intent .EXTRA_STREAM , fileUri );
110+ if (subject != null ) shareIntent .putExtra (Intent .EXTRA_SUBJECT , subject );
111+ if (text != null ) shareIntent .putExtra (Intent .EXTRA_TEXT , text );
112+ shareIntent .setType (mimeType != null ? mimeType : "*/*" );
113+ shareIntent .addFlags (Intent .FLAG_GRANT_READ_URI_PERMISSION );
114+ Intent chooserIntent = Intent .createChooser (shareIntent , null /* dialog title optional */ );
115+ if (mRegistrar .activity () != null ) {
116+ mRegistrar .activity ().startActivity (chooserIntent );
117+ } else {
118+ chooserIntent .addFlags (Intent .FLAG_ACTIVITY_NEW_TASK );
119+ mRegistrar .context ().startActivity (chooserIntent );
120+ }
121+ }
122+
123+ private boolean fileIsOnExternal (File file ) {
124+ try {
125+ String filePath = file .getCanonicalPath ();
126+ File externalDir = Environment .getExternalStorageDirectory ();
127+ return externalDir != null && filePath .startsWith (externalDir .getCanonicalPath ());
128+ } catch (IOException e ) {
129+ return false ;
130+ }
131+ }
132+
133+ @ SuppressWarnings ("ResultOfMethodCallIgnored" )
134+ private void clearExternalShareFolder () {
135+ File folder = getExternalShareFolder ();
136+ if (folder .exists ()) {
137+ for (File file : folder .listFiles ()) {
138+ file .delete ();
139+ }
140+ folder .delete ();
141+ }
142+ }
143+
144+ @ SuppressWarnings ("ResultOfMethodCallIgnored" )
145+ private File copyToExternalShareFolder (File file ) throws IOException {
146+ File folder = getExternalShareFolder ();
147+ if (!folder .exists ()) {
148+ folder .mkdirs ();
149+ }
150+
151+ File newFile = new File (folder , file .getName ());
152+ copy (file , newFile );
153+ return newFile ;
154+ }
155+
156+ @ NonNull
157+ private File getExternalShareFolder () {
158+ return new File (mRegistrar .context ().getExternalCacheDir (), "share" );
159+ }
160+
161+ private static void copy (File src , File dst ) throws IOException {
162+ InputStream in = new FileInputStream (src );
163+ try {
164+ OutputStream out = new FileOutputStream (dst );
165+ try {
166+ // Transfer bytes from in to out
167+ byte [] buf = new byte [1024 ];
168+ int len ;
169+ while ((len = in .read (buf )) > 0 ) {
170+ out .write (buf , 0 , len );
171+ }
172+ } finally {
173+ out .close ();
174+ }
175+ } finally {
176+ in .close ();
177+ }
178+ }
61179}
0 commit comments