Intro
It's not possible to include custom views in app widgets. According to documentation only small predefined subset of views can be used in layout. So, as you've mentioned, it's not possible to create your own AnalogClock
view (as I've shown in my other answer) and include it in app widget.
However there are other means that can be used to create custom AnalogClock
in app widget. In such implementation setting time directly in AnalogClock
should not be a problem.
Short answer
One of the ways to accomplish this is draw custom AnalogClock
into ImageView
(which is one of the allowed views in app widgets). Repeating PendingIntent
is using Service
to draw into ImageView
every 60 seconds (via RemoteViews
).
Note on battery usage
Keep in mind that invoking Service
action to update app widget via RemoteViews
every 60 seconds is not so lightweight on battery. On Jelly Bean example implementation used 2% of battery during night (screen off, average network strength). However, I've been updating the screen every 15 seconds, which is not necessary when it comes to analog clocks. So a rough estimate would be that an app widget with one update per minute will consume about 0.5% of battery juice during night - this might be significant for your users.
Solution details
Changes drawing service
First of all you'll have to create Service
that will draw into ImageView
present in your view. Drawing code is pretty much taken from AnalogClock
implementation and rearrange according to needs. First of all, there are no checks on whether time has changed - this is because Service
will be invoked only when we decide too (e.g. every 60 seconds). Seconds change is that we create Drawable
s from resources in the method.
Another change is that code draws analog clock into local Bitmap
using Canvas
:
// Creating Bitmap and Canvas to which analog clock will be drawn.
Bitmap appWidgetBitmap = Bitmap.createBitmap(availableWidth, availableHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(appWidgetBitmap);
Update is drawn into ImageView
via RemoteViews
. Changes are aggregated and then pushed into app widget on home screen - see docs for details). In our case there is only one change, and it is done through special method:
remoteViews.setImageViewBitmap(R.id.imageView1, appWidgetBitmap);
Bare in mind that for code to work you have to declare what is available space for drawing your analog clock. Based on what is declare in your widget_provider.xml
, you can determine the dimensions of ImageView
. Note that you'll have to convert dp
unit to px
unit, before drawing:
// You'll have to determine tour own dimensions of space to which analog clock is drawn.
final int APP_WIDGET_WIDTH_DP = 210; // Taken from widget_provider.xml: android:minWidth="210dp"
final int APP_WIDGET_HEIGHT_DP = 210; // Taken from widget_provider.xml: android:minHeight="210dp"
final int availableWidth = (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, APP_WIDGET_WIDTH_DP, r.getDisplayMetrics()) + 0.5f);
final int availableHeight = (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, APP_WIDGET_HEIGHT_DP, r.getDisplayMetrics()) + 0.5f);
I've pasted full code of Service
subclass here.
Also at the top you'll find the code that is responsible for setting time that will be displayed - modify as needed:
mCalendar = new Time();
// Line below sets time that will be displayed - modify if needed.
mCalendar.setToNow();
int hour = mCalendar.hour;
int minute = mCalendar.minute;
int second = mCalendar.second;
mMinutes = minute + second / 60.0f;
mHour = hour + mMinutes / 60.0f;
Also don't forget to declare this Service
and app widget in your AndroidManifest.xml
file, e.g.:
<receiver
android:name="TimeSettableAnalogClockAppWidget"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
<action android:name="android.appwidget.action.APPWIDGET_DISABLED"/>
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_provider"/>
</receiver>
<service android:name="TimeSettableAnalogClockService"/>
Most important info about using services to update app widget is described shortly in this blog post.
Invoking drawing
Scheduling updates is done in onUpdate()
method of your AppWidgetProvider
subclass - the on that is declare in your AndroidManifest.xml
file. We'll also need to remove PendingIntent
when app widget is removed from home screen. This is done in overridden onDisabled()
method. Remember to declare both actions that will call one of each methods: APPWIDGET_UPDATE
and APPWIDGET_DISABLED
. See excerpt from AndroidManifest.xml
above.
package com.example.anlogclocksettimeexample;
import java.util.Calendar;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
public class TimeSettableAnalogClockAppWidget extends AppWidgetProvider {
private PendingIntent service = null;
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// super.onUpdate(context, appWidgetManager, appWidgetIds);
final AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
final Calendar time = Calendar.getInstance();
time.set(Calendar.SECOND, 0);
time.set(Calendar.MINUTE, 0);
time.set(Calendar.HOUR, 0);
final Intent intent = new Intent(context, TimeSettableAnalogClockService.class);
if (service == null) {
service = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
}
alarmManager.setRepeating(AlarmManager.RTC, time.getTime().getTime(), 1000 * 60 /* ms */, service);
}
@Override
public void onDisabled(Context context) {
final AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(service);
}
}
Last line of onUpdate()
method schedules a repeating event to happen every 60 seconds, with first update to happen right away.
Drawing AnalogClock
once only
Using above code the simplest way to invoke service only once is to schedule single event in time instead of repeating one. Simple replace alarmManager.setRepeating()
call with following line:
alarmManager.set(AlarmManager.RTC, time.getTime().getTime(), service);
Results
Below is a screen shot with custom AnalogClock
app widget that had time set only once (note difference between analog clock time and time shown in status bar):
