Fix task form issues

This commit is contained in:
Phillip Thelen 2019-04-29 15:53:05 +02:00
parent b6cde7e535
commit c664ec0dc1
22 changed files with 259 additions and 200 deletions

View file

@ -153,7 +153,7 @@ android {
buildConfigField "String", "TESTING_LEVEL", "\"production\""
multiDexEnabled true
versionCode 2103
versionCode 2105
versionName "1.9"
}

View file

@ -23,6 +23,7 @@
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:id="@+id/scroll_view"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -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"/>
<com.habitrpg.android.habitica.ui.views.tasks.form.RewardValueFormView
android:id="@+id/reward_value"

View file

@ -20,7 +20,8 @@
<com.habitrpg.android.habitica.ui.views.RoundedCornerLayout
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginRight="@dimen/spacing_large">
android:layout_marginRight="@dimen/spacing_large"
android:background="@drawable/rounded_avatar_bg">
<com.habitrpg.android.habitica.ui.AvatarView
android:id="@+id/avatarView"
android:layout_width="70dp"

View file

@ -44,32 +44,35 @@
android:layout_weight="1"
android:layout_height="@dimen/button_height"
android:text="@string/invite_to_guild"
style="@style/HabiticaButton.Purple"
android:layout_marginEnd="17dp" />
style="@style/HabiticaButton.Purple" />
<Button
android:id="@+id/join_button"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="@dimen/button_height"
android:text="@string/join_guild"
style="@style/HabiticaButton.Green"/>
style="@style/HabiticaButton.Green"
android:layout_marginStart="@dimen/spacing_large"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="65dp"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:paddingStart="12dp"
android:paddingEnd="12dp">
<com.habitrpg.android.habitica.ui.views.RoundedCornerLayout
android:paddingStart="16dp"
android:paddingEnd="16dp">
<com.habitrpg.android.habitica.ui.RoundedFrameLayout
android:layout_width="40dp"
android:layout_height="40dp">
android:layout_height="40dp"
android:clipChildren="true"
android:layout_marginRight="@dimen/spacing_large"
android:background="@drawable/rounded_avatar_bg">
<com.habitrpg.android.habitica.ui.AvatarView
android:id="@+id/leader_avatar_view"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_gravity="center"/>
</com.habitrpg.android.habitica.ui.views.RoundedCornerLayout>
android:layout_height="78dp"
android:layout_gravity="center"
android:paddingTop="8dp"/>
</com.habitrpg.android.habitica.ui.RoundedFrameLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_weight="1"
@ -106,7 +109,7 @@
android:layout_height="@dimen/button_height"
android:layout_margin="@dimen/spacing_large"
style="@style/HabiticaButton.Red"
android:text="@string/leave_party"/>
android:text="@string/leave_guild"/>
</com.habitrpg.android.habitica.ui.views.PaddedLinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

View file

@ -0,0 +1,11 @@
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@android:id/text1"
android:singleLine="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:textAlignment="inherit"
android:padding="@dimen/spacing_large"
style="@style/Body1"
tools:text="text" />

View file

@ -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">
<com.habitrpg.android.habitica.ui.AvatarView

View file

@ -898,4 +898,8 @@
<string name="i_agree_to_follow_the_guidelines">I agree to follow the guidelines</string>
<string name="report_violation">Report Violation</string>
<string name="trillion_abbrev">t</string>
<string name="leave_guild">Leave Guild</string>
<string name="left_guild">You left the guild</string>
<string name="joined_guild">You joined the guild</string>
<string name="cost">Cost</string>
</resources>

View file

@ -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<Days> CREATOR = new Parcelable.Creator<Days>() {

View file

@ -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

View file

@ -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<CharSequence>(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
}
}

View file

@ -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<C
}
return row ?: View(context)
}
}

View file

@ -9,6 +9,7 @@ import android.widget.Filterable
import android.widget.TextView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.data.ApiClient
import com.habitrpg.android.habitica.data.SocialRepository
import com.habitrpg.android.habitica.extensions.inflate
import com.habitrpg.android.habitica.extensions.notNull
import com.habitrpg.android.habitica.helpers.MainNavigationController
@ -27,7 +28,7 @@ import org.greenrobot.eventbus.EventBus
class PublicGuildsRecyclerViewAdapter(data: OrderedRealmCollection<Group>?, autoUpdate: Boolean) : RealmRecyclerViewAdapter<Group, PublicGuildsRecyclerViewAdapter.GuildViewHolder>(data, autoUpdate), Filterable {
var apiClient: ApiClient? = null
var socialRepository: SocialRepository? = null
private var memberGuildIDs: MutableList<String> = mutableListOf()
fun setMemberGuildIDs(memberGuildIDs: MutableList<String>) {
@ -46,16 +47,16 @@ class PublicGuildsRecyclerViewAdapter(data: OrderedRealmCollection<Group>?, 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) {

View file

@ -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

View file

@ -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()))

View file

@ -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)

View file

@ -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
}
}
}

View file

@ -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<Group> { 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"
}
}

View file

@ -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<String>())
viewAdapter.apiClient = this.apiClient
viewAdapter.setMemberGuildIDs(this.memberGuildIDs?.toMutableList() ?: mutableListOf())
viewAdapter.socialRepository = socialRepository
recyclerView?.adapter = viewAdapter
recyclerView?.itemAnimator = SafeDefaultItemAnimator()
this.fetchGuilds()

View file

@ -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)

View file

@ -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<Group?>()
}
private val leader: MutableLiveData<Member?> by lazy {
loadLeaderFromLocal()
MutableLiveData<Member?>()
}
private val isMemberData: MutableLiveData<Boolean?> by lazy {
loadMembershipFromLocal()
MutableLiveData<Boolean?>()
}
protected val groupIDSubject = BehaviorSubject.create<Optional<String>>()
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?> = group
fun getLeaderData(): LiveData<Member?> = leader
fun getIsMemberData(): LiveData<Boolean?> = 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<RealmResults<ChatMessage>> {
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()))
}
}

View file

@ -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)

View file

@ -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