From 5a78de5a25b0c6989e2e5fee64607dfc4caf8898 Mon Sep 17 00:00:00 2001 From: Victor Yu Date: Sun, 15 Oct 2017 13:34:40 -0400 Subject: [PATCH] Implemented ability to choose multiple habits for stackview --- uhabits-android/src/main/AndroidManifest.xml | 7 + .../uhabits/widgets/HabitGroupPickerDialog.kt | 126 ++++++++++++++++++ .../uhabits/widgets/StackGroupWidget.kt | 7 +- .../uhabits/widgets/StackWidgetProvider.kt | 20 +-- .../uhabits/widgets/StackWidgetService.java | 17 ++- .../res/layout/habit_checkbox_list_item.xml | 22 +++ .../stack_widget_configure_activity.xml | 40 ++++++ .../src/main/res/values/strings.xml | 1 + .../res/xml/widget_checkmark_stack_info.xml | 2 +- .../core/preferences/WidgetPreferences.java | 53 +++++--- 10 files changed, 261 insertions(+), 34 deletions(-) create mode 100644 uhabits-android/src/main/java/org/isoron/uhabits/widgets/HabitGroupPickerDialog.kt create mode 100644 uhabits-android/src/main/res/layout/habit_checkbox_list_item.xml create mode 100644 uhabits-android/src/main/res/layout/stack_widget_configure_activity.xml 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 @@ + + + + + + + + +