diff --git a/uhabits-android/src/main/AndroidManifest.xml b/uhabits-android/src/main/AndroidManifest.xml
index 9cd76e6fc..01fd34fde 100644
--- a/uhabits-android/src/main/AndroidManifest.xml
+++ b/uhabits-android/src/main/AndroidManifest.xml
@@ -84,6 +84,13 @@
+
+
+
+
+
diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/widgets/HabitGroupPickerDialog.kt b/uhabits-android/src/main/java/org/isoron/uhabits/widgets/HabitGroupPickerDialog.kt
new file mode 100644
index 000000000..2737d9d5d
--- /dev/null
+++ b/uhabits-android/src/main/java/org/isoron/uhabits/widgets/HabitGroupPickerDialog.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2016 Álinson Santos Xavier
+ *
+ * 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 .
+ */
+
+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
+ private lateinit var widgetUpdater: WidgetUpdater
+ private lateinit var habitIdsSelected: ArrayList
+ 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()
+
+ habitIds = ArrayList()
+ val habitNames = ArrayList()
+ 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.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) :
+ ArrayAdapter(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
+ }
+
+ }
+}
diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/widgets/StackGroupWidget.kt b/uhabits-android/src/main/java/org/isoron/uhabits/widgets/StackGroupWidget.kt
index d978a7b6a..726f50453 100644
--- a/uhabits-android/src/main/java/org/isoron/uhabits/widgets/StackGroupWidget.kt
+++ b/uhabits-android/src/main/java/org/isoron/uhabits/widgets/StackGroupWidget.kt
@@ -25,11 +25,11 @@ import android.content.Intent
import android.view.View
import android.widget.RemoteViews
import org.isoron.uhabits.R
-import org.isoron.uhabits.widgets.views.CheckmarkWidgetView
class StackGroupWidget(
context: Context,
- widgetId: Int
+ widgetId: Int,
+ private val habitIds: List
) : BaseWidget(context, widgetId) {
override fun getOnClickPendingIntent(context: Context) = null
@@ -42,8 +42,7 @@ class StackGroupWidget(
val remoteViews = RemoteViews(context.packageName, R.layout.widget_stackview)
val serviceIntent = Intent(context, StackWidgetService::class.java)
serviceIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id)
- // TODO this commented line is taken from official examples, but adding it seems to make the widget not update immediately when completing the habit
- // serviceIntent.data = Uri.parse(serviceIntent.toUri(Intent.URI_INTENT_SCHEME)) // embed extras so they don't get ignored
+ serviceIntent.putExtra(StackWidgetService.HABIT_IDS_SELECTED, habitIds.toLongArray())
remoteViews.setRemoteAdapter(R.id.stackWidgetView, serviceIntent)
AppWidgetManager.getInstance(context).notifyAppWidgetViewDataChanged(id, R.id.stackWidgetView)
// TODO what should the empty view look like?
diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/widgets/StackWidgetProvider.kt b/uhabits-android/src/main/java/org/isoron/uhabits/widgets/StackWidgetProvider.kt
index 5abd7603f..23ba09df6 100644
--- a/uhabits-android/src/main/java/org/isoron/uhabits/widgets/StackWidgetProvider.kt
+++ b/uhabits-android/src/main/java/org/isoron/uhabits/widgets/StackWidgetProvider.kt
@@ -1,17 +1,19 @@
package org.isoron.uhabits.widgets
-import android.appwidget.AppWidgetManager
import android.content.Context
-import android.content.Intent
-import android.util.Log
-import org.isoron.uhabits.R
-
-/**
- * Created by victoryu on 9/30/17.
- */
+import org.isoron.uhabits.HabitsApplication
class StackWidgetProvider : BaseWidgetProvider() {
+
override fun getWidgetFromId(context: Context, id: Int): StackGroupWidget {
- return StackGroupWidget(context, id)
+ val habitIds = getHabitGroupFromWidget(context, id)
+ return StackGroupWidget(context, id, habitIds)
+ }
+
+ private fun getHabitGroupFromWidget(context: Context, widgetId: Int) : List {
+ val app = context.getApplicationContext() as HabitsApplication
+ val widgetPrefs = app.component.widgetPreferences
+ val habitIds = widgetPrefs.getHabitIdsGroupFromWidgetId(widgetId)
+ return habitIds
}
}
diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/widgets/StackWidgetService.java b/uhabits-android/src/main/java/org/isoron/uhabits/widgets/StackWidgetService.java
index 6a6f788e7..dc7cff198 100644
--- a/uhabits-android/src/main/java/org/isoron/uhabits/widgets/StackWidgetService.java
+++ b/uhabits-android/src/main/java/org/isoron/uhabits/widgets/StackWidgetService.java
@@ -12,19 +12,19 @@ import org.isoron.uhabits.HabitsApplication;
import org.isoron.uhabits.core.models.Habit;
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_WIDTH;
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT;
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH;
import static org.isoron.androidbase.utils.InterfaceUtils.dpToPixels;
-
-/**
- * Created by victoryu on 9/30/17.
- */
+import static org.isoron.uhabits.widgets.StackWidgetService.HABIT_IDS_SELECTED;
public class StackWidgetService extends RemoteViewsService {
+ public static final String HABIT_IDS_SELECTED = "HABIT_IDS_SELECTED";
+
@Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
return new StackRemoteViewsFactory(this.getApplicationContext(), intent);
@@ -35,10 +35,15 @@ class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
private Context mContext;
private int mAppWidgetId;
private ArrayList mHabitList;
+ private List mHabitsSelected;
public StackRemoteViewsFactory(Context context, Intent intent) {
mContext = context;
mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
+ mHabitsSelected = new ArrayList<>();
+ for (long id : intent.getLongArrayExtra(HABIT_IDS_SELECTED)) {
+ mHabitsSelected.add(id);
+ }
}
public void onCreate() {
@@ -104,7 +109,9 @@ class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
mHabitList = new ArrayList<>();
HabitsApplication app = (HabitsApplication) mContext.getApplicationContext();
for (Habit h : app.getComponent().getHabitList()) {
- mHabitList.add(h);
+ if (mHabitsSelected.contains(h.getId())) {
+ mHabitList.add(h);
+ }
}
}
}
diff --git a/uhabits-android/src/main/res/layout/habit_checkbox_list_item.xml b/uhabits-android/src/main/res/layout/habit_checkbox_list_item.xml
new file mode 100644
index 000000000..e2c574ca8
--- /dev/null
+++ b/uhabits-android/src/main/res/layout/habit_checkbox_list_item.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uhabits-android/src/main/res/layout/stack_widget_configure_activity.xml b/uhabits-android/src/main/res/layout/stack_widget_configure_activity.xml
new file mode 100644
index 000000000..d5eb1a561
--- /dev/null
+++ b/uhabits-android/src/main/res/layout/stack_widget_configure_activity.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uhabits-android/src/main/res/values/strings.xml b/uhabits-android/src/main/res/values/strings.xml
index 45bf1823b..b4b9c8451 100644
--- a/uhabits-android/src/main/res/values/strings.xml
+++ b/uhabits-android/src/main/res/values/strings.xml
@@ -112,6 +112,7 @@
Clear
Select hours
Select minutes
+ Please select at least one habit
- @string/hint_drag
diff --git a/uhabits-android/src/main/res/xml/widget_checkmark_stack_info.xml b/uhabits-android/src/main/res/xml/widget_checkmark_stack_info.xml
index 41f8e8788..8a77f2c34 100644
--- a/uhabits-android/src/main/res/xml/widget_checkmark_stack_info.xml
+++ b/uhabits-android/src/main/res/xml/widget_checkmark_stack_info.xml
@@ -27,7 +27,7 @@
android:previewImage="@drawable/widget_preview_checkmark"
android:resizeMode="none"
android:updatePeriodMillis="3600000"
- android:configure="org.isoron.uhabits.widgets.HabitPickerDialog"
+ android:configure="org.isoron.uhabits.widgets.HabitGroupPickerDialog"
android:widgetCategory="home_screen">
\ No newline at end of file
diff --git a/uhabits-core/src/main/java/org/isoron/uhabits/core/preferences/WidgetPreferences.java b/uhabits-core/src/main/java/org/isoron/uhabits/core/preferences/WidgetPreferences.java
index 85ab9d944..35e0e09ef 100644
--- a/uhabits-core/src/main/java/org/isoron/uhabits/core/preferences/WidgetPreferences.java
+++ b/uhabits-core/src/main/java/org/isoron/uhabits/core/preferences/WidgetPreferences.java
@@ -19,43 +19,66 @@
package org.isoron.uhabits.core.preferences;
-import org.isoron.uhabits.core.*;
-import org.isoron.uhabits.core.models.*;
+import org.isoron.uhabits.core.AppScope;
+import org.isoron.uhabits.core.models.HabitNotFoundException;
+import org.jetbrains.annotations.NotNull;
-import javax.inject.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.inject.Inject;
@AppScope
-public class WidgetPreferences
-{
+public class WidgetPreferences {
private Preferences.Storage storage;
@Inject
- public WidgetPreferences(Preferences.Storage storage)
- {
+ public WidgetPreferences(Preferences.Storage storage) {
this.storage = storage;
}
- public void addWidget(int widgetId, long habitId)
- {
+ public void addWidget(int widgetId, long habitId) {
storage.putLong(getHabitIdKey(widgetId), habitId);
}
- public long getHabitIdFromWidgetId(int widgetId)
- {
+ /**
+ * @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) {
Long habitId = storage.getLong(getHabitIdKey(widgetId), -1);
if (habitId < 0) throw new HabitNotFoundException();
return habitId;
}
- public void removeWidget(int id)
- {
+ public List getHabitIdsGroupFromWidgetId(int widgetId) {
+ String habitIdsGroup = storage.getString(getHabitIdKey(widgetId), "");
+ if (habitIdsGroup.isEmpty()) throw new HabitNotFoundException();
+
+ ArrayList habitIdList = new ArrayList<>();
+ for (String s : parseStringToList(habitIdsGroup)) {
+ habitIdList.add(Long.parseLong(s.trim()));
+ }
+
+ return habitIdList;
+ }
+
+ public void removeWidget(int id) {
String habitIdKey = getHabitIdKey(id);
storage.remove(habitIdKey);
}
- private String getHabitIdKey(int id)
- {
+ private String getHabitIdKey(int id) {
return String.format("widget-%06d-habit", id);
}
+
+ private List parseStringToList(@NotNull String str) {
+ return Arrays.asList(str.replace("[", "")
+ .replace("]", "").split(","));
+ }
}