I am trying to get a picture from the Internet, save it to my phone and send it right away through any instant messenger or mailer without opening the gallery to select a picture.

Activity:

public class MainActivity extends AppCompatActivity { private String remoteUrlString = "https://upload.wikimedia.org/wikipedia/commons/b/b5/Extreme_QR_code_to_Wikipedia_mobile_page.png"; private Button btnStart; private Context context; private static final int PERMISSIONS_REQUEST_SENDER = 14222; private File file = null; public File getFile() { return file; } public void setFile(File file) { this.file = file; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnStart = findViewById(R.id.button); btnStart.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { resendImage(); } }); } private void resendImage() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { System.out.println("PERMISSION_GRANTED == WRITE_EXTERNAL_STORAGE: true"); Picasso.with(this) .load(remoteUrlString) .into(target); sendImg(); } else { System.out.println("PERMISSION_GRANTED == WRITE_EXTERNAL_STORAGE: false"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSIONS_REQUEST_SENDER); } } } private Target target = new Target() { @Override public void onBitmapLoaded(final Bitmap bitmap, Picasso.LoadedFrom from) { new Thread(new Runnable() { @Override public void run() { try { file = new File(Environment.getExternalStorageDirectory().getPath() + "/Extreme_QR_code_to_Wikipedia_mobile_page.jpg"); FileOutputStream ostream = null; ostream = new FileOutputStream(file); bitmap.compress(Bitmap.CompressFormat.JPEG, 75, ostream); ostream.close(); galleryAddPic(file); setFile(file); } catch (Exception e) { e.printStackTrace(); } } }).start(); } @Override public void onBitmapFailed(Drawable errorDrawable) { } @Override public void onPrepareLoad(Drawable placeHolderDrawable) { if (placeHolderDrawable != null) { } } }; private void galleryAddPic(File file) { Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); Uri contentUri = Uri.fromFile(file); mediaScanIntent.setData(contentUri); this.sendBroadcast(mediaScanIntent); } private void sendImg() { Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); sendIntent.putExtra(Intent.EXTRA_STREAM, getFile()); sendIntent.setType("image/jpg"); startActivity(sendIntent); } @RequiresApi(api = Build.VERSION_CODES.M) @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == PERMISSIONS_REQUEST_SENDER && grantResults.length == 1) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { resendImage(); } else { requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSIONS_REQUEST_SENDER); } } } } 

Manifest:

 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.devtolife.myapp"> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.USE_CREDENTIALS" /> <uses-permission android:name="android.permission.GET_ACCOUNTS" /> <uses-permission android:name="android.permission.READ_PROFILE" /> <uses-permission android:name="android.permission.READ_CONTACTS" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest> 

Layout:

 <?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="START" /> </android.support.constraint.ConstraintLayout> 

When the choice of applications for sending is opened, the picture is not in the attachment: enter image description here

Checked the gallery - the picture was loaded and normally opens. What could be?

  • You call sendImg(); without waiting for loading - in an intent, most likely null sent. I wonder what is the gain from Picasso in such a task? - woesss
  • I tried to just get the picture via the URL without Picasso - but I refused, because I received the 405 error from the server I needed. Picaso works fine - the picture is downloading. - V.March
  • Really - null in intent . How to make the sendImg() call occur after the picture is loaded? In picasso found a callback() used only when inserting a photo into an ImageView . - V.March
  • Call where you do setFile(file); . With Picasso, the code turns out to be confusing + the image at the output is not original, but recoded and, accordingly, the cost of decoding and compression. - woesss
  • I tried, at whatever stage I would call sendImg() in any case, it will work out before the picture is loaded onto the phone - as a result, null in the intent . - V.March

2 answers 2

I will not go through the code, I will answer this way:

Wrapping your Thread with FutureTask will quickly save you from the standard here, a bit more code for AsyncTask, and Rx looks better. The dances with Picasso will leave, he is clearly not in order here, and at the end of writing the pictures to the file you can normally call your fun sendImg (). For a quick check you can do:

 setFile(file); sendImg(); 

even though this is bad, startActivity always seems to post on MainThread ... so it should work.

  • Not quite understood about dancing with picasso . I resorted to it only because, with the usual HttpURLConnection from the server, I received an error 405 instead of a picture. - V.March
  • one
    @ V.March, an incorrectly formatted request is not a reason to be perverted with additional libraries, especially since Picasso is not intended for this task. - woesss
  • @woesss I just learned from you that my request was wrong, but before that I truly believed that I did everything right, but the server didn’t want to give a picture — for this reason I began to look for an alternative. Experience a strong thing. - V.March
  • one
    @ V.March, I have a crystal ball and when the moon is at the zenith it shows someone else's code))) Any library is just a ready-made solution for what you can do yourself. If Picasso loads, but you don’t, it’s in your code. - woesss
  • one
    @Shwarz Andrei Your answer, although it does not have a specific solution, and yet it contains exactly the thought that became the basis for solving my problem - work with queues and streams. Having experienced all that was recommended - I chose RXJava / Android. Thank you - V.March

Solution using RXJava 2.1.6 and Picasso 2.5.2 .

In short:
I used Picasso to upload a file to a local repository.
To comply with the sequence of processes wrapped it all in RXJava2 .
At the end of the file download file sent it to send via intent.
Next, select the application (from the list) through which you like to send this photo.

Link to github: ImageResender

