diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/EditHabitActivity.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/EditHabitActivity.kt index c9fac1f4f..ee3adc30b 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/EditHabitActivity.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/EditHabitActivity.kt @@ -47,7 +47,6 @@ import org.isoron.uhabits.core.commands.CreateHabitCommand import org.isoron.uhabits.core.commands.EditHabitCommand import org.isoron.uhabits.core.commands.RefreshParentGroupCommand import org.isoron.uhabits.core.models.Frequency -import org.isoron.uhabits.core.models.Habit import org.isoron.uhabits.core.models.HabitGroup import org.isoron.uhabits.core.models.HabitType import org.isoron.uhabits.core.models.NumericalHabitType @@ -76,7 +75,7 @@ class EditHabitActivity : AppCompatActivity() { private lateinit var binding: ActivityEditHabitBinding private lateinit var commandRunner: CommandRunner - var habitUUID: String? = null + var habitId = -1L lateinit var habitType: HabitType var parentGroup: HabitGroup? = null var unit = "" @@ -99,20 +98,20 @@ class EditHabitActivity : AppCompatActivity() { binding = ActivityEditHabitBinding.inflate(layoutInflater) setContentView(binding.root) - if (intent.hasExtra("parentGroupUUID")) { - val parentGroupUUID = intent.getStringExtra("parentGroupUUID") - parentGroup = component.habitGroupList.getByUUID(parentGroupUUID) + if (intent.hasExtra("groupId")) { + val groupId = intent.getLongExtra("groupId", -1L) + parentGroup = component.habitGroupList.getById(groupId) } - if (intent.hasExtra("habitUUID")) { + if (intent.hasExtra("habitId")) { binding.toolbar.title = getString(R.string.edit_habit) - habitUUID = intent.getStringExtra("habitUUID")!! + habitId = intent.getLongExtra("habitId", -1L) val habitList = if (parentGroup != null) { parentGroup!!.habitList } else { component.habitList } - val habit = habitList.getByUUID(habitUUID)!! + val habit = habitList.getById(habitId)!! habitType = habit.type color = habit.color freqNum = habit.frequency.numerator @@ -133,7 +132,7 @@ class EditHabitActivity : AppCompatActivity() { } if (state != null) { - habitUUID = state.getString("habitUUID") + habitId = state.getLong("habitId") habitType = HabitType.fromInt(state.getInt("habitType")) color = PaletteColor(state.getInt("paletteColor")) freqNum = state.getInt("freqNum") @@ -279,9 +278,8 @@ class EditHabitActivity : AppCompatActivity() { component.habitList } - var original: Habit? = null - if (habitUUID != null) { - original = habitList.getByUUID(habitUUID)!! + if (habitId > 0) { + val original = habitList.getById(habitId)!! habit.copyFrom(original) } @@ -303,13 +301,13 @@ class EditHabitActivity : AppCompatActivity() { } habit.type = habitType habit.parent = parentGroup - habit.parentID = parentGroup?.id - habit.parentUUID = parentGroup?.uuid + habit.groupId = parentGroup?.id + habit.groupUUID = parentGroup?.uuid - val command = if (habitUUID != null) { + val command = if (habitId > 0) { EditHabitCommand( habitList, - habitUUID!!, + habitId, habit ) } else { @@ -321,7 +319,7 @@ class EditHabitActivity : AppCompatActivity() { } component.commandRunner.run(command) - if (habit.parentID != null) { + if (habit.groupId != null) { val habitGroupList = component.habitGroupList val refreshCommand = RefreshParentGroupCommand(habit, habitGroupList) component.commandRunner.run(refreshCommand) @@ -395,7 +393,7 @@ class EditHabitActivity : AppCompatActivity() { override fun onSaveInstanceState(state: Bundle) { super.onSaveInstanceState(state) with(state) { - putString("habitUUID", habitUUID) + putLong("habitId", habitId) putInt("habitType", habitType.value) putInt("paletteColor", color.paletteIndex) putInt("androidColor", androidColor) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/HabitTypeDialog.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/HabitTypeDialog.kt index 299457a89..603335c37 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/HabitTypeDialog.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/edit/HabitTypeDialog.kt @@ -29,7 +29,7 @@ import org.isoron.uhabits.core.models.HabitType import org.isoron.uhabits.databinding.SelectHabitTypeBinding import org.isoron.uhabits.intents.IntentFactory -class HabitTypeDialog(val parentUUID: String? = null) : AppCompatDialogFragment() { +class HabitTypeDialog(val groupId: Long? = null) : AppCompatDialogFragment() { override fun getTheme() = R.style.Translucent override fun onCreateView( @@ -40,13 +40,13 @@ class HabitTypeDialog(val parentUUID: String? = null) : AppCompatDialogFragment( val binding = SelectHabitTypeBinding.inflate(inflater, container, false) binding.buttonYesNo.setOnClickListener { - val intent = IntentFactory().startEditActivity(requireActivity(), HabitType.YES_NO.value, parentUUID) + val intent = IntentFactory().startEditActivity(requireActivity(), HabitType.YES_NO.value, groupId) startActivity(intent) dismiss() } binding.buttonMeasurable.setOnClickListener { - val intent = IntentFactory().startEditActivity(requireActivity(), HabitType.NUMERICAL.value, parentUUID) + val intent = IntentFactory().startEditActivity(requireActivity(), HabitType.NUMERICAL.value, groupId) startActivity(intent) dismiss() } @@ -57,7 +57,7 @@ class HabitTypeDialog(val parentUUID: String? = null) : AppCompatDialogFragment( dismiss() } - if (parentUUID != null) { + if (groupId != null) { binding.buttonHabitGroup.visibility = View.GONE } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.kt index cd10b4285..abfa0e48f 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/ListHabitsScreen.kt @@ -166,8 +166,8 @@ class ListHabitsScreen activity.startActivity(intent) } - override fun showSelectHabitTypeDialog(parentUUID: String?) { - val dialog = HabitTypeDialog(parentUUID) + override fun showSelectHabitTypeDialog(groupId: Long?) { + val dialog = HabitTypeDialog(groupId) dialog.show(activity.supportFragmentManager, "habitType") } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/AddButtonView.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/AddButtonView.kt index 3cff41fcc..fb7885fea 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/AddButtonView.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/AddButtonView.kt @@ -28,7 +28,7 @@ class AddButtonView( } override fun onClick(v: View) { - (context as ListHabitsActivity).component.listHabitsMenu.behavior.onCreateHabit(habitGroup!!.uuid) + (context as ListHabitsActivity).component.listHabitsMenu.behavior.onCreateHabit(habitGroup!!.id) } override fun onDraw(canvas: Canvas) { diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListAdapter.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListAdapter.kt index 27ae9be1a..fcff247a2 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListAdapter.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/list/views/HabitCardListAdapter.kt @@ -34,7 +34,6 @@ import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsSelectionMenuBeh import org.isoron.uhabits.core.utils.MidnightTimer import org.isoron.uhabits.inject.ActivityScope import java.util.LinkedList -import java.util.UUID import javax.inject.Inject /** @@ -75,10 +74,6 @@ class HabitCardListAdapter @Inject constructor( return cache.hasNoHabitGroup() } - fun hasNoSubHabits(): Boolean { - return cache.hasNoSubHabits() - } - /** * Sets all items as not selected. */ @@ -122,22 +117,7 @@ class HabitCardListAdapter @Inject constructor( } override fun getItemId(position: Int): Long { - val uuidString = cache.getUUIDByPosition(position) - return if (uuidString != null) { - val formattedUUIDString = formatUUID(uuidString) - val uuid = UUID.fromString(formattedUUIDString) - uuid.mostSignificantBits and Long.MAX_VALUE - } else { - -1 - } - } - - private fun formatUUID(uuidString: String): String { - return uuidString.substring(0, 8) + "-" + - uuidString.substring(8, 12) + "-" + - uuidString.substring(12, 16) + "-" + - uuidString.substring(16, 20) + "-" + - uuidString.substring(20, 32) + return cache.getIdByPosition(position)!! } /** @@ -165,14 +145,14 @@ class HabitCardListAdapter @Inject constructor( if (listView == null) return val habit = cache.getHabitByPosition(position) if (habit != null) { - val score = cache.getScore(habit.uuid!!) - val checkmarks = cache.getCheckmarks(habit.uuid!!) - val notes = cache.getNotes(habit.uuid!!) + val score = cache.getScore(habit.id!!) + val checkmarks = cache.getCheckmarks(habit.id!!) + val notes = cache.getNotes(habit.id!!) val selected = selectedHabits.contains(habit) listView!!.bindCardView(holder, habit, score, checkmarks, notes, selected) } else { val habitGroup = cache.getHabitGroupByPosition(position) - val score = cache.getScore(habitGroup!!.uuid!!) + val score = cache.getScore(habitGroup!!.id!!) val selected = selectedHabitGroups.contains(habitGroup) listView!!.bindGroupCardView(holder, habitGroup, score, selected) } @@ -253,11 +233,11 @@ class HabitCardListAdapter @Inject constructor( * @param selected list of habits to be removed */ override fun performRemove(selected: List) { - for (habit in selected) cache.remove(habit.uuid!!) + for (habit in selected) cache.remove(habit.id!!) } override fun performRemoveHabitGroup(selected: List) { - for (hgr in selected) cache.remove(hgr.uuid!!) + for (hgr in selected) cache.remove(hgr.id!!) } /** diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitActivity.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitActivity.kt index 1b0e8c6a9..42568fb8e 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitActivity.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/habits/show/ShowHabitActivity.kt @@ -74,14 +74,14 @@ class ShowHabitActivity : AppCompatActivity(), CommandRunner.Listener { val appComponent = (applicationContext as HabitsApplication).component val habitGroupList = appComponent.habitGroupList - val parentUUID = intent.getStringExtra("parentUUID") - val habitList = if (parentUUID == null) { - appComponent.habitList + val groupId = intent.getLongExtra("groupId", -1L) + val habitList = if (groupId > 0) { + habitGroupList.getById(groupId)!!.habitList } else { - habitGroupList.getByUUID(parentUUID)!!.habitList + appComponent.habitList } - val uuid = intent.getStringExtra("habitUUID")!! - habit = habitList.getByUUID(uuid)!! + val id = intent.getLongExtra("habitId", -1L) + habit = habitList.getById(id)!! preferences = appComponent.preferences commandRunner = appComponent.commandRunner widgetUpdater = appComponent.widgetUpdater diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/intents/IntentFactory.kt b/uhabits-android/src/main/java/org/isoron/uhabits/intents/IntentFactory.kt index f446cb041..b6c6b9034 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/intents/IntentFactory.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/intents/IntentFactory.kt @@ -65,8 +65,8 @@ class IntentFactory fun startShowHabitActivity(context: Context, habit: Habit): Intent { val intent = Intent(context, ShowHabitActivity::class.java) - intent.putExtra("habitUUID", habit.uuid) - intent.putExtra("parentUUID", habit.parentUUID) + intent.putExtra("habitId", habit.id) + intent.putExtra("groupId", habit.groupId) return intent } @@ -100,17 +100,17 @@ class IntentFactory fun startEditActivity(context: Context, habit: Habit): Intent { val intent = startEditActivity(context) - intent.putExtra("habitUUID", habit.uuid) + intent.putExtra("habitId", habit.id) intent.putExtra("habitType", habit.type) - intent.putExtra("parentGroupUUID", habit.parentUUID) + intent.putExtra("groupId", habit.groupId) return intent } - fun startEditActivity(context: Context, habitType: Int, parentUUID: String?): Intent { + fun startEditActivity(context: Context, habitType: Int, groupId: Long?): Intent { val intent = startEditActivity(context) intent.putExtra("habitType", habitType) - if (parentUUID != null) { - intent.putExtra("parentGroupUUID", parentUUID) + if (groupId != null) { + intent.putExtra("groupId", groupId) } return intent } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderController.kt b/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderController.kt index 925fe5e7d..e3fd3ee6a 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderController.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/receivers/ReminderController.kt @@ -101,7 +101,13 @@ class ReminderController @Inject constructor( } fun onDismiss(habitGroup: HabitGroup) { - notificationTray.cancel(habitGroup) + if (preferences.shouldMakeNotificationsSticky()) { + // This is a workaround to keep sticky notifications non-dismissible in Android 14+. + // If the notification is dismissed, we immediately reshow it. + notificationTray.reshow(habitGroup) + } else { + notificationTray.cancel(habitGroup) + } } private fun showSnoozeDelayPicker(habit: Habit, context: Context) { diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/commands/EditHabitCommand.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/commands/EditHabitCommand.kt index 733273961..8e168293d 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/commands/EditHabitCommand.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/commands/EditHabitCommand.kt @@ -24,11 +24,11 @@ import org.isoron.uhabits.core.models.HabitNotFoundException data class EditHabitCommand( val habitList: HabitList, - val habitUUID: String, + val habitId: Long, val modified: Habit ) : Command { override fun run() { - val habit = habitList.getByUUID(habitUUID) ?: throw HabitNotFoundException() + val habit = habitList.getById(habitId) ?: throw HabitNotFoundException() habit.copyFrom(modified) habitList.update(habit) habit.observable.notifyListeners() diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/io/LoopDBImporter.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/io/LoopDBImporter.kt index d5bd237a1..0c93c74ad 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/io/LoopDBImporter.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/io/LoopDBImporter.kt @@ -89,7 +89,7 @@ class LoopDBImporter val modified = modelFactory.buildHabit() habitRecord.id = habit.id habitRecord.copyTo(modified) - EditHabitCommand(habitList, habit.uuid!!, modified).run() + EditHabitCommand(habitList, habit.id!!, modified).run() } // Reload saved version of the habit diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/Habit.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/Habit.kt index 25b5320bb..470d75765 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/Habit.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/Habit.kt @@ -40,8 +40,8 @@ data class Habit( val originalEntries: EntryList, val scores: ScoreList, val streaks: StreakList, - var parentID: Long? = null, - var parentUUID: String? = null + var groupId: Long? = null, + var groupUUID: String? = null ) { init { if (uuid == null) this.uuid = UUID.randomUUID().toString().replace("-", "") @@ -56,7 +56,7 @@ data class Habit( val uriString: String get() = "content://org.isoron.uhabits/habit/$id" - fun isSubHabit(): Boolean = parentUUID != null + fun isSubHabit(): Boolean = groupUUID != null fun hasReminder(): Boolean = reminder != null @@ -129,8 +129,8 @@ data class Habit( this.unit = other.unit this.uuid = other.uuid this.parent = other.parent - this.parentID = other.parentID - this.parentUUID = other.parentUUID + this.groupId = other.groupId + this.groupUUID = other.groupUUID } override fun equals(other: Any?): Boolean { @@ -151,8 +151,8 @@ data class Habit( if (type != other.type) return false if (unit != other.unit) return false if (uuid != other.uuid) return false - if (parentID != other.parentID) return false - if (parentUUID != other.parentUUID) return false + if (groupId != other.groupId) return false + if (groupUUID != other.groupUUID) return false return true } @@ -172,8 +172,8 @@ data class Habit( result = 31 * result + type.value result = 31 * result + unit.hashCode() result = 31 * result + (uuid?.hashCode() ?: 0) - result = 31 * result + (parentID?.hashCode() ?: 0) - result = 31 * result + (parentUUID?.hashCode() ?: 0) + result = 31 * result + (groupId?.hashCode() ?: 0) + result = 31 * result + (groupUUID?.hashCode() ?: 0) return result } } diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/HabitGroup.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/HabitGroup.kt index 70f75f736..d08ea3e3a 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/HabitGroup.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/HabitGroup.kt @@ -96,7 +96,7 @@ data class HabitGroup( this.question = other.question this.reminder = other.reminder this.uuid = other.uuid - this.habitList.groupID = this.id + this.habitList.groupId = this.id } override fun equals(other: Any?): Boolean { diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/HabitList.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/HabitList.kt index b3b033903..48ab3cd15 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/HabitList.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/HabitList.kt @@ -34,7 +34,7 @@ abstract class HabitList : Iterable { @JvmField protected val filter: HabitMatcher - var groupID: Long? = null + var groupId: Long? = null /** * Creates a new HabitList. diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/memory/MemoryHabitList.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/memory/MemoryHabitList.kt index 15e6f7ef0..c2d40a3fd 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/memory/MemoryHabitList.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/memory/MemoryHabitList.kt @@ -59,6 +59,7 @@ class MemoryHabitList : HabitList { ) : super(matcher) { this.parent = parent this.comparator = comparator + this.groupId = parent.groupId primaryOrder = parent.primaryOrder secondaryOrder = parent.secondaryOrder parent.observable.addListener { loadFromParent() } diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/sqlite/SQLiteHabitGroupList.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/sqlite/SQLiteHabitGroupList.kt index 34cbab1e4..0cb91d3d6 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/sqlite/SQLiteHabitGroupList.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/sqlite/SQLiteHabitGroupList.kt @@ -40,7 +40,7 @@ class SQLiteHabitGroupList @Inject constructor(private val modelFactory: ModelFa val record = HabitGroupRecord() record.copyFrom(habitGroup) repository.save(record) - habitGroup.habitList.groupID = record.id + habitGroup.habitList.groupId = record.id list.add(habitGroup) observable.notifyListeners() } diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/sqlite/SQLiteHabitList.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/sqlite/SQLiteHabitList.kt index 2fb0b8a61..a70f41147 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/sqlite/SQLiteHabitList.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/sqlite/SQLiteHabitList.kt @@ -37,14 +37,14 @@ class SQLiteHabitList @Inject constructor(private val modelFactory: ModelFactory private fun loadRecords() { if (loaded) return loaded = true - list.groupID = this.groupID + list.groupId = this.groupId list.removeAll() val records = repository.findAll("order by position") for (rec in records) { val h = modelFactory.buildHabit() rec.copyTo(h) (h.originalEntries as SQLiteEntryList).habitId = h.id - if (h.parentID == list.groupID) list.add(h) + if (h.groupId == list.groupId) list.add(h) } } diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/sqlite/records/HabitGroupRecord.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/sqlite/records/HabitGroupRecord.kt index 9f769644e..cd0441fe7 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/sqlite/records/HabitGroupRecord.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/sqlite/records/HabitGroupRecord.kt @@ -79,7 +79,7 @@ class HabitGroupRecord { habitGroup.isArchived = archived != 0 habitGroup.position = position!! habitGroup.uuid = uuid - habitGroup.habitList.groupID = id + habitGroup.habitList.groupId = id if (reminderHour != null && reminderMin != null) { habitGroup.reminder = Reminder( reminderHour!!, diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/sqlite/records/HabitRecord.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/sqlite/records/HabitRecord.kt index 0a61f65f0..c1ce62171 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/sqlite/records/HabitRecord.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/models/sqlite/records/HabitRecord.kt @@ -88,11 +88,11 @@ class HabitRecord { @field:Column var uuid: String? = null - @field:Column(name = "parent_id") - var parentID: Long? = null + @field:Column(name = "group_id") + var groupId: Long? = null - @field:Column(name = "parent_uuid") - var parentUUID: String? = null + @field:Column(name = "group_uuid") + var groupUUID: String? = null fun copyFrom(model: Habit) { id = model.id @@ -108,8 +108,8 @@ class HabitRecord { position = model.position question = model.question uuid = model.uuid - parentID = model.parentID - parentUUID = model.parentUUID + groupId = model.groupId + groupUUID = model.groupUUID val (numerator, denominator) = model.frequency freqNum = numerator freqDen = denominator @@ -138,8 +138,8 @@ class HabitRecord { habit.unit = unit!! habit.position = position!! habit.uuid = uuid - habit.parentID = parentID - habit.parentUUID = parentUUID + habit.groupId = groupId + habit.groupUUID = groupUUID if (reminderHour != null && reminderMin != null) { habit.reminder = Reminder( reminderHour!!, diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/NotificationTray.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/NotificationTray.kt index 0c32b7b7b..25e6bc7cc 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/NotificationTray.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/NotificationTray.kt @@ -124,6 +124,12 @@ class NotificationTray @Inject constructor( } } + fun reshow(habitGroup: HabitGroup) { + activeHabitGroups[habitGroup]?.let { + taskRunner.execute(ShowNotificationTask(habitGroup, it)) + } + } + interface SystemTray { fun removeNotification(notificationId: Int) fun showNotification( diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/list/HabitCardListCache.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/list/HabitCardListCache.kt index ba730984d..e1d723e36 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/list/HabitCardListCache.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/list/HabitCardListCache.kt @@ -76,13 +76,13 @@ class HabitCardListCache @Inject constructor( } @Synchronized - fun getCheckmarks(habitUUID: String): IntArray { - return data.checkmarks[habitUUID]!! + fun getCheckmarks(habitID: Long): IntArray { + return data.checkmarks[habitID]!! } @Synchronized - fun getNotes(habitUUID: String): Array { - return data.notes[habitUUID]!! + fun getNotes(habitID: Long): Array { + return data.notes[habitID]!! } @Synchronized @@ -123,11 +123,11 @@ class HabitCardListCache @Inject constructor( } @Synchronized - fun getUUIDByPosition(position: Int): String? { + fun getIdByPosition(position: Int): Long? { return if (data.positionTypes[position] == STANDALONE_HABIT || data.positionTypes[position] == SUB_HABIT) { - data.positionToHabit[position]!!.uuid + data.positionToHabit[position]!!.id } else { - data.positionToHabitGroup[position]!!.uuid + data.positionToHabitGroup[position]!!.id } } @@ -172,8 +172,8 @@ class HabitCardListCache @Inject constructor( } @Synchronized - fun getScore(uuid: String): Double { - return data.scores[uuid]!! + fun getScore(id: Long): Double { + return data.scores[id]!! } @Synchronized @@ -185,7 +185,7 @@ class HabitCardListCache @Inject constructor( @Synchronized override fun onCommandFinished(command: Command) { if (command is CreateRepetitionCommand) { - command.habit.uuid?.let { refreshHabit(it) } + command.habit.id?.let { refreshHabit(it) } } else { refreshAllHabits() } @@ -205,48 +205,48 @@ class HabitCardListCache @Inject constructor( } @Synchronized - fun refreshHabit(uuid: String) { - taskRunner.execute(RefreshTask(uuid)) + fun refreshHabit(id: Long) { + taskRunner.execute(RefreshTask(id)) } @Synchronized - fun remove(uuid: String) { - val position = data.uuidToPosition[uuid] ?: return + fun remove(id: Long) { + val position = data.idToPosition[id] ?: return val type = data.positionTypes[position] if (type == STANDALONE_HABIT) { - val h = data.uuidToHabit[uuid] + val h = data.idToHabit[id] if (h != null) { data.habits.removeAt(position) - data.removeWithUUID(uuid) + data.removeWithID(id) data.removeWithPos(position) data.decrementPositions(position + 1, data.positionTypes.size) listener.onItemRemoved(position) } } else if (type == SUB_HABIT) { - val h = data.uuidToHabit[uuid] + val h = data.idToHabit[id] if (h != null) { - val hgrUUID = h.parentUUID - val hgr = data.uuidToHabitGroup[hgrUUID] + val hgrID = h.groupId + val hgr = data.idToHabitGroup[hgrID] val hgrIdx = data.habitGroups.indexOf(hgr) data.subHabits[hgrIdx].remove(h) - data.removeWithUUID(uuid) + data.removeWithID(id) data.removeWithPos(position) data.decrementPositions(position + 1, data.positionTypes.size) listener.onItemRemoved(position) } } else if (type == HABIT_GROUP) { - val hgr = data.uuidToHabitGroup[uuid] + val hgr = data.idToHabitGroup[id] if (hgr != null) { val hgrIdx = data.positionIndices[position] for (habit in data.subHabits[hgrIdx].reversed()) { - val habitPos = data.uuidToPosition[habit.uuid]!! - data.removeWithUUID(habit.uuid) + val habitPos = data.idToPosition[habit.id]!! + data.removeWithID(habit.id) listener.onItemRemoved(habitPos) } data.subHabits.removeAt(hgrIdx) data.habitGroups.removeAt(hgrIdx) - data.removeWithUUID(hgr.uuid) + data.removeWithID(hgr.id) data.rebuildPositions() listener.onItemRemoved(position) } @@ -295,29 +295,29 @@ class HabitCardListCache @Inject constructor( } private inner class CacheData { - val uuidToHabit: HashMap = HashMap() - val uuidToHabitGroup: HashMap = HashMap() + val idToHabit: HashMap = HashMap() + val idToHabitGroup: HashMap = HashMap() val habits: MutableList val habitGroups: MutableList val subHabits: MutableList> - val uuidToPosition: HashMap + val idToPosition: HashMap val positionTypes: MutableList val positionIndices: MutableList val positionToHabit: HashMap val positionToHabitGroup: HashMap - val checkmarks: HashMap - val scores: HashMap - val notes: HashMap> + val checkmarks: HashMap + val scores: HashMap + val notes: HashMap> @Synchronized fun copyCheckmarksFrom(oldData: CacheData) { val empty = IntArray(checkmarkCount) - for (uuid in uuidToHabit.keys) { - if (oldData.checkmarks.containsKey(uuid)) { - checkmarks[uuid] = - oldData.checkmarks[uuid]!! + for (id in idToHabit.keys) { + if (oldData.checkmarks.containsKey(id)) { + checkmarks[id] = + oldData.checkmarks[id]!! } else { - checkmarks[uuid] = empty + checkmarks[id] = empty } } } @@ -325,19 +325,19 @@ class HabitCardListCache @Inject constructor( @Synchronized fun copyNoteIndicatorsFrom(oldData: CacheData) { val empty = (0..checkmarkCount).map { "" }.toTypedArray() - for (uuid in uuidToHabit.keys) { - if (oldData.notes.containsKey(uuid)) { - notes[uuid] = - oldData.notes[uuid]!! + for (id in idToHabit.keys) { + if (oldData.notes.containsKey(id)) { + notes[id] = + oldData.notes[id]!! } else { - notes[uuid] = empty + notes[id] = empty } } } @Synchronized fun copyScoresFrom(oldData: CacheData) { - for (uuid in uuidToHabit.keys) { + for (uuid in idToHabit.keys) { if (oldData.scores.containsKey(uuid)) { scores[uuid] = oldData.scores[uuid]!! @@ -345,7 +345,7 @@ class HabitCardListCache @Inject constructor( scores[uuid] = 0.0 } } - for (uuid in uuidToHabitGroup.keys) { + for (uuid in idToHabitGroup.keys) { if (oldData.scores.containsKey(uuid)) { scores[uuid] = oldData.scores[uuid]!! @@ -377,13 +377,13 @@ class HabitCardListCache @Inject constructor( fun rebuildPositions() { positionToHabit.clear() positionToHabitGroup.clear() - uuidToPosition.clear() + idToPosition.clear() positionTypes.clear() positionIndices.clear() var position = 0 for ((idx, h) in habits.withIndex()) { - uuidToHabit[h.uuid] = h - uuidToPosition[h.uuid] = position + idToHabit[h.id] = h + idToPosition[h.id] = position positionToHabit[position] = h positionTypes.add(STANDALONE_HABIT) positionIndices.add(idx) @@ -391,8 +391,8 @@ class HabitCardListCache @Inject constructor( } for ((idx, hgr) in habitGroups.withIndex()) { - uuidToHabitGroup[hgr.uuid] = hgr - uuidToPosition[hgr.uuid] = position + idToHabitGroup[hgr.id] = hgr + idToPosition[hgr.id] = position positionToHabitGroup[position] = hgr positionTypes.add(HABIT_GROUP) positionIndices.add(idx) @@ -400,8 +400,8 @@ class HabitCardListCache @Inject constructor( position++ for ((hIdx, h) in habitList.withIndex()) { - uuidToHabit[h.uuid] = h - uuidToPosition[h.uuid] = position + idToHabit[h.id] = h + idToPosition[h.id] = position positionToHabit[position] = h positionTypes.add(SUB_HABIT) positionIndices.add(hIdx) @@ -412,14 +412,14 @@ class HabitCardListCache @Inject constructor( @Synchronized fun isValidInsert(habit: Habit, position: Int): Boolean { - if (habit.parentUUID == null) { + if (habit.groupId == null) { return position <= habits.size } else { - val parent = uuidToHabitGroup[habit.parentUUID] ?: return false - val parentPosition = uuidToPosition[habit.parentUUID]!! + val parent = idToHabitGroup[habit.groupId] ?: return false + val parentPosition = idToPosition[habit.groupId]!! val parentIndex = habitGroups.indexOf(parent) val nextGroup = habitGroups.getOrNull(parentIndex + 1) - val nextGroupPosition = uuidToPosition[nextGroup?.uuid] + val nextGroupPosition = idToPosition[nextGroup?.id] return (position > parentPosition && position <= positionTypes.size) && (nextGroupPosition == null || position <= nextGroupPosition) } } @@ -443,9 +443,9 @@ class HabitCardListCache @Inject constructor( positionToHabitGroup.remove(pos) } } - for ((key, pos) in uuidToPosition.entries) { + for ((key, pos) in idToPosition.entries) { if (pos in from..to) { - uuidToPosition[key] = pos + 1 + idToPosition[key] = pos + 1 } } } @@ -464,9 +464,9 @@ class HabitCardListCache @Inject constructor( positionToHabitGroup.remove(pos) } } - for ((key, pos) in uuidToPosition.entries) { + for ((key, pos) in idToPosition.entries) { if (pos in fromPosition..toPosition) { - uuidToPosition[key] = pos - 1 + idToPosition[key] = pos - 1 } } } @@ -503,7 +503,7 @@ class HabitCardListCache @Inject constructor( positionTypes.add(checkedToPosition, STANDALONE_HABIT) positionIndices.add(checkedToPosition, checkedToPosition) } else { - val hgr = uuidToHabitGroup[habit.parentUUID] + val hgr = idToHabitGroup[habit.groupId] val hgrIdx = habitGroups.indexOf(hgr) val fromIdx = positionIndices[fromPosition] subHabits[hgrIdx].removeAt(fromIdx) @@ -513,14 +513,14 @@ class HabitCardListCache @Inject constructor( } else { incrementPositions(checkedToPosition, fromPosition - 1) } - val toIdx = checkedToPosition - uuidToPosition[hgr!!.uuid]!! - 1 + val toIdx = checkedToPosition - idToPosition[hgr!!.id]!! - 1 subHabits[hgrIdx].add(toIdx, habit) positionTypes.add(checkedToPosition, SUB_HABIT) positionIndices.add(checkedToPosition, toIdx) } positionToHabit[checkedToPosition] = habit - uuidToPosition[habit.uuid] = checkedToPosition + idToPosition[habit.id] = checkedToPosition listener.onItemMoved(fromPosition, checkedToPosition) } @@ -546,13 +546,13 @@ class HabitCardListCache @Inject constructor( listener.onItemMoved(fromPosition, toPosition) } - fun removeWithUUID(uuid: String?) { - uuidToPosition.remove(uuid) - uuidToHabit.remove(uuid) - uuidToHabitGroup.remove(uuid) - scores.remove(uuid) - notes.remove(uuid) - checkmarks.remove(uuid) + fun removeWithID(id: Long?) { + idToPosition.remove(id) + idToHabit.remove(id) + idToHabitGroup.remove(id) + scores.remove(id) + notes.remove(id) + checkmarks.remove(id) } fun removeWithPos(pos: Int) { @@ -570,7 +570,7 @@ class HabitCardListCache @Inject constructor( subHabits = LinkedList() positionTypes = LinkedList() positionIndices = LinkedList() - uuidToPosition = HashMap() + idToPosition = HashMap() positionToHabit = HashMap() positionToHabitGroup = HashMap() checkmarks = HashMap() @@ -581,19 +581,19 @@ class HabitCardListCache @Inject constructor( private inner class RefreshTask : Task { private val newData: CacheData - private val targetUUID: String? + private val targetID: Long? private var isCancelled = false private var runner: TaskRunner? = null constructor() { newData = CacheData() - targetUUID = null + targetID = null isCancelled = false } - constructor(targetUUID: String) { + constructor(targetID: Long) { newData = CacheData() - this.targetUUID = targetUUID + this.targetID = targetID } @Synchronized @@ -615,8 +615,8 @@ class HabitCardListCache @Inject constructor( if (isCancelled) return if (type == STANDALONE_HABIT || type == SUB_HABIT) { val habit = newData.positionToHabit[position]!! - if (targetUUID != null && targetUUID != habit.uuid) continue - newData.scores[habit.uuid] = habit.scores[today].value + if (targetID != null && targetID != habit.id) continue + newData.scores[habit.id] = habit.scores[today].value val list: MutableList = ArrayList() val notes: MutableList = ArrayList() for ((_, value, note) in habit.computedEntries.getByInterval(dateFrom, today)) { @@ -624,13 +624,13 @@ class HabitCardListCache @Inject constructor( notes.add(note) } val entries = list.toTypedArray() - newData.checkmarks[habit.uuid] = ArrayUtils.toPrimitive(entries) - newData.notes[habit.uuid] = notes.toTypedArray() + newData.checkmarks[habit.id] = ArrayUtils.toPrimitive(entries) + newData.notes[habit.id] = notes.toTypedArray() runner!!.publishProgress(this, position) } else if (type == HABIT_GROUP) { val habitGroup = newData.positionToHabitGroup[position]!! - if (targetUUID != null && targetUUID != habitGroup.uuid) continue - newData.scores[habitGroup.uuid] = habitGroup.scores[today].value + if (targetID != null && targetID != habitGroup.id) continue + newData.scores[habitGroup.id] = habitGroup.scores[today].value runner!!.publishProgress(this, position) } } @@ -655,13 +655,13 @@ class HabitCardListCache @Inject constructor( @Synchronized private fun performInsert(habit: Habit, position: Int) { if (!data.isValidInsert(habit, position)) return - val uuid = habit.uuid - if (habit.parentUUID == null) { + val id = habit.id + if (habit.groupId == null) { data.habits.add(position, habit) data.positionTypes.add(position, STANDALONE_HABIT) data.positionIndices.add(position, position) } else { - val hgrPos = data.uuidToPosition[habit.parentUUID]!! + val hgrPos = data.idToPosition[habit.groupId]!! val hgrIdx = data.positionIndices[hgrPos] val habitIndex = newData.positionIndices[position] data.subHabits[hgrIdx].add(habitIndex, habit) @@ -670,18 +670,18 @@ class HabitCardListCache @Inject constructor( } data.incrementPositions(position, data.positionTypes.size - 1) data.positionToHabit[position] = habit - data.uuidToPosition[uuid] = position - data.uuidToHabit[uuid] = habit - data.scores[uuid] = newData.scores[uuid]!! - data.checkmarks[uuid] = newData.checkmarks[uuid]!! - data.notes[uuid] = newData.notes[uuid]!! + data.idToPosition[id] = position + data.idToHabit[id] = habit + data.scores[id] = newData.scores[id]!! + data.checkmarks[id] = newData.checkmarks[id]!! + data.notes[id] = newData.notes[id]!! listener.onItemInserted(position) } @Synchronized private fun performInsert(habitGroup: HabitGroup, position: Int) { if (!data.isValidInsert(habitGroup, position)) return - val uuid = habitGroup.uuid + val id = habitGroup.id val prevIdx = newData.positionIndices[position] val habitList = newData.subHabits[prevIdx] val idx = if (data.positionIndices.size > position) { @@ -692,37 +692,37 @@ class HabitCardListCache @Inject constructor( data.habitGroups.add(idx, habitGroup) data.subHabits.add(prevIdx, habitList) - data.scores[uuid] = newData.scores[uuid]!! + data.scores[id] = newData.scores[id]!! for (h in habitList) { - data.scores[h.uuid] = newData.scores[h.uuid]!! - data.checkmarks[h.uuid] = newData.checkmarks[h.uuid]!! - data.notes[h.uuid] = newData.notes[h.uuid]!! + data.scores[h.id] = newData.scores[h.id]!! + data.checkmarks[h.id] = newData.checkmarks[h.id]!! + data.notes[h.id] = newData.notes[h.id]!! } data.rebuildPositions() listener.onItemInserted(position) } @Synchronized - private fun performUpdate(uuid: String, position: Int) { + private fun performUpdate(id: Long, position: Int) { var unchanged = true - val oldScore = data.scores[uuid]!! - val newScore = newData.scores[uuid]!! + val oldScore = data.scores[id]!! + val newScore = newData.scores[id]!! if (oldScore != newScore) unchanged = false if (data.positionTypes[position] != HABIT_GROUP) { - val oldCheckmarks = data.checkmarks[uuid] - val newCheckmarks = newData.checkmarks[uuid]!! - val oldNoteIndicators = data.notes[uuid] - val newNoteIndicators = newData.notes[uuid]!! + val oldCheckmarks = data.checkmarks[id] + val newCheckmarks = newData.checkmarks[id]!! + val oldNoteIndicators = data.notes[id] + val newNoteIndicators = newData.notes[id]!! if (!Arrays.equals(oldCheckmarks, newCheckmarks)) unchanged = false if (!Arrays.equals(oldNoteIndicators, newNoteIndicators)) unchanged = false if (unchanged) return - data.checkmarks[uuid] = newCheckmarks - data.notes[uuid] = newNoteIndicators + data.checkmarks[id] = newCheckmarks + data.notes[id] = newNoteIndicators } if (unchanged) return - data.scores[uuid] = newScore + data.scores[id] = newScore listener.onItemChanged(position) } @@ -732,12 +732,12 @@ class HabitCardListCache @Inject constructor( if (type == STANDALONE_HABIT || type == SUB_HABIT) { val habit = newData.positionToHabit[currentPosition]!! - val uuid = habit.uuid ?: throw NullPointerException() - val prevPosition = data.uuidToPosition[uuid] ?: -1 + val id = habit.id ?: throw NullPointerException() + val prevPosition = data.idToPosition[id] ?: -1 val newPosition = if (type == STANDALONE_HABIT) { currentPosition } else { - val hgrPos = data.uuidToPosition[habit.parentUUID]!! + val hgrPos = data.idToPosition[habit.groupId]!! val hgrIdx = data.positionIndices[hgrPos] newData.subHabits[hgrIdx].indexOf(habit) + hgrPos + 1 } @@ -751,12 +751,12 @@ class HabitCardListCache @Inject constructor( newPosition ) } - performUpdate(uuid, currentPosition) + performUpdate(id, currentPosition) } } else if (type == HABIT_GROUP) { val habitGroup = newData.positionToHabitGroup[currentPosition]!! - val uuid = habitGroup.uuid ?: throw NullPointerException() - val prevPosition = data.uuidToPosition[uuid] ?: -1 + val id = habitGroup.id ?: throw NullPointerException() + val prevPosition = data.idToPosition[id] ?: -1 if (prevPosition < 0) { performInsert(habitGroup, currentPosition) } else { @@ -767,18 +767,18 @@ class HabitCardListCache @Inject constructor( currentPosition ) } - performUpdate(uuid, currentPosition) + performUpdate(id, currentPosition) } } } @Synchronized private fun processRemovedHabits() { - val before: Set = (data.uuidToHabit.keys).union(data.uuidToHabitGroup.keys) - val after: Set = (newData.uuidToHabit.keys).union(newData.uuidToHabitGroup.keys) - val removed: MutableSet = TreeSet(before) + val before: Set = (data.idToHabit.keys).union(data.idToHabitGroup.keys) + val after: Set = (newData.idToHabit.keys).union(newData.idToHabitGroup.keys) + val removed: MutableSet = TreeSet(before) removed.removeAll(after) - for (uuid in removed.sortedBy { uuid -> data.uuidToPosition[uuid] }) remove(uuid!!) + for (id in removed.sortedBy { data.idToPosition[it] }) remove(id!!) } } diff --git a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsMenuBehavior.kt b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsMenuBehavior.kt index b3b08751b..4f7101439 100644 --- a/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsMenuBehavior.kt +++ b/uhabits-core/src/jvmMain/java/org/isoron/uhabits/core/ui/screens/habits/list/ListHabitsMenuBehavior.kt @@ -33,8 +33,8 @@ class ListHabitsMenuBehavior @Inject constructor( private var showCompleted: Boolean private var showArchived: Boolean - fun onCreateHabit(parentUUID: String? = null) { - screen.showSelectHabitTypeDialog(parentUUID) + fun onCreateHabit(groupId: Long? = null) { + screen.showSelectHabitTypeDialog(groupId) } fun onViewFAQ() { @@ -132,7 +132,7 @@ class ListHabitsMenuBehavior @Inject constructor( fun showAboutScreen() fun showFAQScreen() fun showSettingsScreen() - fun showSelectHabitTypeDialog(parentUUID: String? = null) + fun showSelectHabitTypeDialog(groupId: Long? = null) } init { diff --git a/uhabits-core/src/jvmMain/resources/migrations/26.sql b/uhabits-core/src/jvmMain/resources/migrations/26.sql index c8bd3f2b2..4548aa4b9 100644 --- a/uhabits-core/src/jvmMain/resources/migrations/26.sql +++ b/uhabits-core/src/jvmMain/resources/migrations/26.sql @@ -49,5 +49,5 @@ create table HabitGroups ( uuid text ); -alter table Habits add column parent_uuid text references habitgroups(uuid); -alter table Habits add column parent_id integer references habitgroups(id); \ No newline at end of file +alter table Habits add column group_uuid text references habitgroups(uuid); +alter table Habits add column group_id integer references habitgroups(id); \ No newline at end of file