I have created everything necessary for my widget to exist and function. Even so at the first click, t does what it is supposed to but then image gets changed and says problem, and does not function. I want it to open flash and then close it.
Help will be much appreciated.
FlashlightWidgetProvider
public class FlashlightWidgetProvider extends AppWidgetProvider {
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
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.flash_widget);
views.setOnClickPendingIntent(R.id.button, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetIds, views);
}
}
FlashlightWidgetReceiver
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.flash_widget);
if(isLightOn) {
views.setImageViewResource(R.id.button, R.drawable.off);
} else {
views.setImageViewResource(R.id.button, R.drawable.on);
}
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
appWidgetManager.updateAppWidget(new ComponentName(context, FlashlightWidgetProvider.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, R.string.no_camera, Toast.LENGTH_SHORT).show();
} else {
// Set the torch flash mode
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, R.string.no_flash, Toast.LENGTH_SHORT).show();
}
}
}
}
}
Make sure the button resource refers to an ImageView and not a regular Button. I just tried this out at first with a Button in my layout file and I was getting the same problem where the widget would basically crash and remove itself from the home screen. When I changed button to be an ImageView in the layout file, the code now works.
I did modify the code a bit from yours, so in case that doesn't work by itself, here is the updated FlashlightWidgetProvider:
public class FlashlightWidgetProvider extends AppWidgetProvider {
#Override
public void onReceive(Context context, Intent intent) {
if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(intent.getAction())) {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, getClass()));
Intent broadcastIntent = new Intent(context, FlashlightWidgetReceiver.class);
broadcastIntent.setAction("COM_FLASHLIGHT");
broadcastIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
0,
broadcastIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.flashlight);
views.setOnClickPendingIntent(R.id.flashButton, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetIds, views);
}
super.onReceive(context, intent);
}
}
Also, make sure to register the widget provider and receiver correctly in the manifest (replacing the relevant pieces with your own, of course):
<receiver
android:name="com.example.stackoverflowtester.widget.FlashlightWidgetProvider"
android:label="Flashlight" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/flashlight_widget_provider" />
</receiver>
<receiver android:name="com.example.stackoverflowtester.widget.FlashlightWidgetReceiver" >
<intent-filter>
<action android:name="COM_FLASHLIGHT" />
</intent-filter>
</receiver>
Related
I want to create a widget for turning on/off the flashlight and this is What I did:
Widget class:
public class FlashLightWidget extends AppWidgetProvider {
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId) {
Intent receiver = new Intent(context, FlashLightReceiver.class);
receiver.setAction("NINJA_KRZYSZTOF_FLASHLIGHT");
receiver.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetId);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, receiver, 0);
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.flash_light_widget);
views.setOnClickPendingIntent(R.id.imageView, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId);
}
}
#Override
public void onEnabled(Context context) {
}
#Override
public void onDisabled(Context context) {
}
}
My BroadcastReceiver:
public class FlashLightReceiver extends BroadcastReceiver {
private boolean isLightOn = false;
private Camera camera;
#Override
public void onReceive(Context context, Intent intent) {
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.flash_light_widget);
if (isLightOn) {
views.setImageViewResource(R.id.imageView, R.drawable.light_off);
} else {
views.setImageViewResource(R.id.imageView, R.drawable.light_on);
}
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
appWidgetManager.updateAppWidget(new ComponentName(context, FlashLightWidget.class), views);
if (isLightOn) {
if (camera != null) {
camera.stopPreview();
camera.release();
camera = null;
isLightOn = false;
}
} else {
camera = Camera.open();
if (camera == null) {
Toast.makeText(context, "No camera found", Toast.LENGTH_SHORT).show();
} else {
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 LED found", Toast.LENGTH_SHORT).show();
}
}
}
}
}
AndroidManifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="ninja.majewski.jutswidgetflashlight">
<uses-permission android:name="android.permission.CAMERA" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<receiver android:name=".FlashLightWidget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/flash_light_widget_info" />
</receiver>
<receiver android:name=".FlashLightReceiver">
<intent-filter>
<action android:name="NINJA_KRZYSZTOF_FLASHLIGHT" />
</intent-filter>
</receiver>
</application>
</manifest>
The problem is when I click this ImageView on widget layout it switches on the flashlight but when I want to switch it off it crashes ("sorry but app was stopped..." message).
What am I doing wong?
You don't state the actual error message but I am getting a crash when pressing the widget a 2nd time as well. I'm seeing:
java.lang.RuntimeException: Fail to connect to camera service
It looks to me like your problem is because isLightOn is always false, so your code is attempting to re-open the already open camera. To fix this specific issue, make isLightOn static, like this:
private static boolean isLightOn = false;
I saw similar questions here on SO, but nothing seems to work in my case...
I created an appwidget with an AdapterViewFlipper (Simple ViewAnimator that will animate between two or more views that have been added to it). The appwidget has a Next button that enables the user to navigate to the next view on the widget.
It all works fine when I first add the appwidget. But if the smartphone reboots, the Next button of the widget no longer works on my Samsung S4 (the method onReceive is called, but nothings happens, it doesn't navigate to the next view and is stuck at the first view). I have to delete the widget and add it again in order for it to work...
I suspect that it is a problem of Touchwiz since I tested it on another phone (Moto G) and it worked fine.
Here are some portions of my code :
AppWidgetProvider
public class AppWidgetProvider extends AppWidgetProvider {
public static final String NEXT_ACTION = VersionUtil.getPackageName() + ".action.NEXT";
private static final String TAG = DailyAppWidget.class.getSimpleName();
#Override
public void onEnabled(Context context) {
// Enter relevant functionality for when the first widget is created
}
#Override
public void onDisabled(Context context) {
// Enter relevant functionality for when the last widget is disabled
}
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// There may be multiple widgets active, so update all of them
for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId, colorValue);
}
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId, int primaryColor) {
Intent intent = new Intent(context, ViewFlipperWidgetService.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
// When intents are compared, the extras are ignored, so we need to embed the extras
// into the data so that the extras will not be ignored.
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
// Instantiate the RemoteViews object for the app widget layout.
RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.app_widget);
// open the activity from the widget
Intent intentApp = new Intent(context, MainActivity.class);
intentApp.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intentApp, 0);
rv.setOnClickPendingIntent(R.id.widget_title, pendingIntent);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
rv.setRemoteAdapter(R.id.adapter_flipper, intent);
} else {
rv.setRemoteAdapter(appWidgetId, R.id.adapter_flipper, intent);
}
// Bind the click intent for the next button on the widget
final Intent nextIntent = new Intent(context,
AppWidgetProvider.class);
nextIntent.setAction(AppWidgetProvider.NEXT_ACTION);
nextIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
final PendingIntent nextPendingIntent = PendingIntent
.getBroadcast(context, 0, nextIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
rv.setOnClickPendingIntent(R.id.widget_btn_next, nextPendingIntent);
appWidgetManager.updateAppWidget(appWidgetId, mRemoteViews);
}
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals(NEXT_ACTION)) {
RemoteViews rv = new RemoteViews(context.getPackageName(),
R.layout.daily_app_widget);
rv.showNext(R.id.adapter_flipper);
int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
Log.e(TAG, "onReceive APPWIDGET ID " + appWidgetId);
AppWidgetManager.getInstance(context).partiallyUpdateAppWidget(
appWidgetId, rv);
}
super.onReceive(context, intent);
}
Service
public class FlipperRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
private Context mContext;
private int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
private static final String TAG = "FILPPERWIDGET";
public FlipperRemoteViewsFactory(Context context, Intent intent) {
mContext = context;
mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
//... get the data
}
#Override
public void onCreate() {
Log.e(TAG, "onCreate()");
}
#Override
public void onDataSetChanged() {
Log.i(TAG, "onDataSetChanged()");
}
#Override
public void onDestroy() {
}
#Override
public int getCount() {
//... return size of dataset
}
#Override
public RemoteViews getViewAt(int position) {
Log.i(TAG, "getViewAt()" + position);
RemoteViews page = new RemoteViews(mContext.getPackageName(), R.layout.app_widget_item);
//... set the data on the layout
return page;
}
#Override
public RemoteViews getLoadingView() {
Log.i(TAG, "getLoadingView()");
return new RemoteViews(mContext.getPackageName(), R.layout.appwidget_loading);
}
#Override
public int getViewTypeCount() {
Log.i(TAG, "getViewTypeCount()");
return 1;
}
#Override
public long getItemId(int position) {
Log.i(TAG, "getItemId()");
return position;
}
#Override
public boolean hasStableIds() {
Log.i(TAG, "hasStableIds()");
return true;
}
}
Manifest
<receiver android:name=".AppWidgetProvider"
android:label="#string/app_name"
android:enabled="#bool/is_at_least_12_api">
<meta-data android:name="android.appwidget.provider"
android:resource="#xml/app_widget_info" />
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
</receiver>
<!-- Service serving the RemoteViews to the collection widget -->
<service android:name=".ViewFlipperWidgetService"
android:permission="android.permission.BIND_REMOTEVIEWS"
android:exported="false" />
app wigdet info
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialKeyguardLayout="#layout/app_widget"
android:initialLayout="#layout/app_widget"
android:minHeight="110dp"
android:minWidth="250dp"
android:previewImage="#drawable/widget_preview"
android:resizeMode="horizontal|vertical"
android:updatePeriodMillis="14400000"
android:widgetCategory="home_screen" />
Any help would be appreciated !
Depends on the launcher, there is no guarantee that your AppWidget will be updated immediately after the device started. It may be refreshed immeidately, or wait till the updatePeriodMillis passed after system started.
To solve your problem, define a BroadcastReceiver that will trigger the update of AppWidget after the reboot.
In AndroidManifest.xml, define the BootReceiver to get the boot_complete message.
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<receiver android:name=".BootReceiver" android:enabled="true" android:exported="false" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
And define the BootReceiver.java to start your AppWidgetUpdateService
public class BootReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent){
//start appwidget update service
}
}
I want 2 separate layouts for homescreen and lockscreen.
I have read https://developer.android.com/guide/topics/appwidgets/index.html#lockscreen
But it is unclear where to implement this and how to change the layout at runtime for both homescreen and lockscreen?
I would be grateful if there is clear tutorial / example to do this.
Thanks
If you know home screen widget implementation, it's easy.
From the code below you can figure out how to use different layout for lock screen and home screen to display the current time every second.
Create different layout for different widgets
#layout/widget_keyguard //For lock screen widget
#layout/widget_home //For home screen widget
Note: Use one TextView with id time_view on both the layouts to display the time
xml/widget_info.xml
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialKeyguardLayout="#layout/widget_keyguard" // layout for lock screen
android:initialLayout="#layout/widget_home" // layout for lock screen (if not provided) & home screen
android:minHeight="100dp"
android:minWidth="300dp"
android:previewImage="#drawable/ic_launcher"
android:resizeMode="none"
android:updatePeriodMillis="180000"
android:widgetCategory="keyguard|home_screen" > //Enable widgets on both home screen and lock screen
</appwidget-provider>
AppWidgetProvider.java
public class TestAppWidgetProvider extends AppWidgetProvider {
#Override
public void onDeleted(Context context, int[] appWidgetIds) {
super.onDeleted(context, appWidgetIds);
}
#Override
public void onDisabled(Context context) {
Intent intent = new Intent(context, AlarmManagerBroadcastReceiver.class);
PendingIntent sender = PendingIntent
.getBroadcast(context, 0, intent, 0);
AlarmManager am = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
am.cancel(sender); //When all the widgets are disabled, do not forget to cancel the service
super.onDisabled(context);
}
#Override
public void onEnabled(Context context) {
super.onEnabled(context);
Toast.makeText(context, "Widget Enabled", Toast.LENGTH_SHORT).show();
//AlarmManager to update the widgets
Intent intent = new Intent(context, AlarmManagerBroadcastReceiver.class);
PendingIntent p_intent = PendingIntent.getBroadcast(context, 0, intent,
0);
AlarmManager am = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
// Here I am updating the widgets every second (1000 ms) , you can use however you want
am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
1000, p_intent);
}
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
Toast.makeText(context, "Widget Updated", Toast.LENGTH_SHORT).show();
ComponentName thisWidget = new ComponentName(context,
TestAppWidgetProvider.class);
for (int widgetId : appWidgetManager.getAppWidgetIds(thisWidget)) {
Bundle myOptions = appWidgetManager.getAppWidgetOptions(widgetId);
int category = myOptions.getInt(
AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, -1);
RemoteViews remoteViews;
if (category == AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD) {
// Get the remote views
remoteViews = new RemoteViews(context.getPackageName(),
R.layout.widget_keyguard);
}
else {
remoteViews = new RemoteViews(context.getPackageName(),
R.layout.widget_home);
}
SimpleDateFormat dateFormat = new SimpleDateFormat(
"HH:mm:ss", Locale.US);
// use TextView with time_view id on both home screen & lock screen layouts
remoteViews.setTextViewText(R.id.time_view,
dateFormat.format(new Date(System.currentTimeMillis())));
appWidgetManager.updateAppWidget(widgetId, remoteViews);
}
}
#Override
public void onAppWidgetOptionsChanged(Context context,
AppWidgetManager appWidgetManager, int appWidgetId,
Bundle newOptions) {
}
}
AlarmManagerBroadcastReceiver class
public class AlarmManagerBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
ComponentName thiswidget = new ComponentName(context,
TestAppWidgetProvider.class);
AppWidgetManager appWidgetManager = AppWidgetManager
.getInstance(context);
for (int widgetId : appWidgetManager.getAppWidgetIds(thiswidget)) {
Bundle myOptions = appWidgetManager.getAppWidgetOptions(widgetId);
int category = myOptions.getInt(
AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, -1);
RemoteViews remoteViews;
if (category == AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD) {
// Get the remote views
Log.d("Widget", "Lockscreen widget");
remoteViews = new RemoteViews(context.getPackageName(),
R.layout.widget_keyguard);
}
else {
Log.d("Widget", "Homescreen widget");
remoteViews = new RemoteViews(context.getPackageName(),
R.layout.widget_home);
}
SimpleDateFormat dateFormat = new SimpleDateFormat(
"HH:mm:ss", Locale.US);
// use TextView with time_view id on both home screen & lock screen layouts
remoteViews.setTextViewText(R.id.time_view,
dateFormat.format(new Date(System.currentTimeMillis())));
appWidgetManager.updateAppWidget(widgetId, remoteViews);
}
}
}
Finally, do not forget to update the manifest file
<application
...
<receiver android:name=".AlarmManagerBroadcastReceiver" />
<receiver android:name=".TestAppWidgetProvider" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/widget_info" />
</receiver>
.....
</application>
that's it.
I hope it'll help you to solve your problem
I am making a flashlight widget which will toggle the flashlight on/off and also I am trying to toggle the icon of the widget-button on clicking the widget button, for this I have the Appwidgetprovider whose onUpdate will use RemoteViews and call the BroadcastReceiver.
In the BroadcastReceiver, the onReceive function will perform the flashlight toggle and the icon toggle for the widget.
The issue I am facing is that the onReceive function is not being called and no action happening with the widget.
below is the code:
AppWidgetProvider class:
public class WidgetActivity extends AppWidgetProvider {
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
Intent receiver = new Intent(context, WidgetActivity.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.widget_flash_layout);
views.setOnClickPendingIntent(R.id.imageButton1, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetIds, views);
}
}
BroadcastReceiver Class:
public class WidgetService extends BroadcastReceiver {
private static boolean isLightOn = false;
private static Camera camera;
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
RemoteViews views = new RemoteViews(context.getPackageName(),
R.layout.widget_flash_layout);
if (isLightOn) {
views.setImageViewResource(R.id.imageButton1,
R.drawable.light_off_widget);
} else {
views.setImageViewResource(R.id.imageButton1,
R.drawable.light_on_widget);
}
AppWidgetManager appWidgetManager = AppWidgetManager
.getInstance(context);
appWidgetManager.updateAppWidget(new ComponentName(context,
WidgetActivity.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
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 flash", Toast.LENGTH_SHORT)
.show();
}
}
}
}
}
Manifest:
<receiver android:name="com.widget.WidgetActivity">
<intent-filter>
<action
android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/flash_widget" />
</receiver>
<receiver android:name="com.widget.WidgetService">
<action android:name="COM_FLASHLIGHT"></action>
</receiver>
In the manifest I have not wrapped the <action> tag of the widget service with
<intent-filter> as it was showing a warning saying "Exported receiver does not require permission".
onReceive function is not being called and no action happening with the widget
Your intent component class should be the Broadcast Receiver class , not the WidgetProvider
Change this
Intent receiver = new Intent(context, WidgetActivity.class);
to
Intent receiver = new Intent(context, WidgetService.class);
When you use PendingIntent.getBroadcast it expect the Intent to be broadcast. So when you click on the button in the widget, the Broadcast Receiver onResume will get called.
You don't really need to set any Action here for the receiver.
But if you want to use a custom Intent , then you can set the Intent action like this
Intent receiver = new Intent("COM_FLASHLIGHT");
and your receiver in manifest should register with intent-filter to handle the custom action.
<receiver android:name=".services.WidgetService">
<intent-filter>
<action android:name="COM_FLASHLIGHT"></action>
</intent-filter>
</receiver>
So, when the BroadCast Receiver onReceive get called, you can check for the specific action like this
public class WidgetService extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equalsIgnoreCase("COM_FLASHLIGHT")){
// do your stuff for this action.
}
}
Custom Actions are normally defined when you have multiple actions in a RemoteView.
if you read the documentation, onDataSetChanged in RemoteViewsService, is called when notifyDataSetChanged() is triggered.
so, if you wanna update your WidgetService from onReceive() method, you can call
notifyAppWidgetViewDataChanged() method from AppWidgetManager
#Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
int[] ids = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
AppWidgetManager.getInstance(context)
.notifyAppWidgetViewDataChanged(ids, R.id.stack_view_movies);
}
My app has a widget that lights the LED Flash when we click on it and swhitch it off when we click again on it.
Here is the code (thanks to Kartik from this post):
WidgetProvider.java
public class WidgetProvider extends AppWidgetProvider {
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
Intent receiver = new Intent(context, WidgetReceiver.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.widget);
views.setOnClickPendingIntent(R.id.imageButtonWidget, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetIds, views);
}
}
WidgetReceiver.java
public class WidgetReceiver extends BroadcastReceiver {
public static boolean isLightOn = false;
public static Camera camera;
#Override
public void onReceive(Context context, Intent intent) {
RemoteViews views = new RemoteViews(context.getPackageName(),
R.layout.widget);
if (isLightOn) {
views.setImageViewResource(R.id.imageButtonWidget,
R.drawable.widget_lamp_button_default);
} else {
views.setImageViewResource(R.id.imageButtonWidget,
R.drawable.widget_lamp_button_checked);
}
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, "R.string.no_camera",
Toast.LENGTH_SHORT).show();
} else {
// Set the torch flash mode
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, "R.string.no_flash",
Toast.LENGTH_SHORT).show();
}
}
}
}
}
Now I would like to switch the widget off from inside my app . With this code in the main activity of my app, I can release the camera taken by the widget :
MainActivity.java
//Stop widget camera
if (WidgetReceiver.isLightOn){
Camera a = WidgetReceiver.camera;
a.stopPreview();
a.release();
a = null;
WidgetReceiver.isLightOn=false;}
But the problem is that the widget is still set to the checked drawable (R.drawable.widget_lamp_button_checked). So the FlashLight is well turned off but I still need to force the widget to set its drawable to the unchecked one (R.drawable.widget_lamp_button_default).
How can I do this ?
Edit : Problem Solved
WidgetProvider.java
public class WidgetProvider extends AppWidgetProvider {
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
// Set widget's drawable to unchecked
RemoteViews views2 = new RemoteViews(context.getPackageName(),
R.layout.widget);
AppWidgetManager mManager = AppWidgetManager.getInstance(MainActivity
.getContext());
ComponentName cn = new ComponentName(MainActivity.getContext(),
WidgetProvider.class);
views2.setImageViewResource(R.id.imageButtonWidget,
R.drawable.widget_lamp_button_default);
mManager.updateAppWidget(cn, views2);
// Widget OnClick Behavior
Intent receiver = new Intent(context, WidgetReceiver.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.widget);
views.setOnClickPendingIntent(R.id.imageButtonWidget, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetIds, views);
}
}
WidgetReceiver.java -> kept the same
MainActivity.java
public class MainActivity extends Activity {
private static Context mContext;
public static Context getContext() {
return mContext;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
//Stop widget camera
if (WidgetReceiver.isLightOn){
Camera a = WidgetReceiver.camera;
a.stopPreview();
a.release();
a = null;
WidgetReceiver.isLightOn=false;}
// Fire Widget's update with Intent
Intent intent = new Intent(this, WidgetProvider.class);
intent.setAction("android.appwidget.action.APPWIDGET_UPDATE");
// Use an array and EXTRA_APPWIDGET_IDS instead of
// AppWidgetManager.EXTRA_APPWIDGET_ID,
// since it seems the onUpdate() is only fired on that:
int[] ids = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids);
sendBroadcast(intent);
}
Send a broadcast to your AppWidgetProvider and update your widget by using updateAppWidget method of AppWidgetManager class,in onReceive() method of AppWidgetProvider:
#Override
public void onReceive(Context context, Intent intent) {
...
views = new RemoteViews(context.getPackageName(),
R.layout.yourwidgetlayout);
AppWidgetManager mManager = AppWidgetManager.getInstance(App
.getContext());
ComponentName cn = new ComponentName(App.getContext(),
YourAppWidgetProvider.class);
//change your views,here I change text of text view witch it's id is "widgettextview"
views.setTextViewText(R.id.widgettextview, "lastWord");
mManager.updateAppWidget(cn, views);
...
}
Here is App definition:
public class App extends Application implements OnInitListener {
private static Context mContext;
public void onCreate() {
super.onCreate();
mContext = this;
}
public static Context getContext() {
return mContext;
}
}