Android Widget update - android

Based on this tutorial I created a widget that should show the time. The java way works, but the service way doesn't.
HelloWidget.java:
public class HelloWidget extends AppWidgetProvider {
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
Intent intent = new Intent(context, UpdateService.class);
context.startService(intent);
}
}
UpdateService.java:
public final class UpdateService extends Service {
#Override
public void onStart(Intent intent, int startId) {
RemoteViews updateViews = new RemoteViews(this.getPackageName(), R.layout.main);
Date date = new Date();
java.text.DateFormat format = SimpleDateFormat.getTimeInstance(
SimpleDateFormat.MEDIUM, Locale.getDefault());
updateViews.setTextViewText(R.id.widget_textview, "Current Time " + format.format(date));
ComponentName thisWidget = new ComponentName(this, HelloWidget.class);
AppWidgetManager manager = AppWidgetManager.getInstance(this);
manager.updateAppWidget(thisWidget, updateViews);
}
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
}
The hello_widget_provider.xml has this row: android:updatePeriodMillis="1000"
Problem:
The widget shows the current time but it's not updating (the java-way is).
My main goal is to update the widget at e.g. 18:56 every day.
Idk if this is the good way, but i tried to modify the onUpdate method like below, but it has a problem with ALARM_SERVICE: ALARM_SERVICE cannot be resolved to a variable
public class HelloWidget extends AppWidgetProvider {
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
AlarmManager alarmManager;
Intent intent = new Intent(context, AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, 18);
cal.set(Calendar.MINUTE, 56);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), 86400, pendingIntent);
}
}
Any help is appreciated.

Looking back the time I asked this question makes me realize how much I learned in the past 1,5 years.
Since I have just been working with a widget in the last two weeks and I have just found this unanswered question of mine I decided to solve it. I hope it helps others.
The AppWidgetProvider should look like this:
public class MyWidget extends AppWidgetProvider {
public static String ACTION_WIDGET_CONFIGURE = "ConfigureWidget";
public static String ACTION_WIDGET_RECEIVER = "ActionReceiverWidget";
private PendingIntent service = null;
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
final AlarmManager m = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
final Calendar TIME = Calendar.getInstance();
TIME.set(Calendar.MINUTE, 0);
TIME.set(Calendar.SECOND, 0);
TIME.set(Calendar.MILLISECOND, 0);
final Intent i = new Intent(context, MyService.class);
if (service == null)
{
service = PendingIntent.getService(context, 0, i, PendingIntent.FLAG_CANCEL_CURRENT);
}
m.setRepeating(AlarmManager.RTC, TIME.getTime().getTime(), 1000 * 1, service);
}
#Override
public void onDeleted(Context context, int[] appWidgetIds) {
super.onDeleted(context, appWidgetIds);
}
#Override
public void onDisabled(Context context) {
super.onDisabled(context);
final AlarmManager m = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (service != null)
{
m.cancel(service);
}
}
#Override
public void onEnabled(Context context) {
super.onEnabled(context);
}
#Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
}
}
The Service:
public class MyService extends Service
{
#Override
public void onCreate()
{
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
buildUpdate();
return super.onStartCommand(intent, flags, startId);
}
private void buildUpdate()
{
String lastUpdated = DateFormat.format("hh:mm:ss", new Date()).toString();
RemoteViews view = new RemoteViews(getPackageName(), R.layout.widget_layout);
view.setTextViewText(R.id.btn_widget, lastUpdated);
ComponentName thisWidget = new ComponentName(this, MyWidget.class);
AppWidgetManager manager = AppWidgetManager.getInstance(this);
manager.updateAppWidget(thisWidget, view);
}
#Override
public IBinder onBind(Intent intent)
{
return null;
}
}
Also, register the provider and the Service in the Manifest:
<receiver android:name="MyWidget" android:label="Tutorial_Widget 1x1">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="com.something.MyWidget.ACTION_WIDGET_RECEIVER"/>
</intent-filter>
<meta-data android:name="android.appwidget.provider" android:resource="#xml/widget_info"/>
</receiver>
<service android:enabled="true" android:name=".MyService" />

ALARM_SERVICE is a static field of the Context class. So type Context.ALARM_SERVICE or use a static import.
Remember that when you set updatePeriodMillis attribute value it's not guaranteed for your onUpdate method to be called with that period exactly.

Related

How to update a Widget in OREO (AlarmManager stops after some hours/days)

