Widgets for HistoryView, ScoreView, etc

pull/30/head
Alinson S. Xavier 10 years ago
parent a2331260e4
commit f0d12e9925

@ -64,7 +64,7 @@
android:label="" android:label=""
android:theme="@style/Theme.AppCompat.Light.NoActionBar"/> android:theme="@style/Theme.AppCompat.Light.NoActionBar"/>
<receiver android:name="SmallWidgetProvider"> <receiver android:name=".HabitWidgetProvider">
<intent-filter> <intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/> <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
</intent-filter> </intent-filter>

@ -18,10 +18,12 @@ package org.isoron.helpers;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.os.Vibrator; import android.os.Vibrator;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View; import android.view.View;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
@ -72,4 +74,11 @@ public abstract class DialogHelper
else else
return attrs.getAttributeValue(ISORON_NAMESPACE, name); return attrs.getAttributeValue(ISORON_NAMESPACE, name);
} }
public static float dpToPixels(Context context, float dp)
{
Resources resources = context.getResources();
DisplayMetrics metrics = resources.getDisplayMetrics();
return dp * (metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);
}
} }

@ -27,7 +27,6 @@ import android.widget.AdapterView;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.ListView; import android.widget.ListView;
import org.isoron.helpers.DialogHelper;
import org.isoron.uhabits.models.Habit; import org.isoron.uhabits.models.Habit;
import java.util.ArrayList; import java.util.ArrayList;
@ -78,7 +77,7 @@ public class HabitWidgetConfigure extends Activity implements AdapterView.OnItem
Long habitId = habitIds.get(position); Long habitId = habitIds.get(position);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences( SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
getApplicationContext()); getApplicationContext());
prefs.edit().putLong(SmallWidgetProvider.getWidgetPrefKey(widgetId), habitId).commit(); prefs.edit().putLong(HabitWidgetProvider.getWidgetPrefKey(widgetId), habitId).commit();
MainActivity.updateWidgets(this); MainActivity.updateWidgets(this);

@ -21,25 +21,39 @@ import android.appwidget.AppWidgetProvider;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.os.Bundle;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.util.Log;
import android.widget.RemoteViews; import android.widget.RemoteViews;
import org.isoron.helpers.DialogHelper;
import org.isoron.uhabits.models.Habit; import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.views.SmallWidgetView; import org.isoron.uhabits.views.HabitHistoryView;
public class SmallWidgetProvider extends AppWidgetProvider public class HabitWidgetProvider extends AppWidgetProvider
{ {
@Override @Override
public void onUpdate(Context context, AppWidgetManager manager, int[] appWidgetIds) public void onUpdate(Context context, AppWidgetManager manager, int[] appWidgetIds)
{ {
for(int id : appWidgetIds) for(int id : appWidgetIds)
updateWidget(context, manager, id); {
Bundle options = manager.getAppWidgetOptions(id);
updateWidget(context, manager, id, options);
}
} }
private void updateWidget(Context context, AppWidgetManager manager, int widgetId) private void updateWidget(Context context, AppWidgetManager manager, int widgetId, Bundle options)
{ {
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.small_widget); int max_height = (int) DialogHelper.dpToPixels(context, options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT));
int min_height = (int) DialogHelper.dpToPixels(context, options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT));
int max_width = (int) DialogHelper.dpToPixels(context, options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH));
int min_width = (int) DialogHelper.dpToPixels(context, options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH));
Log.d("HabitWidgetProvider", String.format("max_h=%d min_h=%d max_w=%d min_w=%d",
max_height, min_height, max_width, min_width));
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_graph);
Context appContext = context.getApplicationContext(); Context appContext = context.getApplicationContext();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(appContext); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(appContext);
@ -48,13 +62,23 @@ public class SmallWidgetProvider extends AppWidgetProvider
Habit habit = Habit.get(habitId); Habit habit = Habit.get(habitId);
SmallWidgetView widgetView = new SmallWidgetView(context); // SmallWidgetView widgetView = new SmallWidgetView(context);
widgetView.setDrawingCacheEnabled(true); HabitHistoryView widgetView = new HabitHistoryView(context, null);
widgetView.measure(180, 200); // HabitScoreView widgetView = new HabitScoreView(context, null);
widgetView.layout(0, 0, 180, 200); // HabitStreakView widgetView = new HabitStreakView(context, null);
widgetView.buildDrawingCache(true);
widgetView.setHabit(habit); widgetView.setHabit(habit);
widgetView.setDrawingCacheEnabled(true);
widgetView.measure(max_width, max_height);
widgetView.layout(0, 0, max_width, max_height);
int width = widgetView.getMeasuredWidth();
int height = widgetView.getMeasuredHeight();
Log.d("SmallWidgetProvider", String.format("width=%d height=%d\n", width, height));
height -= DialogHelper.dpToPixels(context, 12f);
widgetView.measure(width, height);
widgetView.layout(0, 0, width, height);
widgetView.buildDrawingCache(true);
Bitmap drawingCache = widgetView.getDrawingCache(); Bitmap drawingCache = widgetView.getDrawingCache();
remoteViews.setTextViewText(R.id.tvName, habit.name); remoteViews.setTextViewText(R.id.tvName, habit.name);
@ -78,4 +102,11 @@ public class SmallWidgetProvider extends AppWidgetProvider
for(Integer id : appWidgetIds) for(Integer id : appWidgetIds)
prefs.edit().remove(getWidgetPrefKey(id)); prefs.edit().remove(getWidgetPrefKey(id));
} }
@Override
public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager,
int appWidgetId, Bundle newOptions)
{
updateWidget(context, appWidgetManager, appWidgetId, newOptions);
}
} }

