Search code examples
androidwidgetremoteview

Recall remoteView instead of creating new one?


I have a Flashlight widget that stops responding after the launcher is restarted, for example when date is changed in system, the launcher resets on my phone. According to this thread on SO: Link, I need to update the remoteView instead of creating new one every time. However I don't understand how to implement this in my code. I have a provider and a receiver for this.

Note: The widget starts working again after 30 mins as that is the time set for my widget to update in my appwidget-provider XML.

Provider:

public class WidgetProvider extends AppWidgetProvider {
   @Override
  public void onUpdate(Context context, AppWidgetManager appWidgetManager,
    int[] appWidgetIds) {
    final int N = appWidgetIds.length;
    for (int i = 0; i < N; i++) {
      int appWidgetId = appWidgetIds[i];

      Intent receiver = new Intent(context, FlashlightWidgetReceiver.class);
      receiver.setAction("COM_FLASHLIGHT");
      receiver.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
      PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, receiver, 0);

      RemoteViews views = new RemoteViews(context.getPackageName(),
        R.layout.appwidget_layout);
      views.setOnClickPendingIntent(R.id.imageButton, pendingIntent);
      //appWidgetManager.updateAppWidget(appWidgetId, views);
      appWidgetManager.partiallyUpdateAppWidget(appWidgetId, views);
    }

  }
}

Receiver:

public class FlashlightWidgetReceiver extends BroadcastReceiver {
  private static boolean isLightOn = false;
  private static Camera camera;

  @Override
  public void onReceive(Context context, Intent intent) {
    RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_layout);

    if (isLightOn) {
      views.setImageViewResource(R.id.imageButton, R.drawable.btn_switch_off);
    } else {
      views.setImageViewResource(R.id.imageButton, R.drawable.btn_switch_on);
    }

    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
    appWidgetManager.updateAppWidget(new ComponentName(context, WidgetProvider.class),
      views);

    if (isLightOn) {
      if (camera != null) {
        camera.stopPreview();
        camera.release();
        camera = null;
        isLightOn = false;
      }

    } else {
      // Open the default i.e. the first rear facing camera.
      camera = Camera.open();

      if (camera == null) {
        Toast.makeText(context, "No Camera!", Toast.LENGTH_SHORT).show();
      } else {
        // Set the torch flash mode
        Camera.Parameters param = camera.getParameters();
        param.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
        try {
          camera.setParameters(param);
          camera.startPreview();
          isLightOn = true;
        } catch (Exception e) {
          Toast.makeText(context, "No Camera!", Toast.LENGTH_SHORT).show();
        }
      }
    }
  }
}


Solution

  • Alright guys, I finally got time to fix this problem once and for all :)

    I created more methods for the provider instead of doing everything in onUpdate, one important method needed:

     public static PendingIntent buildButtonPendingIntent(Context context) {
       Intent intent = new Intent();
       intent.setAction("COM_FLASHLIGHT");
       return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
     }

    And this method is called through the receiver when the widget is clicked using the code below:

    private void turnFlash(Context context) {
      RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_layout);
      views.setOnClickPendingIntent(R.id.imageButton, WidgetProvider.buildButtonPendingIntent(context));
    }

    That is all, no more hiccups!