I have a information widget which I'm updating each 60 seconds. Before OREO I did it using a service, but now, you can't do it because you can't use the service when the app is in the background, so I followed some posts here and used a alarm and AlarmManager to update the widget.
The problem is that it works for some hours, but in one or two days, the onReceive method of the alarm is not being called anymore. something happens because the alarm stops without user removing the widget, so the wiget stops getting updated.
It seems that AlarmManager is not the best way to do this or I'm doing something wrong.
This is my widget alarm receiver:
public class WidgetAlarmReceiver extends BroadcastReceiver {
private final static String TAG = "WidgetAlarmReceiver";
#Override
public void onReceive(final Context context, Intent intent) {
Log.d(TAG,"onReceive");
ComponentName componentName = new ComponentName(context, AppWidget.class);
RemoteViews remoteViews = WidgetHelper.getInstance().generateRemoteViews(context);
AppWidgetManager.getInstance(context).updateAppWidget(componentName, remoteViews);
}
}
This is the source code of the widget where I start the alarm:
public class AppWidget extends AppWidgetProvider {
private AlarmManager alarmMgr;
private PendingIntent alarmIntent;
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
RemoteViews remoteViews = WidgetHelper.getInstance().generateRemoteViews(context);
appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
}
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId);
}
}
private void startWidgetAlarm(Context context){
alarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, WidgetAlarmReceiver.class);
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
alarmMgr.setInexactRepeating(AlarmManager.RTC, System.currentTimeMillis(), 60000, alarmIntent);
}
private void stopWidgetAlarm(Context context){
alarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, WidgetAlarmReceiver.class);
PendingIntent sender = PendingIntent.getBroadcast(context, 0, intent, 0);
alarmMgr.cancel(sender);
}
#Override
public void onEnabled(Context context) {
super.onEnabled(context);
startWidgetAlarm(context);
}
#Override
public void onDisabled(Context context) {
super.onDisabled(context);
stopWidgetAlarm(context);
}
}
Portion of my manifest:
<receiver android:name=".AppWidget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/new_app_widget_info" />
</receiver>
<receiver android:name=".WidgetAlarmReceiver" android:exported="false"/>
From my experience it's best to use a one time alarm and re-register it once triggered, because for repeating alarms Android might think you're abusing the AlarmManager.. So this is the method I'm using:
void registerOneTimeAlarm(PendingIntent alarmIntent, long delayMillis, boolean triggerNow) {
int SDK_INT = Build.VERSION.SDK_INT;
long timeInMillis = (System.currentTimeMillis() + (triggerNow ? 0 : delayMillis));
if (SDK_INT < Build.VERSION_CODES.KITKAT) {
alarmManager.set(AlarmManager.RTC_WAKEUP, timeInMillis, alarmIntent);
} else if (Build.VERSION_CODES.KITKAT <= SDK_INT && SDK_INT < Build.VERSION_CODES.M) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, timeInMillis, alarmIntent);
} else if (SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeInMillis, alarmIntent);
}
}
Maybe it will work for you as well.

Play sound on widget click