@ -127,9 +127,9 @@ public class MainActivity extends ReplayableActivity
public static void updateWidgets(Context context) public static void updateWidgets(Context context)
{ {
ComponentName provider = new ComponentName(context, SmallWidgetProvider.class); ComponentName provider = new ComponentName(context, HabitWidgetProvider.class);
Intent intent = new Intent(context, SmallWidgetProvider.class); Intent intent = new Intent(context, HabitWidgetProvider.class);
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE); intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
int ids[] = AppWidgetManager.getInstance(context).getAppWidgetIds(provider); int ids[] = AppWidgetManager.getInstance(context).getAppWidgetIds(provider);

@ -54,14 +54,12 @@ public class HabitHistoryView extends ScrollableDataView
private int todayWeekday; private int todayWeekday;
private int colors[]; private int colors[];
private Rect baseLocation; private Rect baseLocation;
private int baseSize;
private int primaryColor; private int primaryColor;
public HabitHistoryView(Context context, AttributeSet attrs) public HabitHistoryView(Context context, AttributeSet attrs)
{ {
super(context, attrs); super(context, attrs);
this.primaryColor = ColorHelper.palette[7]; this.primaryColor = ColorHelper.palette[7];
this.baseSize = (int) context.getResources().getDimension(R.dimen.small_square_size);
init(); init();
} }
@ -76,9 +74,9 @@ public class HabitHistoryView extends ScrollableDataView
private void init() private void init()
{ {
setDimensions(this.baseSize);
createPaints(); createPaints();
createColors(); createColors();
updateDimensions();
wdays = DateHelper.getShortDayNames(); wdays = DateHelper.getShortDayNames();
dfMonth = new SimpleDateFormat("MMM", Locale.getDefault()); dfMonth = new SimpleDateFormat("MMM", Locale.getDefault());
@ -108,8 +106,9 @@ public class HabitHistoryView extends ScrollableDataView
private void createColors() private void createColors()
{ {
int primaryColorBright = ColorHelper.mixColors(primaryColor, Color.WHITE, 0.5f); int primaryColorBright = Color.argb(127, Color.red(primaryColor), Color.green(primaryColor),
int grey = Color.rgb(230, 230, 230); Color.blue(primaryColor));
int grey = Color.argb(25, 0, 0, 0);
colors = new int[3]; colors = new int[3];
colors[0] = grey; colors[0] = grey;
@ -117,19 +116,20 @@ public class HabitHistoryView extends ScrollableDataView
colors[2] = primaryColor; colors[2] = primaryColor;
} }
private void setDimensions(int baseSize) protected void updateDimensions()
{ {
columnWidth = baseSize; squareSpacing = columnWidth / 10;
columnHeight = 8 * baseSize; pSquareFg.setTextSize(columnWidth * 0.5f);
squareSpacing = 2; pTextHeader.setTextSize(columnWidth * 0.5f);
squareTextOffset = pSquareFg.getFontSpacing() * 0.4f;
headerTextOffset = pTextHeader.getFontSpacing() * 0.3f;
} }
private void createPaints() protected void createPaints()
{ {
pTextHeader = new Paint(); pTextHeader = new Paint();
pTextHeader.setColor(Color.LTGRAY); pTextHeader.setColor(Color.LTGRAY);
pTextHeader.setTextAlign(Align.LEFT); pTextHeader.setTextAlign(Align.LEFT);
pTextHeader.setTextSize(columnWidth * 0.5f);
pTextHeader.setAntiAlias(true); pTextHeader.setAntiAlias(true);
pSquareBg = new Paint(); pSquareBg = new Paint();
@ -138,11 +138,7 @@ public class HabitHistoryView extends ScrollableDataView
pSquareFg = new Paint(); pSquareFg = new Paint();
pSquareFg.setColor(Color.WHITE); pSquareFg.setColor(Color.WHITE);
pSquareFg.setAntiAlias(true); pSquareFg.setAntiAlias(true);
pSquareFg.setTextSize(columnWidth * 0.5f);
pSquareFg.setTextAlign(Align.CENTER); pSquareFg.setTextAlign(Align.CENTER);
squareTextOffset = pSquareFg.getFontSpacing() * 0.4f;
headerTextOffset = pTextHeader.getFontSpacing() * 0.3f;
} }
protected void fetchData() protected void fetchData()
@ -203,7 +199,7 @@ public class HabitHistoryView extends ScrollableDataView
for (int column = 0; column < nColumns - 1; column++) for (int column = 0; column < nColumns - 1; column++)
{ {
drawColumn(canvas, baseLocation, currentDate, column); drawColumn(canvas, baseLocation, currentDate, column);
baseLocation.offset(columnWidth, -columnHeight); baseLocation.offset(columnWidth, - columnHeight);
} }
drawAxis(canvas, baseLocation); drawAxis(canvas, baseLocation);
@ -248,6 +244,8 @@ public class HabitHistoryView extends ScrollableDataView
} }
} }
private boolean justSkippedColumn = false;
private void drawColumnHeader(Canvas canvas, Rect location, GregorianCalendar date) private void drawColumnHeader(Canvas canvas, Rect location, GregorianCalendar date)
{ {
String month = dfMonth.format(date.getTime()); String month = dfMonth.format(date.getTime());
@ -256,21 +254,32 @@ public class HabitHistoryView extends ScrollableDataView
if (!month.equals(previousMonth)) if (!month.equals(previousMonth))
{ {
int offset = 0; int offset = 0;
if (justPrintedYear) offset += columnWidth; if (justPrintedYear)
{
offset += columnWidth;
justSkippedColumn = true;
}
canvas.drawText(month, location.left + offset, location.bottom - headerTextOffset, canvas.drawText(month, location.left + offset, location.bottom - headerTextOffset,
pTextHeader); pTextHeader);
previousMonth = month; previousMonth = month;
justPrintedYear = false; justPrintedYear = false;
} }
else if (!year.equals(previousYear)) else if (!year.equals(previousYear))
{ {
canvas.drawText(year, location.left, location.bottom - headerTextOffset, pTextHeader); if(!justSkippedColumn)
previousYear = year; {
justPrintedYear = true; canvas.drawText(year, location.left, location.bottom - headerTextOffset, pTextHeader);
previousYear = year;
justPrintedYear = true;
}
justSkippedColumn = false;
} }
else else
{ {
justSkippedColumn = false;
justPrintedYear = false; justPrintedYear = false;
} }
} }

