From b844b9283e80d13fc9a40b627345eac19333fdbb Mon Sep 17 00:00:00 2001 From: bruh Date: Thu, 20 Oct 2022 01:16:17 +0700 Subject: [PATCH] Add a Stop button to the addiction card. It should have the functionality as described in upstream issue #25. --- .../imdefinitelysober/Addiction.kt | 21 ++++++++++++---- .../imdefinitelysober/AddictionCardAdapter.kt | 24 +++++++++++++++---- .../imdefinitelysober/activities/Main.kt | 23 ++++++++++++++++-- .../main/res/drawable/ic_baseline_stop_24.xml | 9 +++++++ app/src/main/res/layout/card_addiction.xml | 13 ++++++++++ app/src/main/res/values/strings.xml | 4 ++++ 6 files changed, 84 insertions(+), 10 deletions(-) create mode 100644 app/src/main/res/drawable/ic_baseline_stop_24.xml diff --git a/app/src/main/java/com/sixtyninefourtwenty/imdefinitelysober/Addiction.kt b/app/src/main/java/com/sixtyninefourtwenty/imdefinitelysober/Addiction.kt index a581943..e0808b2 100644 --- a/app/src/main/java/com/sixtyninefourtwenty/imdefinitelysober/Addiction.kt +++ b/app/src/main/java/com/sixtyninefourtwenty/imdefinitelysober/Addiction.kt @@ -8,13 +8,18 @@ import java.time.Instant class Addiction( val name: String, var lastRelapse: Instant, - var relapses: CircularBuffer = CircularBuffer(3) //Default is a new one, but you can provide your own (from a cache) + var isStopped: Boolean, + var timeStopped: Long, //in milliseconds + private val relapses: CircularBuffer = CircularBuffer(3) //Default is a new one, but you can provide your own (from a cache) ) : Serializable { var averageRelapseDuration = if (relapses.get(0) == null) -1 else calculateAverageRelapseDuration() private set fun relapse() { - relapses.update(lastRelapse.secondsFromNow()) + if (!isStopped) + relapses.update(lastRelapse.secondsFromNow()) + else + relapses.update(Instant.ofEpochMilli(timeStopped).epochSecond - lastRelapse.epochSecond) averageRelapseDuration = calculateAverageRelapseDuration() lastRelapse = Instant.now() } @@ -27,13 +32,21 @@ class Addiction( val map = HashMap() map[0] = name map[1] = lastRelapse - map[2] = relapses + map[2] = isStopped + map[3] = timeStopped + map[4] = relapses return map } companion object { fun fromCacheable(map: HashMap): Addiction { - return Addiction(map[0] as String, map[1] as Instant, map[2] as CircularBuffer) + return Addiction( + map[0] as String, + map[1] as Instant, + map[2] as Boolean, + map[3] as Long, + map[4] as CircularBuffer + ) } } } \ No newline at end of file diff --git a/app/src/main/java/com/sixtyninefourtwenty/imdefinitelysober/AddictionCardAdapter.kt b/app/src/main/java/com/sixtyninefourtwenty/imdefinitelysober/AddictionCardAdapter.kt index cbf6ab4..1aa5dca 100644 --- a/app/src/main/java/com/sixtyninefourtwenty/imdefinitelysober/AddictionCardAdapter.kt +++ b/app/src/main/java/com/sixtyninefourtwenty/imdefinitelysober/AddictionCardAdapter.kt @@ -11,12 +11,16 @@ import androidx.recyclerview.widget.RecyclerView import com.sixtyninefourtwenty.imdefinitelysober.activities.Main import com.sixtyninefourtwenty.imdefinitelysober.utils.convertSecondsToString import com.sixtyninefourtwenty.imdefinitelysober.utils.secondsFromNow +import java.text.DateFormat +import java.util.* class AddictionCardAdapter(private val context: Context) : RecyclerView.Adapter() { private lateinit var onButtonDeleteClickListener: OnClickListener private lateinit var onButtonRelapseClickListener: OnClickListener + private lateinit var onButtonStopClickListener: OnClickListener + private val dateFormat = DateFormat.getDateTimeInstance() fun setOnButtonDeleteClickListener(onButtonDeleteClickListener: OnClickListener) { this.onButtonDeleteClickListener = onButtonDeleteClickListener @@ -26,13 +30,18 @@ class AddictionCardAdapter(private val context: Context) : this.onButtonRelapseClickListener = onButtonRelapseClickListener } + fun setOnButtonStopClickListener(onButtonStopClickListener: OnClickListener) { + this.onButtonStopClickListener = onButtonStopClickListener + } + override fun onCreateViewHolder( parent: ViewGroup, viewType: Int ): AddictionCardViewHolder { val itemView = LayoutInflater.from(parent.context).inflate(R.layout.card_addiction, parent, false) - return AddictionCardViewHolder(itemView, onButtonDeleteClickListener, onButtonRelapseClickListener) + return AddictionCardViewHolder(itemView, onButtonDeleteClickListener, onButtonRelapseClickListener, + onButtonStopClickListener) } override fun onBindViewHolder( @@ -42,8 +51,10 @@ class AddictionCardAdapter(private val context: Context) : val addiction = Main.addictions[position] holder.textViewName.text = addiction.name - holder.textViewTime.text = - context.convertSecondsToString(addiction.lastRelapse.secondsFromNow()) + holder.textViewTime.text = if (!addiction.isStopped) context.convertSecondsToString(addiction.lastRelapse.secondsFromNow()) + else context.getString(R.string.stop_notice, + dateFormat.format(Date(addiction.timeStopped)), + context.convertSecondsToString((addiction.timeStopped - addiction.lastRelapse.toEpochMilli()) / 1000)) holder.textViewAverage.visibility = if (addiction.averageRelapseDuration == -1L) View.GONE else View.VISIBLE holder.textViewAverage.text = context.getString(R.string.recent_avg, context.convertSecondsToString(addiction.averageRelapseDuration)) @@ -52,7 +63,8 @@ class AddictionCardAdapter(private val context: Context) : override fun getItemCount() = Main.addictions.size class AddictionCardViewHolder(itemView: View, onButtonDeleteClickListener: OnClickListener, - onButtonRelapseClickListener: OnClickListener) : RecyclerView.ViewHolder(itemView) { + onButtonRelapseClickListener: OnClickListener, + onButtonStopClickListener: OnClickListener) : RecyclerView.ViewHolder(itemView) { val textViewName: TextView = itemView.findViewById(R.id.textViewAddictionName) val textViewTime: TextView = itemView.findViewById(R.id.textViewTime) val textViewAverage: TextView = itemView.findViewById(R.id.textViewAverage) @@ -66,6 +78,10 @@ class AddictionCardAdapter(private val context: Context) : tag = this@AddictionCardViewHolder setOnClickListener(onButtonRelapseClickListener) } + itemView.findViewById(R.id.imageStop).apply { + tag = this@AddictionCardViewHolder + setOnClickListener(onButtonStopClickListener) + } } } } \ No newline at end of file diff --git a/app/src/main/java/com/sixtyninefourtwenty/imdefinitelysober/activities/Main.kt b/app/src/main/java/com/sixtyninefourtwenty/imdefinitelysober/activities/Main.kt index 3c2d30c..382c9c4 100644 --- a/app/src/main/java/com/sixtyninefourtwenty/imdefinitelysober/activities/Main.kt +++ b/app/src/main/java/com/sixtyninefourtwenty/imdefinitelysober/activities/Main.kt @@ -5,11 +5,14 @@ import android.content.Intent import android.os.Build import android.os.Bundle import android.view.View +import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.ViewHolder +import com.google.android.material.snackbar.BaseTransientBottomBar +import com.google.android.material.snackbar.Snackbar import com.sixtyninefourtwenty.imdefinitelysober.Addiction import com.sixtyninefourtwenty.imdefinitelysober.AddictionCardAdapter import com.sixtyninefourtwenty.imdefinitelysober.R @@ -40,11 +43,11 @@ class Main : AppCompatActivity() { private val addNewAddiction = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { if (it.resultCode == RESULT_OK) { val name = it.data?.extras?.getString("name") as String - val instant = + @Suppress("DEPRECATION") val instant = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) it.data?.extras?.getSerializable("instant", Instant::class.java) as Instant else it.data?.extras?.getSerializable("instant") as Instant - val addiction = Addiction(name, instant) + val addiction = Addiction(name, instant, false, 0) addictions.add(addiction) cacheHandler.writeCache() adapterAddictions.notifyDataSetChanged() @@ -88,11 +91,27 @@ class Main : AppCompatActivity() { val pos = viewHolder.adapterPosition val action: () -> Unit = { addictions[pos].relapse() + addictions[pos].isStopped = false this.notifyItemChanged(pos) cacheHandler.writeCache() } showConfirmDialog(getString(R.string.relapse), getString(R.string.relapse_confirm, addictions[pos].name), action) } + setOnButtonStopClickListener { + val viewHolder = it.tag as ViewHolder + val pos = viewHolder.adapterPosition + if (addictions[pos].isStopped) + Snackbar.make(binding.root, getString(R.string.already_stopped, addictions[pos].name), BaseTransientBottomBar.LENGTH_SHORT).show() + else { + val action: () -> Unit = { + addictions[pos].isStopped = true + addictions[pos].timeStopped = System.currentTimeMillis() + this.notifyItemChanged(pos) + cacheHandler.writeCache() + } + showConfirmDialog(getString(R.string.stop), getString(R.string.stop_confirm, addictions[pos].name), action) + } + } } val recyclerAddictions = findViewById(R.id.recyclerAddictions) val layoutManager = LinearLayoutManager(this) diff --git a/app/src/main/res/drawable/ic_baseline_stop_24.xml b/app/src/main/res/drawable/ic_baseline_stop_24.xml new file mode 100644 index 0000000..95df030 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_stop_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/card_addiction.xml b/app/src/main/res/layout/card_addiction.xml index d95653b..434ed24 100644 --- a/app/src/main/res/layout/card_addiction.xml +++ b/app/src/main/res/layout/card_addiction.xml @@ -76,6 +76,19 @@ app:layout_constraintTop_toTopOf="@+id/imageDelete" app:tint="?attr/colorIconOnCard" /> + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 25ca009..d5ae2da 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -25,4 +25,8 @@ Do you really want to delete entry \"%s\"?\nThis action cannot be undone. Relapse Log relapse of \"%s\"? + Stop + Stop abstaining from \"%s\"? + Stopped at: %s\nYour attempt lasted %s + You have already stopped abstaining from \"%s\".\nPress the Relapse button to start over. \ No newline at end of file