Code review changes; Made 'stack view' design available for all widgets

pull/346/head
Victor Yu 8 years ago
parent 268cb0bc18
commit 8feb07ff1b

@ -24,6 +24,7 @@ android {
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
} }
buildToolsVersion '26.0.2'
} }
dependencies { dependencies {

@ -18,6 +18,7 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
} }
} }
buildToolsVersion '26.0.2'
} }
dependencies { dependencies {

@ -5,7 +5,7 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.0.0-beta6' classpath 'com.android.tools.build:gradle:3.0.0'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.6.4' classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.6.4'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

@ -89,6 +89,7 @@ android {
sourceSets { sourceSets {
main.assets.srcDirs += '../uhabits-core/src/main/resources/' main.assets.srcDirs += '../uhabits-core/src/main/resources/'
} }
buildToolsVersion '26.0.2'
} }
dependencies { dependencies {

@ -84,13 +84,6 @@
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/> <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
</intent-filter> </intent-filter>
</activity> </activity>
<activity
android:name=".widgets.HabitGroupPickerDialog"
android:theme="@style/Theme.AppCompat.Light.Dialog">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
</intent-filter>
</activity>
<activity <activity
android:name=".activities.about.AboutActivity" android:name=".activities.about.AboutActivity"
android:label="@string/about"> android:label="@string/about">
@ -110,7 +103,7 @@
android:name="android.appwidget.provider" android:name="android.appwidget.provider"
android:resource="@xml/widget_checkmark_info"/> android:resource="@xml/widget_checkmark_info"/>
</receiver> </receiver>
<service android:name=".widgets.CheckmarkStackWidgetService" <service android:name=".widgets.StackWidgetService"
android:permission="android.permission.BIND_REMOTEVIEWS" android:permission="android.permission.BIND_REMOTEVIEWS"
android:exported="false" /> android:exported="false" />
<receiver <receiver

@ -141,10 +141,12 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider
}).start(); }).start();
} }
@NonNull
protected Habit getHabitFromWidgetId(int widgetId) protected Habit getHabitFromWidgetId(int widgetId)
{ {
long habitId = widgetPrefs.getHabitIdFromWidgetId(widgetId); long habitId = widgetPrefs.getHabitIdFromWidgetId(widgetId);
if (habitId == WidgetPreferences.STACK_WIDGET_HABITS) {
return null;
}
Habit habit = habits.getById(habitId); Habit habit = habits.getById(habitId);
if (habit == null) throw new HabitNotFoundException(); if (habit == null) throw new HabitNotFoundException();
return habit; return habit;

@ -1,19 +0,0 @@
package org.isoron.uhabits.widgets
import android.content.Context
import org.isoron.uhabits.HabitsApplication
class CheckmarkStackWidgetProvider : BaseWidgetProvider() {
override fun getWidgetFromId(context: Context, id: Int): CheckmarkStackWidget {
val habitIds = getHabitGroupFromWidget(context, id)
return CheckmarkStackWidget(context, id, habitIds)
}
private fun getHabitGroupFromWidget(context: Context, widgetId: Int) : List<Long> {
val app = context.getApplicationContext() as HabitsApplication
val widgetPrefs = app.component.widgetPreferences
val habitIds = widgetPrefs.getHabitIdsGroupFromWidgetId(widgetId)
return habitIds
}
}

@ -18,24 +18,17 @@
*/ */
package org.isoron.uhabits.widgets package org.isoron.uhabits.widgets
import android.content.* import android.content.Context
import org.isoron.uhabits.HabitsApplication
class CheckmarkWidgetProvider : BaseWidgetProvider() { class CheckmarkWidgetProvider : BaseWidgetProvider() {
override fun getWidgetFromId(context: Context, id: Int): BaseWidget { override fun getWidgetFromId(context: Context, id: Int): BaseWidget {
try { // if the habit was null, but did not have a habit id associated with a stack widget,
// `getHabitFromWidgetId` will throw a HabitNotFoundException
val habit = getHabitFromWidgetId(id) val habit = getHabitFromWidgetId(id)
if (habit != null) {
return CheckmarkWidget(context, id, habit) return CheckmarkWidget(context, id, habit)
} catch (e: Exception) { } else {
val habitIds = getHabitIdsGroupFromWidget(context, id) return StackWidget(context, id, StackWidgetType.CHECKMARK)
return CheckmarkStackWidget(context, id, habitIds)
} }
} }
private fun getHabitIdsGroupFromWidget(context: Context, widgetId: Int) : List<Long> {
val app = context.getApplicationContext() as HabitsApplication
val widgetPrefs = app.component.widgetPreferences
val habitIds = widgetPrefs.getHabitIdsGroupFromWidgetId(widgetId)
return habitIds
}
} }

@ -19,11 +19,17 @@
package org.isoron.uhabits.widgets package org.isoron.uhabits.widgets
import android.content.* import android.content.Context
class FrequencyWidgetProvider : BaseWidgetProvider() { class FrequencyWidgetProvider : BaseWidgetProvider() {
override fun getWidgetFromId(context: Context, id: Int): BaseWidget { override fun getWidgetFromId(context: Context, id: Int): BaseWidget {
// if the habit was null, but did not have a habit id associated with a stack widget,
// `getHabitFromWidgetId` will throw a HabitNotFoundException
val habit = getHabitFromWidgetId(id) val habit = getHabitFromWidgetId(id)
if (habit != null) {
return FrequencyWidget(context, id, habit) return FrequencyWidget(context, id, habit)
} else {
return StackWidget(context, id, StackWidgetType.FREQUENCY)
}
} }
} }

@ -1,133 +0,0 @@
/*
* 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.app.Activity
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID
import android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import android.widget.CompoundButton
import org.isoron.uhabits.HabitsApplication
import org.isoron.uhabits.R
import org.isoron.uhabits.core.models.HabitList
import org.isoron.uhabits.core.preferences.WidgetPreferences
class HabitGroupPickerDialog : Activity(), AdapterView.OnItemClickListener {
private var widgetId = 0
private lateinit var habitList: HabitList
private lateinit var preferences: WidgetPreferences
private lateinit var habitIds: ArrayList<Long>
private lateinit var widgetUpdater: WidgetUpdater
private lateinit var habitIdsSelected: ArrayList<Long>
private lateinit var habitListView: ListView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val component = (applicationContext as HabitsApplication).component
habitList = component.habitList
preferences = component.widgetPreferences
widgetUpdater = component.widgetUpdater
widgetId = intent.extras?.getInt(EXTRA_APPWIDGET_ID, INVALID_APPWIDGET_ID) ?: 0
habitIdsSelected = ArrayList<Long>()
habitIds = ArrayList<Long>()
val habitNames = ArrayList<String>()
for (h in habitList) {
if (h.isArchived) continue
habitIds.add(h.getId()!!)
habitNames.add(h.name)
}
setContentView(R.layout.stack_widget_configure_activity)
habitListView = findViewById(R.id.stackWidgetListView) as ListView
with(habitListView) {
adapter = ListAdapter(context, R.layout.habit_checkbox_list_item,
R.id.listItemHabitName, R.id.listItemHabitCheckbox, habitNames)
onItemClickListener = this@HabitGroupPickerDialog
}
with(findViewById(R.id.doneConfigureButton) as Button) {
setOnClickListener {
if (habitIdsSelected.size == 1) {
preferences.addWidget(widgetId, habitIdsSelected.first())
widgetUpdater.updateWidgets()
setResult(Activity.RESULT_OK, Intent().apply {
putExtra(EXTRA_APPWIDGET_ID, widgetId)
})
finish()
} else if (!habitIdsSelected.isEmpty()) {
preferences.addWidget(widgetId, habitIdsSelected.toString())
widgetUpdater.updateWidgets()
setResult(Activity.RESULT_OK, Intent().apply {
putExtra(EXTRA_APPWIDGET_ID, widgetId)
})
finish()
} else {
Toast.makeText(context, getString(R.string.select_habit_requirement_prompt),
Toast.LENGTH_SHORT).show()
}
}
}
}
override fun onItemClick(parent: AdapterView<*>,
view: View,
position: Int,
id: Long) {
val checkbox = view.findViewById(R.id.listItemHabitCheckbox) as CheckBox
checkbox.isChecked = !checkbox.isChecked
}
private inner class ListAdapter(context: Context,
private var layoutResource: Int,
private var textViewResourceId: Int,
private var checkBoxResourceId: Int,
private var habitNames: List<String>) :
ArrayAdapter<String>(context, layoutResource, textViewResourceId, habitNames) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val layoutInflater: LayoutInflater = LayoutInflater.from(context)
val view = layoutInflater.inflate(layoutResource, null)
val item = getItem(position)
if (item != null) {
val tv = view.findViewById(textViewResourceId) as TextView
tv.text = habitNames.get(position)
val cb = view.findViewById(checkBoxResourceId) as CheckBox
cb.setOnCheckedChangeListener(CompoundButton.OnCheckedChangeListener { buttonView, isChecked ->
if (isChecked) {
habitIdsSelected.add(habitIds.get(position))
} else {
habitIdsSelected.remove(habitIds.get(position))
}
})
}
return view
}
}
}

@ -23,6 +23,7 @@ import android.app.*
import android.appwidget.AppWidgetManager.* import android.appwidget.AppWidgetManager.*
import android.content.* import android.content.*
import android.os.* import android.os.*
import android.util.Log
import android.view.* import android.view.*
import android.widget.* import android.widget.*
import org.isoron.uhabits.* import org.isoron.uhabits.*
@ -61,6 +62,16 @@ class HabitPickerDialog : Activity(), AdapterView.OnItemClickListener {
habitNames) habitNames)
onItemClickListener = this@HabitPickerDialog onItemClickListener = this@HabitPickerDialog
} }
with(findViewById(R.id.createStackWidgetButton) as Button) {
setOnClickListener(View.OnClickListener {
preferences.addWidget(widgetId, WidgetPreferences.STACK_WIDGET_HABITS)
widgetUpdater.updateWidgets()
setResult(Activity.RESULT_OK, Intent().apply {
putExtra(EXTRA_APPWIDGET_ID, widgetId)
})
finish()
})
}
} }
override fun onItemClick(parent: AdapterView<*>, override fun onItemClick(parent: AdapterView<*>,

@ -18,11 +18,17 @@
*/ */
package org.isoron.uhabits.widgets package org.isoron.uhabits.widgets
import android.content.* import android.content.Context
class HistoryWidgetProvider : BaseWidgetProvider() { class HistoryWidgetProvider : BaseWidgetProvider() {
override fun getWidgetFromId(context: Context, id: Int): BaseWidget { override fun getWidgetFromId(context: Context, id: Int): BaseWidget {
// if the habit was null, but did not have a habit id associated with a stack widget,
// `getHabitFromWidgetId` will throw a HabitNotFoundException
val habit = getHabitFromWidgetId(id) val habit = getHabitFromWidgetId(id)
if (habit != null) {
return HistoryWidget(context, id, habit) return HistoryWidget(context, id, habit)
} else {
return StackWidget(context, id, StackWidgetType.HISTORY)
}
} }
} }

@ -18,13 +18,19 @@
*/ */
package org.isoron.uhabits.widgets package org.isoron.uhabits.widgets
import android.content.* import android.content.Context
import org.isoron.uhabits.* import org.isoron.uhabits.HabitsApplication
class ScoreWidgetProvider : BaseWidgetProvider() { class ScoreWidgetProvider : BaseWidgetProvider() {
override fun getWidgetFromId(context: Context, id: Int): BaseWidget { override fun getWidgetFromId(context: Context, id: Int): BaseWidget {
val component = (context.applicationContext as HabitsApplication).component // if the habit was null, but did not have a habit id associated with a stack widget,
// `getHabitFromWidgetId` will throw a HabitNotFoundException
val habit = getHabitFromWidgetId(id) val habit = getHabitFromWidgetId(id)
val component = (context.applicationContext as HabitsApplication).component
if (habit != null) {
return ScoreWidget(context, id, habit, component.preferences) return ScoreWidget(context, id, habit, component.preferences)
} else {
return StackWidget(context, id, StackWidgetType.SCORE)
}
} }
} }

@ -22,14 +22,14 @@ package org.isoron.uhabits.widgets
import android.appwidget.AppWidgetManager import android.appwidget.AppWidgetManager
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri
import android.view.View import android.view.View
import android.widget.RemoteViews import android.widget.RemoteViews
import org.isoron.uhabits.R
class CheckmarkStackWidget( class StackWidget(
context: Context, context: Context,
widgetId: Int, widgetId: Int,
private val habitIds: List<Long> private val widgetType: StackWidgetType
) : BaseWidget(context, widgetId) { ) : BaseWidget(context, widgetId) {
override fun getOnClickPendingIntent(context: Context) = null override fun getOnClickPendingIntent(context: Context) = null
@ -39,14 +39,16 @@ class CheckmarkStackWidget(
} }
override fun getRemoteViews(width: Int, height: Int): RemoteViews { override fun getRemoteViews(width: Int, height: Int): RemoteViews {
val remoteViews = RemoteViews(context.packageName, R.layout.stackview_widget) val remoteViews = RemoteViews(context.packageName, StackWidgetType.getStackWidgetLayoutId(widgetType))
val serviceIntent = Intent(context, CheckmarkStackWidgetService::class.java) val serviceIntent = Intent(context, StackWidgetService::class.java)
serviceIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id) serviceIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id)
serviceIntent.putExtra(CheckmarkStackWidgetService.HABIT_IDS_SELECTED, habitIds.toLongArray()) serviceIntent.putExtra(StackWidgetService.WIDGET_TYPE, widgetType.value)
remoteViews.setRemoteAdapter(R.id.stackWidgetView, serviceIntent) serviceIntent.setData(Uri.parse(serviceIntent.toUri(Intent.URI_INTENT_SCHEME)))
AppWidgetManager.getInstance(context).notifyAppWidgetViewDataChanged(id, R.id.stackWidgetView) remoteViews.setRemoteAdapter(StackWidgetType.getStackWidgetAdapterViewId(widgetType), serviceIntent)
AppWidgetManager.getInstance(context).notifyAppWidgetViewDataChanged(id, StackWidgetType.getStackWidgetAdapterViewId(widgetType))
// TODO what should the empty view look like? // TODO what should the empty view look like?
remoteViews.setEmptyView(R.id.stackWidgetView, R.id.stackWidgetEmptyView) remoteViews.setEmptyView(StackWidgetType.getStackWidgetAdapterViewId(widgetType),
StackWidgetType.getStackWidgetEmptyViewId(widgetType))
return remoteViews return remoteViews
} }

@ -4,6 +4,8 @@ import android.appwidget.AppWidgetManager;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.widget.RemoteViews; import android.widget.RemoteViews;
import android.widget.RemoteViewsService; import android.widget.RemoteViewsService;
@ -12,37 +14,36 @@ import org.isoron.uhabits.HabitsApplication;
import org.isoron.uhabits.core.models.Habit; import org.isoron.uhabits.core.models.Habit;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT; import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT;
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH; import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH;
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT; import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT;
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH; import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH;
import static org.isoron.androidbase.utils.InterfaceUtils.dpToPixels; import static org.isoron.androidbase.utils.InterfaceUtils.dpToPixels;
import static org.isoron.uhabits.widgets.CheckmarkStackWidgetService.HABIT_IDS_SELECTED; import static org.isoron.uhabits.widgets.StackWidgetService.WIDGET_TYPE;
public class CheckmarkStackWidgetService extends RemoteViewsService { public class StackWidgetService extends RemoteViewsService {
public static final String HABIT_IDS_SELECTED = "HABIT_IDS_SELECTED"; public static final String WIDGET_TYPE = "WIDGET_TYPE";
@Override @Override
public RemoteViewsFactory onGetViewFactory(Intent intent) { public RemoteViewsFactory onGetViewFactory(Intent intent) {
return new CheckmarkStackRemoteViewsFactory(this.getApplicationContext(), intent); return new StackRemoteViewsFactory(this.getApplicationContext(), intent);
} }
} }
class CheckmarkStackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory { class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
private Context mContext; private Context mContext;
private int mAppWidgetId; private int mAppWidgetId;
private ArrayList<Habit> mHabitList; private ArrayList<Habit> mHabitList;
private List<Long> mHabitsSelected; private StackWidgetType mWidgetType;
public CheckmarkStackRemoteViewsFactory(Context context, Intent intent) { public StackRemoteViewsFactory(Context context, Intent intent) {
mContext = context; mContext = context;
mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
mHabitsSelected = new ArrayList<>(); int widgetTypeValue = intent.getIntExtra(WIDGET_TYPE, -1);
for (long id : intent.getLongArrayExtra(HABIT_IDS_SELECTED)) { if (widgetTypeValue != -1) {
mHabitsSelected.add(id); mWidgetType = StackWidgetType.getWidgetTypeFromValue(widgetTypeValue);
} }
} }
@ -51,7 +52,7 @@ class CheckmarkStackRemoteViewsFactory implements RemoteViewsService.RemoteViews
} }
public void onDestroy() { public void onDestroy() {
mHabitList.clear();
} }
public int getCount() { public int getCount() {
@ -75,20 +76,62 @@ class CheckmarkStackRemoteViewsFactory implements RemoteViewsService.RemoteViews
public RemoteViews getViewAt(int position) { public RemoteViews getViewAt(int position) {
RemoteViews rv = null; RemoteViews rv = null;
if (position < getCount()) { if (position < getCount()) {
Habit habit = mHabitList.get(position); Habit habit = mHabitList.get(position);
CheckmarkWidget checkmarkWidget = new CheckmarkWidget(mContext, mAppWidgetId, habit); BaseWidget widget = initializeWidget(habit);
Bundle options = AppWidgetManager.getInstance(mContext).getAppWidgetOptions(mAppWidgetId); Bundle options = AppWidgetManager.getInstance(mContext).getAppWidgetOptions(mAppWidgetId);
checkmarkWidget.setDimensions(getDimensionsFromOptions(mContext, options)); widget.setDimensions(getDimensionsFromOptions(mContext, options));
RemoteViews landscape = checkmarkWidget.getLandscapeRemoteViews(); final RemoteViews[] landscape = new RemoteViews[1];
RemoteViews portrait = checkmarkWidget.getPortraitRemoteViews(); final RemoteViews[] portrait = new RemoteViews[1];
rv = new RemoteViews(landscape, portrait);
Object lock = new Object();
final boolean[] flag = {false};
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
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) {
}
}
}
rv = new RemoteViews(landscape[0], portrait[0]);
} }
return rv; return rv;
} }
private BaseWidget initializeWidget(Habit habit) {
switch (mWidgetType) {
case CHECKMARK:
return new CheckmarkWidget(mContext, mAppWidgetId, habit);
case FREQUENCY:
return new FrequencyWidget(mContext, mAppWidgetId, habit);
case SCORE:
HabitsApplication app = (HabitsApplication) mContext.getApplicationContext();
return new ScoreWidget(mContext, mAppWidgetId, habit, app.getComponent().getPreferences());
case HISTORY:
return new HistoryWidget(mContext, mAppWidgetId, habit);
case STREAKS:
return new StreakWidget(mContext, mAppWidgetId, habit);
}
return null;
}
public RemoteViews getLoadingView() { public RemoteViews getLoadingView() {
return null; return null;
} }
@ -109,9 +152,7 @@ class CheckmarkStackRemoteViewsFactory implements RemoteViewsService.RemoteViews
mHabitList = new ArrayList<>(); mHabitList = new ArrayList<>();
HabitsApplication app = (HabitsApplication) mContext.getApplicationContext(); HabitsApplication app = (HabitsApplication) mContext.getApplicationContext();
for (Habit h : app.getComponent().getHabitList()) { for (Habit h : app.getComponent().getHabitList()) {
if (mHabitsSelected.contains(h.getId())) {
mHabitList.add(h); mHabitList.add(h);
} }
} }
} }
}