@ -20,6 +20,8 @@ import android.content.Context;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF; import android.graphics.RectF;
import android.util.AttributeSet; import android.util.AttributeSet;
@ -69,8 +71,8 @@ public class HabitScoreView extends ScrollableDataView
private void init() private void init()
{ {
createPaints(); createPaints();
setDimensions();
createColors(); createColors();
updateDimensions();
dfMonth = new SimpleDateFormat("MMM", Locale.getDefault()); dfMonth = new SimpleDateFormat("MMM", Locale.getDefault());
dfDay = new SimpleDateFormat("d", Locale.getDefault()); dfDay = new SimpleDateFormat("d", Locale.getDefault());
@ -79,15 +81,6 @@ public class HabitScoreView extends ScrollableDataView
prevRect = new RectF(); prevRect = new RectF();
} }
private void setDimensions()
{
this.columnWidth = baseSize;
columnHeight = 8 * baseSize;
headerHeight = baseSize;
footerHeight = baseSize;
em = pText.getFontSpacing();
}
private void createColors() private void createColors()
{ {
colors = new int[4]; colors = new int[4];
@ -98,23 +91,35 @@ public class HabitScoreView extends ScrollableDataView
colors[2] = ColorHelper.mixColors(colors[0], colors[3], 0.33f); colors[2] = ColorHelper.mixColors(colors[0], colors[3], 0.33f);
} }
private void createPaints() protected void createPaints()
{ {
pText = new Paint(); pText = new Paint();
pText.setColor(Color.LTGRAY); pText.setColor(Color.LTGRAY);
pText.setTextAlign(Paint.Align.LEFT); pText.setTextAlign(Paint.Align.LEFT);
pText.setTextSize(baseSize * 0.5f);
pText.setAntiAlias(true); pText.setAntiAlias(true);
pGraph = new Paint(); pGraph = new Paint();
pGraph.setTextAlign(Paint.Align.CENTER); pGraph.setTextAlign(Paint.Align.CENTER);
pGraph.setTextSize(baseSize * 0.5f);
pGraph.setAntiAlias(true); pGraph.setAntiAlias(true);
pGraph.setStrokeWidth(baseSize * 0.1f);
pGrid = new Paint(); pGrid = new Paint();
pGrid.setColor(Color.LTGRAY); pGrid.setColor(Color.LTGRAY);
pGrid.setAntiAlias(true); pGrid.setAntiAlias(true);
}
@Override
protected void updateDimensions()
{
this.columnWidth = baseSize;
columnHeight = 8 * baseSize;
headerHeight = baseSize;
footerHeight = baseSize;
em = pText.getFontSpacing();
pText.setTextSize(baseSize * 0.5f);
pGraph.setTextSize(baseSize * 0.5f);
pGraph.setStrokeWidth(baseSize * 0.1f);
pGrid.setStrokeWidth(baseSize * 0.05f); pGrid.setStrokeWidth(baseSize * 0.05f);
} }
@ -235,15 +240,18 @@ public class HabitScoreView extends ScrollableDataView
private void drawMarker(Canvas canvas, RectF rect) private void drawMarker(Canvas canvas, RectF rect)
{ {
rect.inset(columnWidth * 0.15f, columnWidth * 0.15f); rect.inset(columnWidth * 0.15f, columnWidth * 0.15f);
pGraph.setColor(Color.WHITE); pGraph.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawOval(rect, pGraph); canvas.drawOval(rect, pGraph);
rect.inset(columnWidth * 0.1f, columnWidth * 0.1f); rect.inset(columnWidth * 0.1f, columnWidth * 0.1f);
pGraph.setColor(primaryColor); pGraph.setColor(primaryColor);
pGraph.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
canvas.drawOval(rect, pGraph); canvas.drawOval(rect, pGraph);
rect.inset(columnWidth * 0.1f, columnWidth * 0.1f); rect.inset(columnWidth * 0.1f, columnWidth * 0.1f);
pGraph.setColor(Color.WHITE); pGraph.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawOval(rect, pGraph); canvas.drawOval(rect, pGraph);
pGraph.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
} }
} }

