diff --git a/Habitica/res/navigation/navigation.xml b/Habitica/res/navigation/navigation.xml index e11fc3bac..1379c226f 100644 --- a/Habitica/res/navigation/navigation.xml +++ b/Habitica/res/navigation/navigation.xml @@ -34,6 +34,16 @@ + + + + + + Show assigned and open tasks on your personal task lists Copy shared tasks Group Plan Settings + Task Summary You You, %d other diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.kt index 59160edf4..ab3fe3f3c 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.kt @@ -130,7 +130,7 @@ interface ApiService { fun getTask(@Path("id") id: String): Flowable> @POST("tasks/{id}/score/{direction}") - fun postTaskDirection(@Path("id") id: String, @Path("direction") direction: String): Flowable> + suspend fun postTaskDirection(@Path("id") id: String, @Path("direction") direction: String): HabitResponse @POST("tasks/bulk-score") fun bulkScoreTasks(@Body data: List>): Flowable> @@ -138,7 +138,7 @@ interface ApiService { fun postTaskNewPosition(@Path("id") id: String, @Path("position") position: Int): Flowable>> @POST("tasks/{taskId}/checklist/{itemId}/score") - fun scoreChecklistItem(@Path("taskId") taskId: String, @Path("itemId") itemId: String): Flowable> + suspend fun scoreChecklistItem(@Path("taskId") taskId: String, @Path("itemId") itemId: String): HabitResponse @POST("tasks/user") fun createTask(@Body item: Task): Flowable> diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/components/UserComponent.java b/Habitica/src/main/java/com/habitrpg/android/habitica/components/UserComponent.java index c3a3fef75..6cbab653f 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/components/UserComponent.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/components/UserComponent.java @@ -38,6 +38,8 @@ import com.habitrpg.android.habitica.ui.activities.SetupActivity; import com.habitrpg.android.habitica.ui.activities.SkillMemberActivity; import com.habitrpg.android.habitica.ui.activities.SkillTasksActivity; import com.habitrpg.android.habitica.ui.activities.TaskFormActivity; +import com.habitrpg.android.habitica.ui.activities.TaskSummaryActivity; +import com.habitrpg.android.habitica.ui.activities.TaskSummaryViewModel; import com.habitrpg.android.habitica.ui.activities.VerifyUsernameActivity; import com.habitrpg.android.habitica.ui.adapter.social.challenges.ChallengeTasksRecyclerViewAdapter; import com.habitrpg.android.habitica.ui.adapter.tasks.DailiesRecyclerViewHolder; @@ -364,4 +366,8 @@ public interface UserComponent { void inject(@NotNull DeathActivity deathActivity); void inject(@NotNull DeviceCommunicationService deviceCommunicationService); + + void inject(@NotNull TaskSummaryActivity taskSummaryActivity); + + void inject(@NotNull TaskSummaryViewModel taskSummaryViewModel); } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt index a4184fc9e..26dbacaac 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt @@ -93,12 +93,12 @@ interface ApiClient { fun getTask(id: String): Flowable - fun postTaskDirection(id: String, direction: String): Flowable + suspend fun postTaskDirection(id: String, direction: String): TaskDirectionData? fun bulkScoreTasks(data: List>): Flowable fun postTaskNewPosition(id: String, position: Int): Flowable> - fun scoreChecklistItem(taskId: String, itemId: String): Flowable + suspend fun scoreChecklistItem(taskId: String, itemId: String): Task? fun createTask(item: Task): Flowable diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/TaskRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/TaskRepository.kt index a8e011813..9548b54b3 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/TaskRepository.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/TaskRepository.kt @@ -22,24 +22,24 @@ interface TaskRepository : BaseRepository { suspend fun retrieveTasks(userId: String, tasksOrder: TasksOrder): TaskList? fun retrieveTasks(userId: String, tasksOrder: TasksOrder, dueDate: Date): Flowable - fun taskChecked( + suspend fun taskChecked( user: User?, task: Task, up: Boolean, force: Boolean, notifyFunc: ((TaskScoringResult) -> Unit)? - ): Flowable - fun taskChecked( + ): TaskScoringResult? + suspend fun taskChecked( user: User?, taskId: String, up: Boolean, force: Boolean, notifyFunc: ((TaskScoringResult) -> Unit)? - ): Maybe - fun scoreChecklistItem(taskId: String, itemId: String): Flowable + ): TaskScoringResult? + suspend fun scoreChecklistItem(taskId: String, itemId: String): Task? - fun getTask(taskId: String): Flowable - fun getTaskCopy(taskId: String): Flowable + fun getTask(taskId: String): Flow + fun getTaskCopy(taskId: String): Flow fun createTask(task: Task, force: Boolean = false): Flowable fun updateTask(task: Task, force: Boolean = false): Maybe? fun deleteTask(taskId: String): Flowable @@ -55,7 +55,7 @@ interface TaskRepository : BaseRepository { fun updateTaskPosition(taskType: TaskType, taskID: String, newPosition: Int): Maybe> - fun getUnmanagedTask(taskid: String): Flowable + fun getUnmanagedTask(taskid: String): Flow fun updateTaskInBackground(task: Task) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt index fb8226076..ceecbb0c4 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt @@ -451,8 +451,8 @@ class ApiClientImpl( return apiService.getTask(id).compose(configureApiCallObserver()) } - override fun postTaskDirection(id: String, direction: String): Flowable { - return apiService.postTaskDirection(id, direction).compose(configureApiCallObserver()) + override suspend fun postTaskDirection(id: String, direction: String): TaskDirectionData? { + return handleSuspendCall { apiService.postTaskDirection(id, direction) } } override fun bulkScoreTasks(data: List>): Flowable { @@ -463,8 +463,8 @@ class ApiClientImpl( return apiService.postTaskNewPosition(id, position).compose(configureApiCallObserver()) } - override fun scoreChecklistItem(taskId: String, itemId: String): Flowable { - return apiService.scoreChecklistItem(taskId, itemId).compose(configureApiCallObserver()) + override suspend fun scoreChecklistItem(taskId: String, itemId: String): Task? { + return handleSuspendCall { apiService.scoreChecklistItem(taskId, itemId) } } override fun createTask(item: Task): Flowable { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/TaskRepositoryImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/TaskRepositoryImpl.kt index 565adaa79..a6e0a8196 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/TaskRepositoryImpl.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/TaskRepositoryImpl.kt @@ -24,6 +24,7 @@ import io.reactivex.rxjava3.core.Flowable import io.reactivex.rxjava3.core.Maybe import io.reactivex.rxjava3.core.Single import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.map import java.text.SimpleDateFormat import java.util.Date @@ -70,13 +71,13 @@ class TaskRepositoryImpl( } @Suppress("ReturnCount") - override fun taskChecked( + override suspend fun taskChecked( user: User?, task: Task, up: Boolean, force: Boolean, notifyFunc: ((TaskScoringResult) -> Unit)? - ): Flowable { + ): TaskScoringResult? { val localData = if (user != null && appConfigManager.enableLocalTaskScoring()) { ScoreTaskLocallyInteractor.score(user, task, if (up) TaskDirection.UP else TaskDirection.DOWN) } else null @@ -90,41 +91,33 @@ class TaskRepositoryImpl( val now = Date().time val id = task.id if (lastTaskAction > now - 500 && !force || id == null) { - return Flowable.empty() + return null } lastTaskAction = now - return this.apiClient.postTaskDirection(id, (if (up) TaskDirection.UP else TaskDirection.DOWN).text) - .flatMapMaybe { - // There are cases where the user object is not set correctly. So the app refetches it as a fallback - if (user == null) { - localRepository.getUser(userID).firstElement() - } else { - Maybe.just(user) - }.map { user -> Pair(it, user) } - } - .map { (res, user): Pair -> - // save local task changes + val res = this.apiClient.postTaskDirection(id, (if (up) TaskDirection.UP else TaskDirection.DOWN).text) ?: return null + // There are cases where the user object is not set correctly. So the app refetches it as a fallback + val thisUser = user ?: localRepository.getUser(userID).firstOrNull() ?: return null + // save local task changes - analyticsManager.logEvent( - "task_scored", - bundleOf( - Pair("type", task.type), - Pair("scored_up", up), - Pair("value", task.value) - ) - ) - if (res.lvl == 0) { - // Team tasks that require approval have weird data that we should just ignore. - return@map TaskScoringResult() - } - val result = TaskScoringResult(res, user.stats) - if (localData == null) { - notifyFunc?.invoke(result) - } - handleTaskResponse(user, res, task, up, localData?.delta ?: 0f) - result - } + analyticsManager.logEvent( + "task_scored", + bundleOf( + Pair("type", task.type), + Pair("scored_up", up), + Pair("value", task.value) + ) + ) + if (res.lvl == 0) { + // Team tasks that require approval have weird data that we should just ignore. + return TaskScoringResult() + } + val result = TaskScoringResult(res, thisUser.stats) + if (localData == null) { + notifyFunc?.invoke(result) + } + handleTaskResponse(thisUser, res, task, up, localData?.delta ?: 0f) + return result } override fun bulkScoreTasks(data: List>): Flowable { @@ -195,31 +188,29 @@ class TaskRepositoryImpl( } } - override fun taskChecked( + override suspend fun taskChecked( user: User?, taskId: String, up: Boolean, force: Boolean, notifyFunc: ((TaskScoringResult) -> Unit)? - ): Maybe { - return localRepository.getTask(taskId).firstElement() - .flatMap { task -> taskChecked(user, task, up, force, notifyFunc).singleElement() } + ): TaskScoringResult? { + val task = localRepository.getTask(taskId).firstOrNull() ?: return null + return taskChecked(user, task, up, force, notifyFunc) } - override fun scoreChecklistItem(taskId: String, itemId: String): Flowable { - return apiClient.scoreChecklistItem(taskId, itemId) - .flatMapMaybe { localRepository.getTask(taskId).firstElement() } - .doOnNext { task -> - val updatedItem: ChecklistItem? = task.checklist?.lastOrNull { itemId == it.id } - if (updatedItem != null) { - localRepository.modify(updatedItem) { liveItem -> liveItem.completed = !liveItem.completed } - } - } + override suspend fun scoreChecklistItem(taskId: String, itemId: String): Task? { + val task = apiClient.scoreChecklistItem(taskId, itemId) + val updatedItem: ChecklistItem? = task?.checklist?.lastOrNull { itemId == it.id } + if (updatedItem != null) { + localRepository.modify(updatedItem) { liveItem -> liveItem.completed = !liveItem.completed } + } + return task } - override fun getTask(taskId: String): Flowable = localRepository.getTask(taskId) + override fun getTask(taskId: String) = localRepository.getTask(taskId) - override fun getTaskCopy(taskId: String): Flowable = localRepository.getTaskCopy(taskId) + override fun getTaskCopy(taskId: String) = localRepository.getTaskCopy(taskId) override fun createTask(task: Task, force: Boolean): Flowable { val now = Date().time @@ -310,8 +301,7 @@ class TaskRepositoryImpl( .doOnSuccess { localRepository.updateTaskPositions(it) } } - override fun getUnmanagedTask(taskid: String): Flowable = - getTask(taskid).map { localRepository.getUnmanagedCopy(it) } + override fun getUnmanagedTask(taskid: String) = getTask(taskid).map { localRepository.getUnmanagedCopy(it) } override fun updateTaskInBackground(task: Task) { updateTask(task).subscribe({ }, ExceptionHandler.rx()) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/TaskLocalRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/TaskLocalRepository.kt index 4697c2ead..6f8b80a60 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/TaskLocalRepository.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/TaskLocalRepository.kt @@ -19,8 +19,8 @@ interface TaskLocalRepository : BaseLocalRepository { fun deleteTask(taskID: String) - fun getTask(taskId: String): Flowable - fun getTaskCopy(taskId: String): Flowable + fun getTask(taskId: String): Flow + fun getTaskCopy(taskId: String): Flow fun markTaskCompleted(taskId: String, isCompleted: Boolean) @@ -33,6 +33,7 @@ interface TaskLocalRepository : BaseLocalRepository { fun updateTaskPositions(taskOrder: List) fun saveCompletedTodos(userId: String, tasks: MutableCollection) fun getErroredTasks(userID: String): Flowable> - fun getUser(userID: String): Flowable + fun getUserFlowable(userID: String): Flowable + fun getUser(userID: String): Flow fun getTasksForChallenge(challengeID: String?, userID: String?): Flowable> } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmTaskLocalRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmTaskLocalRepository.kt index d7b66a56c..6d97a4e61 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmTaskLocalRepository.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmTaskLocalRepository.kt @@ -18,6 +18,8 @@ import io.realm.kotlin.toFlow import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.map class RealmTaskLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm), TaskLocalRepository { @@ -187,19 +189,17 @@ class RealmTaskLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm), } } - override fun getTask(taskId: String): Flowable { + override fun getTask(taskId: String): Flow { if (realm.isClosed) { - return Flowable.empty() + return emptyFlow() } - return RxJavaBridge.toV3Flowable( - realm.where(Task::class.java).equalTo("id", taskId).findAll().asFlowable() - .filter { realmObject -> realmObject.isLoaded && realmObject.isNotEmpty() } - .map { it.first() } - .cast(Task::class.java) - ) + return realm.where(Task::class.java).equalTo("id", taskId).findAll().toFlow() + .filter { realmObject -> realmObject.isLoaded && realmObject.isNotEmpty() } + .map { it.first() } + .filterNotNull() } - override fun getTaskCopy(taskId: String): Flowable { + override fun getTaskCopy(taskId: String): Flow { return getTask(taskId) .map { task -> return@map if (task.isManaged && task.isValid) { @@ -270,7 +270,7 @@ class RealmTaskLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm), ).retry(1) } - override fun getUser(userID: String): Flowable { + override fun getUserFlowable(userID: String): Flowable { return RxJavaBridge.toV3Flowable( realm.where(User::class.java) .equalTo("id", userID) @@ -281,6 +281,16 @@ class RealmTaskLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm), ) } + override fun getUser(userID: String): Flow { + return realm.where(User::class.java) + .equalTo("id", userID) + .findAll() + .toFlow() + .filter { realmObject -> realmObject.isLoaded && realmObject.isValid && !realmObject.isEmpty() } + .map { users -> users.first() } + .filterNotNull() + } + override fun getTasksForChallenge(challengeID: String?, userID: String?): Flowable> { return RxJavaBridge.toV3Flowable( realm.where(Task::class.java) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/TaskAlarmManager.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/TaskAlarmManager.kt index 44791f639..060f5ef1a 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/TaskAlarmManager.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/TaskAlarmManager.kt @@ -16,7 +16,11 @@ import com.habitrpg.android.habitica.receivers.TaskReceiver import com.habitrpg.shared.habitica.HLogger import com.habitrpg.shared.habitica.LogLevel import com.habitrpg.shared.habitica.models.tasks.TaskType +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.launch import java.time.ZoneId import java.time.ZonedDateTime import java.time.format.DateTimeFormatter @@ -55,10 +59,12 @@ class TaskAlarmManager( // We currently only use this function to schedule the next reminder for dailies // We may be able to use repeating alarms instead of this in the future fun addAlarmForTaskId(taskId: String) { - taskRepository.getTaskCopy(taskId) - .filter { task -> task.isValid && task.isManaged && TaskType.DAILY == task.type } - .firstElement() - .subscribe({ this.setAlarmsForTask(it) }, ExceptionHandler.rx()) + MainScope().launch(ExceptionHandler.coroutine()) { + val task = taskRepository.getTaskCopy(taskId) + .filter { task -> task.isValid && task.isManaged && TaskType.DAILY == task.type } + .first() + setAlarmsForTask(task) + } } suspend fun scheduleAllSavedAlarms(preventDailyReminder: Boolean) { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/BuyRewardUseCase.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/BuyRewardUseCase.kt index 8f0c30ac5..617c50a8a 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/BuyRewardUseCase.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/BuyRewardUseCase.kt @@ -1,30 +1,27 @@ package com.habitrpg.android.habitica.interactors import com.habitrpg.android.habitica.data.TaskRepository -import com.habitrpg.android.habitica.executors.PostExecutionThread import com.habitrpg.android.habitica.helpers.SoundManager -import com.habitrpg.shared.habitica.models.responses.TaskScoringResult import com.habitrpg.android.habitica.models.tasks.Task import com.habitrpg.android.habitica.models.user.User -import io.reactivex.rxjava3.core.Flowable +import com.habitrpg.shared.habitica.models.responses.TaskScoringResult import javax.inject.Inject class BuyRewardUseCase @Inject constructor( private val taskRepository: TaskRepository, private val soundManager: SoundManager, - postExecutionThread: PostExecutionThread -) : UseCase(postExecutionThread) { +) : FlowUseCase() { - override fun buildUseCaseObservable(requestValues: RequestValues): Flowable { - return taskRepository - .taskChecked(requestValues.user, requestValues.task, false, false, requestValues.notifyFunc) - .doOnNext { soundManager.loadAndPlayAudio(SoundManager.SoundReward) } + override suspend fun run(requestValues: RequestValues): TaskScoringResult? { + val response = taskRepository.taskChecked(requestValues.user, requestValues.task, false, false, requestValues.notifyFunc) + soundManager.loadAndPlayAudio(SoundManager.SoundReward) + return response } class RequestValues( internal val user: User?, val task: Task, val notifyFunc: (TaskScoringResult) -> Unit - ) : UseCase.RequestValues + ) : FlowUseCase.RequestValues } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/UseCase.java b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/UseCase.java deleted file mode 100644 index 7f4088282..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/UseCase.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.habitrpg.android.habitica.interactors; - -import com.habitrpg.android.habitica.executors.PostExecutionThread; - -import io.reactivex.rxjava3.core.Flowable; - -public abstract class UseCase { - - private final PostExecutionThread postExecutionThread; - - protected UseCase(PostExecutionThread postExecutionThread) { - this.postExecutionThread = postExecutionThread; - } - - protected abstract Flowable buildUseCaseObservable(Q requestValues); - - public Flowable observable(Q requestValues) { - return this.buildUseCaseObservable(requestValues) - .subscribeOn(postExecutionThread.getScheduler()) - .observeOn(postExecutionThread.getScheduler()); - } - - public interface RequestValues { - - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/UseCase.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/UseCase.kt new file mode 100644 index 000000000..d60e1ed32 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/interactors/UseCase.kt @@ -0,0 +1,28 @@ +package com.habitrpg.android.habitica.interactors + +import com.habitrpg.android.habitica.executors.PostExecutionThread +import io.reactivex.rxjava3.core.Flowable +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +abstract class UseCase protected constructor(private val postExecutionThread: PostExecutionThread) { + protected abstract fun buildUseCaseObservable(requestValues: Q): Flowable + fun observable(requestValues: Q): Flowable { + return buildUseCaseObservable(requestValues) + .subscribeOn(postExecutionThread.scheduler) + .observeOn(postExecutionThread.scheduler) + } + + interface RequestValues +} + +abstract class FlowUseCase { + protected abstract suspend fun run(requestValues: Q): T + suspend fun observable(requestValues: Q): T { + return withContext(Dispatchers.IO) { + run(requestValues) + } + } + + interface RequestValues +} \ No newline at end of file diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/LocalNotificationActionReceiver.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/LocalNotificationActionReceiver.kt index a20769fa4..4ecc891e7 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/LocalNotificationActionReceiver.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/LocalNotificationActionReceiver.kt @@ -113,16 +113,17 @@ class LocalNotificationActionReceiver : BroadcastReceiver() { } context?.getString(R.string.complete_task_action) -> { taskID?.let { - taskRepository.taskChecked(null, it, up = true, force = false) { - }.subscribe({ - val pair = NotifyUserUseCase.getNotificationAndAddStatsToUserAsText( - it.experienceDelta, - it.healthDelta, - it.goldDelta, - it.manaDelta - ) - showToast(pair.first) - }, ExceptionHandler.rx()) + MainScope().launch(ExceptionHandler.coroutine()) { + taskRepository.taskChecked(null, it, up = true, force = false) { + val pair = NotifyUserUseCase.getNotificationAndAddStatsToUserAsText( + it.experienceDelta, + it.healthDelta, + it.goldDelta, + it.manaDelta + ) + showToast(pair.first) + } + } } } } @@ -134,6 +135,8 @@ class LocalNotificationActionReceiver : BroadcastReceiver() { } private fun getMessageText(key: String?): String? { - return intent?.let { RemoteInput.getResultsFromIntent(it)?.getCharSequence(key)?.toString() } + return intent?.let { + RemoteInput.getResultsFromIntent(it)?.getCharSequence(key)?.toString() + } } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/TaskReceiver.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/TaskReceiver.kt index 839925764..b5dfbebb6 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/TaskReceiver.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/TaskReceiver.kt @@ -21,7 +21,9 @@ import com.habitrpg.android.habitica.ui.activities.MainActivity import com.habitrpg.shared.habitica.HLogger import com.habitrpg.shared.habitica.LogLevel import com.habitrpg.shared.habitica.models.tasks.TaskType -import io.reactivex.rxjava3.functions.Consumer +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.launch import javax.inject.Inject class TaskReceiver : BroadcastReceiver() { @@ -43,18 +45,13 @@ class TaskReceiver : BroadcastReceiver() { taskAlarmManager.addAlarmForTaskId(taskId) } - taskRepository.getTask(taskId ?: "") - .firstElement() - .subscribe( - Consumer { - if (it.isUpdatedToday && it.completed) { - return@Consumer - } - - createNotification(context, it) - }, - ExceptionHandler.rx() - ) + MainScope().launch(ExceptionHandler.coroutine()) { + val task = taskRepository.getTask(taskId ?: "").firstOrNull() ?: return@launch + if (task.isUpdatedToday && task.completed) { + return@launch + } + createNotification(context, task) + } } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/AdventureGuideActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/AdventureGuideActivity.kt index 15b6b6907..55564e5c6 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/AdventureGuideActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/AdventureGuideActivity.kt @@ -16,8 +16,8 @@ import com.habitrpg.android.habitica.databinding.AdventureGuideItemBinding import com.habitrpg.android.habitica.extensions.fromHtml import com.habitrpg.android.habitica.helpers.AmplitudeManager import com.habitrpg.android.habitica.models.user.User -import com.habitrpg.common.habitica.extensions.loadImage import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel +import com.habitrpg.common.habitica.extensions.loadImage import javax.inject.Inject class AdventureGuideActivity : BaseActivity() { @@ -33,7 +33,7 @@ class AdventureGuideActivity : BaseActivity() { return R.layout.activity_main } - override fun getContentView(): View { + override fun getContentView(layoutResId: Int?): View { binding = ActivityAdventureGuideBinding.inflate(layoutInflater) return binding.root } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ArmoireActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ArmoireActivity.kt index 63d6d453f..c09c999f1 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ArmoireActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ArmoireActivity.kt @@ -49,7 +49,7 @@ class ArmoireActivity : BaseActivity() { component?.inject(this) } - override fun getContentView(): View { + override fun getContentView(layoutResId: Int?): View { binding = ActivityArmoireBinding.inflate(layoutInflater) return binding.root } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/BaseActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/BaseActivity.kt index 95bbb7b9f..0e63b948f 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/BaseActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/BaseActivity.kt @@ -26,11 +26,9 @@ import com.habitrpg.android.habitica.HabiticaBaseApplication import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.components.UserComponent import com.habitrpg.android.habitica.data.UserRepository -import com.habitrpg.common.habitica.extensions.getThemeColor -import com.habitrpg.common.habitica.extensions.isUsingNightModeResources import com.habitrpg.android.habitica.extensions.updateStatusBarColor -import com.habitrpg.android.habitica.helpers.NotificationsManager import com.habitrpg.android.habitica.helpers.ExceptionHandler +import com.habitrpg.android.habitica.helpers.NotificationsManager import com.habitrpg.android.habitica.interactors.ShowNotificationInteractor import com.habitrpg.android.habitica.proxy.AnalyticsManager import com.habitrpg.android.habitica.ui.helpers.ToolbarColorHelper @@ -62,10 +60,10 @@ abstract class BaseActivity : AppCompatActivity() { internal var toolbar: Toolbar? = null - protected abstract fun getLayoutResId(): Int + protected abstract fun getLayoutResId(): Int? - open fun getContentView(): View { - return (getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater).inflate(getLayoutResId(), null) + open fun getContentView(layoutResId: Int? = getLayoutResId()): View { + return (getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater).inflate(layoutResId ?: 0, null) } var compositeSubscription = CompositeDisposable() @@ -94,7 +92,9 @@ abstract class BaseActivity : AppCompatActivity() { super.onCreate(savedInstanceState) habiticaApplication injectActivity(HabiticaBaseApplication.userComponent) - setContentView(getContentView()) + getLayoutResId()?.let { + setContentView(getContentView(it)) + } compositeSubscription = CompositeDisposable() compositeSubscription.add(notificationsManager.displayNotificationEvents.subscribe( { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ChallengeFormActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ChallengeFormActivity.kt index 4889bfcb8..9e56e29c7 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ChallengeFormActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ChallengeFormActivity.kt @@ -78,7 +78,7 @@ class ChallengeFormActivity : BaseActivity() { private var savingInProgress = false - override fun getContentView(): View { + override fun getContentView(layoutResId: Int?): View { binding = ActivityCreateChallengeBinding.inflate(layoutInflater) return binding.root } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ClassSelectionActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ClassSelectionActivity.kt index 492cce57c..5011641e4 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ClassSelectionActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ClassSelectionActivity.kt @@ -60,7 +60,7 @@ class ClassSelectionActivity : BaseActivity() { return R.layout.activity_class_selection } - override fun getContentView(): View { + override fun getContentView(layoutResId: Int?): View { binding = ActivityClassSelectionBinding.inflate(layoutInflater) return binding.root } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/DeathActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/DeathActivity.kt index 70c204ba7..4e035a944 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/DeathActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/DeathActivity.kt @@ -40,7 +40,7 @@ class DeathActivity: BaseActivity() { component?.inject(this) } - override fun getContentView(): View { + override fun getContentView(layoutResId: Int?): View { binding = ActivityDeathBinding.inflate(layoutInflater) return binding.root } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/FixCharacterValuesActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/FixCharacterValuesActivity.kt index ba36246ca..7a1286c8c 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/FixCharacterValuesActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/FixCharacterValuesActivity.kt @@ -27,7 +27,7 @@ class FixCharacterValuesActivity : BaseActivity() { override fun getLayoutResId(): Int = R.layout.activity_fixcharacter - override fun getContentView(): View { + override fun getContentView(layoutResId: Int?): View { binding = ActivityFixcharacterBinding.inflate(layoutInflater) return binding.root } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/FullProfileActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/FullProfileActivity.kt index 1fd8b47ce..4b598252a 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/FullProfileActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/FullProfileActivity.kt @@ -496,7 +496,7 @@ class FullProfileActivity : BaseActivity() { return R.layout.activity_full_profile } - override fun getContentView(): View { + override fun getContentView(layoutResId: Int?): View { binding = ActivityFullProfileBinding.inflate(layoutInflater) return binding.root } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GiftGemsActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GiftGemsActivity.kt index 8917adaa1..a894a2d8c 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GiftGemsActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GiftGemsActivity.kt @@ -49,7 +49,7 @@ class GiftGemsActivity : PurchaseActivity() { return R.layout.activity_gift_gems } - override fun getContentView(): View { + override fun getContentView(layoutResId: Int?): View { binding = ActivityGiftGemsBinding.inflate(layoutInflater) return binding.root } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GiftSubscriptionActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GiftSubscriptionActivity.kt index a429ade2c..fc755c822 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GiftSubscriptionActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GiftSubscriptionActivity.kt @@ -46,7 +46,7 @@ class GiftSubscriptionActivity : PurchaseActivity() { component?.inject(this) } - override fun getContentView(): View { + override fun getContentView(layoutResId: Int?): View { binding = ActivityGiftSubscriptionBinding.inflate(layoutInflater) return binding.root } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GroupFormActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GroupFormActivity.kt index 726cc1a50..3ebc9adc9 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GroupFormActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GroupFormActivity.kt @@ -28,7 +28,7 @@ class GroupFormActivity : BaseActivity() { return R.layout.activity_group_form } - override fun getContentView(): View { + override fun getContentView(layoutResId: Int?): View { binding = ActivityGroupFormBinding.inflate(layoutInflater) return binding.root } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GroupInviteActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GroupInviteActivity.kt index b75dedd99..8ad8fbf1e 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GroupInviteActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GroupInviteActivity.kt @@ -38,7 +38,7 @@ class GroupInviteActivity : BaseActivity() { return R.layout.activity_party_invite } - override fun getContentView(): View { + override fun getContentView(layoutResId: Int?): View { binding = ActivityPartyInviteBinding.inflate(layoutInflater) return binding.root } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/HabitButtonWidgetActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/HabitButtonWidgetActivity.kt index 392a71189..1b7c805a8 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/HabitButtonWidgetActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/HabitButtonWidgetActivity.kt @@ -42,7 +42,7 @@ class HabitButtonWidgetActivity : BaseActivity() { return R.layout.widget_configure_habit_button } - override fun getContentView(): View { + override fun getContentView(layoutResId: Int?): View { binding = WidgetConfigureHabitButtonBinding.inflate(layoutInflater) return binding.root } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/IntroActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/IntroActivity.kt index 6610a3f62..1d1786216 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/IntroActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/IntroActivity.kt @@ -32,7 +32,7 @@ class IntroActivity : BaseActivity(), View.OnClickListener, ViewPager.OnPageChan return R.layout.activity_intro } - override fun getContentView(): View { + override fun getContentView(layoutResId: Int?): View { binding = ActivityIntroBinding.inflate(layoutInflater) return binding.root } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/LoginActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/LoginActivity.kt index 6ea4c7f23..e2ec696a9 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/LoginActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/LoginActivity.kt @@ -113,7 +113,7 @@ class LoginActivity : BaseActivity() { return R.layout.activity_login } - override fun getContentView(): View { + override fun getContentView(layoutResId: Int?): View { binding = ActivityLoginBinding.inflate(layoutInflater) return binding.root } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt index b39b10e16..64dcf69f2 100755 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt @@ -119,7 +119,7 @@ open class MainActivity : BaseActivity(), SnackbarActivity { return R.layout.activity_main } - override fun getContentView(): View { + override fun getContentView(layoutResId: Int?): View { binding = ActivityMainBinding.inflate(layoutInflater) return binding.root } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MaintenanceActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MaintenanceActivity.kt index cc972c6cc..386d8cd5f 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MaintenanceActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MaintenanceActivity.kt @@ -32,7 +32,7 @@ class MaintenanceActivity : BaseActivity() { return R.layout.activity_maintenance } - override fun getContentView(): View { + override fun getContentView(layoutResId: Int?): View { binding = ActivityMaintenanceBinding.inflate(layoutInflater) return binding.root } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/NotificationsActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/NotificationsActivity.kt index 819453959..6ccfc37ea 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/NotificationsActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/NotificationsActivity.kt @@ -51,7 +51,7 @@ class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget override fun getLayoutResId(): Int = R.layout.activity_notifications - override fun getContentView(): View { + override fun getContentView(layoutResId: Int?): View { binding = ActivityNotificationsBinding.inflate(layoutInflater) return binding.root } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ReportMessageActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ReportMessageActivity.kt index 3d70206cb..b08d214da 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ReportMessageActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ReportMessageActivity.kt @@ -41,7 +41,7 @@ class ReportMessageActivity : BaseActivity() { component?.inject(this) } - override fun getContentView(): View { + override fun getContentView(layoutResId: Int?): View { binding = ActivityReportMessageBinding.inflate(layoutInflater) return binding.root } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SetupActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SetupActivity.kt index 99b23dbc3..7f078676c 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SetupActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SetupActivity.kt @@ -64,7 +64,7 @@ class SetupActivity : BaseActivity(), ViewPager.OnPageChangeListener { return R.layout.activity_setup } - override fun getContentView(): View { + override fun getContentView(layoutResId: Int?): View { binding = ActivitySetupBinding.inflate(layoutInflater) return binding.root } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillMemberActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillMemberActivity.kt index a041329ec..dc4a75519 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillMemberActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillMemberActivity.kt @@ -36,7 +36,7 @@ class SkillMemberActivity : BaseActivity() { component?.inject(this) } - override fun getContentView(): View { + override fun getContentView(layoutResId: Int?): View { binding = ActivitySkillMembersBinding.inflate(layoutInflater) return binding.root } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillTasksActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillTasksActivity.kt index 176fe5364..10c8a9062 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillTasksActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillTasksActivity.kt @@ -41,7 +41,7 @@ class SkillTasksActivity : BaseActivity() { loadTaskLists() } - override fun getContentView(): View { + override fun getContentView(layoutResId: Int?): View { binding = ActivitySkillTasksBinding.inflate(layoutInflater) return binding.root } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/TaskFormActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/TaskFormActivity.kt index 70fe0fc0a..a00a30eeb 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/TaskFormActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/TaskFormActivity.kt @@ -31,8 +31,6 @@ import com.habitrpg.android.habitica.data.TaskRepository import com.habitrpg.android.habitica.databinding.ActivityTaskFormBinding import com.habitrpg.android.habitica.extensions.OnChangeTextWatcher import com.habitrpg.android.habitica.extensions.addCancelButton -import com.habitrpg.common.habitica.extensions.dpToPx -import com.habitrpg.common.habitica.extensions.getThemeColor import com.habitrpg.android.habitica.helpers.ExceptionHandler import com.habitrpg.android.habitica.helpers.TaskAlarmManager import com.habitrpg.android.habitica.models.Tag @@ -48,9 +46,9 @@ import com.habitrpg.shared.habitica.models.tasks.Frequency import com.habitrpg.shared.habitica.models.tasks.HabitResetOption import com.habitrpg.shared.habitica.models.tasks.TaskType import io.realm.RealmList -import java.util.Date +import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.launch -import java.util.* +import java.util.Date import javax.inject.Inject class TaskFormActivity : BaseActivity() { @@ -105,7 +103,7 @@ class TaskFormActivity : BaseActivity() { return R.layout.activity_task_form } - override fun getContentView(): View { + override fun getContentView(layoutResId: Int?): View { binding = ActivityTaskFormBinding.inflate(layoutInflater) return binding.root } @@ -203,36 +201,32 @@ class TaskFormActivity : BaseActivity() { when { taskId != null -> { isCreating = false - compositeSubscription.add( - taskRepository.getUnmanagedTask(taskId).firstElement().subscribe( - { - if (!it.isValid) return@subscribe - task = it - initialTaskInstance = it - // tintColor = ContextCompat.getColor(this, it.mediumTaskColor) - fillForm(it) - it.challengeID?.let { challengeID -> - compositeSubscription.add( - challengeRepository.retrieveChallenge(challengeID) - .subscribe( - { challenge -> - this.challenge = challenge - binding.challengeNameView.text = getString(R.string.challenge_task_name, challenge.name) - binding.challengeNameView.visibility = View.VISIBLE - disableEditingForUneditableFieldsInChallengeTask() - }, - ExceptionHandler.rx() - ) + lifecycleScope.launch(ExceptionHandler.coroutine()) { + val task = taskRepository.getUnmanagedTask(taskId).firstOrNull() ?: return@launch + if (!task.isValid) return@launch + this@TaskFormActivity.task = task + initialTaskInstance = task + // tintColor = ContextCompat.getColor(this, it.mediumTaskColor) + fillForm(task) + task.challengeID?.let { challengeID -> + compositeSubscription.add( + challengeRepository.retrieveChallenge(challengeID) + .subscribe( + { challenge -> + this@TaskFormActivity.challenge = challenge + binding.challengeNameView.text = getString(R.string.challenge_task_name, challenge.name) + binding.challengeNameView.visibility = View.VISIBLE + disableEditingForUneditableFieldsInChallengeTask() + }, + ExceptionHandler.rx() ) - } - }, - ExceptionHandler.rx() - ) - ) + ) + } + } } bundle.containsKey(PARCELABLE_TASK) -> { isCreating = false - task = bundle.getParcelable(PARCELABLE_TASK) + task = bundle.getParcelable(PARCELABLE_TASK, Task::class.java) task?.let { fillForm(it) } } else -> { @@ -547,16 +541,13 @@ class TaskFormActivity : BaseActivity() { resultIntent.putExtra(TASK_TYPE_KEY, taskType.value) if (!isChallengeTask) { if (isCreating) { - if (isDiscardCancelled) { - analyticsManager.logEvent("back_to_task", bundleOf(Pair("is_creating", isCreating))) - } taskRepository.createTaskInBackground(thisTask) } else { - if (isDiscardCancelled) { - analyticsManager.logEvent("back_to_task", bundleOf(Pair("is_creating", isCreating))) - } taskRepository.updateTaskInBackground(thisTask) } + if (isDiscardCancelled) { + analyticsManager.logEvent("back_to_task", bundleOf(Pair("is_creating", isCreating))) + } if (thisTask.type == TaskType.DAILY || thisTask.type == TaskType.TODO) { taskAlarmManager.scheduleAlarmsForTask(thisTask) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/TaskSummaryActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/TaskSummaryActivity.kt new file mode 100644 index 000000000..0e53ac81f --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/TaskSummaryActivity.kt @@ -0,0 +1,58 @@ +package com.habitrpg.android.habitica.ui.activities + +import android.os.Bundle +import androidx.activity.compose.setContent +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.lifecycle.asLiveData +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.components.UserComponent +import com.habitrpg.android.habitica.data.TaskRepository +import com.habitrpg.android.habitica.ui.viewmodels.BaseViewModel +import javax.inject.Inject + +class TaskSummaryViewModel(val taskId: String) : BaseViewModel() { + @Inject + lateinit var taskRespository: TaskRepository + val task = taskRespository.getTask(taskId).asLiveData() + + override fun inject(component: UserComponent) { + component.inject(this) + } +} + +class TaskSummaryActivity: BaseActivity() { + override fun getLayoutResId(): Int? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + + } + } + + override fun injectActivity(component: UserComponent?) { + component?.inject(this) + } +} + +@Composable +fun TaskSummaryView(viewModel: TaskSummaryViewModel) { + val task by viewModel.task.observeAsState() + Column { + Text(stringResource(R.string.task_summary), Modifier) + } +} + +@Preview +@Composable +private fun TaskSummaryViewPreview() { + TaskSummaryView(TaskSummaryViewModel("")) +} \ No newline at end of file diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/VerifyUsernameActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/VerifyUsernameActivity.kt index 83d7c26f4..fde4b03c7 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/VerifyUsernameActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/VerifyUsernameActivity.kt @@ -39,7 +39,7 @@ class VerifyUsernameActivity : BaseActivity() { return R.layout.activity_verify_username } - override fun getContentView(): View { + override fun getContentView(layoutResId: Int?): View { binding = ActivityVerifyUsernameBinding.inflate(layoutInflater) return binding.root } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TaskRecyclerViewFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TaskRecyclerViewFragment.kt index 42c17ed82..23f0e279f 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TaskRecyclerViewFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TaskRecyclerViewFragment.kt @@ -1,7 +1,6 @@ package com.habitrpg.android.habitica.ui.fragments.tasks import android.content.Context -import android.content.Intent import android.content.SharedPreferences import android.graphics.drawable.BitmapDrawable import android.os.Bundle @@ -30,6 +29,7 @@ import com.habitrpg.android.habitica.helpers.HapticFeedbackManager import com.habitrpg.android.habitica.helpers.MainNavigationController import com.habitrpg.android.habitica.helpers.NotificationsManager import com.habitrpg.android.habitica.helpers.SoundManager +import com.habitrpg.android.habitica.models.tasks.ChecklistItem import com.habitrpg.android.habitica.models.tasks.Task import com.habitrpg.android.habitica.ui.activities.MainActivity import com.habitrpg.android.habitica.ui.activities.TaskFormActivity @@ -141,10 +141,9 @@ open class TaskRecyclerViewFragment : BaseFragment notificationsManager.dismissTaskNotification(it1, it.first) } }?.subscribeWithErrorHandler { scoreTask(it.first, it.second) }?.let { recyclerSubscription.add(it) } - recyclerAdapter?.checklistItemScoreEvents - ?.flatMap { - taskRepository.scoreChecklistItem(it.first.id ?: "", it.second.id ?: "") - }?.subscribeWithErrorHandler {}?.let { recyclerSubscription.add(it) } + recyclerAdapter?.checklistItemScoreEvents?.subscribeWithErrorHandler { + scoreChecklistItem(it.first, it.second) + }?.let { recyclerSubscription.add(it) } recyclerAdapter?.brokenTaskEvents?.subscribeWithErrorHandler { showBrokenChallengeDialog(it) }?.let { recyclerSubscription.add(it) } recyclerAdapter?.adventureGuideOpenEvents?.subscribeWithErrorHandler { MainNavigationController.navigate(R.id.adventureGuideActivity) }?.let { recyclerSubscription.add(it) } @@ -163,6 +162,12 @@ open class TaskRecyclerViewFragment : BaseFragment @@ -530,12 +535,12 @@ open class TaskRecyclerViewFragment : BaseFragment Unit ) { - compositeSubscription.add( + viewModelScope.launch(ExceptionHandler.coroutine()) { taskRepository.taskChecked( null, task.id ?: "", @@ -134,8 +134,8 @@ class TasksViewModel : BaseViewModel(), GroupPlanInfoProvider { putLong("last_task_reporting", Date().time) } } - }.subscribe({}, ExceptionHandler.rx()) - ) + } + } } private val filterSets: HashMap>>> = diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/NPCBannerView.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/NPCBannerView.kt index 2c894bdb0..ce2f650c4 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/NPCBannerView.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/NPCBannerView.kt @@ -10,11 +10,10 @@ import android.widget.ImageView import androidx.core.graphics.drawable.toBitmap import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.databinding.NpcBannerBinding -import com.habitrpg.android.habitica.helpers.ExceptionHandler import com.habitrpg.common.habitica.extensions.DataBindingUtils import com.habitrpg.common.habitica.extensions.layoutInflater -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers -import io.reactivex.rxjava3.core.Observable +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.launch import kotlin.math.roundToInt class NPCBannerView(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs) { @@ -50,14 +49,9 @@ class NPCBannerView(context: Context, attrs: AttributeSet?) : FrameLayout(contex val width = (height * aspectRatio).roundToInt() val drawable = BitmapDrawable(context.resources, Bitmap.createScaledBitmap(it.toBitmap(), width, height, false)) drawable.tileModeX = Shader.TileMode.REPEAT - Observable.just(drawable) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - { - binding.backgroundView.background = it - }, - ExceptionHandler.rx() - ) + MainScope().launch { + binding.backgroundView.background = it + } } } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/yesterdailies/YesterdailyDialog.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/yesterdailies/YesterdailyDialog.kt index 7ccb35cd3..155833d03 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/yesterdailies/YesterdailyDialog.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/yesterdailies/YesterdailyDialog.kt @@ -9,6 +9,7 @@ import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView import androidx.core.content.ContextCompat +import androidx.lifecycle.lifecycleScope import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.data.TaskRepository import com.habitrpg.android.habitica.data.UserRepository @@ -22,7 +23,7 @@ import com.habitrpg.common.habitica.extensions.isUsingNightModeResources import com.habitrpg.shared.habitica.models.tasks.TaskType import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.core.Observable -import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.MainScope import kotlinx.coroutines.launch import java.lang.ref.WeakReference import java.util.Calendar @@ -83,7 +84,7 @@ class YesterdailyDialog private constructor( } } lastCronRun = Date() - GlobalScope.launch(ExceptionHandler.coroutine()) { + MainScope().launch(ExceptionHandler.coroutine()) { userRepository.runCron(completedTasks) } displayedDialog = null @@ -141,12 +142,12 @@ class YesterdailyDialog private constructor( val checkboxHolder = checklistView.findViewById(R.id.checkBoxHolder) as? ViewGroup checkboxHolder?.setOnClickListener { _ -> item.completed = !item.completed - taskRepository.scoreChecklistItem(task.id ?: "", item.id ?: "").subscribe({ }, ExceptionHandler.rx()) + scoreChecklistItem(task, item) configureChecklistView(checklistView, task, item) } checklistView.setOnClickListener { item.completed = !item.completed - taskRepository.scoreChecklistItem(task.id ?: "", item.id ?: "").subscribe({ }, ExceptionHandler.rx()) + scoreChecklistItem(task, item) configureChecklistView(checklistView, task, item) } checkboxHolder?.setBackgroundResource( @@ -175,6 +176,15 @@ class YesterdailyDialog private constructor( ) } + private fun scoreChecklistItem( + task: Task, + item: ChecklistItem + ) { + lifecycleScope.launch(ExceptionHandler.coroutine()) { + taskRepository.scoreChecklistItem(task.id ?: "", item.id ?: "") + } + } + private fun configureTaskView(taskView: View, task: Task) { val completed = !task.isDisplayedActive val checkmark = taskView.findViewById(R.id.checkmark) @@ -274,7 +284,7 @@ class YesterdailyDialog private constructor( ) } else { lastCronRun = Date() - GlobalScope.launch(ExceptionHandler.coroutine()) { + MainScope().launch(ExceptionHandler.coroutine()) { userRepository.runCron() } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetProvider.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetProvider.kt index cd14793f1..2684fd174 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetProvider.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetProvider.kt @@ -10,6 +10,9 @@ import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.data.TaskRepository import com.habitrpg.android.habitica.helpers.ExceptionHandler import com.habitrpg.shared.habitica.models.responses.TaskDirection +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.launch import javax.inject.Inject class HabitButtonWidgetProvider : BaseWidgetProvider() { @@ -72,8 +75,12 @@ class HabitButtonWidgetProvider : BaseWidgetProvider() { val ids = intArrayOf(appWidgetId) if (taskId != null) { - userRepository.getUserFlowable().firstElement().flatMap { user -> taskRepository.taskChecked(user, taskId, TaskDirection.UP.text == direction, false, null) } - .subscribe({ taskDirectionData -> showToastForTaskDirection(context, taskDirectionData) }, ExceptionHandler.rx(), { this.onUpdate(context, mgr, ids) }) + MainScope().launch(ExceptionHandler.coroutine()) { + val user = userRepository.getUser().firstOrNull() + val response = taskRepository.taskChecked(user, taskId, TaskDirection.UP.text == direction, false, null) + showToastForTaskDirection(context, response) + this@HabitButtonWidgetProvider.onUpdate(context, mgr, ids) + } } } super.onReceive(context, intent) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetService.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetService.kt index 21786a1d5..52c469901 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetService.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetService.kt @@ -20,6 +20,9 @@ import com.habitrpg.android.habitica.helpers.ExceptionHandler import com.habitrpg.android.habitica.models.tasks.Task import com.habitrpg.common.habitica.helpers.MarkdownParser import com.habitrpg.shared.habitica.models.responses.TaskDirection +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.launch import javax.inject.Inject import kotlin.math.min @@ -42,10 +45,14 @@ class HabitButtonWidgetService : Service() { allWidgetIds = appWidgetManager?.getAppWidgetIds(thisWidget) makeTaskMapping() - for (taskid in this.taskMapping.keys) { - taskRepository.getUnmanagedTask(taskid).firstElement().subscribe({ this.updateData(it) }, ExceptionHandler.rx()) + MainScope().launch(ExceptionHandler.coroutine()) { + for (taskid in taskMapping.keys) { + val task = taskRepository.getUnmanagedTask(taskid).firstOrNull() + updateData(task) + } } + stopSelf() return START_STICKY diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/TaskListWidgetProvider.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/TaskListWidgetProvider.kt index 342b5894c..72bda08f9 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/TaskListWidgetProvider.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/TaskListWidgetProvider.kt @@ -14,6 +14,9 @@ import com.habitrpg.android.habitica.extensions.withImmutableFlag import com.habitrpg.android.habitica.extensions.withMutableFlag import com.habitrpg.android.habitica.helpers.ExceptionHandler import com.habitrpg.android.habitica.ui.activities.MainActivity +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.launch import javax.inject.Inject abstract class TaskListWidgetProvider : BaseWidgetProvider() { @@ -44,14 +47,12 @@ abstract class TaskListWidgetProvider : BaseWidgetProvider() { val taskId = intent.getStringExtra(TASK_ID_ITEM) if (taskId != null) { - userRepository.getUserFlowable().firstElement().flatMap { user -> taskRepository.taskChecked(user, taskId, up = true, force = false, notifyFunc = null) } - .subscribe( - { taskDirectionData -> - showToastForTaskDirection(context, taskDirectionData) - AppWidgetManager.getInstance(context).notifyAppWidgetViewDataChanged(appWidgetId, R.id.list_view) - }, - ExceptionHandler.rx() - ) + MainScope().launch(ExceptionHandler.coroutine()) { + val user = userRepository.getUser().firstOrNull() + val response = taskRepository.taskChecked(user, taskId, up = true, force = false, notifyFunc = null) + showToastForTaskDirection(context, response) + AppWidgetManager.getInstance(context).notifyAppWidgetViewDataChanged(appWidgetId, R.id.list_view) + } } } super.onReceive(context, intent) diff --git a/Habitica/src/test/java/com/habitrpg/android/habitica/data/implementation/TaskRepositoryImplTest.kt b/Habitica/src/test/java/com/habitrpg/android/habitica/data/implementation/TaskRepositoryImplTest.kt index 02905f207..bfa0ada14 100644 --- a/Habitica/src/test/java/com/habitrpg/android/habitica/data/implementation/TaskRepositoryImplTest.kt +++ b/Habitica/src/test/java/com/habitrpg/android/habitica/data/implementation/TaskRepositoryImplTest.kt @@ -81,10 +81,10 @@ class TaskRepositoryImplTest : WordSpec({ every { apiClient.postTaskDirection(any(), "up") } returns Flowable.just( TaskDirectionData() ) - every { localRepository.getUser("") } returns Flowable.just(user) + every { localRepository.getUserFlowable("") } returns Flowable.just(user) repository.taskChecked(null, task, true, false, null) eventually(5000) { - localRepository.getUser("") + localRepository.getUserFlowable("") } } "does not update user for team tasks" {