First RYA implementation

This commit is contained in:
Phillip Thelen 2022-06-23 12:20:10 +02:00
parent f464e61b9a
commit 253210d0c2
17 changed files with 216 additions and 34 deletions

View file

@ -5,6 +5,8 @@ import com.google.android.gms.wearable.MessageEvent
import com.google.android.gms.wearable.Wearable
import com.google.android.gms.wearable.WearableListenerService
import com.habitrpg.android.habitica.HabiticaBaseApplication
import com.habitrpg.android.habitica.ui.activities.LoginActivity
import com.habitrpg.android.habitica.ui.activities.MainActivity
import com.habitrpg.android.habitica.ui.activities.TaskFormActivity
import com.habitrpg.common.habitica.api.HostConfig
import javax.inject.Inject
@ -23,10 +25,17 @@ class DeviceCommunicationService : WearableListenerService() {
super.onMessageReceived(event)
when (event.path) {
"/request/auth" -> processAuthRequest(event)
"/show/register" -> openActivity(LoginActivity::class.java)
"/show/rya" -> openActivity(MainActivity::class.java)
"/tasks/edit" -> openTaskForm(event)
}
}
private fun openActivity(activityClass: Class<*>) {
val intent = Intent(this, activityClass)
startActivity(intent)
}
private fun openTaskForm(event: MessageEvent) {
val taskID = String(event.data)
val startIntent = Intent(this, TaskFormActivity::class.java).apply {

View file

@ -8,6 +8,7 @@ import com.habitrpg.common.habitica.views.HabiticaIconsHelper
import com.habitrpg.wearos.habitica.data.repositories.UserRepository
import com.habitrpg.wearos.habitica.ui.activities.BaseActivity
import com.habitrpg.wearos.habitica.ui.activities.FaintActivity
import com.habitrpg.wearos.habitica.ui.activities.RYAActivity
import dagger.hilt.android.HiltAndroidApp
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.flow.collect
@ -32,6 +33,10 @@ class MainApplication : Application() {
val intent = Intent(this@MainApplication, FaintActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
} else if (it.needsCron && BaseActivity.currentActivityClassName != RYAActivity::class.java.name) {
val intent = Intent(this@MainApplication, RYAActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
}
}.collect()
}

View file

@ -4,11 +4,12 @@ import com.habitrpg.common.habitica.models.auth.UserAuth
import com.habitrpg.common.habitica.models.auth.UserAuthResponse
import com.habitrpg.common.habitica.models.auth.UserAuthSocial
import com.habitrpg.common.habitica.models.responses.TaskDirectionData
import com.habitrpg.wearos.habitica.models.user.User
import com.habitrpg.wearos.habitica.models.EmptyResponse
import com.habitrpg.wearos.habitica.models.WearableHabitResponse
import com.habitrpg.wearos.habitica.models.tasks.BulkTaskScoringData
import com.habitrpg.wearos.habitica.models.tasks.Task
import com.habitrpg.wearos.habitica.models.tasks.TaskList
import com.habitrpg.wearos.habitica.models.user.User
import retrofit2.http.Body
import retrofit2.http.DELETE
import retrofit2.http.GET
@ -92,5 +93,5 @@ interface ApiService {
suspend fun removePushDevice(@Path("regId") regId: String): WearableHabitResponse<List<Void>>
@POST("cron")
suspend fun runCron(): WearableHabitResponse<Void>
suspend fun runCron(): WearableHabitResponse<EmptyResponse>
}

View file

@ -21,4 +21,5 @@ class UserRepository @Inject constructor(val apiClient: ApiClient, val localRepo
suspend fun sleep() = apiClient.sleep()
suspend fun revive() = apiClient.revive()
suspend fun runCron() = apiClient.runCron()
}

View file

@ -0,0 +1,6 @@
package com.habitrpg.wearos.habitica.models
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
class EmptyResponse

View file

@ -24,6 +24,8 @@ class User: Avatar {
get() = items?.gear?.equipped
override val hasClass: Boolean = false
var needsCron: Boolean = false
var profile: Profile? = null
override fun isValid(): Boolean {

View file

@ -2,16 +2,72 @@ package com.habitrpg.wearos.habitica.ui.activities
import android.os.Bundle
import androidx.activity.viewModels
import androidx.core.view.children
import androidx.core.view.isVisible
import com.google.android.gms.wearable.CapabilityClient
import com.google.android.gms.wearable.MessageClient
import com.google.android.gms.wearable.Wearable
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.databinding.ActivityRyaBinding
import com.habitrpg.android.habitica.databinding.RowDailyBinding
import com.habitrpg.common.habitica.extensions.dpToPx
import com.habitrpg.wearos.habitica.models.tasks.Task
import com.habitrpg.wearos.habitica.ui.viewHolders.tasks.DailyViewHolder
import com.habitrpg.wearos.habitica.ui.viewmodels.RYAViewModel
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class RYAActivity: BaseActivity<ActivityRyaBinding, RYAViewModel>() {
class RYAActivity : BaseActivity<ActivityRyaBinding, RYAViewModel>() {
val messageClient: MessageClient by lazy { Wearable.getMessageClient(this) }
val capabilityClient: CapabilityClient by lazy { Wearable.getCapabilityClient(this) }
override val viewModel: RYAViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
binding = ActivityRyaBinding.inflate(layoutInflater)
super.onCreate(savedInstanceState)
viewModel.tasks.observe(this) {
createTaskListViews(it)
}
binding.ryaButton.setOnClickListener {
binding.titleView.text = getString(R.string.check_off_yesterday)
binding.descriptionView.isVisible = false
binding.ryaButton.isVisible = false
binding.phoneButton.isVisible = false
binding.taskView.isVisible = true
binding.startDayButton.isVisible = true
}
binding.phoneButton.setOnClickListener {
}
binding.startDayButton.setOnClickListener {
startAnimatingProgress()
binding.startDayButton.isEnabled = false
viewModel.runCron {
stopAnimatingProgress()
if (it) {
finish()
} else {
binding.startDayButton.isEnabled = true
}
}
}
}
private fun createTaskListViews(list: List<Task>) {
binding.taskView.removeAllViews()
for (task in list) {
val taskBinding = RowDailyBinding.inflate(layoutInflater, binding.taskView, true)
val holder = DailyViewHolder(taskBinding.root)
taskBinding.root.setOnClickListener {
viewModel.tappedTask(task)
}
val verticalPadding = 2.dpToPx(this)
taskBinding.root.children.first().setPadding(0, verticalPadding, 0, verticalPadding)
holder.bind(task)
}
}
}

View file

@ -18,8 +18,8 @@ import kotlinx.coroutines.launch
@AndroidEntryPoint
class SplashActivity: BaseActivity<ActivitySplashBinding, SplashViewModel>() {
override val viewModel: SplashViewModel by viewModels()
val messageClient: MessageClient by lazy { Wearable.getMessageClient(this) }
val capabilityClient: CapabilityClient by lazy { Wearable.getCapabilityClient(this) }
private val messageClient: MessageClient by lazy { Wearable.getMessageClient(this) }
private val capabilityClient: CapabilityClient by lazy { Wearable.getCapabilityClient(this) }
override fun onCreate(savedInstanceState: Bundle?) {
binding = ActivitySplashBinding.inflate(layoutInflater)

View file

@ -1,13 +1,49 @@
package com.habitrpg.wearos.habitica.ui.viewmodels
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import com.habitrpg.common.habitica.models.responses.TaskDirection
import com.habitrpg.common.habitica.models.tasks.TaskType
import com.habitrpg.wearos.habitica.data.repositories.TaskRepository
import com.habitrpg.wearos.habitica.data.repositories.UserRepository
import com.habitrpg.wearos.habitica.models.tasks.Task
import com.habitrpg.wearos.habitica.util.ExceptionHandlerBuilder
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class RYAViewModel @Inject constructor(userRepository: UserRepository,
class RYAViewModel @Inject constructor(
userRepository: UserRepository,
val taskRepository: TaskRepository,
exceptionBuilder: ExceptionHandlerBuilder
) : BaseViewModel(userRepository, exceptionBuilder) {
val tasks = taskRepository.getTasks(TaskType.DAILY).asLiveData()
private val tasksToComplete = mutableListOf<Task>()
fun tappedTask(task: Task) {
task.completed = !task.completed
taskRepository.localRepository.updateTask(task)
if (task.completed) {
if (!tasksToComplete.contains(task)) {
tasksToComplete.add(task)
}
} else {
if (tasksToComplete.contains(task)) {
tasksToComplete.remove(task)
}
}
}
fun runCron(function: (Boolean) -> Unit) {
viewModelScope.launch(exceptionBuilder.userFacing(this)) {
for (task in tasksToComplete) {
taskRepository.scoreTask(null, task, TaskDirection.UP)
}
userRepository.runCron()
userRepository.retrieveUser()
function(true)
}
}
}

View file

@ -1,6 +1,5 @@
package com.habitrpg.wearos.habitica.ui.viewmodels
import androidx.lifecycle.SavedStateHandle
import com.habitrpg.common.habitica.models.tasks.Frequency
import com.habitrpg.common.habitica.models.tasks.TaskType
import com.habitrpg.wearos.habitica.data.repositories.TaskRepository
@ -12,7 +11,6 @@ import javax.inject.Inject
@HiltViewModel
class TaskFormViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
userRepository: UserRepository,
val taskRepository: TaskRepository,
exceptionBuilder: ExceptionHandlerBuilder

View file

@ -4,7 +4,6 @@ import android.content.Context
import android.content.res.Resources
import android.util.AttributeSet
import androidx.core.view.children
import androidx.core.view.setPadding
import androidx.core.widget.NestedScrollView
class HabiticaScrollView @JvmOverloads constructor(
@ -15,8 +14,17 @@ class HabiticaScrollView @JvmOverloads constructor(
super.onLayout(changed, l, t, r, b)
if (changed) {
if (context.resources.configuration.isScreenRound) {
val verticalPadding =
(0.146467f * Resources.getSystem().displayMetrics.widthPixels).toInt()
val horizontalPadding =
(0.1f * Resources.getSystem().displayMetrics.widthPixels).toInt()
children.firstOrNull()
?.setPadding((0.146467f * Resources.getSystem().displayMetrics.widthPixels).toInt())
?.setPadding(
horizontalPadding,
verticalPadding,
horizontalPadding,
verticalPadding
)
}
}
}

View file

@ -18,16 +18,17 @@ class IndeterminateProgressView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
var progressBarWidth = 4f.dpToPx(context)
var progressBarWidth = 5f.dpToPx(context)
private val rainbow = listOf(
ContextCompat.getColor(context, R.color.black),
ContextCompat.getColor(context, R.color.watch_red_100),
ContextCompat.getColor(context, R.color.watch_orange_100),
ContextCompat.getColor(context, R.color.watch_yellow_100),
ContextCompat.getColor(context, R.color.watch_green_100),
ContextCompat.getColor(context, R.color.watch_blue_100),
ContextCompat.getColor(context, R.color.watch_purple_100),
ContextCompat.getColor(context, R.color.watch_red_100),
ContextCompat.getColor(context, R.color.black),
).toIntArray()
val gradient = SweepGradient(225f, 225f, rainbow, null)
private val paint = Paint()

View file

@ -29,7 +29,7 @@ var customDateAdapter: Any = object : Any() {
var index = 0
while (index < dateFormats.size && date == null) {
try {
date = dateFormats[index].parse(s)
date = s?.let { dateFormats[index].parse(it) }
} catch (_: ParseException) {}
index += 1
}

View file

@ -5,10 +5,8 @@ import android.util.AttributeSet
import android.view.View
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.ViewCompat
import java.lang.Float
import kotlin.Boolean
import kotlin.Int
import kotlin.IntArray
import kotlin.math.max
import kotlin.math.min
class ScrollAwayBehavior<V : View>(context: Context, attrs: AttributeSet) :
CoordinatorLayout.Behavior<V>(context, attrs) {
@ -24,7 +22,7 @@ class ScrollAwayBehavior<V : View>(context: Context, attrs: AttributeSet) :
) {
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type)
child.translationY =
Float.max(0f, Float.min(child.height.toFloat(), child.translationY + dy))
max(0f, min(child.height.toFloat(), child.translationY + dy))
}
override fun onStopNestedScroll(

View file

@ -1,7 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<com.habitrpg.wearos.habitica.ui.views.HabiticaScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/title_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="@color/watch_purple_200"
android:text="@string/day_start"
android:fontFamily="sans-serif-medium"
android:layout_marginBottom="10dp"/>
<TextView
android:id="@+id/description_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/rya_description"
android:gravity="center"
android:textColor="@color/white"
android:textSize="16sp"
android:layout_marginBottom="@dimen/spacing_large"/>
<Button
android:id="@+id/rya_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/review_dailies"
style="@style/ChipButton.Purple"
android:layout_marginBottom="@dimen/spacing_small"/>
<Button
android:id="@+id/phone_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/check_on_phone"
android:drawableStart="@drawable/handoff"
style="@style/ChipButton"/>
</LinearLayout>
<LinearLayout
android:id="@+id/task_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone"/>
<Button
android:id="@+id/start_day_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/ChipButton.Purple"
android:text="@string/start_new_day"
android:layout_marginTop="2dp"
android:visibility="gone"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/checklist_disclaimer"
android:textColor="@color/gray_400"
android:gravity="center"
android:layout_marginTop="@dimen/spacing_large"
android:textSize="14sp" />
</LinearLayout>
</com.habitrpg.wearos.habitica.ui.views.HabiticaScrollView>

View file

@ -17,4 +17,11 @@
<string name="continue_text">Continue</string>
<string name="you_ran_out_of_hp">You ran out of HP</string>
<string name="faint_description">You lost a Level, Exp, and Gold. Dont give up!</string>
<string name="day_start">Day start</string>
<string name="rya_description">There are incomplete Dailies from yesterday</string>
<string name="review_dailies">Review dailies</string>
<string name="check_on_phone">Check on phone</string>
<string name="checklist_disclaimer">Task checklists are available on the phone</string>
<string name="start_new_day">Start new day</string>
<string name="check_off_yesterday">Check off any you did yesterday:</string>
</resources>

View file

@ -17,37 +17,31 @@
<item name="android:paddingHorizontal">@dimen/row_padding_horizontal</item>
<item name="android:paddingVertical">@dimen/row_padding_vertical</item>
<item name="android:background">@drawable/row_background</item>
<item name="android:textSize">14sp</item>
<item name="android:textSize">16sp</item>
<item name="fontFamily">sans-serif-medium</item>
<item name="android:gravity">center</item>
<item name="android:minHeight">52dp</item>
<item name="android:textAllCaps">false</item>
<item name="android:letterSpacing">0.05</item>
<item name="android:letterSpacing">0.01</item>
<item name="android:drawablePadding">8dp</item>
</style>
<style name="ChipButton.Small">
<item name="android:paddingHorizontal">@dimen/row_padding_horizontal</item>
<item name="android:paddingVertical">@dimen/row_padding_vertical</item>
<item name="android:background">@drawable/row_background</item>
<style name="ChipButton.Small" parent="ChipButton">
<item name="android:textSize">14sp</item>
<item name="fontFamily">sans-serif-medium</item>
<item name="android:gravity">center</item>
<item name="android:minHeight">32dp</item>
<item name="android:textAllCaps">false</item>
<item name="android:letterSpacing">0.05</item>
<item name="android:drawablePadding">8dp</item>
</style>
<style name="ChipButton.Purple">
<style name="ChipButton.Purple" parent="ChipButton">
<item name="android:backgroundTint">@color/watch_purple_100</item>
<item name="android:textColor">@color/black</item>
</style>
<style name="ChipButton.Red">
<style name="ChipButton.Red" parent="ChipButton">
<item name="android:backgroundTint">@color/watch_red_200</item>
<item name="android:textColor">@color/black</item>
</style>
<style name="ChipButton.Small.Red">
<style name="ChipButton.Small.Red" parent="ChipButton.Small">
<item name="android:backgroundTint">@color/watch_red_200</item>
<item name="android:textColor">@color/black</item>
</style>