11

i have an app widget, which contains only one imageview. i redraw that image and store it as png in apps's private memory and then i set the RemoteViews' image with the uri (widget.setImageViewUri(R.id.widget_icon, uri)) instead of sendding the bitmap it self, because in very rare situations i get !!! FAILED BINDER TRANSACTION !!!

the problem is, that as the image changes over time, the widget's image does not change. i checked the saved png with root explorer and it is updated correctly. but the correct image is not displayed. each widget shows the image from the time it was added to the home screen. if i use setImageViewBitmap(...) it works correctly, except the rare failed binder transaction.

i update the widget from a service with the method below. it does not work on android 2.3.7 and also with emulator running 2.2. what could be the problem? is it somehow cached?

public void updateWidget(Context context) {
  // redraws the widget's image
  updateIcon();

  OutputStream stream;
  try {
    stream = context.openFileOutput("icon.png", Context.MODE_WORLD_READABLE);
  }
  catch (FileNotFoundException ex) {
    ex.printStackTrace();
    return;
  }

  m_Icon.compress(CompressFormat.PNG, 100, stream);

  try {
    stream.close();
  }
  catch (IOException ex) {
    ex.printStackTrace();
  }

  AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
  int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, WidgetProvider.class));

  Intent intent = new Intent(context, MyDialog.class);
  intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

  PendingIntent clickIntent = PendingIntent.getActivity(context, 0, intent, 0);

  File file = new File(context.getFilesDir().getAbsolutePath() + File.separator + "icon.png");
//      Uri uri = Uri.fromFile(file);

  // update all widgets on home screen
  for (int wid : appWidgetIds) {
    RemoteViews widget = new RemoteViews(context.getPackageName(), R.layout.widget);
    widget.setOnClickPendingIntent(R.id.widget_layout, clickIntent);

    // === EDIT ===
    // Uri.fromFile(file); does not work correctly on android 2.1, only 2.2 and up
    // widget's image will disappear
//        widget.setImageViewUri(R.id.widget_icon, uri);
    widget.setImageViewUri(R.id.widget_icon, Uri.parse(file.getPath()));

    intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, wid);

    appWidgetManager.updateAppWidget(wid, widget);
  }
}

EDIT: i also tried a custom ContentProvider and setting "content://..." uri, but with the same result. the widget shows the image from the time it was added to the home screen and does not update over time.

logcat does not show any errors.

shelll
  • 3,234
  • 3
  • 33
  • 67
  • Your are using the same URI (same file name) for all of your icon, and its that is the issue. URI must be unique by file, resource... The system do not update the drawable and use the first loaded and its an expected behavior. I think it's better to use FileProvider, and provide multiple file. you can delete obsolete one, and update the widget with the new URI. – Anis BEN NSIR Jan 02 '17 at 15:23
  • @AnisBENNSIR This question was asked and solved five year ago... – shelll Jan 03 '17 at 12:48
  • Sorry, but the bounty is in open state... Mybe my comment will be usefull for new user the accepted answer is not the best solution. – Anis BEN NSIR Jan 03 '17 at 12:53

3 Answers3

9

If the Uri remains unchanged, it seems Android won't update the image. I've found a bit of a hack to get around this though. Just call widget.setImageViewUri(R.id.widget_icon, Uri.parse("")); before widget.setImageViewUri(R.id.widget_icon, uri);.

It calls setImageViewUri() twice, but seems to be working OK. I'd love to hear a better way to fix it though, as I'm experiencing this issue myself.

