I'm using a custom RemoteView for AndroidNotification, and I would like to mimic the system behavior.
How does Android update its notification time format - do they ever change once they are set? How am I able to mimic this behavior?
I don't know much about the notification time format, but if you want to mimic their behaviour should have a look at the DateUtils class, especially the formatSameDayTime which I think does what you described.
It is not possible to update the notification after being added unless .notify is called again with the same ID.
If dealing with time stamp, it is best to use the native Notification NotificationCompat.Builder without using RemoteViews.
I'm not sure whether you are still looking for answers, given that you have provided one yourself. However, if you are looking to achieve your original goal you will probably want to
Rebuild the RemoteView whenever the time changes (it's just way easier)
Set up a BroadcastReceiver to catch the ticks of the clock so you know when the time has changed.
So, some code a bit like this:
class MyCleverThing extends Service (say) {
// Your stuff here
private static IntentFilter timeChangeIntentFilter;
static {
timeChangeIntentFilter = new IntentFilter();
timeChangeIntentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
timeChangeIntentFilter.addAction(Intent.ACTION_TIME_CHANGED);
}
// Somewhere in onCreate or equivalent to set up the receiver
registerReceiver(timeChangedReceiver, timeChangeIntentFilter);
// The actual receiver
private final BroadcastReceiver timeChangedReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals(Intent.ACTION_TIME_CHANGED) ||
action.equals(Intent.ACTION_TIMEZONE_CHANGED))
{
updateWidgets(); // Your code to rebuild the remoteViews or whatever
}
}
};
Every time you update your notification, do something as simple as this (24 hour time)...
public void updateNotifTime(RemoteViews customNotifView){
Date currentTime = new Date();
int mins = currentTime.getMinutes();
String minString = "";
if(mins<10){
minString = "0";
}
minString += mins;
customNotifView.setTextViewText(R.id.time, currentTime.getHours()+":"+minString);
}
Related
I want to run a task on every minute, this Problem is already analogue to mine. A user posted the following:
BroadcastReceiver _broadcastReceiver;
private final SimpleDateFormat _sdfWatchTime = new SimpleDateFormat("HH:mm");
private TextView _tvTime;
#Override
public void onStart()
{
super.onStart();
_broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context ctx, Intent intent)
{
if (intent.getAction().compareTo(Intent.ACTION_TIME_TICK) == 0)
_tvTime.setText(_sdfWatchTime.format(new Date()));
}
};
registerReceiver(_broadcastReceiver, new IntentFilter(Intent.ACTION_TIME_TICK));
}
#Override
public void onStop()
{
super.onStop();
if (_broadcastReceiver != null)
unregisterReceiver(_broadcastReceiver);
}
My problem is to understand the if statement inside onReceive.
Why does it have to be 0? What does it say?
Why is it necessary to check for the action ACTION_TIME_TICK in the first place, when we already set an IntentFilter with that action?
I would have asked this question in the original context, but I'm new to stackoverflow.
Why does it have to be 0? What does it say?
Well, let's break it apart.
intent.getAction()
If you look right here, basically it tells you what the intent is doing. It returns a String. Intent.ACTION_TIME_TICK is actually a String.
.compareTo() == 0
This compares two objects (in this case, strings). When you check if it equals 0, you are checking if the two strings are equal, the same.
So putting it together,
intent.getAction().compareTo(Intent.ACTION_TIME_TICK) == 0
This checks whether the the action for the intent is the Broadcast action ACTION_TIME_TICK.
Why is it necessary to check for the action ACTION_TIME_TICK in the first place, when we already set an IntentFilter with that action?
Sometimes you accidentally (or purposely register) the same BroadcastReceiver several times, with different intent filters. Your BroadcastReceiver would be receiving broadcasts for several different actions. In the onReceive method, you would want to make sure you are responding to the appropriate action.
I am trying to make a notification for a music player with controls. I am successfully listening to the button click events and functions are being fired properly. Only problem i am facing is changing the text of notification on these click events.
Here's what i am trying.
This is the receiver successfully receiving the calls and firing each and every line perfectly. But i cant the text changing. I think i have to reset the content view to Notification. If so, how do i do that?
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals("stop")) {
ABCFragment.stopSong();
Log.d("Notification","Stopping");
}else if (action.equals("play")) {
ABCFragment.togglePlayPause();
Log.d("Notification","Toggle Play/Pause");
RemoteViews contentView = new RemoteViews(context.getPackageName(),R.layout.notification_layout);
contentView.setTextViewText(R.id.songName, "SOME NEW SONG");
}else if (action.equals("next")) {
ABCFragment.playNextSong();
Log.d("Notification","Next");
}
}
Solution :
I updated my Notification class constructor to pass an extra arguments and got it working!
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals("stop")) {
ABCFragment.stopSong();
Log.d("Notification","Stopping");
}else if (action.equals("play")) {
ABCFragment.togglePlayPause();
Log.d("Notification","Toggle Play/Pause");
new ABCNotification(context, "SOME NEW SONG");
}else if (action.equals("next")) {
ABCFragment.playNextSong();
Log.d("Notification","Next");
}
}
constructor is handling the new passed arguments.
you can't really change stuff on notification. It's a bit annoying for sure, you just have to replace the current notification with a new one with the new text.
My app there's an upload process and every 3 seconds we're updating the notification to change percentage.
so simply re-build the notification and call notify() on the NotificationManager with the same ID.
I want to reset a variable of my service when user clears my notification: that's all!
Looking around I see that everyone suggest to add a delete intent on my notification, but intent is used to start an activity, a service o whatever while I just need a thing like this:
void onClearPressed(){
aVariable = 0;
}
how to obtain this result?
Notifications are not managed by your app and all things like showing notifications and clearing them are actually happening in another process. You can't make another app directly execute a piece of code just because of security reasons.
The only possibility in your case is to provide a PendingIntent which just wraps around a regular Intent and will be started on behalf of your app when notification is cleared.
You need to use PendingIntent for sending broadcast or starting a service and then doing what you want in the broadcast receiver or in the service. What exactly to use depends on from which application component you are showing notifications.
In case of broadcast receiver you can just create an anonymous inner class for broadcast receiver and register it dynamically before showing notification. It will look something like that:
public class NotificationHelper {
private static final String NOTIFICATION_DELETED_ACTION = "NOTIFICATION_DELETED";
private final BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
aVariable = 0; // Do what you want here
unregisterReceiver(this);
}
};
public void showNotification(Context ctx, String text) {
Intent intent = new Intent(NOTIFICATION_DELETED_ACTION);
PendingIntent pendintIntent = PendingIntent.getBroadcast(ctx, 0, intent, 0);
registerReceiver(receiver, new IntentFilter(NOTIFICATION_DELETED_ACTION));
Notification n = new Notification.Builder(mContext).
setContentText(text).
setDeleteIntent(pendintIntent).
build();
NotificationManager.notify(0, n);
}
}
Andrei is correct.
If you want multiple messages back such as:
you want to know if the message was clicked
you attached an action with an icon that you want to catch
AND you want to know if the message was canceled
you must register each of those response filters:
public void showNotification(Context ctx, String text) ()
{
/… create intents and pending intents same format as Andrie did../
/… you could also set up the style of your message box etc. …/
//need to register each response filter
registerReceiver(receiver, new IntentFilter(CLICK_ACTION));
registerReceiver(receiver, new IntentFilter(USER_RESPONSE_ACTION));
registerReceiver(receiver, new IntentFilter(NOTIFICATION_DELETED_ACTION));
Notification n = new Notification.Builder(mContext)
.setContentText(text)
.setContentIntent(pendingIntent) //Click action
.setDeleteIntent(pendingCancelIntent) //Cancel/Deleted action
.addAction(R.drawable.icon, "Title", pendingActionIntent) //Response action
.build();
NotificationManager.notify(0, n);
}
Then you can catch the different responses with if, else statements (as Andrei did), or with a switch statement.
Note: I make this response primarily because I could not find this anywhere, and I had to figure it out on my own. (perhaps I will remember it better for that :-) Have Fun!
I have a service that performs background updates.
I want to give the user the the option to disable the updates when their battery percentage reaches a certain level.
From my research, I'm going to use a receiver in the onCreate method of my Service class, eg:
public class MainService extends Service
{
#Override
public void onCreate()
{
this.registerReceiver(this.BatInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
}
private BroadcastReceiver BatInfoReceiver = new BroadcastReceiver(){
#Override
public void onReceive(Context arg0, Intent intent) {
int level = intent.getIntExtra("level", 0);
}
};
}
I'm assuming that the best practice is to leave the service running, check the battery level in the service, and not perform the CPU-intensive code, based on the percentage?
I don't actually need to stop the service itself and start it up again, based on the battery percentage?
UPDATE:
This seems to be a better solution, but not 100% sure. I registered a BroadcastReceiver in the AndroidManifest:
<receiver android:name="BatteryReceiver" />
Then created a BroadcastReceiver:
public class BatteryReceiver extends BroadcastReceiver
{
#Override
public void onReceive(final Context context, final Intent intent)
{
final int currentBatteryPercent = intent.getIntExtra("level", 0);
final int disableBatteryPercent = Integer.parseInt(PreferenceManager.getDefaultSharedPreferences(context).getString("batteryPercent", 0);
//AlarmReceiver is the service that performs the background updates
final ComponentName component = new ComponentName(context, AlarmReceiver.class);
if (currentBatteryPercent < disableBatteryPercent)
{
context.getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_DISABLED , PackageManager.DONT_KILL_APP);
}
else
{
context.getPackageManager().setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED , PackageManager.DONT_KILL_APP);
}
}
}
That's right. What you will typically do is schedule some broadcast intent for an update (perhaps through an AlarmManager). When you get the notification that there is a low battery, you will stow this away in your service, and then before doing an update, check to ensure that the battery isn't too low. here is a good tutorial of watching the battery level. You shouldn't do much when handling this intent, just stick the battery level somewhere and (before doing an update) make sure it's appropriately "full."
Your update is a really bad way to stop an app. In general, asking the package manager to stop your component is much more of a hack than a solution. Instead, you should move some code into the component that actually does the updating, and store / update the information for the battery info there. Before you do an update, you check the battery level and don't proceed unless it's at a level where you feel comfortable updating the app. Using a broadcast receiver is fine, but you need to store the level somewhere (perhaps a sharedprefs). Instead, this is why putting the receiver within the service where you do the updating is probably best.
Is it possible to manage this requirement with a broadcastReceiver instead of running a service continuously ?
public void onReceive(final Context context, final Intent intent) {
if(intent != null && intent.getAction() != null) {
String action = intent.getAction();
if(action.equals(Intent.ACTION_BOOT_COMPLETED)) {
// Set alarm
}
else if(action.equals(Intent.ACTION_BATTERY_LOW)) {
setLocationAlarmReceiverEnabled(context, false);
}
else if(action.equals(Intent.ACTION_BATTERY_OKAY)) {
setLocationAlarmReceiverEnabled(context, true);
}
}
}
Is it possible to create an IntentFilter in android that matches ALL intents that are Broadcasted on the phone (perhaps by way of using a BroadcastReceiver)? I.E. the ones I see in ddms when I use the phone, under the ActivityManager tag? After digging through the documentation, and looking at the framework source, I am left to think it can't be done? That you must specify some sort of data, to paraphrase the docs, "some sort of data must be specified, or else you'll only get intents with no data". The app I am writing needs to know about every app that is started on the system. So far, the only way I have been able to do this is by polling ActivityManager. It seems the best way would be to have an event driven solution, using whatever underlying logic ActivityManager uses, but it's all greek to me inside of the ActivityManager.java framework source, and seems like a lot of the stuff underneath (if not ALL) is deliberately encapsulated from me.
Any ideas?
You said it yourself, the documentation quite clearly specifies how intent filters function and that this is not possible to receive all broadcasts.
Neither this nor retrieving task information is something that is supported by the APIs made public in the Android SDK.
You can register a costume receiver for each event type that will hold a reference to a parent broadcast receiver and call its onReceive method
class ChildBroadcastReceiver extends BroadcastReceiver {
private BroadcastReceiver parent;
public ChildBroadcastReceiver(BroadcastReceiver parent) {
this.parent = parent;
}
#Override
public void onReceive(Context context, Intent intent) {
parent.onReceive(context, intent);
}
}
Then you can register to all the possible events by using reflection:
final BroadcastReceiver parent = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
android.util.Log.d("GlobalBroadcastReceiver", "Recieved: " + intent.getAction() + " " + context.toString());
}
};
Intent intent = new Intent();
for(Field field : intent.getClass().getDeclaredFields()) {
int modifiers = field.getModifiers();
if( Modifier.isPublic(modifiers) &&
Modifier.isStatic(modifiers) &&
Modifier.isFinal(modifiers) &&
field.getType().equals(String.class)) {
String filter = (String)field.get(intent);
android.util.Log.d("GlobalBroadcastReceiver", "Registered: " + filter);
application.registerReceiver(new ChildBroadcastReceiver(parent), new IntentFilter(filter));
}
}