Here's my code, it opens the main activity, but I can't seem to find a way to just make the widget play a sound.
I tried to:
add a button to the widget (didn't work)
add an OnClickListener to the main activity (which works but it opens the main activity and I just want the sound not the activity)
write a new method to play the sound with the MediaPlayer sound.start() method in it and calling it (didn't work)
I've looked in the android dev page and all I was able to find was how to use the MediaPlayer, but it says nothing about playing audio from a widget.
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.widget.RemoteViews;
public class ClockWidget extends AppWidgetProvider {
public static String ACTION_WIDGET_RECEIVER = "ActionReceiverWidget";
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
final int N = appWidgetIds.length;
for (int i=0; i<N; i++) {
int appWidgetId = appWidgetIds[i];
Intent intent = new Intent(context, MainActivity.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent,PendingIntent.FLAG_UPDATE_CURRENT);
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.clock_widget_layout);
views.setOnClickPendingIntent(R.id.AnalogClock0, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
}
To do it you need to use a service which you call like this
Intent svc=new Intent(this, BackgroundSoundService.class);
startService(svc);
public class BackgroundSoundService extends Service {
private static final String TAG = null;
MediaPlayer player;
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
player = MediaPlayer.create(this, R.raw.trck);
//configure other settings
}
public int onStartCommand(Intent intent, int flags, int startId) {
player.start();
return 1;
}
public void onStart(Intent intent, int startId) {
// TO DO
}
public IBinder onUnBind(Intent arg0) {
// TO DO Auto-generated method
return null;
}
public void onStop() {
}
public void onPause() {
}
#Override
public void onDestroy() {
player.stop();
player.release();
}
}
Please register this service in Manifest.
<service android:enabled="true" android:name=".BackgroundSoundService" />
and you could do smth like this in widget
public class ClockWidget extends AppWidgetProvider {
private final String ACTION_WIDGET_PLAY = "PlaySong";
private final String ACTION_WIDGET_PAUSE = "PauseSong";
private final String ACTION_WIDGET_STOP = "StopSong";
private final int INTENT_FLAGS = 0;
private final int REQUEST_CODE = 0;
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
RemoteViews controlButtons = new RemoteViews(context.getPackageName(),
R.layout.main);
Intent playIntent = new Intent(this, BackgroundSoundService.class);
Intent pauseIntent = new Intent(this, BackgroundSoundService.class);
Intent stopIntent = new Intent(this, BackgroundSoundService.class);
PendingIntent playPendingIntent = PendingIntent.getService(
this, REQUEST_CODE, playIntent, INTENT_FLAGS);
PendingIntent pausePendingIntent = PendingIntent.getService(
this, REQUEST_CODE, pauseIntent, INTENT_FLAGS);
PendingIntent stopPendingIntent = PendingIntent.getService(
this, REQUEST_CODE, stopIntent, INTENT_FLAGS);
controlButtons.setOnClickPendingIntent(
R.id.btnPlay, playPendingIntent);
controlButtons.setOnClickPendingIntent(
R.id.btnPause, pausePendingIntent);
controlButtons.setOnClickPendingIntent(
R.id.btnStop, stopPendingIntent);
appWidgetManager.updateAppWidget(appWidgetIds, controlButtons);
}
}

Start my application by clicking on widget

I'm trying How do I run an Activity from a button on a widget in Android? in order to launch my application on click on an image button. When i un-comment the code it switches the images but when i try to use this code it does nothing. I'd like to kick off my application instead.
public class MyWidgetIntentReceiver extends BroadcastReceiver {
private static int clickCount = 0;
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals("pl.looksok.intent.action.CHANGE_PICTURE")){
updateWidgetPictureAndButtonListener(context);
}
}
private void updateWidgetPictureAndButtonListener(Context context) {
Log.i("PROJECTCARUSO", "updateWidgetPictureAndButtonListener");
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget);
// remoteViews.setImageViewResource(R.id.widget_button, getImageToSet());
//
// //REMEMBER TO ALWAYS REFRESH YOUR BUTTON CLICK LISTENERS!!!
// remoteViews.setOnClickPendingIntent(R.id.widget_button, MyWidgetProvider.buildButtonPendingIntent(context));
//
// MyWidgetProvider.pushWidgetUpdate(context.getApplicationContext(), remoteViews);
Intent intentClick = new Intent(context, FragmentChange.class);
PendingIntent pendingIntent = PendingIntent.getActivity (context, 0,
intentClick, 0);
remoteViews.setOnClickPendingIntent (R.id.widget_button, pendingIntent);
}
private int getImageToSet() {
clickCount++;
return clickCount % 2 == 0 ? R.drawable.app_icon : R.drawable.energy;
}
}
Here's what i finally got to work:
public class WidgetProvider extends AppWidgetProvider {
public static String SWITCH_WIDGET_UPDATE = "MainActivity.Switch";
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
Log.i("PROJECTCARUSO","onUpdate");
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget);
remoteViews.setOnClickPendingIntent(R.id.widget_button, buildButtonPendingIntent(context));
Intent intentClick = new Intent(context, FragmentChange.class);
PendingIntent pendingIntent = PendingIntent.getActivity (context, 0, intentClick, 0);
remoteViews.setOnClickPendingIntent (R.id.widget_button, pendingIntent);
pushWidgetUpdate(context, remoteViews);
}
public static PendingIntent buildButtonPendingIntent(Context context) {
Intent intent = new Intent();
intent.setAction("CHANGE_PICTURE");
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
public static void pushWidgetUpdate(Context context, RemoteViews remoteViews) {
ComponentName myWidget = new ComponentName(context, WidgetProvider.class);
AppWidgetManager manager = AppWidgetManager.getInstance(context);
manager.updateAppWidget(myWidget, remoteViews);
}
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
super.onReceive(context, intent);
}
}

Why is AlarmManager not updating my widget?

