From d2d9f74372716c70d4a9f7814f9ee31044042171 Mon Sep 17 00:00:00 2001 From: olegivo Date: Mon, 28 Oct 2019 11:28:29 +0300 Subject: [PATCH] convert Habit to kotlin #300 --- .../automation/EditSettingController.kt | 4 +- .../uhabits/intents/PendingIntentFactory.kt | 2 +- .../org/isoron/uhabits/core/models/Habit.java | 484 ------------------ .../org/isoron/uhabits/core/models/Habit.kt | 372 ++++++++++++++ 4 files changed, 375 insertions(+), 487 deletions(-) delete mode 100644 android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Habit.java create mode 100644 android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Habit.kt diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/EditSettingController.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/EditSettingController.kt index 2fdd01250..678f4ccab 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/EditSettingController.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/automation/EditSettingController.kt @@ -29,13 +29,13 @@ import org.isoron.uhabits.core.models.* class EditSettingController(private val activity: Activity) { fun onSave(habit: Habit, action: Int) { - if (habit.getId() == null) return + if (habit.id == null) return val actionName = getActionName(action) val blurb = String.format("%s: %s", actionName, habit.name) val bundle = Bundle() bundle.putInt("action", action) - bundle.putLong("habit", habit.getId()!!) + bundle.putLong("habit", habit.id!!) activity.setResult(Activity.RESULT_OK, Intent().apply { putExtra(EXTRA_STRING_BLURB, blurb) diff --git a/android/uhabits-android/src/main/java/org/isoron/uhabits/intents/PendingIntentFactory.kt b/android/uhabits-android/src/main/java/org/isoron/uhabits/intents/PendingIntentFactory.kt index 860e2f735..3f52d6654 100644 --- a/android/uhabits-android/src/main/java/org/isoron/uhabits/intents/PendingIntentFactory.kt +++ b/android/uhabits-android/src/main/java/org/isoron/uhabits/intents/PendingIntentFactory.kt @@ -76,7 +76,7 @@ class PendingIntentFactory timestamp: Long): PendingIntent = PendingIntent.getBroadcast( context, - (habit.getId()!! % Integer.MAX_VALUE).toInt() + 1, + (habit.id!! % Integer.MAX_VALUE).toInt() + 1, Intent(context, ReminderReceiver::class.java).apply { action = ReminderReceiver.ACTION_SHOW_REMINDER data = Uri.parse(habit.uriString) diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Habit.java b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Habit.java deleted file mode 100644 index 69e41bead..000000000 --- a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Habit.java +++ /dev/null @@ -1,484 +0,0 @@ -/* - * 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.core.models; - -import android.support.annotation.*; - -import org.apache.commons.lang3.builder.*; - -import java.util.*; - -import javax.annotation.concurrent.*; -import javax.inject.*; - -import static org.isoron.uhabits.core.models.Checkmark.*; -import static org.isoron.uhabits.core.utils.StringUtils.defaultToStringStyle; - -/** - * The thing that the user wants to track. - */ -@ThreadSafe -public class Habit -{ - public static final int AT_LEAST = 0; - - public static final int AT_MOST = 1; - - public static final String HABIT_URI_FORMAT = - "content://org.isoron.uhabits/habit/%d"; - - public static final int NUMBER_HABIT = 1; - - public static final int YES_NO_HABIT = 0; - - @Nullable - public Long id; - - @NonNull - private HabitData data; - - @NonNull - private StreakList streaks; - - @NonNull - private ScoreList scores; - - @NonNull - private RepetitionList repetitions; - - @NonNull - private CheckmarkList checkmarks; - - private ModelObservable observable = new ModelObservable(); - - /** - * Constructs a habit with default data. - *

