Finish 3.2.1

This commit is contained in:
Phillip Thelen 2021-03-11 14:39:36 +01:00
parent f20f7b13c3
commit 6176efdbb5
11 changed files with 118 additions and 149 deletions

View file

@ -150,7 +150,7 @@ android {
buildConfigField "String", "TESTING_LEVEL", "\"production\""
resConfigs "en", "bg", "de", "en-rGB", "es", "fr", "hr-rHR", "in", "it", "iw", "ja", "ko", "lt", "nl", "pl", "pt-rBR", "pt-rPT", "ru", "tr", "zh", "zh-rTW"
versionCode 2858
versionCode 2868
versionName "3.2.1"
}

View file

@ -935,8 +935,8 @@
<string name="mount_category">%s Monturas</string>
<string name="pet_category">%s Mascotas</string>
<string name="challenge_task_name">%s Tareas del Desafío</string>
<string name="delete_x_tasks">Eliminar %s Tareas</string>
<string name="keep_x_tasks">Mantener %s Tareas</string>
<string name="delete_x_tasks">Eliminar %d Tareas</string>
<string name="keep_x_tasks">Mantener %d Tareas</string>
<string name="broken_challenge">Desafío Terminado</string>
<string name="pms_disabled_description">Tu puedes enviar mensajes todavía, pero nadie puede enviartelas a ti. Puedes habilitarlo de nuevo desde Configuraciones.</string>
<string name="pms_disabled">Los Mensajes Privados están desactivados</string>

View file