I have a widget that is simply a textview, that I am attempting to update every second using an AlarmManager. The Log.d("test", "hello1") shows up in my LogCat when I run the app, but the Log.d("test", "service") does not show up, so it appears the program is not even reaching my MyService class.
I would greatly appreciate any help.
Thank you.
Here is my AppWidgetProvider class:
public class NetworkSpeedWidget extends AppWidgetProvider {
private PendingIntent service = null;
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
Log.d("test", "hello");
super.onUpdate(context, appWidgetManager, appWidgetIds);
final AlarmManager m = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
final Intent i = new Intent(context, MyService.class);
if (service == null)
{
service = PendingIntent.getService(context, 0, i, PendingIntent.FLAG_CANCEL_CURRENT);
}
Log.d("test", "hello1");
m.setRepeating(AlarmManager.RTC, System.currentTimeMillis(),1000,service);
}
}
Here is my MyService class:
public class MyService extends Service{
#Override
public void onCreate()
{
Log.d("test", "service");
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
buildUpdate();
return super.onStartCommand(intent, flags, startId);
}
private void buildUpdate()
{
MainActivity object = new MainActivity();
String objectString = object.getMegaBitsPerSecondString();
AppWidgetManager manager = AppWidgetManager.getInstance(this);
ComponentName thisWidget = new ComponentName(this, NetworkSpeedWidget.class);
int[]ids = manager.getAppWidgetIds(thisWidget);
final int N = ids.length;
for (int i = 0; i < N; i++){
int awID = ids[i];
RemoteViews v = new RemoteViews(getPackageName(), R.layout.widget);
v.setTextViewText(R.id.widgetTextView,objectString);
manager.updateAppWidget(awID, v);
}
}
#Override
public IBinder onBind(Intent intent)
{
return null;
}
}
All my code is correct, I just needed to declare it in my Manifest.
I simply added the following line:
<service android:enabled="true" android:name=".MyService" />
Thank you jul for your help.

Android: Clickable imageview widget

I want to make a very simple widget:
It must consist from just one image view
1) on incoming sms it should change the image
2) on click it also should change the image
I tried to make it using ImageButton but failed: there were problems with changing the image on sms received event: new image had wrong scale.
Anyway now I want to make an ImageView without anything else.
The problem is that I can't handle onClick event:
I've got a running service which should handle all events: sms received and click:
widget provider:
public class MyWidgetProvider extends AppWidgetProvider {
#Override
public void onDisabled(Context context) {
context.stopService(new Intent(context, UpdateService.class));
super.onDisabled(context);
}
#Override
public void onEnabled(Context context) {
super.onEnabled(context);
Intent intent = new Intent(context, UpdateService.class);
context.startService(intent);
}
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
for (int i = 0; i < appWidgetIds.length; i++) {
int appWidgetId = appWidgetIds[i];
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.main);
Intent widgetClickIntent = new Intent(context, UpdateService.class);
widgetClickIntent.setAction(UpdateService.ACTION_ON_CLICK);
PendingIntent pendingIntentViewClick = PendingIntent.getService(context, 0, widgetClickIntent, 0);
remoteViews.setOnClickPendingIntent(R.id.widget_imageview, pendingIntentViewClick);
appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
}
}
}
service:
public class UpdateService extends Service {
static final String ACTION_SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";
static final String ACTION_ON_CLICK = "android.MyWidget.ACTION_ON_CLICK";
private final static IntentFilter intentFilter;
static {
intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_SMS_RECEIVED);
intentFilter.addAction(ACTION_ON_CLICK);
}
public final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals(ACTION_SMS_RECEIVED)) {
reactOnSms(context);
}
if (action.equals(ACTION_ON_CLICK)) {
onCLick(context);
}
}
};
#Override
public void onCreate() {
super.onCreate();
registerReceiver(broadcastReceiver, intentFilter);
}
#Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(broadcastReceiver);
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
private void reactOnSms(Context context) {
// doSomething
}
public void onCLick(Context context) {
// doSomething
}
Manifest:
<receiver a:name="..."...>
<intent-filter>
<action a:name="android.provider.Telephony.SMS_RECEIVED"/>
<action a:name="android.MyWidget.ACTION_ON_CLICK"/>
</intent-filter>
<meta-data a:name="android.appwidget.provider"
a:resource="#xml/my_widget_provider_info"/>
</receiver>
<service a:name=".UpdateService"
a:label="UpdateService">
<intent-filter>
<action a:name="android.provider.Telephony.SMS_RECEIVED"/>
<action a:name="android.MyWidget.ACTION_ON_CLICK"/>
</intent-filter>
</service>
I tried this Clickable widgets in android
I found the solution.
Sorry for those of you who read the question. Too much code inside, I understand.
The problem was that UpdateService was not the real handler of the broadcast intent. Anonymous implementation of BroadcastReceiver made all the work.
So the problem was in this code (widgetProvider):
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
for (int i = 0; i < appWidgetIds.length; i++) {
int appWidgetId = appWidgetIds[i];
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.main);
// wrong:
// Intent widgetClickIntent = new Intent(context, UpdateService.class);
// widgetClickIntent.setAction(UpdateService.ACTION_ON_CLICK);
// PendingIntent pendingIntentViewClick = PendingIntent.getService(context, 0, widgetClickIntent, 0);
// correct:
Intent widgetClickIntent = new Intent(UpdateService.ACTION_ON_CLICK);
PendingIntent pendingIntentViewClick = PendingIntent.getBroadcast(context, 0, widgetClickIntent, 0);
///////
remoteViews.setOnClickPendingIntent(R.id.widget_imageview, pendingIntentViewClick);
appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
}
}

Categories

Resources