So I have code put together to download an image that's displayed in an ImageView, and I'm trying to put up a Toast when the user clicks the download button so they know some-thing's happening. The problem is that the Toast is coming up after a few seconds rather than when they have clicked the download button. I've tried wrapping it in a runOnUiThread method while it's in the new thread as well, but the issue is still there. Anyone able to point me in the right direction for fixing this? Code from the class is below.
public static final String EXTRA_IMAGE_URL = "extra_image_url";
private ImageView mImage;
private ImageView mDownloadImage;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView( R.layout.activity_social_image_download );
if( getIntent() == null || getIntent().getExtras() == null || !getIntent().getExtras().containsKey( EXTRA_IMAGE_URL ) ) {
finish();
}
String imageUrl = getIntent().getExtras().getString( EXTRA_IMAGE_URL, "" );
if( TextUtils.isEmpty( imageUrl ) ) {
finish();
}
mImage = (ImageView) findViewById( R.id.image );
mDownloadImage = (ImageView) findViewById( R.id.download_button );
mDownloadImage.setOnClickListener( new View.OnClickListener() {
#Override
public void onClick(View v) {
saveImage();
}
});
Picasso.with( this ).load( imageUrl ).into( mImage );
}
private void saveImage() {
Toast.makeText( this, "Downloading Image", Toast.LENGTH_SHORT ).show();
new Thread(new Runnable() {
#Override
public void run() {
File file1 = new File( Environment.getExternalStorageDirectory().getAbsolutePath() + "/DCIM/" + getString( R.string.app_name ) );
if( !file1.isDirectory() )
file1.mkdir();
File file = new File( file1.getPath() + "/" + (new Date()).getTime() + ".png");
ByteArrayOutputStream out = new ByteArrayOutputStream();
((BitmapDrawable) mImage.getDrawable() ).getBitmap().compress(Bitmap.CompressFormat.PNG, 100, out);
try {
file.createNewFile();
FileOutputStream fout = new FileOutputStream( file );
if( fout != null ) {
fout.write( out.toByteArray() );
fout.flush();
fout.close();
}
MediaScannerConnection.scanFile(getApplicationContext(), new String[] { file.getAbsolutePath() }, null, new MediaScannerConnection.OnScanCompletedListener() {
#Override
public void onScanCompleted(String path, Uri uri) {
}
});
} catch( IOException e ) {}
}
}).start();
}
Maybe if you were to use an AsyncTask ,you could handle this.Because the onPreExecute() could give you the the toast that you need.
private class SaveImageTask extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute (){
Toast.makeText( this, "Downloading Image", Toast.LENGTH_SHORT ).show();
}
#Override
protected void doInBackground(URL... urls) {
File file1 = new File( Environment.getExternalStorageDirectory().getAbsolutePath() + "/DCIM/" + getString( R.string.app_name ) );
if( !file1.isDirectory() )
file1.mkdir();
File file = new File( file1.getPath() + "/" + (new Date()).getTime() + ".png");
ByteArrayOutputStream out = new ByteArrayOutputStream();
((BitmapDrawable) mImage.getDrawable() ).getBitmap().compress(Bitmap.CompressFormat.PNG, 100, out);
try {
file.createNewFile();
FileOutputStream fout = new FileOutputStream( file );
if( fout != null ) {
fout.write( out.toByteArray() );
fout.flush();
fout.close();
}
}catch(Exception e){
//Error handling.
}
}
#Override
protected void onPostExecute(Long result) {
MediaScannerConnection.scanFile(getApplicationContext(), new String[] { file.getAbsolutePath() }, null, new MediaScannerConnection.OnScanCompletedListener() {
#Override
public void onScanCompleted(String path, Uri uri) {
}
});
}
}
}
Turned out the issue has nothing to do with the threads and toast. There's a couple second delay between when the activity is created and when the button is able to respond to a click. If the activity is started and I wait to press the download button until after four seconds, everything works as expected. Trying to hunt down what would cause the UI thread to hang in such a basic Activity now. Even when Picasso is removed and the bitmap is just passed as an extra in the intent, the issue persists.
Related
I have to share an image in my app.
Glide.with(PhotoFullScreenActivity.this)
.asBitmap()
.load(getBestDownscaledAlternative(mPhotoList.get(mCurrentPosition)))
.into(new SimpleTarget<Bitmap>() {
#Override
public void onResourceReady(Bitmap resource, Transition<? super Bitmap> transition) {
startShareImageTask(resource);
}
#Override
public void onLoadFailed(#Nullable Drawable errorDrawable) {
super.onLoadFailed(errorDrawable);
Log.e("Check","error");
}
});
I'm using the above snippet to convert the URL into a bitmap and after the resource is ready I call the startShareImageTask to create a file.
The startShareImageTask looks like this:
private void startShareImageTask(Bitmap resource){
new AsyncTask<Bitmap, Void, Uri>(){
#Override
protected void onPreExecute() {
super.onPreExecute();
mShareLoadingPB.setVisibility(View.VISIBLE);
Log.d("ShareImageTask", "onPreExecute: " + System.currentTimeMillis());
}
#Override
protected Uri doInBackground(Bitmap... bitmaps) {
Uri bmpUri = null;
try {
// Use methods on Context to access package-specific directories on external storage.
// This way, you don't need to request external read/write permission.
File file = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), "share_image_" + System.currentTimeMillis() + ".png");
FileOutputStream out = new FileOutputStream(file);
resource.compress(Bitmap.CompressFormat.JPEG, 90, out);
out.close();
// wrap File object into a content provider. NOTE: authority here should match authority in manifest declaration
bmpUri = ImageFileProvider.getUriForFile(PhotoFullScreenActivity.this, getApplicationContext().getPackageName() + ".fileprovider", file); // use this version for API >= 24
} catch (IOException e) {
e.printStackTrace();
mShareLoadingPB.setVisibility(View.GONE);
}
return bmpUri;
}
#Override
protected void onPostExecute(Uri uri) {
super.onPostExecute(uri);
mShareLoadingPB.setVisibility(View.GONE);
mShareIntent = new Intent();
mShareIntent.setAction(Intent.ACTION_SEND);
mShareIntent.putExtra(Intent.EXTRA_STREAM, uri);
mShareIntent.setType("image/*");
startActivity(Intent.createChooser(mShareIntent, "Share image"));
}
}.execute();
}
All works out great but my app needs to record something in a background task and when it does, my doInBackground method is not called until I finish the recording...I want to share the image while I'm recording something.
What can I do when my background thread is busy with another task?
Is there any workaround to this?
I was able to make this work by changing the AsyncTask to RxJava. I'll leave the code difference here in case anyone bumps into this issue.
mShareLoadingPB.setVisibility(View.VISIBLE);
Log.d("ShareImageTask", "onPreExecute: " + System.currentTimeMillis());
Observable.fromCallable(() -> {
Uri bmpUri = null;
try {
// Use methods on Context to access package-specific directories on external storage.
// This way, you don't need to request external read/write permission.
File file = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), "share_image_" + System.currentTimeMillis() + ".png");
FileOutputStream out = new FileOutputStream(file);
resource.compress(Bitmap.CompressFormat.JPEG, 90, out);
out.close();
// wrap File object into a content provider. NOTE: authority here should match authority in manifest declaration
bmpUri = ImageFileProvider.getUriForFile(PhotoFullScreenActivity.this, getApplicationContext().getPackageName() + ".fileprovider", file); // use this version for API >= 24
} catch (IOException e) {
e.printStackTrace();
mShareLoadingPB.setVisibility(View.GONE);
}
return bmpUri;
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new DefaultObserver<Uri>() {
#Override
public void onNext(Uri uri) {
mShareLoadingPB.setVisibility(View.GONE);
mShareIntent = new Intent();
mShareIntent.setAction(Intent.ACTION_SEND);
mShareIntent.putExtra(Intent.EXTRA_STREAM, uri);
mShareIntent.setType("image/*");
startActivity(Intent.createChooser(mShareIntent, "Share image"));
}
#Override
public void onError(Throwable e) {
Log.e("Error","Error");
}
#Override
public void onComplete() {
}
});
I have gone through many articles. But somehow i could not achieve what i wanted to achieve. I have a http image link in my app and i want to share this as an image attachment from Twitter application. I have tried following. But it seems not to be working as Twitter expects it to be a local url.
TweetComposer.Builder builder = new TweetComposer.Builder(context)
.text(text)
.image(Uri.parse("https://dummyimage.com/300/09f/fff.png"));
builder.show();
So now that i know it requires a local path, i tried to download the image to phone using Picasso as below.
Picasso.get().load(CONSTANT.IAMGE_URI + list.get(position).getEvent_model().getPhoto_link())
.placeholder(R.drawable.placeholder)
.error(R.drawable.placeholder)
.into(getTarget(list.get(position).getEvent_model().getPhoto_link()));
File myImageFile = new File(getFileFullPath(list.get(position).getEvent_model().getPhoto_link()));
Picasso.get().load(myImageFile).into(holder.iv_album_image);
And this is code to get the target.
private static Target getTarget(final String fileName) {
Target target = new Target() {
//This method in target is called by picasso when image downloaded
#Override
public void onBitmapLoaded(final Bitmap bitmap, Picasso.LoadedFrom from) {
new Thread(new Runnable() {
#Override
public void run() {
try {
File file = new File(getFilename(fileName));
if (file.exists()) {
file.delete();
}
file.createNewFile();
FileOutputStream fileoutputstream = new FileOutputStream(file);
ByteArrayOutputStream bytearrayoutputstream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 60, bytearrayoutputstream);
fileoutputstream.write(bytearrayoutputstream.toByteArray());
fileoutputstream.close();
} catch (IOException e) {
Log.e("IOException", e.getLocalizedMessage());
}
}
}).start();
}
#Override
public void onBitmapFailed(Exception e, Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
};
return target;
}
public static String getFilename(String fileName) {
File file = new File(Environment.getExternalStorageDirectory().getPath(), "Images");
if (!file.exists()) {
file.mkdirs();
}
return (file.getAbsolutePath() + "/" + fileName);
}
In this case photo is not loading in to imageview and also not being downloaded to local.
You need to download the image to your phone, and use a local path.
https://docs.fabric.io/javadocs/tweet-composer/2.0.0/com/twitter/sdk/android/tweetcomposer/TweetComposer.Builder.html#image(android.net.Uri)
Ultimately for image:
The Uri should be a file Uri to a local file
Uri.fromFile(someExternalStorageFile)
Can you change your getFilename to match as such:
public static String getFilename(String fileName, Context context) {
int code = context.getPackageManager().checkPermission(
android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
context.getPackageName());
if (code == PackageManager.PERMISSION_GRANTED) {
File file = new File(Environment.getExternalStorageDirectory().getPath(), "Images");
if (!file.exists()) {
file.mkdirs();
}
return (file.getAbsolutePath() + "/" + fileName);
}
return "";
}
And set a breakpoint at each return. My assumption is you're returning "";
My AsyncTask is logging an error "println needs a message" however no exception is being thrown in my class. The task is started in an Activity which implements a callback interface I wrote called TaskCallback. In the onPostExecute() it calles the callback in the Activity. From this callback, I run another AsyncTask. Below is the code:
public class SaveImageTask extends AsyncTask<byte[], String, File> {
private static final String IMAGE_DATA_PATH =
Environment.getExternalStorageDirectory().toString() + "/MyAppFolder/AppImages/";
private static final String TAG = "SaveImageTask";
private TaskCallback mTaskCallback;
private ProgressDialog mProgressDialog;
public SaveImageTask(TaskCallback taskCallback) {
mTaskCallback = taskCallback;
}
#Override
protected void onPreExecute() {
mProgressDialog = new ProgressDialog((Context) mTaskCallback);
mProgressDialog.setMessage("Saving Image...");
mProgressDialog.setCanceledOnTouchOutside(false);
mProgressDialog.setCancelable(false);
mProgressDialog.show();
}
#Override
protected File doInBackground(byte[]... data) {
File imageFile = createOutputPictureFile();
if(imageFile == null) {
return null;
}
try {
Bitmap image = BitmapFactory.decodeByteArray(data[0], 0, data[0].length);
FileOutputStream out = new FileOutputStream(imageFile);
image.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
} catch (IOException e) {
Log.e(TAG, e.getMessage());
}
return imageFile;
}
#Override
public void onPostExecute(File imageFile) {
if(mProgressDialog != null && mProgressDialog.isShowing()) {
mProgressDialog.dismiss();
}
if(mTaskCallback != null) {
mTaskCallback.onTaskComplete(imageFile);
}
}
private File createOutputPictureFile() {
File imageStorageDirectory = new File(IMAGE_DATA_PATH);
// If the default save directory doesn't exist, try and create it
if (!imageStorageDirectory.exists()){
if (!imageStorageDirectory.mkdirs()){
//Log.e(TAG, "Required media storage does not exist");
return null;
}
}
// Create a timestamp and use it as part of the file name
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss", Locale.UK);
String timeStamp = dateFormat.format(new Date());
String fileName = "img_"+ timeStamp + ".jpg";
return new File (imageStorageDirectory, fileName);
}
}
The onTaskComplete(File file) looks like this:
#Override
public void onTaskComplete(File file) {
if(file == null) {
Util.showToast(this, "Save Failed.", Toast.LENGTH_SHORT);
return;
}
notifyDeviceOfNewFile(file);
ProcessImageTask pit = new ProcessImageTask(this);
pit.execute(file);
}
And the error logged is:
E/SaveImageTask: println needs a message
As it says in the title, no exception is thrown and the code actually does what it is supposed to do. I've narrowed the issue down to this line of code in the callback:
pit.execute(file);
If I comment out this line the error doesn't appear. I'm a bit stumped on what's going on. If I remove all logging in my SaveImageTask it still appears so something else is logging it.
There is a reason why a exception is not thrown, because you catch it. That is the whole concept about try-catch.
try {
Bitmap image = BitmapFactory.decodeByteArray(data[0], 0, data[0].length);
FileOutputStream out = new FileOutputStream(imageFile);
image.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
} catch (IOException e) {
Log.e(TAG, e.getMessage());
// normally you do stuff here when it fails.
}
I believe "println needs a message" is what's shown if you pass a null to Log.x(). You're probably getting an IOException - e.printstacktrace() will probably give you a better idea of why.
Ok, turns out I was being an idiot and the error was from another class which for some reason had the same TAG it was logging with. Thank you for your input and suggestions, an important lesson about copy/pasting code was learned today.
I need to show picture of user in my application and I retrieve that picture from server since my application also works in offline mode so I need to save that picture from server to my SD card , also I when i sync data from server next time If picture has changed then i need to change that picture in my SD card too how to determine if picture for particular user has changed
currently i save the image from server as follow though I use hardcoded url as of now and static user id
public class fetchImage extends Activity implements OnClickListener {
int id;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
id = 1;// declaring static as of now
}
{
new BackgroundTask().execute();
File storagePath = Environment.getExternalStorageDirectory();
File imgFile = new File(storagePath, "/Pictures/" + id + ".jpg");
if (imgFile.exists()) {
Bitmap myBitmap = BitmapFactory.decodeFile(imgFile
.getAbsolutePath());
}
}
class BackgroundTask extends AsyncTask<Void, Void, Void> {
ProgressDialog mDialog;
protected void onPreExecute() {
mDialog = ProgressDialog.show(fetchImage.this, "",
getString(R.string.progress_bar_loading), true);
};
#Override
protected Void doInBackground(Void... params) {
try {
savesd(id, null);
} catch (final Exception e) {
}
return null;
}
private void savesd(int id, URL uri) throws IOException {
URL url;
if (uri == null) {
url = new URL("http://i.zdnet.com/blogs/3-29-androids.jpg");
} else {
url = uri;
}
InputStream input = url.openStream();
try {
File storagePath = Environment.getExternalStorageDirectory();
OutputStream output = new FileOutputStream(new File(
storagePath, "/Pictures/" + id + ".jpg"));
try {
byte[] buffer = new byte[20000];
int bytesRead = 0;
while ((bytesRead = input.read(buffer, 0, buffer.length)) >= 0) {
output.write(buffer, 0, bytesRead);
}
} finally {
output.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
input.close();
}
}
protected void onPostExecute(Void result) {
mDialog.dismiss();
};
}
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
}
Also i've got one problem that when I uninstall this app from Device it should also clear these user images from sd card
I used timestamp to save the time when I last sync data and download files only after that time stamp
I have a webview which loads a local html page and I need to know when it has finished loading so I was using the function onPageFinished() which is fired straight away but for some reason it works fine on websites e.g. google.co.uk.
public class TestwebviewActivity extends Activity {
/** Called when the activity is first created. */
WebView webview = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
webview = (WebView) findViewById(R.id.webview);
Log.d("Webview", "created");
webview.getSettings().setJavaScriptEnabled(true);
webview.setWebViewClient(new WebViewClient()
{
public void onPageFinished(WebView view, String url)
{
Log.d("Webview", "Finished Loading");
Picture picture = view.capturePicture();
Bitmap b = Bitmap.createBitmap( 300, 300, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas( b );
picture.draw( c );
FileOutputStream fos = null;
try {
fos = new FileOutputStream( "/sdcard/google_" + System.currentTimeMillis() + ".jpg" );
if ( fos != null )
{
b.compress(Bitmap.CompressFormat.JPEG, 90, fos );
fos.close();
Log.d("Webview", "Image Created");
}
} catch( Exception e )
{
//...
}
}
});
webview.loadUrl("file:///android_asset/htmlpage.html");
}
}
This has only happened since upgraded to android 3.2 - previously on 2.3
Anyone come across the same issues or any suggestions?
Thanks if anyone can help.
Try to use deprecated PictureListener:
wv.setPictureListener(new PictureListener() {
#Override
public void onNewPicture(WebView view, Picture picture) {
if (i < pageList.size()) {
wv.loadUrl("javascript: highlightSearchTerms('" + searchKey + "');");
} else listener.onFinishSearch();
i++;
}
});