- * The habit is not archived, not highlighted, has no reminders and is - * placed in the last position of the list of habits. - */ - @Inject - Habit(@NonNull ModelFactory factory) - { - this.data = new HabitData(); - checkmarks = factory.buildCheckmarkList(this); - streaks = factory.buildStreakList(this); - scores = factory.buildScoreList(this); - repetitions = factory.buildRepetitionList(this); - } - - Habit(@NonNull ModelFactory factory, @NonNull HabitData data) - { - this.data = new HabitData(data); - checkmarks = factory.buildCheckmarkList(this); - streaks = factory.buildStreakList(this); - scores = factory.buildScoreList(this); - repetitions = factory.buildRepetitionList(this); - observable = new ModelObservable(); - } - - /** - * Clears the reminder for a habit. - */ - public synchronized void clearReminder() - { - data.reminder = null; - observable.notifyListeners(); - } - - /** - * Copies all the attributes of the specified habit into this habit - * - * @param model the model whose attributes should be copied from - */ - public synchronized void copyFrom(@NonNull Habit model) - { - this.data = new HabitData(model.data); - observable.notifyListeners(); - } - - /** - * List of checkmarks belonging to this habit. - */ - @NonNull - public synchronized CheckmarkList getCheckmarks() - { - return checkmarks; - } - - /** - * Color of the habit. - *

- * This number is not an android.graphics.Color, but an index to the - * activity color palette, which changes according to the theme. To convert - * this color into an android.graphics.Color, use ColorHelper.getColor(context, - * habit.color). - */ - @NonNull - public synchronized Integer getColor() - { - return data.color; - } - - public synchronized void setColor(@NonNull Integer color) - { - data.color = color; - } - - @NonNull - public synchronized String getDescription() - { - return data.description; - } - - public synchronized void setDescription(@NonNull String description) - { - data.description = description; - } - - @NonNull - public synchronized Frequency getFrequency() - { - return data.frequency; - } - - public synchronized void setFrequency(@NonNull Frequency frequency) - { - data.frequency = frequency; - } - - @Nullable - public synchronized Long getId() - { - return id; - } - - public synchronized void setId(@Nullable Long id) - { - this.id = id; - } - - @NonNull - public synchronized String getName() - { - return data.name; - } - - public synchronized void setName(@NonNull String name) - { - data.name = name; - } - - public ModelObservable getObservable() - { - return observable; - } - - /** - * Returns the reminder for this habit. - *

- * Before calling this method, you should call {@link #hasReminder()} to - * verify that a reminder does exist, otherwise an exception will be - * thrown. - * - * @return the reminder for this habit - * @throws IllegalStateException if habit has no reminder - */ - @NonNull - public synchronized Reminder getReminder() - { - if (data.reminder == null) throw new IllegalStateException(); - return data.reminder; - } - - public synchronized void setReminder(@Nullable Reminder reminder) - { - data.reminder = reminder; - } - - @NonNull - public RepetitionList getRepetitions() - { - return repetitions; - } - - @NonNull - public ScoreList getScores() - { - return scores; - } - - @NonNull - public StreakList getStreaks() - { - return streaks; - } - - public synchronized int getTargetType() - { - return data.targetType; - } - - public synchronized void setTargetType(int targetType) - { - if (targetType != AT_LEAST && targetType != AT_MOST) - throw new IllegalArgumentException( - String.format("invalid targetType: %d", targetType)); - data.targetType = targetType; - } - - public synchronized double getTargetValue() - { - return data.targetValue; - } - - public synchronized void setTargetValue(double targetValue) - { - if (targetValue < 0) throw new IllegalArgumentException(); - data.targetValue = targetValue; - } - - public synchronized int getType() - { - return data.type; - } - - public synchronized void setType(int type) - { - if (type != YES_NO_HABIT && type != NUMBER_HABIT) - throw new IllegalArgumentException(); - - data.type = type; - } - - @NonNull - public synchronized String getUnit() - { - return data.unit; - } - - public synchronized void setUnit(@NonNull String unit) - { - data.unit = unit; - } - - /** - * Returns the public URI that identifies this habit - * - * @return the URI - */ - public String getUriString() - { - return String.format(Locale.US, HABIT_URI_FORMAT, getId()); - } - - public synchronized boolean hasId() - { - return getId() != null; - } - - /** - * Returns whether the habit has a reminder. - * - * @return true if habit has reminder, false otherwise - */ - public synchronized boolean hasReminder() - { - return data.reminder != null; - } - - public void invalidateNewerThan(Timestamp timestamp) - { - getScores().invalidateNewerThan(timestamp); - getCheckmarks().invalidateNewerThan(timestamp); - getStreaks().invalidateNewerThan(timestamp); - } - - public synchronized boolean isArchived() - { - return data.archived; - } - - public synchronized void setArchived(boolean archived) - { - data.archived = archived; - } - - public synchronized boolean isCompletedToday() - { - int todayCheckmark = getCheckmarks().getTodayValue(); - if (isNumerical()) - { - if(getTargetType() == AT_LEAST) - return todayCheckmark >= data.targetValue; - else - return todayCheckmark <= data.targetValue; - } - else return (todayCheckmark != UNCHECKED); - } - - public synchronized boolean isNumerical() - { - return data.type == NUMBER_HABIT; - } - - public HabitData getData() - { - return new HabitData(data); - } - - public Integer getPosition() - { - return data.position; - } - - public void setPosition(int newPosition) - { - data.position = newPosition; - } - - public static final class HabitData - { - @NonNull - public String name; - - @NonNull - public String description; - - @NonNull - public Frequency frequency; - - public int color; - - public boolean archived; - - public int targetType; - - public double targetValue; - - public int type; - - @NonNull - public String unit; - - @Nullable - public Reminder reminder; - - public int position; - - public HabitData() - { - this.color = 8; - this.archived = false; - this.frequency = new Frequency(3, 7); - this.type = YES_NO_HABIT; - this.name = ""; - this.description = ""; - this.targetType = AT_LEAST; - this.targetValue = 100; - this.unit = ""; - this.position = 0; - } - - public HabitData(@NonNull HabitData model) - { - this.name = model.name; - this.description = model.description; - this.frequency = model.frequency; - this.color = model.color; - this.archived = model.archived; - this.targetType = model.targetType; - this.targetValue = model.targetValue; - this.type = model.type; - this.unit = model.unit; - this.reminder = model.reminder; - this.position = model.position; - } - - @Override - public String toString() - { - return new ToStringBuilder(this, defaultToStringStyle()) - .append("name", name) - .append("description", description) - .append("frequency", frequency) - .append("color", color) - .append("archived", archived) - .append("targetType", targetType) - .append("targetValue", targetValue) - .append("type", type) - .append("unit", unit) - .append("reminder", reminder) - .append("position", position) - .toString(); - } - - @Override - public boolean equals(Object o) - { - if (this == o) return true; - - if (o == null || getClass() != o.getClass()) return false; - - HabitData habitData = (HabitData) o; - - return new EqualsBuilder() - .append(color, habitData.color) - .append(archived, habitData.archived) - .append(targetType, habitData.targetType) - .append(targetValue, habitData.targetValue) - .append(type, habitData.type) - .append(name, habitData.name) - .append(description, habitData.description) - .append(frequency, habitData.frequency) - .append(unit, habitData.unit) - .append(reminder, habitData.reminder) - .append(position, habitData.position) - .isEquals(); - } - - @Override - public int hashCode() - { - return new HashCodeBuilder(17, 37) - .append(name) - .append(description) - .append(frequency) - .append(color) - .append(archived) - .append(targetType) - .append(targetValue) - .append(type) - .append(unit) - .append(reminder) - .append(position) - .toHashCode(); - } - } - - @Override - public String toString() - { - return new ToStringBuilder(this, defaultToStringStyle()) - .append("id", id) - .append("data", data) - .toString(); - } -} diff --git a/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Habit.kt b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Habit.kt new file mode 100644 index 000000000..380443924 --- /dev/null +++ b/android/uhabits-core/src/main/java/org/isoron/uhabits/core/models/Habit.kt @@ -0,0 +1,372 @@ +/* + * 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.core.models + +import org.apache.commons.lang3.builder.EqualsBuilder +import org.apache.commons.lang3.builder.HashCodeBuilder +import org.apache.commons.lang3.builder.ToStringBuilder +import org.isoron.uhabits.core.models.Checkmark.UNCHECKED +import org.isoron.uhabits.core.utils.StringUtils.defaultToStringStyle +import java.util.* +import javax.annotation.concurrent.ThreadSafe +import javax.inject.Inject + +/** + * The thing that the user wants to track. + */ +@ThreadSafe +class Habit { + + @get:Synchronized + @set:Synchronized + var id: Long? = null + + private var data: HabitData + + var streaks: StreakList + private set + + var scores: ScoreList + private set + + var repetitions: RepetitionList + private set + + /** + * List of checkmarks belonging to this habit. + */ + @get:Synchronized + var checkmarks: CheckmarkList + private set + + var observable = ModelObservable() + + /** + * Color of the habit. + * + * + * This number is not an android.graphics.Color, but an index to the + * activity color palette, which changes according to the theme. To convert + * this color into an android.graphics.Color, use ColorHelper.getColor(context, + * habit.color). + */ + var color: Int + @Synchronized get() = data.color + @Synchronized set(color) { + data.color = color + } + + var description: String + @Synchronized get() = data.description + @Synchronized set(description) { + data.description = description + } + + var frequency: Frequency + @Synchronized get() = data.frequency + @Synchronized set(frequency) { + data.frequency = frequency + } + + var name: String + @Synchronized get() = data.name + @Synchronized set(name) { + data.name = name + } + + /** + * Returns the reminder for this habit. + * + * + * Before calling this method, you should call [.hasReminder] to + * verify that a reminder does exist, otherwise an exception will be + * thrown. + * + * @return the reminder for this habit + * @throws IllegalStateException if habit has no reminder + */ + var reminder: Reminder? + @Synchronized get() { + checkNotNull(data.reminder) + return data.reminder + } + @Synchronized set(reminder) { + data.reminder = reminder + } + + var targetType: Int + @Synchronized get() = data.targetType + @Synchronized set(targetType) { + require(!(targetType != AT_LEAST && targetType != AT_MOST)) { + String.format("invalid targetType: %d", targetType) + } + data.targetType = targetType + } + + var targetValue: Double + @Synchronized get() = data.targetValue + @Synchronized set(targetValue) { + require(targetValue >= 0) + data.targetValue = targetValue + } + + var type: Int + @Synchronized get() = data.type + @Synchronized set(type) { + require(!(type != YES_NO_HABIT && type != NUMBER_HABIT)) + data.type = type + } + + var unit: String + @Synchronized get() = data.unit + @Synchronized set(unit) { + data.unit = unit + } + + /** + * Returns the public URI that identifies this habit + * + * @return the URI + */ + val uriString: String + get() = String.format(Locale.US, HABIT_URI_FORMAT, id) + + var isArchived: Boolean + @Synchronized get() = data.archived + @Synchronized set(archived) { + data.archived = archived + } + + val isCompletedToday: Boolean + @Synchronized get() { + val todayCheckmark = checkmarks.todayValue + return if (isNumerical) { + if (targetType == AT_LEAST) + todayCheckmark >= data.targetValue + else + todayCheckmark <= data.targetValue + } else + todayCheckmark != UNCHECKED + } + + val isNumerical: Boolean + @Synchronized get() = data.type == NUMBER_HABIT + + val position: Int? + get() = data.position + + /** + * Constructs a habit with default data. + * + * + * The habit is not archived, not highlighted, has no reminders and is + * placed in the last position of the list of habits. + */ + @Inject + internal constructor(factory: ModelFactory) { + this.data = HabitData() + checkmarks = factory.buildCheckmarkList(this) + streaks = factory.buildStreakList(this) + scores = factory.buildScoreList(this) + repetitions = factory.buildRepetitionList(this) + } + + internal constructor(factory: ModelFactory, data: HabitData) { + this.data = HabitData(data) + checkmarks = factory.buildCheckmarkList(this) + streaks = factory.buildStreakList(this) + scores = factory.buildScoreList(this) + repetitions = factory.buildRepetitionList(this) + observable = ModelObservable() + } + + /** + * Clears the reminder for a habit. + */ + @Synchronized + fun clearReminder() { + data.reminder = null + observable.notifyListeners() + } + + /** + * Copies all the attributes of the specified habit into this habit + * + * @param model the model whose attributes should be copied from + */ + @Synchronized + fun copyFrom(model: Habit) { + this.data = HabitData(model.data) + observable.notifyListeners() + } + + @Synchronized + fun hasId(): Boolean { + return id != null + } + + /** + * Returns whether the habit has a reminder. + * + * @return true if habit has reminder, false otherwise + */ + @Synchronized + fun hasReminder(): Boolean { + return data.reminder != null + } + + fun invalidateNewerThan(timestamp: Timestamp) { + scores.invalidateNewerThan(timestamp) + checkmarks.invalidateNewerThan(timestamp) + streaks.invalidateNewerThan(timestamp) + } + + fun getData(): HabitData { + return HabitData(data) + } + + fun setPosition(newPosition: Int) { + data.position = newPosition + } + + class HabitData { + var name: String + + var description: String + + var frequency: Frequency + + var color: Int = 0 + + var archived: Boolean = false + + var targetType: Int = 0 + + var targetValue: Double = 0.toDouble() + + var type: Int = 0 + + var unit: String + + var reminder: Reminder? = null + + var position: Int = 0 + + constructor() { + this.color = 8 + this.archived = false + this.frequency = Frequency(3, 7) + this.type = YES_NO_HABIT + this.name = "" + this.description = "" + this.targetType = AT_LEAST + this.targetValue = 100.0 + this.unit = "" + this.position = 0 + } + + constructor(model: HabitData) { + this.name = model.name + this.description = model.description + this.frequency = model.frequency + this.color = model.color + this.archived = model.archived + this.targetType = model.targetType + this.targetValue = model.targetValue + this.type = model.type + this.unit = model.unit + this.reminder = model.reminder + this.position = model.position + } + + override fun toString(): String { + return ToStringBuilder(this, defaultToStringStyle()) + .append("name", name) + .append("description", description) + .append("frequency", frequency) + .append("color", color) + .append("archived", archived) + .append("targetType", targetType) + .append("targetValue", targetValue) + .append("type", type) + .append("unit", unit) + .append("reminder", reminder) + .append("position", position) + .toString() + } + + override fun equals(o: Any?): Boolean { + if (this === o) return true + + if (o == null || javaClass != o.javaClass) return false + + val habitData = o as HabitData? + + return EqualsBuilder() + .append(color, habitData!!.color) + .append(archived, habitData.archived) + .append(targetType, habitData.targetType) + .append(targetValue, habitData.targetValue) + .append(type, habitData.type) + .append(name, habitData.name) + .append(description, habitData.description) + .append(frequency, habitData.frequency) + .append(unit, habitData.unit) + .append(reminder, habitData.reminder) + .append(position, habitData.position) + .isEquals + } + + override fun hashCode(): Int { + return HashCodeBuilder(17, 37) + .append(name) + .append(description) + .append(frequency) + .append(color) + .append(archived) + .append(targetType) + .append(targetValue) + .append(type) + .append(unit) + .append(reminder) + .append(position) + .toHashCode() + } + } + + override fun toString(): String { + return ToStringBuilder(this, defaultToStringStyle()) + .append("id", id) + .append("data", data) + .toString() + } + + companion object { + const val AT_LEAST = 0 + + const val AT_MOST = 1 + + const val HABIT_URI_FORMAT = "content://org.isoron.uhabits/habit/%d" + + const val NUMBER_HABIT = 1 + + const val YES_NO_HABIT = 0 + } +}