@ -88,13 +88,15 @@ public class HabitStreakView extends ScrollableDataView
private void createColors() private void createColors()
{ {
colors = new int[4]; colors = new int[4];
colors[0] = Color.rgb(230, 230, 230);
colors[3] = primaryColor; colors[3] = primaryColor;
colors[1] = ColorHelper.mixColors(colors[0], colors[3], 0.66f); colors[1] = Color.argb(80, Color.red(primaryColor), Color.green(primaryColor), Color.blue(
colors[2] = ColorHelper.mixColors(colors[0], colors[3], 0.33f); primaryColor));
colors[2] = Color.argb(170, Color.red(primaryColor), Color.green(primaryColor), Color.blue(
primaryColor));
colors[0] = Color.argb(30, 0, 0, 0);
} }
private void createPaints() protected void createPaints()
{ {
pText = new Paint(); pText = new Paint();
pText.setColor(Color.LTGRAY); pText.setColor(Color.LTGRAY);

@ -22,17 +22,20 @@ package org.isoron.uhabits.views;
import android.animation.ValueAnimator; import android.animation.ValueAnimator;
import android.content.Context; import android.content.Context;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector; import android.view.GestureDetector;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.widget.Scroller; import android.widget.Scroller;
import org.isoron.uhabits.R;
public abstract class ScrollableDataView extends View implements GestureDetector.OnGestureListener, public abstract class ScrollableDataView extends View implements GestureDetector.OnGestureListener,
ValueAnimator.AnimatorUpdateListener ValueAnimator.AnimatorUpdateListener
{ {
protected int dataOffset; protected int dataOffset;
protected int nColumns; protected int nColumns, nRows;
protected int columnWidth, columnHeight; protected int columnWidth, columnHeight;
protected int headerHeight, footerHeight; protected int headerHeight, footerHeight;
@ -54,6 +57,7 @@ public abstract class ScrollableDataView extends View implements GestureDetector
private void init(Context context) private void init(Context context)
{ {
this.columnWidth = (int) context.getResources().getDimension(R.dimen.small_square_size);
detector = new GestureDetector(context, this); detector = new GestureDetector(context, this);
scroller = new Scroller(context, null, true); scroller = new Scroller(context, null, true);
scrollAnimator = ValueAnimator.ofFloat(0, 1); scrollAnimator = ValueAnimator.ofFloat(0, 1);
@ -72,7 +76,25 @@ public abstract class ScrollableDataView extends View implements GestureDetector
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{ {
super.onMeasure(widthMeasureSpec, heightMeasureSpec); super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(getMeasuredWidth(), columnHeight + headerHeight + footerHeight);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
Log.d("ScrollableDataView", String.format("onMeasure width=%d height=%d", width, height));
columnWidth = height / 8;
columnHeight = columnWidth * 8 + footerHeight + headerHeight;
height = columnHeight;
width = (width / columnWidth) * columnWidth;
setMeasuredDimension(width, height);
updateDimensions();
}
protected void updateDimensions()
{
} }
@Override @Override

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#30000000" />
<corners android:radius="10dp" />
</shape>

@ -41,7 +41,7 @@
<org.isoron.uhabits.views.HabitScoreView <org.isoron.uhabits.views.HabitScoreView
android:id="@+id/scoreView" android:id="@+id/scoreView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"/> android:layout_height="180dp"/>
</LinearLayout> </LinearLayout>
@ -61,7 +61,7 @@
<org.isoron.uhabits.views.HabitHistoryView <org.isoron.uhabits.views.HabitHistoryView
android:id="@+id/historyView" android:id="@+id/historyView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"/> android:layout_height="180dp"/>
</LinearLayout> </LinearLayout>
@ -81,7 +81,7 @@
<org.isoron.uhabits.views.HabitStreakView <org.isoron.uhabits.views.HabitStreakView
android:id="@+id/streakView" android:id="@+id/streakView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"/> android:layout_height="180dp"/>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/widget_background"
android:gravity="center"
android:orientation="vertical"
android:paddingTop="4dp"
android:paddingLeft="10dp"
android:paddingRight="0dp">
<TextView
android:id="@+id/tvName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Wake up early"
android:textColor="@color/white"/>
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:adjustViewBounds="true"
/>
</LinearLayout>

@ -2,9 +2,9 @@
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minHeight="40dp" android:minHeight="40dp"
android:minWidth="40dp" android:minWidth="40dp"
android:initialLayout="@layout/small_widget" android:initialLayout="@layout/widget_small"
android:previewImage="@mipmap/ic_small_widget_preview" android:previewImage="@mipmap/ic_small_widget_preview"
android:resizeMode="none" android:resizeMode="horizontal|vertical"
android:updatePeriodMillis="3600000" android:updatePeriodMillis="3600000"
android:configure="org.isoron.uhabits.HabitWidgetConfigure" android:configure="org.isoron.uhabits.HabitWidgetConfigure"
android:widgetCategory="home_screen"> android:widgetCategory="home_screen">

Loading…
Cancel
Save