Detail the code itself:

Manifest: (note the Provider and Permissions)

 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.devtolife.imageresender"> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.INTERNET" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <provider android:name="android.support.v4.content.FileProvider" android:authorities="${applicationId}.provider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths" /> </provider> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest> 

Layout: (just a button to start receiving and sending a picture).

 <?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="START" /> </android.support.constraint.ConstraintLayout> 

Provider: ( provider_paths.xml is in res/xml path)

 <?xml version="1.0" encoding="utf-8"?> <paths> <external-path name="external_files" path="." /> </paths> 

Gradle: (connect libraries).

 implementation 'com.squareup.picasso:picasso:2.5.2' implementation 'io.reactivex.rxjava2:rxjava:2.1.6' 

Activity:
Notes: 1) 14222 is an ephemeral number taken from the ceiling.
2) In photoURL - The link to the picture is taken arbitrarily from the Internet without the goal of either advertising or offending anyone.

 package com.devtolife.imageresender; import android.Manifest; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.support.annotation.NonNull; import android.support.annotation.RequiresApi; import android.support.v4.content.ContextCompat; import android.support.v4.content.FileProvider; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.Button; import com.squareup.picasso.Picasso; import com.squareup.picasso.Target; import java.io.File; import java.io.FileOutputStream; import io.reactivex.Observable; import io.reactivex.ObservableEmitter; import io.reactivex.ObservableOnSubscribe; import io.reactivex.Observer; import io.reactivex.disposables.Disposable; import static android.os.Environment.getExternalStoragePublicDirectory; public class MainActivity extends AppCompatActivity { private static final int PERMISSIONS_REQUEST_SENDER = 14222; // 14222 it is the fictional number. private static final String TAG = "log"; private Button btnStart; private Context context; private String imgName; private String photoURL = "http://www.freepngimg.com/thumb/color_effects/1-2-color-effects-free-download-png-thumb.png"; // just for example. Uri photoUriForGalleryIndexation; Uri photoUri; File storageDir; private File imgFile = null; public File getImgFile() { return imgFile; } public void setImgFile(File imgFile) { this.imgFile = imgFile; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); context = getApplicationContext(); btnStart = findViewById(R.id.button); btnStart.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { getRemoteUrlString(); if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { createEmptyFile(); shareImage(); } else { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSIONS_REQUEST_SENDER); } } } }); } private void getRemoteUrlString() { String nameWithExtension = Uri.parse(photoURL).getLastPathSegment(); int pos = nameWithExtension.lastIndexOf("."); if (pos > 0) imgName = nameWithExtension.substring(0, pos); else imgName = nameWithExtension; } private void createEmptyFile() { storageDir = new File(getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "MyImages"); if (!storageDir.exists()) { storageDir.mkdirs(); storageDir = new File(getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "MyImages"); } setImgFile(new File(storageDir + "/" + imgName + ".jpg")); createPhotoUriForIndexation(getImgFile()); createPhotoUriForSharing(getImgFile()); } private void createPhotoUriForIndexation(File fileIndex) { String contentPathString = "imgFile:" + fileIndex.getAbsolutePath(); photoUriForGalleryIndexation = Uri.parse(contentPathString); } private void createPhotoUriForSharing(File fileShare) { if (Build.VERSION.SDK_INT <= 19) { photoUri = Uri.fromFile(fileShare); } else { photoUri = FileProvider.getUriForFile(getApplicationContext(), BuildConfig.APPLICATION_ID + ".provider", fileShare); } } private void shareImage() { Observable<File> observable = Observable .create(new ObservableOnSubscribe<File>() { @Override public void subscribe(final ObservableEmitter<File> emitt) { Picasso.with(context).load(photoURL) .into(new Target() { @Override public void onBitmapLoaded(final Bitmap bitmap, Picasso.LoadedFrom from) { try { FileOutputStream ostream = new FileOutputStream(getImgFile()); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, ostream); if (bitmap.compress(Bitmap.CompressFormat.JPEG, 100, ostream)) { galleryAddPic(); emitt.onComplete(); } else { emitt.onNext(getImgFile()); } ostream.close(); } catch (Exception e) { e.printStackTrace(); } } @Override public void onBitmapFailed(Drawable errorDrawable) { } @Override public void onPrepareLoad(Drawable placeHolderDrawable) { } }); } } ); Observer<File> observer = new Observer<File>() { @Override public void onSubscribe(Disposable d) { Log.e(TAG, "onSubscribe: "); } @Override public void onNext(File value) { Log.e(TAG, "onNext: "); } @Override public void onError(Throwable e) { Log.e(TAG, "onError: "); } @Override public void onComplete() { Log.e(TAG, "onComplete: All Done!"); sendImg(); } }; observable.subscribe(observer); } private void galleryAddPic() { Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); mediaScanIntent.setData(photoUriForGalleryIndexation); this.sendBroadcast(mediaScanIntent); } private void sendImg() { Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); sendIntent.putExtra(Intent.EXTRA_STREAM, photoUri); sendIntent.setType("image/jpg"); startActivity(sendIntent); } @RequiresApi(api = Build.VERSION_CODES.M) @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == PERMISSIONS_REQUEST_SENDER && grantResults.length == 1) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { createEmptyFile(); shareImage(); } else { requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSIONS_REQUEST_SENDER); } } } } 

Maybe it will save someone a couple of hours.