diff --git a/Habitica/build.gradle b/Habitica/build.gradle
index 2927958d8..1702f34fe 100644
--- a/Habitica/build.gradle
+++ b/Habitica/build.gradle
@@ -153,7 +153,7 @@ android {
buildConfigField "String", "TESTING_LEVEL", "\"production\""
multiDexEnabled true
- versionCode 2103
+ versionCode 2105
versionName "1.9"
}
diff --git a/Habitica/res/layout/activity_task_form.xml b/Habitica/res/layout/activity_task_form.xml
index 15e578153..190e80ef3 100644
--- a/Habitica/res/layout/activity_task_form.xml
+++ b/Habitica/res/layout/activity_task_form.xml
@@ -23,6 +23,7 @@
@@ -78,7 +79,7 @@
android:id="@+id/reward_value_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="@string/checklist"
+ android:text="@string/cost"
style="@style/TaskFormSectionheader"/>
+ android:layout_marginRight="@dimen/spacing_large"
+ android:background="@drawable/rounded_avatar_bg">
+ style="@style/HabiticaButton.Purple" />
+ style="@style/HabiticaButton.Green"
+ android:layout_marginStart="@dimen/spacing_large"/>
-
+
+ android:layout_height="40dp"
+ android:clipChildren="true"
+ android:layout_marginRight="@dimen/spacing_large"
+ android:background="@drawable/rounded_avatar_bg">
-
+ android:layout_height="78dp"
+ android:layout_gravity="center"
+ android:paddingTop="8dp"/>
+
+ android:text="@string/leave_guild"/>
\ No newline at end of file
diff --git a/Habitica/res/layout/spinner_item.xml b/Habitica/res/layout/spinner_item.xml
new file mode 100644
index 000000000..6e260b60f
--- /dev/null
+++ b/Habitica/res/layout/spinner_item.xml
@@ -0,0 +1,11 @@
+
\ No newline at end of file
diff --git a/Habitica/res/layout/tavern_chat_item.xml b/Habitica/res/layout/tavern_chat_item.xml
index 139e50469..c35b821d2 100644
--- a/Habitica/res/layout/tavern_chat_item.xml
+++ b/Habitica/res/layout/tavern_chat_item.xml
@@ -14,7 +14,6 @@
android:layout_width="@dimen/avatar_chat_size"
android:layout_height="@dimen/avatar_chat_size"
android:clipChildren="true"
- android:layout_marginRight="@dimen/spacing_medium"
android:layout_marginEnd="@dimen/spacing_medium"
android:background="@drawable/rounded_avatar_bg">
I agree to follow the guidelines
Report Violation
t
+ Leave Guild
+ You left the guild
+ You joined the guild
+ Cost
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/tasks/Days.java b/Habitica/src/main/java/com/habitrpg/android/habitica/models/tasks/Days.java
index b37700be0..aac0f3e37 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/tasks/Days.java
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/tasks/Days.java
@@ -117,7 +117,13 @@ public class Days extends RealmObject implements Parcelable {
}
public Days() {
-
+ m = true;
+ t = true;
+ w = true;
+ th = true;
+ f = true;
+ s = true;
+ su = true;
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
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 0efd1b8bc..3c9ac7bee 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
@@ -21,6 +21,7 @@ import androidx.appcompat.widget.Toolbar
import androidx.core.content.ContextCompat
import androidx.core.view.children
import androidx.core.view.forEachIndexed
+import androidx.core.widget.NestedScrollView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.AppComponent
import com.habitrpg.android.habitica.data.TagRepository
@@ -51,6 +52,7 @@ class TaskFormActivity : BaseActivity() {
lateinit var tagRepository: TagRepository
private val toolbar: Toolbar by bindView(R.id.toolbar)
+ private val scrollView: NestedScrollView by bindView(R.id.scroll_view)
private val upperTextWrapper: LinearLayout by bindView(R.id.upper_text_wrapper)
private val textEditText: EditText by bindView(R.id.text_edit_text)
private val notesEditText: EditText by bindView(R.id.notes_edit_text)
@@ -150,6 +152,9 @@ class TaskFormActivity : BaseActivity() {
statIntelligenceButton.setOnClickListener { selectedStat = Stats.INTELLIGENCE }
statConstitutionButton.setOnClickListener { selectedStat = Stats.CONSTITUTION }
statPerceptionButton.setOnClickListener { selectedStat = Stats.PERCEPTION }
+ scrollView.setOnScrollChangeListener { _: NestedScrollView?, _: Int, _: Int, _: Int, _: Int ->
+ dismissKeyboard()
+ }
if (taskId != null) {
isCreating = false
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/SimpleSpinnerAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/SimpleSpinnerAdapter.kt
new file mode 100644
index 000000000..274a1556b
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/SimpleSpinnerAdapter.kt
@@ -0,0 +1,25 @@
+package com.habitrpg.android.habitica.ui.adapter
+
+import android.content.Context
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ArrayAdapter
+import android.widget.TextView
+import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.extensions.inflate
+
+class SimpleSpinnerAdapter(context: Context, resource: Int) : ArrayAdapter(context, resource, R.id.textView, context.resources.getTextArray(resource)) {
+
+ override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup?): View {
+ val view = parent?.inflate(R.layout.spinner_item, false) ?: View(context)
+ (view as? TextView)?.text = getItem(position)
+ return view
+ }
+
+ override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
+ val view = parent?.inflate(android.R.layout.simple_spinner_item, false) ?: View(context)
+ (view as? TextView)?.text = getItem(position)
+ return view
+ }
+
+}
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/HabiticaClassArrayAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/HabiticaClassArrayAdapter.kt
index 259037bb8..ab2d72060 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/HabiticaClassArrayAdapter.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/HabiticaClassArrayAdapter.kt
@@ -1,12 +1,12 @@
package com.habitrpg.android.habitica.ui.adapter.inventory
import android.content.Context
-import androidx.core.content.ContextCompat
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.ImageView
import android.widget.TextView
+import androidx.core.content.ContextCompat
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.inflate
import com.habitrpg.android.habitica.models.user.Stats
@@ -54,5 +54,4 @@ class HabiticaClassArrayAdapter(context: Context, resource: Int, objects: List?, autoUpdate: Boolean) : RealmRecyclerViewAdapter(data, autoUpdate), Filterable {
- var apiClient: ApiClient? = null
+ var socialRepository: SocialRepository? = null
private var memberGuildIDs: MutableList = mutableListOf()
fun setMemberGuildIDs(memberGuildIDs: MutableList) {
@@ -46,16 +47,16 @@ class PublicGuildsRecyclerViewAdapter(data: OrderedRealmCollection?, auto
val guild = v.tag as? Group ?: return@setOnClickListener
val isMember = this.memberGuildIDs.contains(guild.id)
if (isMember) {
- this@PublicGuildsRecyclerViewAdapter.apiClient?.leaveGroup(guild.id)
+ this@PublicGuildsRecyclerViewAdapter.socialRepository?.leaveGroup(guild.id)
?.subscribe(Consumer {
memberGuildIDs.remove(guild.id)
if (data != null) {
- val indexOfGroup = data!!.indexOf(guild)
- notifyItemChanged(indexOfGroup)
+ val indexOfGroup = data?.indexOf(guild)
+ notifyItemChanged(indexOfGroup ?: 0)
}
}, RxErrorHandler.handleEmptyError())
} else {
- this@PublicGuildsRecyclerViewAdapter.apiClient?.joinGroup(guild.id)
+ this@PublicGuildsRecyclerViewAdapter.socialRepository?.joinGroup(guild.id)
?.subscribe(Consumer { group ->
memberGuildIDs.add(group.id)
if (data != null) {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/StatsFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/StatsFragment.kt
index 7a09b89cf..708900ddc 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/StatsFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/StatsFragment.kt
@@ -1,12 +1,11 @@
package com.habitrpg.android.habitica.ui.fragments
import android.app.AlertDialog
-import android.os.Build
import android.os.Bundle
-import androidx.core.content.ContextCompat
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.core.content.ContextCompat
import com.habitrpg.android.habitica.HabiticaBaseApplication
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.AppComponent
@@ -18,6 +17,7 @@ import com.habitrpg.android.habitica.extensions.setScaledPadding
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.helpers.UserStatComputer
import com.habitrpg.android.habitica.models.user.Stats
+import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.modules.AppModule
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
import com.habitrpg.android.habitica.ui.views.stats.BulkAllocateStatsDialog
@@ -30,6 +30,7 @@ import javax.inject.Named
class StatsFragment: BaseMainFragment() {
+ private var canAllocatePoints: Boolean = false
@field:[Inject Named(AppModule.NAMED_USER_ID)]
lateinit var userId: String
@@ -84,10 +85,10 @@ class StatsFragment: BaseMainFragment() {
}
compositeSubscription.add(userRepository.getUser(userId).subscribe(Consumer {
- user = it
- unlock_at_level.visibility = if (user?.stats?.lvl ?: 0 < 10) View.VISIBLE else View.GONE
- updateStats()
- updateAttributePoints()
+ canAllocatePoints = user?.stats?.lvl ?: 0 >= 10 && user?.stats?.points ?: 0 > 0
+ unlock_at_level.visibility = if (it?.stats?.lvl ?: 0 < 10) View.VISIBLE else View.GONE
+ updateStats(it)
+ updateAttributePoints(it)
}, RxErrorHandler.handleEmptyError()))
distributeEvenlyButton.setOnCheckedChangeListener { _, isChecked ->
@@ -120,11 +121,8 @@ class StatsFragment: BaseMainFragment() {
distributeTaskHelpButton.setOnClickListener { showHelpAlert(R.string.distribute_task_help) }
statsAllocationButton.setOnClickListener {
- if (user?.stats?.points ?: 0 > 0) {
- val lvl = user?.stats?.lvl
- if (lvl != null && lvl >= 10) {
- showBulkAllocateDialog()
- }
+ if (canAllocatePoints) {
+ showBulkAllocateDialog()
}
}
}
@@ -153,18 +151,18 @@ class StatsFragment: BaseMainFragment() {
compositeSubscription.add(userRepository.allocatePoint(user, stat).subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
}
- private fun updateAttributePoints() {
- val automaticAllocation = user?.preferences?.automaticAllocation ?: false
+ private fun updateAttributePoints(user: User) {
+ val automaticAllocation = user.preferences?.automaticAllocation ?: false
automaticAllocationSwitch.isChecked = automaticAllocation
autoAllocationModeWrapper.visibility = if (automaticAllocation) View.VISIBLE else View.GONE
- val allocationMode = user?.preferences?.allocationMode ?: ""
+ val allocationMode = user.preferences?.allocationMode ?: ""
distributeEvenlyButton.isChecked = allocationMode == Stats.AUTO_ALLOCATE_FLAT
distributeClassButton.isChecked = allocationMode == Stats.AUTO_ALLOCATE_CLASSBASED
distributeTaskButton.isChecked = allocationMode == Stats.AUTO_ALLOCATE_TASKBASED
- val canDistributePoints = 0 < (user?.stats?.points ?: 0) && 10 <= (user?.stats?.lvl ?: 0)
- if (10 <= (user?.stats?.lvl ?: 0)) {
+ val canDistributePoints = 0 < (user.stats?.points ?: 0) && 10 <= (user.stats?.lvl ?: 0)
+ if (10 <= (user.stats?.lvl ?: 0)) {
automaticAllocationSwitch.visibility = View.VISIBLE
automaticAllocationSwitch.visibility = View.VISIBLE
} else {
@@ -177,12 +175,10 @@ class StatsFragment: BaseMainFragment() {
perceptionStatsView.canDistributePoints = canDistributePoints
context.notNull { context ->
if (canDistributePoints) {
- val points = user?.stats?.points ?: 0
+ val points = user.stats?.points ?: 0
numberOfPointsTextView.text = getString(R.string.points_to_allocate, points)
numberOfPointsTextView.setTextColor(ContextCompat.getColor(context, R.color.white))
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- numberOfPointsTextView.background = ContextCompat.getDrawable(context, R.drawable.button_gray_100)
- }
+ numberOfPointsTextView.background = ContextCompat.getDrawable(context, R.drawable.button_gray_100)
leftSparklesView.visibility = View.VISIBLE
rightSparklesView.visibility = View.VISIBLE
} else {
@@ -200,8 +196,7 @@ class StatsFragment: BaseMainFragment() {
component.inject(this)
}
- private fun updateStats() {
- val currentUser = user ?: return
+ private fun updateStats(currentUser: User) {
val levelStat = Math.min((currentUser.stats?.lvl ?: 0) / 2.0f, 50f).toInt()
totalStrength = levelStat
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/faq/FAQOverviewFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/faq/FAQOverviewFragment.kt
index b4a8cc6df..0350c07a8 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/faq/FAQOverviewFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/faq/FAQOverviewFragment.kt
@@ -57,7 +57,7 @@ class FAQOverviewFragment : BaseMainFragment() {
}
private fun loadArticles() {
- if (user == null || adapter == null) {
+ if (adapter == null) {
return
}
compositeSubscription.add(faqRepository.getArticles().subscribe(Consumer { adapter?.setArticles(it) }, RxErrorHandler.handleEmptyError()))
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/ChatFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/ChatFragment.kt
index 8975b9981..d2f16aacf 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/ChatFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/ChatFragment.kt
@@ -18,8 +18,8 @@ import com.habitrpg.android.habitica.MainNavDirections
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.AppComponent
import com.habitrpg.android.habitica.extensions.notNull
-import com.habitrpg.android.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.helpers.AppConfigManager
+import com.habitrpg.android.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.social.ChatMessage
import com.habitrpg.android.habitica.ui.activities.FullProfileActivity
@@ -27,7 +27,7 @@ import com.habitrpg.android.habitica.ui.activities.MainActivity
import com.habitrpg.android.habitica.ui.adapter.social.ChatRecyclerViewAdapter
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator
-import com.habitrpg.android.habitica.ui.viewmodels.PartyViewModel
+import com.habitrpg.android.habitica.ui.viewmodels.GroupViewModel
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar.Companion.showSnackbar
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar.SnackbarDisplayType
import io.reactivex.Observable
@@ -42,7 +42,7 @@ import javax.inject.Inject
class ChatFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener {
- var viewModel: PartyViewModel? = null
+ var viewModel: GroupViewModel? = null
@Inject
lateinit var configManager: AppConfigManager
@@ -52,6 +52,7 @@ class ChatFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener {
private var navigatedOnceToFragment = false
private var isScrolledToTop = true
private var refreshDisposable: Disposable? = null
+ var autocompleteContext: String = ""
override fun injectFragment(component: AppComponent) {
component.inject(this)
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/GuildDetailFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/GuildDetailFragment.kt
index fe0e35e66..489a0c3df 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/GuildDetailFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/GuildDetailFragment.kt
@@ -9,33 +9,28 @@ import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.TextView
+import androidx.lifecycle.Observer
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.AppComponent
-import com.habitrpg.android.habitica.data.SocialRepository
-import com.habitrpg.android.habitica.data.UserRepository
import com.habitrpg.android.habitica.helpers.AppConfigManager
-import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.members.Member
import com.habitrpg.android.habitica.models.social.Group
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.ui.AvatarView
import com.habitrpg.android.habitica.ui.activities.GroupFormActivity
+import com.habitrpg.android.habitica.ui.activities.MainActivity
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
import com.habitrpg.android.habitica.ui.helpers.MarkdownParser
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.ui.helpers.resetViews
+import com.habitrpg.android.habitica.ui.viewmodels.GroupViewModel
+import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar
import com.habitrpg.android.habitica.ui.views.social.UsernameLabel
-import io.reactivex.functions.Consumer
import javax.inject.Inject
class GuildDetailFragment : BaseFragment() {
- var isMember: Boolean = false
- @Inject
- lateinit var socialRepository: SocialRepository
- @Inject
- lateinit var userRepository: UserRepository
@Inject
lateinit var configManager: AppConfigManager
@@ -50,9 +45,7 @@ class GuildDetailFragment : BaseFragment() {
private val joinGuildButton: Button by bindView(R.id.join_button)
private val leaveGuildButton: Button by bindView(R.id.leave_button)
- var groupID: String? = null
- var guild: Group? = null
- private var user: User? = null
+ var viewModel: GroupViewModel? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
inflater.inflate(R.layout.fragment_guild_detail, container, false)
@@ -64,62 +57,60 @@ class GuildDetailFragment : BaseFragment() {
refreshLayout.setOnRefreshListener { this.refresh() }
- compositeSubscription.add(socialRepository.getGroup(groupID ?: "")
- .doOnNext {
- guild = it
- updateGuild(it)
- }
- .distinctUntilChanged { group1, group2 -> group1.id == group2.id }
- .flatMap { socialRepository.getMember(it.leaderID) }
- .subscribe(Consumer {
- setLeader(it)
- }, RxErrorHandler.handleEmptyError()))
+ viewModel?.getGroupData()?.observe(viewLifecycleOwner, Observer { updateGuild(it) })
+ viewModel?.getLeaderData()?.observe(viewLifecycleOwner, Observer { setLeader(it) })
+ viewModel?.getIsMemberData()?.observe(viewLifecycleOwner, Observer { updateMembership(it) })
guildDescriptionView.movementMethod = LinkMovementMethod.getInstance()
leaveGuildButton.setOnClickListener {
- compositeSubscription.add(socialRepository.leaveGroup(groupID).subscribe(Consumer {}, RxErrorHandler.handleEmptyError()))
+ viewModel?.leaveGroup {
+ val activity = activity as? MainActivity
+ if (activity != null) {
+ HabiticaSnackbar.showSnackbar(activity.floatingMenuWrapper, getString(R.string.left_guild), HabiticaSnackbar.SnackbarDisplayType.NORMAL)
+ }
+ }
}
joinGuildButton.setOnClickListener {
- compositeSubscription.add(socialRepository.joinGroup(groupID).subscribe(Consumer {}, RxErrorHandler.handleEmptyError()))
+ viewModel?.joinGroup {
+ val activity = activity as? MainActivity
+ if (activity != null) {
+ HabiticaSnackbar.showSnackbar(activity.floatingMenuWrapper, getString(R.string.joined_guild), HabiticaSnackbar.SnackbarDisplayType.NORMAL)
+ }
+ }
}
-
- joinGuildButton.visibility = if (isMember) View.GONE else View.VISIBLE
- leaveGuildButton.visibility = if (isMember) View.VISIBLE else View.GONE
}
- private fun setLeader(leader: Member) {
+ private fun setLeader(leader: Member?) {
+ if (leader == null) {
+ return
+ }
leaderAvatarView.setAvatar(leader)
leaderProfileNameView.username = leader.profile?.name
leaderProfileNameView.tier = leader.contributor?.level ?: 0
leaderUsernameView.text = leader.formattedUsername
}
+ private fun updateMembership(isMember: Boolean?) {
+ joinGuildButton.visibility = if (isMember == true) View.GONE else View.VISIBLE
+ leaveGuildButton.visibility = if (isMember == true) View.VISIBLE else View.GONE
+ }
+
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
GroupFormActivity.GROUP_FORM_ACTIVITY -> {
if (resultCode == Activity.RESULT_OK) {
- val bundle = data?.extras
- this.socialRepository.updateGroup(this.guild,
- bundle?.getString("name"),
- bundle?.getString("description"),
- bundle?.getString("leader"),
- bundle?.getBoolean("leaderCreateChallenge"))
- .subscribe(Consumer { }, RxErrorHandler.handleEmptyError())
+ viewModel?.updateGroup(data?.extras)
}
}
}
}
private fun refresh() {
- compositeSubscription.add(socialRepository.retrieveGroup(guild?.id ?: "").subscribe(Consumer {}, RxErrorHandler.handleEmptyError()))
- }
-
- override fun onDestroy() {
- userRepository.close()
- socialRepository.close()
- super.onDestroy()
+ viewModel?.retrieveGroup {
+ refreshLayout.isRefreshing = false
+ }
}
override fun injectFragment(component: AppComponent) {
@@ -129,21 +120,16 @@ class GuildDetailFragment : BaseFragment() {
private fun updateGuild(guild: Group?) {
guildTitleView.text = guild?.name
guildDescriptionView.text = MarkdownParser.parseMarkdown(guild?.description)
- //gemCountWrapper.visibility = if (group?.balance != null && group.balance > 0) View.VISIBLE else View.GONE
- //gemCountTextView.text = (group?.balance ?: 0 * 4.0).toInt().toString()
}
companion object {
-
- fun newInstance(group: Group?, user: User?): GuildDetailFragment {
+ fun newInstance(viewModel: GroupViewModel?, user: User?): GuildDetailFragment {
val args = Bundle()
val fragment = GuildDetailFragment()
fragment.arguments = args
- fragment.groupID = group?.id
- fragment.user = user
+ fragment.viewModel = viewModel
return fragment
}
}
-
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/GuildFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/GuildFragment.kt
index 23c217556..fce1f1681 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/GuildFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/GuildFragment.kt
@@ -3,47 +3,27 @@ package com.habitrpg.android.habitica.ui.fragments.social
import android.app.Activity
import android.content.Intent
import android.os.Bundle
+import android.view.*
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentPagerAdapter
-import android.view.LayoutInflater
-import android.view.Menu
-import android.view.MenuInflater
-import android.view.MenuItem
-import android.view.View
-import android.view.ViewGroup
-
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProviders
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.AppComponent
-import com.habitrpg.android.habitica.data.SocialRepository
-import com.habitrpg.android.habitica.extensions.notNull
-import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.social.Group
import com.habitrpg.android.habitica.ui.activities.GroupFormActivity
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
import com.habitrpg.android.habitica.ui.helpers.bindView
-import io.reactivex.functions.Consumer
-
-import javax.inject.Inject
+import com.habitrpg.android.habitica.ui.helpers.resetViews
+import com.habitrpg.android.habitica.ui.viewmodels.GroupViewModel
+import com.habitrpg.android.habitica.ui.viewmodels.GroupViewType
class GuildFragment : BaseMainFragment() {
- @Inject
- internal lateinit var socialRepository: SocialRepository
-
- var isMember: Boolean = false
- set(value) {
- field = value
- guildInformationFragment?.isMember = value
- }
private val viewPager: androidx.viewpager.widget.ViewPager? by bindView(R.id.viewPager)
- private var guild: Group? = null
+ internal lateinit var viewModel: GroupViewModel
private var guildInformationFragment: GuildDetailFragment? = null
- private var chatListFragment: ChatListFragment? = null
- private var guildId: String? = null
-
- fun setGuildId(guildId: String) {
- this.guildId = guildId
- }
+ private var chatFragment: ChatFragment? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
@@ -53,10 +33,6 @@ class GuildFragment : BaseMainFragment() {
return inflater.inflate(R.layout.fragment_viewpager, container, false)
}
- override fun onDestroy() {
- socialRepository.close()
- super.onDestroy()
- }
override fun injectFragment(component: AppComponent) {
component.inject(this)
@@ -64,28 +40,44 @@ class GuildFragment : BaseMainFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
+ resetViews()
+
+ viewModel = ViewModelProviders.of(this).get(GroupViewModel::class.java)
+ viewModel.groupViewType = GroupViewType.GUILD
+ viewModel.getGroupData().observe(viewLifecycleOwner, Observer { setGroup(it) })
+ viewModel.getIsMemberData().observe(viewLifecycleOwner, Observer { activity?.invalidateOptionsMenu() })
+
val newArguments = arguments
if (newArguments != null) {
val args = GuildFragmentArgs.fromBundle(newArguments)
- guildId = args.groupID
- isMember = args.isMember
+ viewModel.setGroupID(args.groupID)
}
viewPager?.currentItem = 0
setViewPagerAdapter()
+ setFragments()
+ }
- guildId.notNull { guildId ->
- compositeSubscription.add(socialRepository.getGroup(guildId).subscribe(Consumer { this.setGroup(it) }, RxErrorHandler.handleEmptyError()))
- socialRepository.retrieveGroup(guildId).subscribe(Consumer { }, RxErrorHandler.handleEmptyError())
- socialRepository.getGroup(guildId).subscribe(Consumer { this.setGroup(it) }, RxErrorHandler.handleEmptyError())
+ private fun setFragments() {
+ val fragments = childFragmentManager.fragments
+ for (childFragment in fragments) {
+ if (childFragment is ChatFragment) {
+ chatFragment = childFragment
+ chatFragment?.viewModel = viewModel
+ }
+ if (childFragment is GuildDetailFragment) {
+ guildInformationFragment = childFragment
+ childFragment.viewModel = viewModel
+ }
}
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
- if (this.activity != null && this.guild != null) {
- if (this.isMember) {
- if (this.user != null && this.user?.id == this.guild?.leaderID) {
+ val guild = viewModel.getGroupData().value
+ if (this.activity != null && guild != null) {
+ if (viewModel.isMember) {
+ if (this.user != null && this.user?.id == guild.leaderID) {
this.activity?.menuInflater?.inflate(R.menu.guild_admin, menu)
} else {
this.activity?.menuInflater?.inflate(R.menu.guild_member, menu)
@@ -99,20 +91,13 @@ class GuildFragment : BaseMainFragment() {
@Suppress("ReturnCount")
override fun onOptionsItemSelected(item: MenuItem): Boolean {
- val id = item.itemId
-
- when (id) {
+ when (item.itemId) {
R.id.menu_guild_join -> {
- this.socialRepository.joinGroup(this.guild?.id).subscribe(Consumer { this.setGroup(it) }, RxErrorHandler.handleEmptyError())
- this.isMember = true
+ viewModel.joinGroup()
return true
}
R.id.menu_guild_leave -> {
- this.socialRepository.leaveGroup(this.guild?.id)
- .subscribe(Consumer {
- fragmentManager?.popBackStack()
- }, RxErrorHandler.handleEmptyError())
- this.isMember = false
+ viewModel.leaveGroup { fragmentManager?.popBackStack() }
return true
}
R.id.menu_guild_edit -> {
@@ -134,14 +119,13 @@ class GuildFragment : BaseMainFragment() {
when (position) {
0 -> {
- guildInformationFragment = GuildDetailFragment.newInstance(this@GuildFragment.guild, user)
- guildInformationFragment?.isMember = isMember
+ guildInformationFragment = GuildDetailFragment.newInstance(viewModel, user)
fragment = guildInformationFragment
}
1 -> {
- chatListFragment = ChatListFragment()
- chatListFragment?.configure(this@GuildFragment.guildId ?: "", user, false, "guild")
- fragment = chatListFragment
+ chatFragment = ChatFragment()
+ chatFragment?.viewModel = viewModel
+ fragment = chatFragment
}
else -> fragment = Fragment()
}
@@ -164,14 +148,14 @@ class GuildFragment : BaseMainFragment() {
viewPager?.addOnPageChangeListener(object : androidx.viewpager.widget.ViewPager.OnPageChangeListener {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
- if (position == 1 && this@GuildFragment.guild != null) {
- chatListFragment?.setNavigatedToFragment()
+ if (position == 1) {
+ chatFragment?.setNavigatedToFragment()
}
}
override fun onPageSelected(position: Int) {
- if (position == 1 && this@GuildFragment.guild != null && chatListFragment != null) {
- chatListFragment?.setNavigatedToFragment()
+ if (position == 1) {
+ chatFragment?.setNavigatedToFragment()
}
}
@@ -185,6 +169,7 @@ class GuildFragment : BaseMainFragment() {
private fun displayEditForm() {
val bundle = Bundle()
+ val guild = viewModel.getGroupData().value
bundle.putString("groupID", guild?.id)
bundle.putString("name", guild?.name)
bundle.putString("description", guild?.description)
@@ -205,31 +190,19 @@ class GuildFragment : BaseMainFragment() {
GroupFormActivity.GROUP_FORM_ACTIVITY -> {
if (resultCode == Activity.RESULT_OK) {
val bundle = data?.extras
- this.socialRepository.updateGroup(this.guild,
- bundle?.getString("name"),
- bundle?.getString("description"),
- bundle?.getString("leader"),
- bundle?.getBoolean("leaderCreateChallenge"))
- .subscribe(Consumer { }, RxErrorHandler.handleEmptyError())
+ viewModel.updateGroup(bundle)
}
}
}
}
private fun setGroup(group: Group?) {
- if (group != null) {
- guildInformationFragment?.groupID = group.id
-
- this.chatListFragment?.groupId = group.id
-
- this.guild = group
- }
this.activity?.invalidateOptionsMenu()
if (group?.privacy == "public") {
- chatListFragment?.autocompleteContext = "publicGuild"
+ chatFragment?.autocompleteContext = "publicGuild"
} else {
- chatListFragment?.autocompleteContext = "privateGuild"
+ chatFragment?.autocompleteContext = "privateGuild"
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/PublicGuildsFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/PublicGuildsFragment.kt
index ee6c7fadd..9fe707b58 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/PublicGuildsFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/PublicGuildsFragment.kt
@@ -1,9 +1,9 @@
package com.habitrpg.android.habitica.ui.fragments.social
import android.os.Bundle
-import androidx.core.content.ContextCompat
-import androidx.appcompat.widget.SearchView
import android.view.*
+import androidx.appcompat.widget.SearchView
+import androidx.core.content.ContextCompat
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.AppComponent
import com.habitrpg.android.habitica.data.SocialRepository
@@ -47,8 +47,8 @@ class PublicGuildsFragment : BaseMainFragment(), SearchView.OnQueryTextListener
recyclerView?.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(this.activity)
recyclerView?.addItemDecoration(androidx.recyclerview.widget.DividerItemDecoration(getActivity()!!, androidx.recyclerview.widget.DividerItemDecoration.VERTICAL))
viewAdapter = PublicGuildsRecyclerViewAdapter(null, true)
- viewAdapter.setMemberGuildIDs(this.memberGuildIDs?.toMutableList() ?: mutableListOf())
- viewAdapter.apiClient = this.apiClient
+ viewAdapter.setMemberGuildIDs(this.memberGuildIDs?.toMutableList() ?: mutableListOf())
+ viewAdapter.socialRepository = socialRepository
recyclerView?.adapter = viewAdapter
recyclerView?.itemAnimator = SafeDefaultItemAnimator()
this.fetchGuilds()
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyFragment.kt
index 34feb263e..b7e8d0203 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyFragment.kt
@@ -91,7 +91,7 @@ class PartyFragment : BaseMainFragment() {
this.tutorialText = getString(R.string.tutorial_party)
}
- fun setFragments() {
+ private fun setFragments() {
val fragments = childFragmentManager.fragments
for (childFragment in fragments) {
if (childFragment is ChatFragment) {
@@ -144,9 +144,7 @@ class PartyFragment : BaseMainFragment() {
@Suppress("ReturnCount")
override fun onOptionsItemSelected(item: MenuItem): Boolean {
- val id = item.itemId
-
- when (id) {
+ when (item.itemId) {
R.id.menu_invite_item -> {
val intent = Intent(activity, PartyInviteActivity::class.java)
startActivityForResult(intent, PartyInviteActivity.RESULT_SEND_INVITES)
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/GroupViewModel.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/GroupViewModel.kt
index 96976a55f..5f124e2d7 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/GroupViewModel.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/GroupViewModel.kt
@@ -6,7 +6,9 @@ import androidx.lifecycle.MutableLiveData
import com.habitrpg.android.habitica.components.AppComponent
import com.habitrpg.android.habitica.data.SocialRepository
import com.habitrpg.android.habitica.extensions.*
+import com.habitrpg.android.habitica.extensions.Optional
import com.habitrpg.android.habitica.helpers.RxErrorHandler
+import com.habitrpg.android.habitica.models.members.Member
import com.habitrpg.android.habitica.models.social.ChatMessage
import com.habitrpg.android.habitica.models.social.Group
import io.reactivex.BackpressureStrategy
@@ -15,7 +17,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.functions.Consumer
import io.reactivex.subjects.BehaviorSubject
import io.realm.RealmResults
-import java.util.HashMap
+import java.util.*
import javax.inject.Inject
@@ -37,6 +39,16 @@ open class GroupViewModel : BaseViewModel() {
MutableLiveData()
}
+ private val leader: MutableLiveData by lazy {
+ loadLeaderFromLocal()
+ MutableLiveData()
+ }
+
+ private val isMemberData: MutableLiveData by lazy {
+ loadMembershipFromLocal()
+ MutableLiveData()
+ }
+
protected val groupIDSubject = BehaviorSubject.create>()
var gotNewMessages: Boolean = false
@@ -53,7 +65,14 @@ open class GroupViewModel : BaseViewModel() {
groupIDSubject.onNext(groupID.asOptional())
}
+ val groupID: String?
+ get() = groupIDSubject.value?.value
+ val isMember: Boolean
+ get() = isMemberData.value ?: false
+
fun getGroupData(): LiveData = group
+ fun getLeaderData(): LiveData = leader
+ fun getIsMemberData(): LiveData = isMemberData
private fun loadGroupFromLocal() {
disposable.add(groupIDSubject.toFlowable(BackpressureStrategy.LATEST)
@@ -63,6 +82,26 @@ open class GroupViewModel : BaseViewModel() {
.subscribe(Consumer { group.value = it }, RxErrorHandler.handleEmptyError()))
}
+ private fun loadLeaderFromLocal() {
+ disposable.add(groupIDSubject.toFlowable(BackpressureStrategy.LATEST)
+ .filterOptionalDoOnEmpty { group.value = null }
+ .flatMap { socialRepository.getGroup(it) }
+ .distinctUntilChanged { group1, group2 -> group1.id == group2.id }
+ .flatMap { socialRepository.getMember(it.leaderID) }
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(Consumer { leader.value = it }, RxErrorHandler.handleEmptyError()))
+ }
+
+ private fun loadMembershipFromLocal() {
+ disposable.add(groupIDSubject.toFlowable(BackpressureStrategy.LATEST)
+ .filterOptionalDoOnEmpty { group.value = null }
+ .flatMap { socialRepository.getGroupMemberships() }
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(Consumer {
+ isMemberData.value = it.firstOrNull { membership -> membership.groupID == groupID } != null
+ }, RxErrorHandler.handleEmptyError()))
+ }
+
fun getChatMessages(): Flowable> {
return groupIDSubject.toFlowable(BackpressureStrategy.BUFFER)
.filterMapEmpty()
@@ -101,20 +140,24 @@ open class GroupViewModel : BaseViewModel() {
}
}
- fun leaveGroup(function: () -> Unit) {
+ fun leaveGroup(function: (() -> Unit)? = null) {
disposable.add(socialRepository.leaveGroup(this.group.value?.id ?: "")
.flatMap { userRepository.retrieveUser(false, true) }
.subscribe(Consumer {
- function()
+ function?.invoke()
}, RxErrorHandler.handleEmptyError()))
}
- fun joinGroup(groupID: String) {
- disposable.add(socialRepository.joinGroup(groupID).subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
+ fun joinGroup(id: String? = null, function: (() -> Unit)? = null) {
+ disposable.add(socialRepository.joinGroup(id ?: groupID).subscribe(Consumer {
+ function?.invoke()
+ }, RxErrorHandler.handleEmptyError()))
}
- fun rejectGroupInvite(groupID: String) {
- disposable.add(socialRepository.rejectGroupInvite(groupID).subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
+ fun rejectGroupInvite(id: String? = null) {
+ groupID?.let {
+ disposable.add(socialRepository.rejectGroupInvite(id ?: it).subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
+ }
}
fun markMessagesSeen() {
@@ -151,4 +194,13 @@ open class GroupViewModel : BaseViewModel() {
onComplete()
}, RxErrorHandler.handleEmptyError()))
}
+
+ fun updateGroup(bundle: Bundle?) {
+ disposable.add(socialRepository.updateGroup(group.value,
+ bundle?.getString("name"),
+ bundle?.getString("description"),
+ bundle?.getString("leader"),
+ bundle?.getBoolean("leaderCreateChallenge"))
+ .subscribe(Consumer {}, RxErrorHandler.handleEmptyError()))
+ }
}
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/RoundedCornerLayout.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/RoundedCornerLayout.kt
index a6ffbba9b..5ec567078 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/RoundedCornerLayout.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/RoundedCornerLayout.kt
@@ -1,13 +1,7 @@
package com.habitrpg.android.habitica.ui.views
import android.content.Context
-import android.graphics.Bitmap
-import android.graphics.Canvas
-import android.graphics.Color
-import android.graphics.Paint
-import android.graphics.PorterDuff
-import android.graphics.PorterDuffXfermode
-import android.graphics.RectF
+import android.graphics.*
import android.util.AttributeSet
import android.util.TypedValue
import android.widget.FrameLayout
@@ -45,13 +39,13 @@ class RoundedCornerLayout : FrameLayout {
}
override fun draw(canvas: Canvas) {
- val offscreenBitmap = Bitmap.createBitmap(canvas.width, canvas.height, Bitmap.Config.ARGB_8888)
+ val offscreenBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val offscreenCanvas = Canvas(offscreenBitmap)
super.draw(offscreenCanvas)
if (maskBitmap == null) {
- maskBitmap = createMask(canvas.width, canvas.height)
+ maskBitmap = createMask(width, height)
}
offscreenCanvas.drawBitmap(maskBitmap, 0f, 0f, maskPaint)
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/form/TaskSchedulingControls.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/form/TaskSchedulingControls.kt
index 618195c2d..f33a92071 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/form/TaskSchedulingControls.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/form/TaskSchedulingControls.kt
@@ -14,6 +14,7 @@ import com.habitrpg.android.habitica.extensions.dpToPx
import com.habitrpg.android.habitica.extensions.inflate
import com.habitrpg.android.habitica.models.tasks.Days
import com.habitrpg.android.habitica.models.tasks.Task
+import com.habitrpg.android.habitica.ui.adapter.SimpleSpinnerAdapter
import com.habitrpg.android.habitica.ui.helpers.bindView
import java.text.DateFormat
import java.text.DateFormatSymbols
@@ -39,8 +40,7 @@ class TaskSchedulingControls @JvmOverloads constructor(
private val summaryTextView: TextView by bindView(R.id.summary_textview)
private val dateFormatter = DateFormat.getDateInstance(DateFormat.MEDIUM)
- private val frequencyAdapter = ArrayAdapter.createFromResource(context,
- R.array.repeatables_frequencies, android.R.layout.simple_spinner_item)
+ private val frequencyAdapter = SimpleSpinnerAdapter(context, R.array.repeatables_frequencies)
var taskType = Task.TYPE_DAILY
set(value) {
@@ -109,6 +109,11 @@ class TaskSchedulingControls @JvmOverloads constructor(
inflate(R.layout.task_form_task_scheduling, true)
repeatsEverySpinner.adapter = frequencyAdapter
+ frequency = Task.FREQUENCY_WEEKLY
+ startDate = Date()
+ everyX = 1
+ weeklyRepeat = Days()
+
repeatsEverySpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {
frequency = frequency