Michell Bak
  • 13,182
  • 11
  • 64
  • 121
  • i was trying to the this but i was sending a null Uri, which was crashing. an empty string solved this problem, thank you! there is some kind of cache, which should be able to turn off if needed :( – shelll Oct 27 '11 at 09:40
  • setting an empty uri first and then setting to correct uri solves the issue, but it gives huge amounts of warnings in logcat on every widget update, because the empty uri is invalid... so this is only a partial solutions, but it works. – shelll Oct 27 '11 at 10:47
  • Yeah, that's the issue with using this solution. Like I said, it's a hack, and I'd love to find a better way, but I simply don't know of a better way to do it. – Michell Bak Oct 27 '11 at 11:04
  • there is another issue with this approach. the widget flickers on some updates. i think i will have to save the image twice and each time assign the second image's uri. – shelll Oct 31 '11 at 10:00
  • I haven't experienced that, I must say. – Michell Bak Oct 31 '11 at 10:11
  • 1
    We're now nearly 6 years on from this answer, which has worked fine for me until now. But recently I'm finding that in certain circumstances (Android O + Pixel launcher) the widget update procedure never seems to reach the second (genuine) URI load... it fails after the first (fake) URI but doesn't load the genuine URI, thus leaving a blank widget. With the same code and same device and same OS but different launcher (Nova) it still works OK. So this may be launcher specific, but probably results from the use of a kludgy solution like using an invalid URI. There must be a better way? – drmrbrewer Aug 03 '17 at 08:54
2

Well it looks like setImageViewUri caches images and does not refresh them if the have the same name.

Not the most elegant solution but it works, every second time it will use a different file name.

String filename = new String("bitmap-1.png");
// rotate files, due to not being refreshed if same name 
if ( context.deleteFile(filename) ) {
   filename = new String("bitmap-0.png");
}
FileOutputStream out = context.openFileOutput(filename, Context.MODE_WORLD_READABLE);
bitmap.compress(Bitmap.CompressFormat.PNG, 80, out);
file = context.getFileStreamPath(filename);
out.close();
updateViews.setImageViewUri(R.id.image1,Uri.parse(file.toString())); 
Nys
  • 556
  • 4
  • 10
  • i had this problem a long time ago, but my current solution is similar to your's combined with the accepted answer. instead of setting empty uri or rotating two image names i just always set two image uris and save my bitmap to both of them. it works, there is no branching and does not give any errors to logcat like empty uri. – shelll Apr 29 '12 at 10:20
  • This will inflate storage usage, think if the code is used by many widgets, that's not good practice, instead use the accepted answer and forget about the log. – YEH Nov 27 '15 at 08:13
  • please can you expand upon the solution you're using @shelll ? So you just store two identical copies of the bitmap with different names, and similar to the accepted answer you do `widget.setImageViewUri()` twice in a row, pointing to the two bitmaps respectively? i.e. same as accepted answer except that instead of an empty URI you use a URI that points to an identical copy of the bitmap, so effectively it is loading the same image twice in quick succession? – drmrbrewer Aug 03 '17 at 09:07
  • @drmrbrewer yes, i am doing exactly that. i store the bitmap twice with different names. the imagaes are small, thus no storage concern. there is no flicker, like with the "empty url" trick. and there are no warnings in the logcat. – shelll Aug 03 '17 at 11:42
  • OK thanks @shelll I'm going to try implementing that. As mentioned in my comment on the accepted solution, it was working fine for me (without flicker) until I hit a particular device / OS / launcher combination, so I need to try something different. Any reason you don't just rotate between two filenames, keeping only *one* bitmap in storage at any time (deleting the other), and using only *one* `setImageViewUri()` rather than two? Other than adding complexity (for little gain, if images are small anyway)? – drmrbrewer Aug 03 '17 at 12:15
  • 1
    @drmrbrewer I did not want to make a complex logic for such a small issue. having two images saved all the time and always setting the uri twice is bullet proof. my app was installed on more then 200k devices without any user complaints about storage issues :) – shelll Aug 03 '17 at 13:36
  • This should be the best answer for 2018. Other accepted answer has some problems with some devices. – woltran Dec 25 '18 at 17:00
0

I hope this helps anyone else. I was trying to use that method in a remove list view but it never worked. But using bitmaps worked for me. I was able to update pictures in any of the records and they were updating and there was no caching. This is the static method I used.

public static Bitmap getBitmap( String imageLocation ) {

    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inPreferredConfig = Bitmap.Config.ARGB_8888;

    return BitmapFactory.decodeFile(imageLocation, options);
}

then

bitmap = BitmapUtils.getBitmap( address.getPhotoLocation() );
row.setImageViewBitmap( R.id.row_image, bitmap );
Juan Mendez
  • 2,658
  • 1
  • 27
  • 23