I've developped some homescreen widgets displaying an adapterviewflipper layout. They automatically start and have a flip interval of 3 seconds. They display a small collection with custom animation in and out animation (total time 2 seconds). The collection data is refreshed each minute.
After some indeterminate time the flip interval is getting bigger and can take about 17 seconds to animate. I dont understand why.
Is there an android limitation ? Memory leak issue ? If i restart the phone, time is respected but i will encounter the issue in a near future.
Widget Layout
<AdapterViewFlipper
android:id="@+id/calendar_widget_adapterViewFlipper"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:autoStart="true"
android:flipInterval="3000"
android:animateLayoutChanges="true"
android:inAnimation="@animator/slide_in_bottom"
android:outAnimation="@animator/fade_out"
/>
AppWidget Provider
@Override
public void onReceive(Context context, Intent intent) {
//Log.d(LOG_TAG, "Received intent " + intent);
switch (intent.getAction()){
case ACTION_MESSAGE_DATA_UPDATE :
ComponentName thisAppWidget = new ComponentName(context.getPackageName(), getClass().getName());
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
int ids[] = appWidgetManager.getAppWidgetIds(thisAppWidget);
for (int appWidgetID: ids) {
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetID, R.id.calendar_widget_adapterViewFlipper);
}
break;
...
App Widget Provider configuration file
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialKeyguardLayout="@layout/calendar_widget"
android:initialLayout="@layout/calendar_widget"
android:autoAdvanceViewId="@id/calendar_widget_adapterViewFlipper"
android:minWidth="250dp"
android:minHeight="50dp"
android:minResizeWidth="250dp"
android:minResizeHeight="50dp"
android:previewImage="@drawable/calendar_ori_portrait"
android:resizeMode="horizontal|vertical"
android:updatePeriodMillis="0"
android:widgetCategory="home_screen|keyguard"></appwidget-provider>
CalendarWidgetRemoteViewsService
public class CalendarWidgetRemoteViewsService extends RemoteViewsService {
@Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
return new CalendarWidgetRemoteViewsFactory(this.getApplicationContext(), intent);
}
}
class CalendarWidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
private Context mContext;
private int mAppWidgetId;
private List<WidgetItem> mWidgetItems = new ArrayList<WidgetItem>();
public CalendarWidgetRemoteViewsFactory(Context context, Intent intent) {
mContext = context;
mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
}
@Override
public void onCreate() {
// In onCreate() you setup any connections / cursors to your data source. Heavy lifting,
// for example downloading or creating content etc, should be deferred to onDataSetChanged()
// or getViewAt(). Taking more than 20 seconds in this call will result in an ANR.
}
@Override
public void onDataSetChanged() {
// This is triggered when you call AppWidgetManager notifyAppWidgetViewDataChanged
// on the collection view corresponding to this factory. You can do heaving lifting in
// here, synchronously. For example, if you need to process an image, fetch something
// from the network, etc., it is ok to do it here, synchronously. The widget will remain
// in its current state while work is being done here, so you don't need to worry about
// locking up the widget.
//Log.d("CWRVS", "onDataSetChanged begins");
final int[] ErrorCode = {0};
mWidgetItems.clear();
if (!UtilsHelper.isOnline(mContext)){
ErrorCode[0] = 1;
}
if (ErrorCode[0] == 0)
{
final CountDownLatch latchSignal = new CountDownLatch(1);
AuthenticationHelper authenticationHelper = AuthenticationHelper.getInstance(mContext);
authenticationHelper.acquireTokenSilentlyWithAccountCheck(new AuthenticationCallback() {
@Override
public void onSuccess(AuthenticationResult authenticationResult) {
//Log.d("AUTH", "onSuccess");
final GraphHelper graphHelper = GraphHelper.getInstance();
graphHelper.getAllCalendarsEventsSupEqToday(authenticationResult.getAccessToken(), new ICallback<List<WidgetItem>>() {
@Override
public void success(List<WidgetItem> lwi) {
mWidgetItems = lwi;
latchSignal.countDown();
}
@Override
public void failure(ClientException ex) {
//Log.e("GRAPH", "Error getting events", ex);
latchSignal.countDown();
}
});
}
@Override
public void onError(MsalException exception) {
//Log.d("AUTH", "onError");
//if ( exception.getErrorCode() == AuthenticationHelper.NO_CLIENT_ACCOUNT_REGISTERED ){
//}
ErrorCode[0] = 2;
latchSignal.countDown();
}
@Override
public void onCancel() {
//Log.d("AUTH", "onCancel");
latchSignal.countDown();
}
});
//}
try{
latchSignal.await(15, TimeUnit.SECONDS);
}
catch (InterruptedException e){
Log.e("CDN", "CountDownException", e);
}
}
//Sending BroadCast for Error
Intent ErrorIntent = new Intent(mContext, CalendarWidgetProvider.class);
ErrorIntent.setAction(CalendarWidgetProvider.ACTION_CALENDAR_DATA_PARTIAL_UPDATE);
Bundle extras = new Bundle();
extras.putInt(CalendarWidgetProvider.EXTRA_ERROR_CODE, ErrorCode[0]);
ErrorIntent.putExtras(extras);
mContext.sendBroadcast(ErrorIntent);
//Log.d("CWRVS", "onDataSetChanged ends");
}
@Override
public void onDestroy() {
// In onDestroy() you should tear down anything that was setup for your data source,
// eg. cursors, connections, etc.
mWidgetItems.clear();
}
@Override
public int getCount() {
return mWidgetItems.size();
}
@Override
public RemoteViews getViewAt(int position) {
// position will always range from 0 to getCount() - 1.
// We construct a remote views item based on our widget item xml file, and set the
//
if (this.getCount() == 0){
return null;
}
WidgetItem wi = mWidgetItems.get(position);
RemoteViews rv = wi.BuildRemoteViews(mContext, position, this.getCount());
// You can do heaving lifting in here, synchronously. For example, if you need to
// process an image, fetch something from the network, etc., it is ok to do it here,
// synchronously. A loading view will show up in lieu of the actual contents in the
// interim.
// Return the remote views object.
return rv;
}
@Override
public RemoteViews getLoadingView() {
return null;
}
@Override
public int getViewTypeCount() {
return 1;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public boolean hasStableIds() {
return true;
}
}
I expect the flipinterval respect its value.
Regards