@ -0,0 +1,89 @@
package org.isoron.uhabits.widgets;
import org.isoron.uhabits.R;
/**
* Created by victoryu on 11/3/17.
*/
public enum StackWidgetType {
CHECKMARK(0),
FREQUENCY(1),
SCORE(2), // habit strength widget
HISTORY(3),
STREAKS(4);
private int value;
StackWidgetType(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static StackWidgetType getWidgetTypeFromValue(int value) {
if (CHECKMARK.getValue() == value) {
return CHECKMARK;
} else if (FREQUENCY.getValue() == value) {
return FREQUENCY;
} else if (SCORE.getValue() == value) {
return SCORE;
} else if (HISTORY.getValue() == value) {
return HISTORY;
} else if (STREAKS.getValue() == value) {
return STREAKS;
}
return null;
}
public static int getStackWidgetLayoutId(StackWidgetType type) {
switch (type) {
case CHECKMARK:
return R.layout.checkmark_stackview_widget;
case FREQUENCY:
return R.layout.frequency_stackview_widget;
case SCORE:
return R.layout.score_stackview_widget;
case HISTORY:
return R.layout.history_stackview_widget;
case STREAKS:
return R.layout.streak_stackview_widget;
}
return 0;
}
public static int getStackWidgetAdapterViewId(StackWidgetType type) {
switch (type) {
case CHECKMARK:
return R.id.checkmarkStackWidgetView;
case FREQUENCY:
return R.id.frequencyStackWidgetView;
case SCORE:
return R.id.scoreStackWidgetView;
case HISTORY:
return R.id.historyStackWidgetView;
case STREAKS:
return R.id.streakStackWidgetView;
}
return 0;
}
public static int getStackWidgetEmptyViewId(StackWidgetType type) {
switch (type) {
case CHECKMARK:
return R.id.checkmarkStackWidgetEmptyView;
case FREQUENCY:
return R.id.frequencyStackWidgetEmptyView;
case SCORE:
return R.id.scoreStackWidgetEmptyView;
case HISTORY:
return R.id.historyStackWidgetEmptyView;
case STREAKS:
return R.id.streakStackWidgetEmptyView;
}
return 0;
}
}

@ -18,11 +18,17 @@
*/ */
package org.isoron.uhabits.widgets package org.isoron.uhabits.widgets
import android.content.* import android.content.Context
class StreakWidgetProvider : BaseWidgetProvider() { class StreakWidgetProvider : BaseWidgetProvider() {
override fun getWidgetFromId(context: Context, id: Int): BaseWidget { override fun getWidgetFromId(context: Context, id: Int): BaseWidget {
// if the habit was null, but did not have a habit id associated with a stack widget,
// `getHabitFromWidgetId` will throw a HabitNotFoundException
val habit = getHabitFromWidgetId(id) val habit = getHabitFromWidgetId(id)
if (habit != null) {
return StreakWidget(context, id, habit) return StreakWidget(context, id, habit)
} else {
return StackWidget(context, id, StackWidgetType.STREAKS)
}
} }
} }

@ -3,12 +3,12 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<StackView xmlns:android="http://schemas.android.com/apk/res/android" <StackView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/stackWidgetView" android:id="@+id/checkmarkStackWidgetView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:loopViews="true" /> android:loopViews="true" />
<TextView xmlns:android="http://schemas.android.com/apk/res/android" <TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/stackWidgetEmptyView" android:id="@+id/checkmarkStackWidgetEmptyView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:text="@string/checkmark_stack_widget" android:text="@string/checkmark_stack_widget"

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<StackView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/frequencyStackWidgetView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:loopViews="true" />
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/frequencyStackWidgetEmptyView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/frequency_stack_widget"
android:gravity="center"
android:textColor="#ffffff"
android:textStyle="bold"
android:textSize="16sp" />
</FrameLayout>

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<StackView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/historyStackWidgetView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:loopViews="true" />
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/historyStackWidgetEmptyView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/history_stack_widget"
android:gravity="center"
android:textColor="#ffffff"
android:textStyle="bold"
android:textSize="16sp" />
</FrameLayout>

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<StackView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/scoreStackWidgetView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:loopViews="true" />
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/scoreStackWidgetEmptyView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/score_stack_widget"
android:gravity="center"
android:textColor="#ffffff"
android:textStyle="bold"
android:textSize="16sp" />
</FrameLayout>

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<StackView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/streakStackWidgetView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:loopViews="true" />
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/streakStackWidgetEmptyView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/streaks_stack_widget"
android:gravity="center"
android:textColor="#ffffff"
android:textStyle="bold"
android:textSize="16sp" />
</FrameLayout>

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?><!--
<!--
~ Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com> ~ Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
~ ~
~ This file is part of Loop Habit Tracker. ~ This file is part of Loop Habit Tracker.
@ -18,10 +17,23 @@
~ with this program. If not, see <http://www.gnu.org/licenses/>. ~ with this program. If not, see <http://www.gnu.org/licenses/>.
--> -->
<ListView android:id="@+id/listView" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<Button
android:id="@+id/createStackWidgetButton"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:text="@string/create_stackview_widget_button"/>
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="vertical"
android:layout_weight="1">
</ListView> </ListView>
</LinearLayout>

@ -125,7 +125,13 @@
<string name="version_n">Version %s</string> <string name="version_n">Version %s</string>
<string name="frequency">Frequency</string> <string name="frequency">Frequency</string>
<string name="checkmark">Checkmark</string> <string name="checkmark">Checkmark</string>
<string name="create_stackview_widget_button">StackView Widget For All Habits</string>
<string name="checkmark_stack_widget">Checkmark Stack Widget</string> <string name="checkmark_stack_widget">Checkmark Stack Widget</string>
<string name="frequency_stack_widget">Frequency Stack Widget</string>
<string name="score_stack_widget">Score Stack Widget</string>
<string name="history_stack_widget">History Stack Widget</string>
<string name="streaks_stack_widget">Streaks Stack Widget</string>
<string name="strength">Strength</string> <string name="strength">Strength</string>
<string name="best_streaks">Best streaks</string> <string name="best_streaks">Best streaks</string>

@ -25,6 +25,6 @@
android:previewImage="@drawable/widget_preview_checkmark" android:previewImage="@drawable/widget_preview_checkmark"
android:resizeMode="none" android:resizeMode="none"
android:updatePeriodMillis="3600000" android:updatePeriodMillis="3600000"
android:configure="org.isoron.uhabits.widgets.HabitGroupPickerDialog" android:configure="org.isoron.uhabits.widgets.HabitPickerDialog"
android:widgetCategory="home_screen"> android:widgetCategory="home_screen">
</appwidget-provider> </appwidget-provider>

@ -21,11 +21,6 @@ package org.isoron.uhabits.core.preferences;
import org.isoron.uhabits.core.AppScope; import org.isoron.uhabits.core.AppScope;
import org.isoron.uhabits.core.models.HabitNotFoundException; import org.isoron.uhabits.core.models.HabitNotFoundException;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
@ -33,6 +28,9 @@ import javax.inject.Inject;
public class WidgetPreferences { public class WidgetPreferences {
private Preferences.Storage storage; private Preferences.Storage storage;
public static final long STACK_WIDGET_HABITS = -1;
public static final long HABIT_NOT_FOUND = -2;
@Inject @Inject
public WidgetPreferences(Preferences.Storage storage) { public WidgetPreferences(Preferences.Storage storage) {
this.storage = storage; this.storage = storage;
@ -42,32 +40,13 @@ public class WidgetPreferences {
storage.putLong(getHabitIdKey(widgetId), habitId); storage.putLong(getHabitIdKey(widgetId), habitId);
} }
/**
* @param habitIds the string should be in the format: [habitId1, habitId2, habitId3...]
*/
public void addWidget(int widgetId, String habitIds) {
storage.putString(getHabitIdKey(widgetId), habitIds);
}
public long getHabitIdFromWidgetId(int widgetId) { public long getHabitIdFromWidgetId(int widgetId) {
Long habitId = storage.getLong(getHabitIdKey(widgetId), -1); Long habitId = storage.getLong(getHabitIdKey(widgetId), HABIT_NOT_FOUND);
if (habitId < 0) throw new HabitNotFoundException(); if (habitId == HABIT_NOT_FOUND) throw new HabitNotFoundException();
return habitId; return habitId;
} }
public List<Long> getHabitIdsGroupFromWidgetId(int widgetId) {
String habitIdsGroup = storage.getString(getHabitIdKey(widgetId), "");
if (habitIdsGroup.isEmpty()) throw new HabitNotFoundException();
ArrayList<Long> habitIdList = new ArrayList<>();
for (String s : parseStringToList(habitIdsGroup)) {
habitIdList.add(Long.parseLong(s.trim()));
}
return habitIdList;
}
public void removeWidget(int id) { public void removeWidget(int id) {
String habitIdKey = getHabitIdKey(id); String habitIdKey = getHabitIdKey(id);
storage.remove(habitIdKey); storage.remove(habitIdKey);
@ -76,9 +55,4 @@ public class WidgetPreferences {
private String getHabitIdKey(int id) { private String getHabitIdKey(int id) {
return String.format("widget-%06d-habit", id); return String.format("widget-%06d-habit", id);
} }
private List<String> parseStringToList(@NotNull String str) {
return Arrays.asList(str.replace("[", "")
.replace("]", "").split(","));
}
} }

Loading…
Cancel
Save