Simplify StackWidgetService; reduce flicker

pull/428/head
Alinson S. Xavier 7 years ago
parent 436d19dfea
commit dc74c0e54b

@ -0,0 +1,36 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.widgets
import android.content.*
import android.view.*
import org.isoron.uhabits.widgets.views.*
class EmptyWidget(
context: Context,
widgetId: Int
) : BaseWidget(context, widgetId) {
override fun getOnClickPendingIntent(context: Context) = null
override fun refreshData(v: View) {}
override fun buildView() = EmptyWidgetView(context)
override fun getDefaultHeight() = 200
override fun getDefaultWidth() = 200
}

@ -52,7 +52,6 @@ class StackWidget(
serviceIntent.data = Uri.parse(serviceIntent.toUri(Intent.URI_INTENT_SCHEME)) serviceIntent.data = Uri.parse(serviceIntent.toUri(Intent.URI_INTENT_SCHEME))
remoteViews.setRemoteAdapter(StackWidgetType.getStackWidgetAdapterViewId(widgetType), serviceIntent) remoteViews.setRemoteAdapter(StackWidgetType.getStackWidgetAdapterViewId(widgetType), serviceIntent)
manager.notifyAppWidgetViewDataChanged(id, StackWidgetType.getStackWidgetAdapterViewId(widgetType)) manager.notifyAppWidgetViewDataChanged(id, StackWidgetType.getStackWidgetAdapterViewId(widgetType))
// TODO what should the empty view look like?
remoteViews.setEmptyView(StackWidgetType.getStackWidgetAdapterViewId(widgetType), remoteViews.setEmptyView(StackWidgetType.getStackWidgetAdapterViewId(widgetType),
StackWidgetType.getStackWidgetEmptyViewId(widgetType)) StackWidgetType.getStackWidgetEmptyViewId(widgetType))
return remoteViews return remoteViews

@ -4,6 +4,7 @@ import android.appwidget.*;
import android.content.*; import android.content.*;
import android.os.*; import android.os.*;
import android.support.annotation.*; import android.support.annotation.*;
import android.util.Log;
import android.widget.*; import android.widget.*;
import org.isoron.uhabits.*; import org.isoron.uhabits.*;
@ -33,14 +34,14 @@ class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory
private Context context; private Context context;
private int widgetId; private int widgetId;
private long[] habitIds; private long[] habitIds;
private ArrayList<Habit> habits = new ArrayList<>();
private StackWidgetType widgetType; private StackWidgetType widgetType;
private ArrayList<RemoteViews> remoteViews = new ArrayList<>();
public StackRemoteViewsFactory(Context context, Intent intent) public StackRemoteViewsFactory(Context context, Intent intent)
{ {
this.context = context; this.context = context;
widgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID); AppWidgetManager.INVALID_APPWIDGET_ID);
int widgetTypeValue = intent.getIntExtra(WIDGET_TYPE, -1); int widgetTypeValue = intent.getIntExtra(WIDGET_TYPE, -1);
String habitIdsStr = intent.getStringExtra(HABIT_IDS); String habitIdsStr = intent.getStringExtra(HABIT_IDS);
@ -63,76 +64,30 @@ class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory
public int getCount() public int getCount()
{ {
return habits.size(); return habitIds.length;
} }
@NonNull @NonNull
public WidgetDimensions getDimensionsFromOptions(@NonNull Context ctx, public WidgetDimensions getDimensionsFromOptions(@NonNull Context ctx,
@NonNull Bundle options) @NonNull Bundle options)
{ {
int maxWidth = int maxWidth = (int) dpToPixels(ctx, options.getInt(OPTION_APPWIDGET_MAX_WIDTH));
(int) dpToPixels(ctx, options.getInt(OPTION_APPWIDGET_MAX_WIDTH)); int maxHeight = (int) dpToPixels(ctx, options.getInt(OPTION_APPWIDGET_MAX_HEIGHT));
int maxHeight = int minWidth = (int) dpToPixels(ctx, options.getInt(OPTION_APPWIDGET_MIN_WIDTH));
(int) dpToPixels(ctx, options.getInt(OPTION_APPWIDGET_MAX_HEIGHT)); int minHeight = (int) dpToPixels(ctx, options.getInt(OPTION_APPWIDGET_MIN_HEIGHT));
int minWidth =
(int) dpToPixels(ctx, options.getInt(OPTION_APPWIDGET_MIN_WIDTH));
int minHeight =
(int) dpToPixels(ctx, options.getInt(OPTION_APPWIDGET_MIN_HEIGHT));
return new WidgetDimensions(minWidth, maxHeight, maxWidth, minHeight); return new WidgetDimensions(minWidth, maxHeight, maxWidth, minHeight);
} }
public RemoteViews getViewAt(int position) public RemoteViews getViewAt(int position)
{ {
RemoteViews rv = null; Log.i("StackRemoteViewsFactory", "getViewAt " + position);
if (position < getCount()) if (position < 0 || position > remoteViews.size()) return null;
{ return remoteViews.get(position);
Habit habit = habits.get(position);
BaseWidget widget = initializeWidget(habit);
Bundle options =
AppWidgetManager.getInstance(context).getAppWidgetOptions(widgetId);
widget.setDimensions(getDimensionsFromOptions(context, options));
final RemoteViews[] landscape = new RemoteViews[1];
final RemoteViews[] portrait = new RemoteViews[1];
Object lock = new Object();
final boolean[] flag = {false};
new Handler(Looper.getMainLooper()).post(() ->
{
synchronized (lock)
{
landscape[0] =
widget.getLandscapeRemoteViews();
portrait[0] =
widget.getPortraitRemoteViews();
flag[0] = true;
lock.notifyAll();
}
});
synchronized (lock)
{
while (!flag[0])
{
try
{
lock.wait();
}
catch (InterruptedException e)
{
// ignored
}
}
}
rv = new RemoteViews(landscape[0], portrait[0]);
}
return rv;
} }
private BaseWidget initializeWidget(Habit habit) @NonNull
private BaseWidget constructWidget(@NonNull Habit habit)
{ {
switch (widgetType) switch (widgetType)
{ {
@ -147,12 +102,18 @@ class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory
case STREAKS: case STREAKS:
return new StreakWidget(context, widgetId, habit); return new StreakWidget(context, widgetId, habit);
} }
return null;
throw new IllegalStateException();
} }
public RemoteViews getLoadingView() public RemoteViews getLoadingView()
{ {
return null; Bundle options = AppWidgetManager.getInstance(context).getAppWidgetOptions(widgetId);
EmptyWidget widget = new EmptyWidget(context, widgetId);
widget.setDimensions(getDimensionsFromOptions(context, options));
RemoteViews landscapeViews = widget.getLandscapeRemoteViews();
RemoteViews portraitViews = widget.getPortraitRemoteViews();
return new RemoteViews(landscapeViews, portraitViews);
} }
public int getViewTypeCount() public int getViewTypeCount()
@ -162,25 +123,41 @@ class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory
public long getItemId(int position) public long getItemId(int position)
{ {
return position; return habitIds[position];
} }
public boolean hasStableIds() public boolean hasStableIds()
{ {
return false; return true;
} }
public void onDataSetChanged() public void onDataSetChanged()
{ {
habits.clear(); Log.i("StackRemoteViewsFactory", "onDataSetChanged started");
HabitsApplication app = (HabitsApplication) context.getApplicationContext(); HabitsApplication app = (HabitsApplication) context.getApplicationContext();
HabitList habitList = app.getComponent().getHabitList(); HabitList habitList = app.getComponent().getHabitList();
Bundle options = AppWidgetManager.getInstance(context).getAppWidgetOptions(widgetId);
ArrayList<RemoteViews> newRemoteViews = new ArrayList<>();
if (Looper.myLooper() == null) Looper.prepare();
for (long id : habitIds) for (long id : habitIds)
{ {
Habit h = habitList.getById(id); Habit h = habitList.getById(id);
if (h == null) throw new HabitNotFoundException(); if (h == null) throw new HabitNotFoundException();
habits.add(h);
BaseWidget widget = constructWidget(h);
widget.setDimensions(getDimensionsFromOptions(context, options));
RemoteViews landscapeViews = widget.getLandscapeRemoteViews();
RemoteViews portraitViews = widget.getPortraitRemoteViews();
newRemoteViews.add(new RemoteViews(landscapeViews, portraitViews));
Log.i("StackRemoteViewsFactory", "onDataSetChanged constructed widget " + id);
} }
remoteViews = newRemoteViews;
Log.i("StackRemoteViewsFactory", "onDataSetChanged ended");
} }
} }

@ -0,0 +1,56 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.widgets.views;
import android.content.Context;
import android.support.annotation.NonNull;
import android.widget.TextView;
import org.isoron.uhabits.R;
public class EmptyWidgetView extends HabitWidgetView
{
private TextView title;
public EmptyWidgetView(Context context)
{
super(context);
init();
}
public void setTitle(String text)
{
title.setText(text);
}
@Override
@NonNull
protected Integer getInnerLayoutId()
{
return R.layout.widget_graph;
}
private void init()
{
title = (TextView) findViewById(R.id.title);
title.setVisibility(VISIBLE);
}
}

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
~
~ This file is part of Loop Habit Tracker.
~
~ Loop Habit Tracker is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by the
~ Free Software Foundation, either version 3 of the License, or (at your
~ option) any later version.
~
~ Loop Habit Tracker is distributed in the hope that it will be useful, but
~ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
~ more details.
~
~ You should have received a copy of the GNU General Public License along
~ with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<FrameLayout
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/frame"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<LinearLayout
android:id="@+id/innerFrame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingTop="4dp"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingBottom="4dp"
tools:ignore="UselessParent">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="@color/white"/>
</LinearLayout>
</FrameLayout>
Loading…
Cancel
Save