Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge I'm Definitely Sober features back into Sobriety #41

Merged
merged 10 commits into from
Oct 20, 2022
30 changes: 19 additions & 11 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-android'
}

android {
compileSdk 30
compileSdk 33

defaultConfig {
applicationId "com.katiearose.sobriety"
minSdk 26
targetSdk 30
minSdk 21
targetSdk 33
versionCode 14
versionName "v5.9.9"
setProperty("archivesBaseName", "Sobriety $versionName")
Expand All @@ -35,23 +36,30 @@ android {
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '11'
jvmTarget = '1.8'
}
buildFeatures {
viewBinding true
}
compileSdkVersion 30
compileSdkVersion 33
namespace 'com.katiearose.sobriety'
}

dependencies {
implementation 'androidx.core:core-ktx:1.6.0'
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.5.1'
implementation 'com.google.android.material:material:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
coreLibraryDesugaring ('com.android.tools:desugar_jdk_libs') {
version {
strictly ("1.2.0") //2.0.0 requires gradle 7.4.0-alpha10
}
}
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
Expand Down
26 changes: 18 additions & 8 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.katiearose.sobriety">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<application
android:allowBackup="true"
Expand All @@ -11,14 +10,25 @@
android:supportsRtl="true"
android:theme="@style/Theme.Sobriety">
<activity
android:name=".Create"
android:name=".activities.Timeline"
android:exported="false"
android:label="Add new addiction"
android:windowSoftInputMode="adjustResize" />
android:label="@string/timeline">
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
<activity
android:name=".activities.Create"
android:exported="false"
android:label="@string/add_new_addiction"
android:windowSoftInputMode="adjustResize" >
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
<activity
android:name=".Main"
android:exported="true"
android:label="@string/app_name">
android:name=".activities.Main"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

Expand Down
39 changes: 34 additions & 5 deletions app/src/main/java/com/katiearose/sobriety/Addiction.kt
Original file line number Diff line number Diff line change
@@ -1,18 +1,37 @@
package com.katiearose.sobriety

import com.katiearose.sobriety.internal.CircularBuffer
import com.katiearose.sobriety.utils.putLast
import com.katiearose.sobriety.utils.secondsFromNow
import java.io.Serializable
import java.time.Instant

class Addiction(
val name: String,
var lastRelapse: Instant,
var relapses: CircularBuffer<Long> = CircularBuffer(3) //Default is a new one, but you can provide your own (from a cache)
var isStopped: Boolean,
var timeStopped: Long, //in milliseconds
val history: LinkedHashMap<Long, Long> = LinkedHashMap(), //in milliseconds
private val relapses: CircularBuffer<Long> = CircularBuffer(3) //Default is a new one, but you can provide your own (from a cache)
) : Serializable {
var averageRelapseDuration = Main.timeSinceInstant(lastRelapse)
var averageRelapseDuration = if (relapses.get(0) == null) -1 else calculateAverageRelapseDuration()
private set

fun stopAbstaining() {
isStopped = true
timeStopped = System.currentTimeMillis()
relapses.update(Instant.ofEpochMilli(timeStopped).epochSecond - lastRelapse.epochSecond)
averageRelapseDuration = calculateAverageRelapseDuration()
history.putLast(System.currentTimeMillis())
}

fun relapse() {
relapses.update(Main.timeSinceInstant(lastRelapse))
if (!isStopped) {
relapses.update(lastRelapse.secondsFromNow())
history.putLast(System.currentTimeMillis())
}
history[System.currentTimeMillis()] = 0
isStopped = false
averageRelapseDuration = calculateAverageRelapseDuration()
lastRelapse = Instant.now()
}
Expand All @@ -25,13 +44,23 @@ class Addiction(
val map = HashMap<Int, Any>()
map[0] = name
map[1] = lastRelapse
map[2] = relapses
map[2] = isStopped
map[3] = timeStopped
map[4] = history
map[5] = relapses
return map
}

companion object {
fun fromCacheable(map: HashMap<Int, Any>): Addiction {
return Addiction(map[0] as String, map[1] as Instant, map[2] as CircularBuffer<Long>)
return Addiction(
map[0] as String,
map[1] as Instant,
map[2] as Boolean,
map[3] as Long,
map[4] as LinkedHashMap<Long, Long>,
map[5] as CircularBuffer<Long>
)
}
}
}
89 changes: 61 additions & 28 deletions app/src/main/java/com/katiearose/sobriety/AddictionCardAdapter.kt
Original file line number Diff line number Diff line change
@@ -1,21 +1,51 @@
package com.katiearose.sobriety

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.katiearose.sobriety.activities.Main
import com.katiearose.sobriety.utils.convertSecondsToString
import com.katiearose.sobriety.utils.secondsFromNow
import java.text.DateFormat
import java.util.*

class AddictionCardAdapter(private val activity: Main, private val cacheHandler: CacheHandler) :
class AddictionCardAdapter(private val context: Context) :
RecyclerView.Adapter<AddictionCardAdapter.AddictionCardViewHolder>() {

private lateinit var onButtonDeleteClickListener: View.OnClickListener
private lateinit var onButtonRelapseClickListener: View.OnClickListener
private lateinit var onButtonStopClickListener: View.OnClickListener
private lateinit var onTimelineButtonClickListener: View.OnClickListener
private val dateFormat = DateFormat.getDateTimeInstance()

fun setOnButtonDeleteClickListener(onButtonDeleteClickListener: View.OnClickListener) {
this.onButtonDeleteClickListener = onButtonDeleteClickListener
}

fun setOnButtonRelapseClickListener(onButtonRelapseClickListener: View.OnClickListener) {
this.onButtonRelapseClickListener = onButtonRelapseClickListener
}

fun setOnButtonStopClickListener(onButtonStopClickListener: View.OnClickListener) {
this.onButtonStopClickListener = onButtonStopClickListener
}

fun setOnTimelineButtonClickListener(onTimelineButtonClickListener: View.OnClickListener) {
this.onTimelineButtonClickListener = onTimelineButtonClickListener
}

override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): AddictionCardViewHolder {
val itemView =
LayoutInflater.from(parent.context).inflate(R.layout.card_addiction, parent, false)
return AddictionCardViewHolder(itemView)
return AddictionCardViewHolder(itemView, onButtonDeleteClickListener, onButtonRelapseClickListener,
onButtonStopClickListener, onTimelineButtonClickListener)
}

override fun onBindViewHolder(
Expand All @@ -25,39 +55,42 @@ class AddictionCardAdapter(private val activity: Main, private val cacheHandler:
val addiction = Main.addictions[position]

holder.textViewName.text = addiction.name
holder.textViewTime.text =
Main.secondsToString(Main.timeSinceInstant(addiction.lastRelapse))
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 =
"Recent Average: ${Main.secondsToString(addiction.averageRelapseDuration)}"

holder.buttonDelete.setOnClickListener {
val action: () -> Unit = {
Main.addictions.remove(addiction)
activity.updatePromptVisibility()
notifyItemRemoved(position)
Main.deleting = true
cacheHandler.writeCache()
}
activity.dialogConfirm("Delete entry \"${addiction.name}\" ?", action)
}

holder.buttonReset.setOnClickListener {
val action: () -> Unit = {
addiction.relapse()
notifyItemChanged(position)
cacheHandler.writeCache()
}
activity.dialogConfirm("Log relapse of \"${addiction.name}\" ?", action)
}
context.getString(R.string.recent_avg, context.convertSecondsToString(addiction.averageRelapseDuration))
}

override fun getItemCount() = Main.addictions.size

class AddictionCardViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
class AddictionCardViewHolder(itemView: View, onButtonDeleteClickListener: View.OnClickListener,
onButtonRelapseClickListener: View.OnClickListener,
onButtonStopClickListener: View.OnClickListener,
onTimelineButtonClickListener: View.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)
val buttonDelete: ImageView = itemView.findViewById(R.id.imageDelete)
val buttonReset: ImageView = itemView.findViewById(R.id.imageReset)
init {
itemView.findViewById<ImageView>(R.id.imageDelete).apply {
tag = this@AddictionCardViewHolder
setOnClickListener(onButtonDeleteClickListener)
}
itemView.findViewById<ImageView>(R.id.imageReset).apply {
tag = this@AddictionCardViewHolder
setOnClickListener(onButtonRelapseClickListener)
}
itemView.findViewById<ImageView>(R.id.imageStop).apply {
tag = this@AddictionCardViewHolder
setOnClickListener(onButtonStopClickListener)
}
itemView.findViewById<ImageView>(R.id.imageTimeline).apply {
tag = this@AddictionCardViewHolder
setOnClickListener(onTimelineButtonClickListener)
}
}
}
}
Loading