When a user clicks on my appwidget, the widget's configuration activity is opened up. So far that has suited my needs.
Now I'd like to add the ability to perform a special action (e.g. a widget refresh) when the user touches a particular feature or area on the widget, rather than open up the configuration activity.
Is there any way to do that? Even if it's just a case of detecting whether the touch was in the left half of the widget, for example, that would be useful.
Intent myIntent = new Intent(...);
...
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, myIntent, 0);
remoteViewsFive.setOnClickPendingIntent(R.id.imgRefresh, pendingIntent);
Like this, imgRefresh is your ImageView with refresh icon (for example). You should define an Intent then create a PendingIntent that set on view
A easy solution to do what you want it's use a transparent png in imgRefresh
Related
My app has a notification, that has an action button.
When clicking the button, I want the notification panel (or drawer, you name it) to collapse.
I've found a bunch of solutions, all suggesting the same (one of them for example).
This solution doesn't work anymore, at least not for me (on a Oneplus 8t Android 12 device). When that command is called, the app crashes.
What's the updated way to collapse the notification panel?
I came up with an idea after thinking about this question for months. All you need is an empty activity that doesn't show anything. I think this is a little bit hacky, but I don't have better ideas.
First, create a dummy empty activity that shows nothing, and put the code you would like to do inside that dummy activity.
DummyActivity.class
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Do your work here, put the work you would like to do after pressing the button, such as send a broadcast, so your work will still be executed
//No setContentView() is being called, so nothing will be displayed after opening this activity.
finish();
}
Remember to add android:excludeFromRecents="true" for the dummy activity in AndroidManifest.xml so the dummy activity doesn't show in recent apps.
Second, use PendingIntent.getActivity() instead, don't use PendingIntent.getBroadcast(), so you should have something like this :
RemoteViews remoteviews = new RemoteViews(context.getPackageName(), R.layout.notification_layout);
Intent notificationIntent = new Intent(context, DummyActivity.class);
PendingIntent pendingIntent;
pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
remoteviews.setOnClickPendingIntent(R.id.MyButton, pendingIntent);
With this kind of implementation, after clicking the button, the transparent activity will be started, showing nothing but causing the notification panel to be collapsed.
According to the specification, .setDeleteIntent(pendingIntent) is associated to both actions (CLEAR all events from notification bar and user action like swiping).
My requirements are that when the user touches the notification that appears on the notification bar, he must be forwarded to the NotificationsList.class. This is done with my pendingInent:
PendingIntent sendPendingIntent = PendingIntent.getActivity(context, reminderId, new Intent(context, NotificationsList.class), PendingIntent.FLAG_UPDATE_CURRENT);
However, on clicking the CLEAR button, the user must not be navigated to the application at all. With the .setDeleteIntent(pendingIndent) I cannot fulfill the 2nd requirement. The user is still navigated to NotificationsList.class.
Is there a way to programmatically distinguish the CLEAR all notifications events fired from the CLEAR button from user actions like touch or swipe on the specific notification on the notification bar?
What you're describing is very obtuse behavior. You need only set the pending intent to your notification and when it is clicked, the intent that is backing it will be executed.
If your code is navigating the user back to the app when the notification is cleared, then you already have a problem with your design. If the user clears your notification you should NOT be trying to navigate them back. Hence the setDeleteIntent() should NOT be associated with starting any activity.
Note that the intent that is backed when you click the notification (setContentIntent()) and clear (setDeleteIntent()) the notification are basically two PendingIntents, they should not be the same, which is what your problem is describing.
You cannot distinguish the two events. As the documentation says:
Notifications remain visible until one of the following happens:
The user dismisses the notification either individually or by using "Clear All" (if the notification can be cleared).
The user clicks the notification, and you called setAutoCancel() when you created the notification.
You call cancel() for a specific notification ID. This method also deletes ongoing notifications.
You call cancelAll(), which removes all of the notifications you previously issued.
So there are basically three different events in the view of a programmer:
You dismisses the notification
The user clicks on the notification
The user dismisses the notification (either by swiping or clearing it)
The first event is fired by yourself by calling cancelAll() or cancel().
You can handle the second like (which you wanna do I think):
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
//....
.setContentIntent(sendPendingIntent);
And you can handle the third event like (as you have described above):
builder.setDeleteIntent(pendingIndent)
I don't recommend to start an activity after the user dismisses your notification, because the user won't expect it and it will be a bad user experience.
I hope I could help.
According to the design guidelines, the user can expect to interact with your notification using higher-level gestures like click, swipe, and pinch zoom. Responding instantly to a lower level event like touch would short circuit these gestures, so your requirements would violate the design guidelines and you should not implement it.
If the requirements are changed so that the user is forwarded when they click on the notification, there is no need to distinguish between swiping and clearing, which is impossible in any case.
So your issue should be resolved by changing one word in the requirements: touch --> click.
I googled deleteIntent to find some info for a problem which led me here.
English is my second language. Sorry for some misuse of words in advance. I'm an android newbie, just downvote the answer if it sucks :)
For your last question, just as #x-code and #bendaf said, it's impossible to
distinguish swiping and clearing.
I am following the codelabs about notifications and encountered the same question(The description in your title). So I decided to offer more detail about how to use .setDeleteIntent in your case. Maybe you had done that.
In your case, the wrapped intent is for starting an activity, so do the pendingIntent.
PendingIntent sendPendingIntent = PendingIntent.getActivity(context, reminderId, new Intent(context, NotificationsList.class), PendingIntent.FLAG_UPDATE_CURRENT);
But for performing a broadcast, e.g. doing some stuff when the notification is cleared, use:
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, NOTIFICATION_ID, new Intent(yourCustomActionString), PendingIntent.FLAG_UPDATE_CURRENT);
builder.setDeleteIntent(pendingIntent); // the pendingIntent will be sent when the notification is cleared
Then we need a custom broadcast receiver receive that custom action contained in the Intent object, in your case, this action relates to the clearing:
// Inside onCreate, register the broadcast receiver;
registerReceiver(new MyReceiver(), new IntentFilter(yourCustomActionString));
.
.
// Create an inner class
public class MyReceiver extends BroadcastReceiver {
public NotificationReceiver() {}
#Override
public void onReceive(Context context, Intent intent) {
// code inside will be executed when pendingIntent is sent
Log("taG", "Notification is cleared"); // a message will be logged if the notification is cleared
// for more than one action, using switch...case to decide
}
}
I've got a somewhat graphically complex Android homescreen (via AppWidgetProvider) widget that I simply can't get to respond to touch events. The general idea is that tapping the widget should make it change modes for the next 4-5 seconds to display different information, but for the life of me it's showing no sign of ever receiving the Touch event.
The steps I've taken are as follows:
I've implemented an intent-filter within the Manifest.xml like so...
<action android:name="foo.kung.fancywidget.TOUCHED" />;
... and it's inside the <receiver /> container for the widget, right next to the expected APPWIDGET_UPDATE entry that Android Studio helpfully adds.
I've ensured that the Layout being used has the clickable attribute set to true on every single element (just to be thorough) including the top-level RelativeLayout itself.
I've defined the static string for the thing at the top of the ExtraFancyWidget.class, like so...
public static final String TOUCHED = "foo.kung.fancywidget.TOUCHED";
...and according to what I've been reading it should come through as the broadcast via the onRecieve handler when done like this...
if (TOUCHED.equals(intent.getAction())) {
Log.i("onReceive", "Touch event received");
}
...but with a Log.d entry at the top of the onReceive handler I can tell I'm not getting any sort of signals through there at all aside from the heartbeat coming from the system service every ten seconds.
Lastly, I'm assigning the intent just like I've been reading about
private PendingIntent createOnClickIntent(Context context) {
Intent intent = new Intent(context, ExtraFancyWidget.class);
intent.setAction(TOUCHED); // WHY DOESN'T THIS WORK?!?!?
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
...and it's literally the last thing happening in onUpdate before the view is updated
views.setOnClickPendingIntent(R.id.TheWholeWidget, createOnClickIntent(context));
appWidgetManager.updateAppWidget(appWidgetId, views);
What could I possibly be overlooking or not seeing at this point? I don't know of anything else I'm supposed to be doing or changing to make this work... it just mysteriously ignores the user. ADB never shows me any of the log entries (although it does show a number of other, silly log messages so I know that's working) that would indicate the widget ever sees me tapping it.
The problem is in step 5, where you're creating the intent. Do NOT set a specific class to the intent, rather create it with only an action, like this:
Intent intent = new Intent();
intent.setAction(TOUCHED); // this should work now
or the shorthand
Intent intent = new Intent(TOUCHED);
If it still doesn't work, consider putting a requestCode other than 0 when building the PendingIntent. Depending on the Android version you're building from, there were some bugs when using just 0.
return PendingIntent.getBroadcast(context, 1000, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Lastly, if this broadcast will be used only within your app (which is highly probable), consider using a LocalBroadcastManager.
I would like to change the background of a widget button on a click event and then return to the original background, something like ACTION_DOWN and ACTION_UP.
I know that i can change the background with:
RemoteViews.setInt(viewId, methodName, value);
and add an action to a click event:
Intent intent = new Intent(context, MyWidget.class);
intent.setAction("Restart");
PendingIntent pIntent = PendingIntent.getBroasdcast(context, 0, intent, 0);
RemoteViews.setOnClickPendingIntent(viewId, pIntent);
Have a look at StateList xml files. The selectors that these files use will handle changing your View's background in all of its various states, and you don't have to mess with writing your own touchlistener.
http://developer.android.com/guide/topics/resources/drawable-resource.html#StateList
I have a simple appWidget that displays an image. The image is selected in the configuration activity and the widget is updated via the remove view. This is done on a button push executing the code below:
Intent intent = new Intent(context, KidDialog.class);
intent.setData(selectedImage);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
widgetView.setOnClickPendingIntent(R.id.centerBrd, pendingIntent);
widgetView.setImageViewUri(R.id.widImg, selectedImage);
appWidgetManager.updateAppWidget(appWidgetId, widgetView);
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
setResult(RESULT_OK, resultValue);
finish();`
As you can see, it is also seeing an PendingIntent for the onClickListener of the image. I don't think that is relevant here. Anyway, this work great. The widget does exactly what I want it to. Until I restart the device.
When I restart the widget loads with the default view from the xml in the apk. It does not keep the image update from the initial configuration. My questions is how do I get the widget to load back up after a restart with the updated view set during the configuration activity? I will also need to reset the onClick Pendingintent, but I will save that for later. I expect they are related anyway. So I am out of ideas.
I am not sure of the best answer, but when you receive the intent from the button push you could save the image name to a SharedPreference.
And when you create the widget, check the value of the sharedpreference and use that to restore the desired image..