@ -39,16 +39,24 @@ class UserRepositoryImpl(localRepository: UserLocalRepository, apiClient: ApiCli
override fun getUser(userID: String): Flowable<User> = localRepository.getUser(userID)
override fun updateUser(updateData: Map<String, Any>): Flowable<User> {
private fun updateUser(userID: String, updateData: Map<String, Any>): Flowable<User> {
return Flowable.zip(apiClient.updateUser(updateData),
localRepository.getUser(userID).firstElement().toFlowable(),
{ newUser, user -> mergeUser(user, newUser) })
}
override fun updateUser(key: String, value: Any): Flowable<User> {
private fun updateUser(userID: String, key: String, value: Any): Flowable<User> {
val updateData = HashMap<String, Any>()
updateData[key] = value
return updateUser(updateData)
return updateUser(userID, updateData)
}
override fun updateUser(updateData: Map<String, Any>): Flowable<User> {
return updateUser(userID, updateData)
}
override fun updateUser(key: String, value: Any): Flowable<User> {
return updateUser(userID, key, value)
}
override fun retrieveUser(withTasks: Boolean): Flowable<User> =
@ -76,7 +84,7 @@ class UserRepositoryImpl(localRepository: UserLocalRepository, apiClient: ApiCli
val timeZone = calendar.timeZone
val offset = -TimeUnit.MINUTES.convert(timeZone.getOffset(calendar.timeInMillis).toLong(), TimeUnit.MILLISECONDS)
if (offset.toInt() != user.preferences?.timezoneOffset ?: 0) {
return@flatMap updateUser("preferences.timezoneOffset", offset.toString())
return@flatMap updateUser(user.id ?: "", "preferences.timezoneOffset", offset.toString())
} else {
return@flatMap Flowable.just(user)
}

View file

@ -1,48 +0,0 @@
package com.habitrpg.android.habitica.models.auth;
/**
* Created by magicmicky on 04/02/15.
*/
public class UserAuthResponse {
//we need apiToken and token, as both are possible returns
private String apiToken;
private String token;
private Boolean newUser = false;
private String id;
public String getToken() {
if (this.token == null) {
return this.apiToken;
} else {
return this.token;
}
}
public void setToken(String token) {
this.token = token;
}
public String getApiToken() {
return apiToken;
}
public void setApiToken(String apiToken) {
this.apiToken = apiToken;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Boolean getNewUser() {
return newUser;
}
public void setNewUser(Boolean newUser) {
this.newUser = newUser;
}
}

View file

@ -0,0 +1,15 @@
package com.habitrpg.android.habitica.models.auth
class UserAuthResponse {
//we need apiToken and token, as both are possible returns
var apiToken: String = ""
var token: String
get() {
return apiToken
}
set(value) {
apiToken = value
}
var newUser = false
var id: String = ""
}

View file

@ -364,7 +364,7 @@ class LoginActivity : BaseActivity(), Consumer<UserAuthResponse> {
hideProgress()
dismissKeyboard()
try {
saveTokens(userAuthResponse.token, userAuthResponse.id)
saveTokens(userAuthResponse.apiToken, userAuthResponse.id)
} catch (e: Exception) {
crashlyticsProxy.logException(e)
}
@ -375,7 +375,7 @@ class LoginActivity : BaseActivity(), Consumer<UserAuthResponse> {
FirebaseAnalytics.getInstance(this).logEvent("user_registered", null)
}
compositeSubscription.add(userRepository.retrieveUser(true)
compositeSubscription.add(userRepository.retrieveUser(true, true)
.subscribe({
if (userAuthResponse.newUser) {
this.startSetupActivity()

View file

@ -14,7 +14,6 @@ import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.subscribeWithErrorHandler
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.shops.ShopItem
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.ui.activities.MainActivity
import com.habitrpg.android.habitica.ui.activities.SkillMemberActivity
import com.habitrpg.android.habitica.ui.adapter.tasks.RewardsRecyclerViewAdapter
@ -123,7 +122,7 @@ class RewardsRecyclerviewFragment : TaskRecyclerViewFragment() {
fun newInstance(context: Context?, classType: String, showCustomRewards: Boolean): RewardsRecyclerviewFragment {
val fragment = RewardsRecyclerviewFragment()
fragment.retainInstance = true
fragment.classType = classType
fragment.taskType = classType
fragment.showCustomRewards = showCustomRewards
if (context != null) {

View file

@ -28,8 +28,6 @@ import com.habitrpg.android.habitica.helpers.TaskFilterHelper
import com.habitrpg.android.habitica.models.responses.TaskDirection
import com.habitrpg.android.habitica.models.responses.TaskScoringResult
import com.habitrpg.android.habitica.models.tasks.Task
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.modules.AppModule
import com.habitrpg.android.habitica.ui.activities.MainActivity
import com.habitrpg.android.habitica.ui.activities.TaskFormActivity
import com.habitrpg.android.habitica.ui.adapter.BaseRecyclerViewAdapter
@ -44,7 +42,6 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.inject.Named
open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBinding>(), androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener {
internal var canEditTasks: Boolean = true
@ -75,17 +72,17 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
internal var layoutManager: RecyclerView.LayoutManager? = null
internal var classType: String? = null
internal var taskType: String = Task.TYPE_HABIT
private var itemTouchCallback: ItemTouchHelper.Callback? = null
var refreshAction: ((() -> Unit) -> Unit)? = null
internal val className: String
get() = this.classType ?: ""
get() = this.taskType ?: ""
// TODO needs a bit of cleanup
private fun setInnerAdapter() {
val adapter: BaseRecyclerViewAdapter<*, *>? = when (this.classType) {
val adapter: BaseRecyclerViewAdapter<*, *>? = when (this.taskType) {
Task.TYPE_HABIT -> {
HabitsRecyclerViewAdapter(null, true, R.layout.habit_item_card, taskFilterHelper)
}
@ -106,10 +103,29 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
binding?.recyclerView?.adapter = adapter
context?.let { recyclerAdapter?.taskDisplayMode = configManager.taskDisplayMode(it) }
recyclerAdapter?.errorButtonEvents?.subscribe({
taskRepository.syncErroredTasks().subscribe({}, RxErrorHandler.handleEmptyError())
}, RxErrorHandler.handleEmptyError())?.let { compositeSubscription.add(it) }
recyclerAdapter?.taskOpenEvents?.subscribeWithErrorHandler {
openTaskForm(it)
}?.let { compositeSubscription.add(it) }
recyclerAdapter?.taskScoreEvents
?.doOnNext { playSound(it.second) }
?.subscribeWithErrorHandler { scoreTask(it.first, it.second) }?.let { compositeSubscription.add(it) }
recyclerAdapter?.checklistItemScoreEvents
?.flatMap { taskRepository.scoreChecklistItem(it.first.id ?: "", it.second.id ?: "")
}?.subscribeWithErrorHandler {}?.let { compositeSubscription.add(it) }
recyclerAdapter?.brokenTaskEvents?.subscribeWithErrorHandler { showBrokenChallengeDialog(it) }?.let { compositeSubscription.add(it) }
compositeSubscription.add(taskRepository.getTasks(this.taskType, ownerID).subscribe({
this.recyclerAdapter?.updateUnfilteredData(it)
this.recyclerAdapter?.filter()
}, RxErrorHandler.handleEmptyError()))
}
private fun handleTaskResult(result: TaskScoringResult, value: Int) {
if (classType == Task.TYPE_REWARD) {
if (taskType == Task.TYPE_REWARD) {
(activity as? MainActivity)?.let { activity ->
HabiticaSnackbar.showSnackbar(activity.snackbarContainer, null, getString(R.string.notification_purchase_reward),
BitmapDrawable(resources, HabiticaIconsHelper.imageOfGold()),
@ -123,7 +139,7 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
}
private fun playSound(direction: TaskDirection) {
val soundName = when (classType) {
val soundName = when (taskType) {
Task.TYPE_HABIT -> if (direction == TaskDirection.UP) SoundManager.SoundPlusHabit else SoundManager.SoundMinusHabit
Task.TYPE_DAILY -> SoundManager.SoundDaily
Task.TYPE_TODO -> SoundManager.SoundTodo
@ -160,13 +176,13 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
context?.let { binding?.recyclerView?.setBackgroundColor(ContextCompat.getColor(it, R.color.content_background)) }
savedInstanceState?.let { this.classType = savedInstanceState.getString(CLASS_TYPE_KEY, "") }
savedInstanceState?.let { this.taskType = savedInstanceState.getString(CLASS_TYPE_KEY, "") }
when (classType) {
when (taskType) {
Task.TYPE_TODO -> taskFilterHelper.setActiveFilter(Task.TYPE_TODO, Task.FILTER_ACTIVE)
Task.TYPE_DAILY -> {
val user = (activity as? MainActivity)?.user
if (user?.isValid == true && user?.preferences?.dailyDueDefaultView == true) {
if (user?.isValid == true && user.preferences?.dailyDueDefaultView == true) {
taskFilterHelper.setActiveFilter(Task.TYPE_DAILY, Task.FILTER_ACTIVE)
}
}
@ -221,7 +237,7 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
if (validTaskId != null) {
recyclerAdapter?.ignoreUpdates = true
compositeSubscription.add(taskRepository.updateTaskPosition(
classType ?: "", validTaskId, viewHolder.adapterPosition
taskType, validTaskId, viewHolder.adapterPosition
)
.delay(1, TimeUnit.SECONDS)
.observeOn(AndroidSchedulers.mainThread())
@ -246,27 +262,6 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
allowReordering()
if (this.classType != null) {
recyclerAdapter?.errorButtonEvents?.subscribe({
taskRepository.syncErroredTasks().subscribe({}, RxErrorHandler.handleEmptyError())
}, RxErrorHandler.handleEmptyError())?.let { compositeSubscription.add(it) }
recyclerAdapter?.taskOpenEvents?.subscribeWithErrorHandler {
openTaskForm(it)
}?.let { compositeSubscription.add(it) }
recyclerAdapter?.taskScoreEvents
?.doOnNext { playSound(it.second) }
?.subscribeWithErrorHandler { scoreTask(it.first, it.second) }?.let { compositeSubscription.add(it) }
recyclerAdapter?.checklistItemScoreEvents
?.flatMap { taskRepository.scoreChecklistItem(it.first.id ?: "", it.second.id ?: "")
}?.subscribeWithErrorHandler {}?.let { compositeSubscription.add(it) }
recyclerAdapter?.brokenTaskEvents?.subscribeWithErrorHandler { showBrokenChallengeDialog(it) }?.let { compositeSubscription.add(it) }
compositeSubscription.add(taskRepository.getTasks(this.classType ?: "", ownerID).subscribe({
this.recyclerAdapter?.updateUnfilteredData(it)
this.recyclerAdapter?.filter()
}, RxErrorHandler.handleEmptyError()))
}
val bottomPadding = ((binding?.recyclerView?.paddingBottom ?: 0) + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 60f, resources.displayMetrics)).toInt()
binding?.recyclerView?.setPadding(0, 0, 0, bottomPadding)
binding?.recyclerView?.itemAnimator = itemAnimator
@ -319,52 +314,50 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
}
private fun setEmptyLabels() {
if (this.classType != null) {
binding?.recyclerView?.setEmptyView(binding?.emptyView)
context?.let { binding?.emptyIconView?.setColorFilter(ContextCompat.getColor(it, R.color.text_dimmed), android.graphics.PorterDuff.Mode.MULTIPLY) }
if (taskFilterHelper.howMany(classType) > 0) {
when (this.classType) {
Task.TYPE_HABIT -> {
binding?.emptyIconView?.setImageResource(R.drawable.icon_habits)
binding?.emptyViewTitle?.setText(R.string.empty_title_habits_filtered)
binding?.emptyViewDescription?.setText(R.string.empty_description_habits_filtered)
}
Task.TYPE_DAILY -> {
binding?.emptyIconView?.setImageResource(R.drawable.icon_dailies)
binding?.emptyViewTitle?.setText(R.string.empty_title_dailies_filtered)
binding?.emptyViewDescription?.setText(R.string.empty_description_dailies_filtered)
}
Task.TYPE_TODO -> {
binding?.emptyIconView?.setImageResource(R.drawable.icon_todos)
binding?.emptyViewTitle?.setText(R.string.empty_title_todos_filtered)
binding?.emptyViewDescription?.setText(R.string.empty_description_todos_filtered)
}
Task.TYPE_REWARD -> {
binding?.emptyIconView?.setImageResource(R.drawable.icon_rewards)
binding?.emptyViewTitle?.setText(R.string.empty_title_rewards)
}
binding?.recyclerView?.setEmptyView(binding?.emptyView)
context?.let { binding?.emptyIconView?.setColorFilter(ContextCompat.getColor(it, R.color.text_dimmed), android.graphics.PorterDuff.Mode.MULTIPLY) }
if (taskFilterHelper.howMany(taskType) > 0) {
when (this.taskType) {
Task.TYPE_HABIT -> {
binding?.emptyIconView?.setImageResource(R.drawable.icon_habits)
binding?.emptyViewTitle?.setText(R.string.empty_title_habits_filtered)
binding?.emptyViewDescription?.setText(R.string.empty_description_habits_filtered)
}
} else {
when (this.classType) {
Task.TYPE_HABIT -> {
binding?.emptyIconView?.setImageResource(R.drawable.icon_habits)
binding?.emptyViewTitle?.setText(R.string.empty_title_habits)
binding?.emptyViewDescription?.setText(R.string.empty_description_habits)
}
Task.TYPE_DAILY -> {
binding?.emptyIconView?.setImageResource(R.drawable.icon_dailies)
binding?.emptyViewTitle?.setText(R.string.empty_title_dailies)
binding?.emptyViewDescription?.setText(R.string.empty_description_dailies)
}
Task.TYPE_TODO -> {
binding?.emptyIconView?.setImageResource(R.drawable.icon_todos)
binding?.emptyViewTitle?.setText(R.string.empty_title_todos)
binding?.emptyViewDescription?.setText(R.string.empty_description_todos)
}
Task.TYPE_REWARD -> {
binding?.emptyIconView?.setImageResource(R.drawable.icon_rewards)
binding?.emptyViewTitle?.setText(R.string.empty_title_rewards)
}
Task.TYPE_DAILY -> {
binding?.emptyIconView?.setImageResource(R.drawable.icon_dailies)
binding?.emptyViewTitle?.setText(R.string.empty_title_dailies_filtered)
binding?.emptyViewDescription?.setText(R.string.empty_description_dailies_filtered)
}
Task.TYPE_TODO -> {
binding?.emptyIconView?.setImageResource(R.drawable.icon_todos)
binding?.emptyViewTitle?.setText(R.string.empty_title_todos_filtered)
binding?.emptyViewDescription?.setText(R.string.empty_description_todos_filtered)
}
Task.TYPE_REWARD -> {
binding?.emptyIconView?.setImageResource(R.drawable.icon_rewards)
binding?.emptyViewTitle?.setText(R.string.empty_title_rewards)
}
}
} else {
when (this.taskType) {
Task.TYPE_HABIT -> {
binding?.emptyIconView?.setImageResource(R.drawable.icon_habits)
binding?.emptyViewTitle?.setText(R.string.empty_title_habits)
binding?.emptyViewDescription?.setText(R.string.empty_description_habits)
}
Task.TYPE_DAILY -> {
binding?.emptyIconView?.setImageResource(R.drawable.icon_dailies)
binding?.emptyViewTitle?.setText(R.string.empty_title_dailies)
binding?.emptyViewDescription?.setText(R.string.empty_description_dailies)
}
Task.TYPE_TODO -> {
binding?.emptyIconView?.setImageResource(R.drawable.icon_todos)
binding?.emptyViewTitle?.setText(R.string.empty_title_todos)
binding?.emptyViewDescription?.setText(R.string.empty_description_todos)
}
Task.TYPE_REWARD -> {
binding?.emptyIconView?.setImageResource(R.drawable.icon_rewards)
binding?.emptyViewTitle?.setText(R.string.empty_title_rewards)
}
}
}
@ -378,11 +371,11 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString(CLASS_TYPE_KEY, this.classType)
outState.putString(CLASS_TYPE_KEY, this.taskType)
}
override val displayedClassName: String?
get() = this.classType + super.displayedClassName
get() = this.taskType + super.displayedClassName
override fun onRefresh() {
binding?.refreshLayout?.isRefreshing = true
@ -394,10 +387,13 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
override fun onResume() {
super.onResume()
context?.let { recyclerAdapter?.taskDisplayMode = configManager.taskDisplayMode(it) }
if (binding?.recyclerView?.adapter == null || recyclerAdapter == null) {
setInnerAdapter()
}
}
fun setActiveFilter(activeFilter: String) {
taskFilterHelper.setActiveFilter(classType ?: "", activeFilter)
taskFilterHelper.setActiveFilter(taskType, activeFilter)
recyclerAdapter?.filter()
setEmptyLabels()
@ -431,10 +427,10 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
fun newInstance(context: Context?, classType: String): TaskRecyclerViewFragment {
val fragment = TaskRecyclerViewFragment()
fragment.retainInstance = true
fragment.classType = classType
fragment.taskType = classType
var tutorialTexts: List<String>? = null
if (context != null) {
when (fragment.classType) {
when (fragment.taskType) {
Task.TYPE_HABIT -> {
fragment.tutorialStepIdentifier = "habits"
tutorialTexts = listOf(context.getString(R.string.tutorial_overview), context.getString(R.string.tutorial_habits_1), context.getString(R.string.tutorial_habits_2), context.getString(R.string.tutorial_habits_3), context.getString(R.string.tutorial_habits_4))

View file

@ -18,7 +18,6 @@ import com.habitrpg.android.habitica.extensions.getThemeColor
import com.habitrpg.android.habitica.extensions.setTintWith
import com.habitrpg.android.habitica.helpers.*
import com.habitrpg.android.habitica.models.tasks.Task
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.modules.AppModule
import com.habitrpg.android.habitica.ui.activities.TaskFormActivity
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
@ -178,7 +177,7 @@ class TasksFragment : BaseMainFragment<FragmentViewpagerBinding>(), SearchView.O
disposable = tagRepository.getTags().subscribe({ tagsList -> dialog.setTags(tagsList)}, RxErrorHandler.handleEmptyError())
dialog.setActiveTags(taskFilterHelper.tags)
if (activeFragment != null) {
val taskType = activeFragment?.classType
val taskType = activeFragment?.taskType
if (taskType != null) {
dialog.setTaskType(taskType, taskFilterHelper.getActiveFilter(taskType))
}
@ -257,9 +256,9 @@ class TasksFragment : BaseMainFragment<FragmentViewpagerBinding>(), SearchView.O
}
private fun updateFilterIcon() {
val filterCount = taskFilterHelper.howMany(activeFragment?.classType)
val filterCount = taskFilterHelper.howMany(activeFragment?.taskType)
filterMenuItem?.isVisible = activeFragment?.classType != Task.TYPE_REWARD
filterMenuItem?.isVisible = activeFragment?.taskType != Task.TYPE_REWARD
if (filterCount == 0) {
filterMenuItem?.setIcon(R.drawable.ic_action_filter_list)
context?.let {

View file

@ -178,7 +178,7 @@ class TeamBoardFragment : BaseMainFragment<FragmentViewpagerBinding>(), SearchVi
disposable = tagRepository.getTags().subscribe({ tagsList -> dialog.setTags(tagsList)}, RxErrorHandler.handleEmptyError())
dialog.setActiveTags(taskFilterHelper.tags)
if (activeFragment != null) {
val taskType = activeFragment?.classType
val taskType = activeFragment?.taskType
if (taskType != null) {
dialog.setTaskType(taskType, taskFilterHelper.getActiveFilter(taskType))
}
@ -265,7 +265,7 @@ class TeamBoardFragment : BaseMainFragment<FragmentViewpagerBinding>(), SearchVi
}
var filterCount = 0
if (activeFragment != null) {
filterCount = taskFilterHelper.howMany(activeFragment?.classType)
filterCount = taskFilterHelper.howMany(activeFragment?.taskType)
}
if (filterCount == 0) {
filterMenuItem?.setIcon(R.drawable.ic_action_filter_list)

View file

@ -1 +1 @@
In this update we've cleaned up Guilds and Challenges to look better than ever. Check them out and maybe find something new! Starting or leaving a Quest will update the Party view right away. Snowballs will actually transform your party members now. Certain notifications work better, bringing you to the right place and no longer crashing the app in some cases. Subscriber Mystery Boxes have a fancy new view when opening as well. Check these out and lots of other fixes by downloading the update!
Weve added the ability to select different default launch screens! Do you mainly use Dailies or find yourself constantly checking off To Dos? Now you can set the app to automatically launch to either of those screens, and more! Go to Settings to explore the various launch screen options. Weve also fixed a few bugs with views not updating automatically. Hope you enjoy this newest Habitica update!