begin implementing guild list redesign

This commit is contained in:
Phillip Thelen 2021-02-04 18:21:04 +01:00
parent df4c3dfff3
commit 5f9e5ca6df
25 changed files with 351 additions and 269 deletions

View file

@ -110,9 +110,9 @@ dependencies {
//Leak Detection
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.5'
//Push Notifications
implementation 'com.google.firebase:firebase-core:18.0.1'
implementation 'com.google.firebase:firebase-core:18.0.2'
implementation 'com.google.firebase:firebase-messaging:21.0.1'
implementation 'com.google.firebase:firebase-config:20.0.2'
implementation 'com.google.firebase:firebase-config:20.0.3'
implementation 'com.google.firebase:firebase-perf:19.1.0'
implementation 'com.google.android.gms:play-services-auth:19.0.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
@ -121,8 +121,8 @@ dependencies {
implementation 'androidx.core:core-ktx:1.3.2'
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation "androidx.lifecycle:lifecycle-common-java8:2.2.0"
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.2'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.2'
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.3'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.3'
implementation "androidx.paging:paging-runtime-ktx:2.1.2"
implementation 'com.plattysoft.leonids:LeonidsLib:1.3.2'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2'
@ -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 2695
versionCode 2803
versionName "3.2"
}

View file

@ -73,13 +73,15 @@
<ImageView
android:layout_width="18dp"
android:layout_height="20dp"
android:src="@drawable/dialogue_participants" />
android:src="@drawable/dialogue_participants"
app:tint="@color/text_ternary"/>
<TextView
android:id="@+id/participantCount"
style="@style/ChallengeTaskDetails"
android:layout_width="wrap_content"
android:layout_height="20dp"
android:textColor="@color/text_ternary"
android:layout_marginStart="@dimen/spacing_small"
android:gravity="center_vertical"
tools:text="12334" />

View file

@ -4,21 +4,37 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_margin="@dimen/row_padding"
android:background="?attr/selectableItemBackground">
android:layout_marginHorizontal="10dp"
android:layout_marginVertical="10dp"
android:background="@drawable/layout_rounded_bg_window"
android:padding="12dp">
<TextView
android:id="@+id/nameTextView"
style="@style/RowTitle"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<TextView
android:id="@+id/descriptionTextView"
android:layout_width="match_parent"
android:layout_height="match_parent"
style="@style/Caption2"
android:textColor="@color/text_ternary"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/nameTextView"
style="@style/RowTitle"
android:orientation="horizontal"
android:gravity="center">
<Space
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content" />
<ImageView
android:id="@+id/guild_badge_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1" />
android:layout_marginEnd="@dimen/spacing_small"/>
<TextView
android:id="@+id/memberCountTextView"
style="@style/RowText"
@ -26,23 +42,4 @@
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/descriptionTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="New Text ase rqwer qwer qsdfqw erasdf qwredas drfqwer asdrqw eras drqwer asdr aswer asdfaswer wer sdwe 000"
android:layout_marginBottom="20dp"/>
<Button
android:id="@+id/joinleaveButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="New Button" />
</LinearLayout>
</LinearLayout>

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginHorizontal="10dp"
android:layout_marginVertical="10dp"
android:background="@drawable/layout_rounded_bg_window"
android:padding="12dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center">
<ImageView
android:id="@+id/guild_badge_view"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginEnd="12dp" />
<TextView
android:id="@+id/title_text_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
style="@style/RowTitle"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:src="@drawable/ic_keyboard_arrow_right"/>
</LinearLayout>
</LinearLayout>

View file

@ -4,10 +4,18 @@
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.habitrpg.android.habitica.ui.activities.MainActivity">
<item
android:id="@+id/action_guild_search"
android:id="@+id/action_search"
android:title="@string/guild_search_hint"
android:orderInCategory="103"
android:visible="true"
app:showAsAction="always"
app:actionViewClass="androidx.appcompat.widget.SearchView"/>
<item android:id="@+id/menu_create_item"
android:title="@string/create"
app:showAsAction="never" />
<item
android:id="@+id/menu_refresh"
android:orderInCategory="1"
app:showAsAction="never"
android:title="@string/action_refresh" />
</menu>

View file

@ -86,8 +86,8 @@
<deepLink app:uri="habitica.com/tavern" />
</fragment>
<fragment
android:id="@+id/guildsOverviewFragment"
android:name="com.habitrpg.android.habitica.ui.fragments.social.GuildsOverviewFragment"
android:id="@+id/guildOverviewFragment"
android:name="com.habitrpg.android.habitica.ui.fragments.social.guilds.GuildOverviewFragment"
android:label="@string/sidebar_guilds" >
<deepLink app:uri="habitica.com/groups/myGuilds" />
<action
@ -229,7 +229,7 @@
</fragment>
<fragment
android:id="@+id/guildFragment"
android:name="com.habitrpg.android.habitica.ui.fragments.social.GuildFragment"
android:name="com.habitrpg.android.habitica.ui.fragments.social.guilds.GuildFragment"
android:label="@string/guild" >
<deepLink app:uri="habitica.com/groups/guild/{groupID}" />
<argument
@ -246,7 +246,7 @@
</fragment>
<fragment
android:id="@+id/publicGuildsFragment"
android:name="com.habitrpg.android.habitica.ui.fragments.social.PublicGuildsFragment"
android:name="com.habitrpg.android.habitica.ui.fragments.social.guilds.GuildListFragment"
android:label="@string/public_guilds" >
<action
android:id="@+id/openGuildDetail"

View file

@ -1138,5 +1138,6 @@
<string name="twelve_months_one_time">12 month one-time subscription</string>
<string name="sidebar_teams">Teams</string>
<string name="team_information">Team Information</string>
<string name="mystery_item_title">You open the box and find...</string>
<string name="mystery_item_title">You open the box and find…</string>
<string name="cant_like_own_message">Can\'t like your own messages.</string>
</resources>

View file

@ -75,19 +75,19 @@ import com.habitrpg.android.habitica.ui.fragments.setup.WelcomeFragment;
import com.habitrpg.android.habitica.ui.fragments.skills.SkillTasksRecyclerViewFragment;
import com.habitrpg.android.habitica.ui.fragments.skills.SkillsFragment;
import com.habitrpg.android.habitica.ui.fragments.social.ChatFragment;
import com.habitrpg.android.habitica.ui.fragments.social.GuildDetailFragment;
import com.habitrpg.android.habitica.ui.fragments.social.GuildFragment;
import com.habitrpg.android.habitica.ui.fragments.social.GuildsOverviewFragment;
import com.habitrpg.android.habitica.ui.fragments.social.guilds.GuildDetailFragment;
import com.habitrpg.android.habitica.ui.fragments.social.guilds.GuildFragment;
import com.habitrpg.android.habitica.ui.fragments.social.InboxMessageListFragment;
import com.habitrpg.android.habitica.ui.fragments.social.InboxOverviewFragment;
import com.habitrpg.android.habitica.ui.fragments.social.NoPartyFragmentFragment;
import com.habitrpg.android.habitica.ui.fragments.social.PublicGuildsFragment;
import com.habitrpg.android.habitica.ui.fragments.social.guilds.GuildListFragment;
import com.habitrpg.android.habitica.ui.fragments.social.QuestDetailFragment;
import com.habitrpg.android.habitica.ui.fragments.social.TavernDetailFragment;
import com.habitrpg.android.habitica.ui.fragments.social.TavernFragment;
import com.habitrpg.android.habitica.ui.fragments.social.challenges.ChallengeDetailFragment;
import com.habitrpg.android.habitica.ui.fragments.social.challenges.ChallengeListFragment;
import com.habitrpg.android.habitica.ui.fragments.social.challenges.ChallengesOverviewFragment;
import com.habitrpg.android.habitica.ui.fragments.social.guilds.GuildOverviewFragment;
import com.habitrpg.android.habitica.ui.fragments.social.party.PartyDetailFragment;
import com.habitrpg.android.habitica.ui.fragments.social.party.PartyFragment;
import com.habitrpg.android.habitica.ui.fragments.social.party.PartyInviteFragment;
@ -187,9 +187,7 @@ public interface UserComponent {
void inject(GuildFragment guildFragment);
void inject(GuildsOverviewFragment guildsOverviewFragment);
void inject(PublicGuildsFragment publicGuildsFragment);
void inject(GuildListFragment guildListFragment);
void inject(TavernFragment tavernFragment);
@ -340,4 +338,6 @@ public interface UserComponent {
void inject(PromoInfoFragment promoInfoFragment);
void inject(@NotNull TeamBoardFragment teamBoardFragment);
void inject(@NotNull GuildOverviewFragment guildOverviewFragment);
}

View file

@ -120,7 +120,7 @@ class TaskRepositoryImpl(localRepository: TaskLocalRepository, apiClient: ApiCli
}
override fun bulkScoreTasks(data: List<Map<String, String>>): Flowable<BulkTaskScoringData> {
return apiClient.bulkScoreTasks(listOf())
return apiClient.bulkScoreTasks(data)
}
private fun handleTaskResponse(user: User, res: TaskDirectionData, task: Task, up: Boolean, localDelta: Float) {

View file

@ -61,7 +61,7 @@ class NotificationOpenHandler {
private fun openGuildDetailScreen(groupID: String?) {
if (groupID?.isNotEmpty() != true) {
MainNavigationController.navigate(R.id.guildsOverviewFragment)
MainNavigationController.navigate(R.id.guildOverviewFragment)
} else {
MainNavigationController.navigate(R.id.guildFragment, bundleOf("groupID" to groupID))
}

View file

@ -4,21 +4,25 @@ import android.view.View
import android.view.ViewGroup
import android.widget.Filter
import android.widget.Filterable
import androidx.core.os.bundleOf
import androidx.recyclerview.widget.RecyclerView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.data.SocialRepository
import com.habitrpg.android.habitica.databinding.ItemPublicGuildBinding
import com.habitrpg.android.habitica.databinding.ItemUserGuildBinding
import com.habitrpg.android.habitica.extensions.inflate
import com.habitrpg.android.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.helpers.NumberAbbreviator
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.social.Group
import com.habitrpg.android.habitica.ui.adapter.BaseRecyclerViewAdapter
import com.habitrpg.android.habitica.ui.fragments.social.PublicGuildsFragmentDirections
import com.habitrpg.android.habitica.ui.helpers.setMarkdown
import com.habitrpg.android.habitica.ui.views.HabiticaIcons
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
import io.realm.Case
import io.realm.OrderedRealmCollection
class PublicGuildsRecyclerViewAdapter : BaseRecyclerViewAdapter<Group, PublicGuildsRecyclerViewAdapter.GuildViewHolder>(), Filterable {
class GuildListAdapter(private val onlyShowUsersGuilds: Boolean) : BaseRecyclerViewAdapter<Group, RecyclerView.ViewHolder>(), Filterable {
var socialRepository: SocialRepository? = null
private var memberGuildIDs: List<String> = listOf()
@ -27,41 +31,28 @@ class PublicGuildsRecyclerViewAdapter : BaseRecyclerViewAdapter<Group, PublicGui
this.memberGuildIDs = memberGuildIDs
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GuildViewHolder {
val guildViewHolder = GuildViewHolder(parent.inflate(R.layout.item_public_guild))
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val guildViewHolder = if (onlyShowUsersGuilds) {
UserGuildViewHolder(parent.inflate(R.layout.item_user_guild))
} else {
GuildViewHolder(parent.inflate(R.layout.item_public_guild))
}
guildViewHolder.itemView.setOnClickListener { v ->
val guild = v.tag as? Group ?: return@setOnClickListener
val directions = PublicGuildsFragmentDirections.openGuildDetail(guild.id)
directions.isMember = isInGroup(guild)
MainNavigationController.navigate(directions)
}
guildViewHolder.binding.joinleaveButton.setOnClickListener { v ->
val guild = v.tag as? Group ?: return@setOnClickListener
val isMember = this.memberGuildIDs.contains(guild.id)
if (isMember) {
this@PublicGuildsRecyclerViewAdapter.socialRepository?.leaveGroup(guild.id, true)
?.subscribe({
val indexOfGroup = data.indexOf(guild)
notifyItemChanged(indexOfGroup)
}, RxErrorHandler.handleEmptyError())
} else {
this@PublicGuildsRecyclerViewAdapter.socialRepository?.joinGroup(guild.id)
?.subscribe({ group ->
val indexOfGroup = data.indexOf(group)
notifyItemChanged(indexOfGroup)
}, RxErrorHandler.handleEmptyError())
}
MainNavigationController.navigate(R.id.guildFragment, bundleOf(Pair("groupID", guild.id)))
}
return guildViewHolder
}
override fun onBindViewHolder(holder: GuildViewHolder, position: Int) {
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val guild = data[position]
val isInGroup = isInGroup(guild)
holder.bind(guild, isInGroup)
holder.itemView.tag = guild
holder.binding.joinleaveButton.tag = guild
if (onlyShowUsersGuilds && holder is UserGuildViewHolder) {
holder.bind(guild)
} else if (holder is GuildViewHolder) {
val isInGroup = isInGroup(guild)
holder.bind(guild, isInGroup)
holder.itemView.tag = guild
}
}
private fun isInGroup(guild: Group): Boolean {
@ -99,18 +90,29 @@ class PublicGuildsRecyclerViewAdapter : BaseRecyclerViewAdapter<Group, PublicGui
}
}
class UserGuildViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val binding = ItemUserGuildBinding.bind(itemView)
fun bind(guild: Group) {
binding.titleTextView.text = guild.name
binding.guildBadgeView.setImageBitmap(
HabiticaIconsHelper.imageOfGuildCrest(itemView.context,
false,
guild.privacy == "public",
guild.memberCount.toFloat(),
NumberAbbreviator.abbreviate(itemView.context, guild.memberCount.toDouble()))
)
}
}
class GuildViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val binding = ItemPublicGuildBinding.bind(itemView)
fun bind(guild: Group, isInGroup: Boolean) {
binding.nameTextView.text = guild.name
binding.memberCountTextView.text = guild.memberCount.toString()
binding.memberCountTextView.text = NumberAbbreviator.abbreviate(itemView.context, guild.memberCount.toDouble())
binding.descriptionTextView.setMarkdown(guild.summary)
if (isInGroup) {
binding.joinleaveButton.setText(R.string.leave)
} else {
binding.joinleaveButton.setText(R.string.join)
}
binding.guildBadgeView.setImageBitmap(HabiticaIconsHelper.imageOfGuildCrestSmall(guild.memberCount.toFloat()))
}
}
}

View file

@ -339,7 +339,7 @@ class NavigationDrawerFragment : DialogFragment() {
items.add(HabiticaDrawerItem(0, SIDEBAR_SOCIAL, context.getString(R.string.sidebar_section_social), true))
items.add(HabiticaDrawerItem(R.id.partyFragment, SIDEBAR_PARTY, context.getString(R.string.sidebar_party)))
items.add(HabiticaDrawerItem(R.id.tavernFragment, SIDEBAR_TAVERN, context.getString(R.string.sidebar_tavern)))
items.add(HabiticaDrawerItem(R.id.guildsOverviewFragment, SIDEBAR_GUILDS, context.getString(R.string.sidebar_guilds)))
items.add(HabiticaDrawerItem(R.id.guildOverviewFragment, SIDEBAR_GUILDS, context.getString(R.string.sidebar_guilds)))
items.add(HabiticaDrawerItem(R.id.challengesOverviewFragment, SIDEBAR_CHALLENGES, context.getString(R.string.sidebar_challenges)))
items.add(HabiticaDrawerItem(0, SIDEBAR_ABOUT_HEADER, context.getString(R.string.sidebar_about), true))

View file

@ -1,137 +0,0 @@
package com.habitrpg.android.habitica.ui.fragments.social
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.*
import android.widget.TextView
import androidx.core.net.toUri
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.data.ChallengeRepository
import com.habitrpg.android.habitica.data.SocialRepository
import com.habitrpg.android.habitica.databinding.FragmentGuildsOverviewBinding
import com.habitrpg.android.habitica.extensions.addCloseButton
import com.habitrpg.android.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.social.Group
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
import io.realm.RealmResults
import java.util.*
import javax.inject.Inject
class GuildsOverviewFragment : BaseMainFragment<FragmentGuildsOverviewBinding>(), androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener {
override var binding: FragmentGuildsOverviewBinding? = null
override fun createBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentGuildsOverviewBinding {
return FragmentGuildsOverviewBinding.inflate(inflater, container, false)
}
@Inject
lateinit var socialRepository: SocialRepository
@Inject
lateinit var challengeRepository: ChallengeRepository
private var guilds: List<Group>? = null
private var guildIDs: ArrayList<String>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
this.fetchGuilds()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
this.hidesToolbar = true
return super.onCreateView(inflater, container, savedInstanceState)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding?.refreshLayout?.setOnRefreshListener(this)
binding?.publicGuildsButton?.setOnClickListener {
MainNavigationController.navigate(GuildsOverviewFragmentDirections.openPublicGuilds())
}
compositeSubscription.add(socialRepository.getUserGroups("guild").subscribe({ this.setGuilds(it) }, RxErrorHandler.handleEmptyError()))
}
override fun onDestroy() {
socialRepository.close()
challengeRepository.close()
super.onDestroy()
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_create_refresh, menu)
super.onCreateOptionsMenu(menu, inflater)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.menu_refresh -> {
onRefresh()
return true
}
R.id.menu_create_item -> {
showCreationDialog()
return true
}
}
return super.onOptionsItemSelected(item)
}
private fun showCreationDialog() {
val context = context ?: return
val dialog = HabiticaAlertDialog(context)
dialog.setTitle(R.string.create_guild)
dialog.setMessage(R.string.create_guild_description)
dialog.addButton(R.string.open_website, true, false) { _, _ ->
val uriUrl = "https://habitica.com/groups/myGuilds".toUri()
val launchBrowser = Intent(Intent.ACTION_VIEW, uriUrl)
val l = context.packageManager.queryIntentActivities(launchBrowser, PackageManager.MATCH_DEFAULT_ONLY)
val notHabitica = l.first { !it.activityInfo.processName.contains("habitica") }
launchBrowser.setPackage(notHabitica.activityInfo.processName)
startActivity(launchBrowser)
}
dialog.addCloseButton()
dialog.show()
}
override fun injectFragment(component: UserComponent) {
component.inject(this)
}
override fun onRefresh() {
binding?.refreshLayout?.isRefreshing = true
fetchGuilds()
}
private fun fetchGuilds() {
compositeSubscription.add(this.socialRepository.retrieveGroups("guilds")
.subscribe({
binding?.refreshLayout?.isRefreshing = false
}, RxErrorHandler.handleEmptyError()))
}
private fun setGuilds(guilds: RealmResults<Group>) {
this.guilds = guilds
if (binding?.myGuildsListview == null) {
return
}
this.guildIDs = ArrayList()
binding?.myGuildsListview?.removeAllViewsInLayout()
val inflater = context?.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as? LayoutInflater
for (guild in guilds) {
val entry = inflater?.inflate(R.layout.plain_list_item, binding?.myGuildsListview, false) as? TextView
entry?.text = guild.name
entry?.setOnClickListener {
MainNavigationController.navigate(GuildsOverviewFragmentDirections.openGuildDetail(guild.id))
}
binding?.myGuildsListview?.addView(entry)
this.guildIDs?.add(guild.id)
}
}
}

View file

@ -102,7 +102,7 @@ class ChallengesOverviewFragment : BaseMainFragment<FragmentViewpagerBinding>()
private fun setViewPagerAdapter() {
val fragmentManager = childFragmentManager
statePagerAdapter = object : FragmentStatePagerAdapter(fragmentManager) {
statePagerAdapter = object : FragmentStatePagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
override fun getItem(position: Int): Fragment {
return if (position == 0) {
@ -128,5 +128,4 @@ class ChallengesOverviewFragment : BaseMainFragment<FragmentViewpagerBinding>()
tabLayout?.setupWithViewPager(binding?.viewPager)
statePagerAdapter?.notifyDataSetChanged()
}
}

View file

@ -1,4 +1,4 @@
package com.habitrpg.android.habitica.ui.fragments.social
package com.habitrpg.android.habitica.ui.fragments.social.guilds
import android.app.Activity
import android.content.Intent

View file

@ -1,4 +1,4 @@
package com.habitrpg.android.habitica.ui.fragments.social
package com.habitrpg.android.habitica.ui.fragments.social.guilds
import android.app.Activity
import android.content.Intent
@ -14,6 +14,8 @@ import com.habitrpg.android.habitica.databinding.FragmentViewpagerBinding
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.fragments.social.ChatFragment
import com.habitrpg.android.habitica.ui.fragments.social.guilds.GuildFragmentArgs
import com.habitrpg.android.habitica.ui.viewmodels.GroupViewModel
import com.habitrpg.android.habitica.ui.viewmodels.GroupViewType

View file

@ -1,33 +1,33 @@
package com.habitrpg.android.habitica.ui.fragments.social
package com.habitrpg.android.habitica.ui.fragments.social.guilds
import android.os.Bundle
import android.view.*
import androidx.appcompat.widget.SearchView
import androidx.core.content.ContextCompat
import com.habitrpg.android.habitica.R
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.data.SocialRepository
import com.habitrpg.android.habitica.databinding.FragmentRecyclerviewBinding
import com.habitrpg.android.habitica.databinding.FragmentRefreshRecyclerviewBinding
import com.habitrpg.android.habitica.extensions.subscribeWithErrorHandler
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.ui.adapter.social.PublicGuildsRecyclerViewAdapter
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
import com.habitrpg.android.habitica.ui.adapter.social.GuildListAdapter
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
import com.habitrpg.android.habitica.ui.helpers.KeyboardUtil
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator
import javax.inject.Inject
class PublicGuildsFragment : BaseMainFragment<FragmentRecyclerviewBinding>(), SearchView.OnQueryTextListener {
class GuildListFragment(val onlyShowUsersGuilds: Boolean) : BaseFragment<FragmentRefreshRecyclerviewBinding>(), SearchView.OnQueryTextListener, SwipeRefreshLayout.OnRefreshListener {
@Inject
lateinit var socialRepository: SocialRepository
override var binding: FragmentRecyclerviewBinding? = null
override var binding: FragmentRefreshRecyclerviewBinding? = null
override fun createBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRecyclerviewBinding {
return FragmentRecyclerviewBinding.inflate(inflater, container, false)
override fun createBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRefreshRecyclerviewBinding {
return FragmentRefreshRecyclerviewBinding.inflate(inflater, container, false)
}
private var viewAdapter = PublicGuildsRecyclerViewAdapter()
private var viewAdapter = GuildListAdapter(onlyShowUsersGuilds)
override fun injectFragment(component: UserComponent) {
component.inject(this)
@ -37,13 +37,23 @@ class PublicGuildsFragment : BaseMainFragment<FragmentRecyclerviewBinding>(), Se
super.onViewCreated(view, savedInstanceState)
binding?.recyclerView?.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(activity)
binding?.recyclerView?.addItemDecoration(androidx.recyclerview.widget.DividerItemDecoration(activity, androidx.recyclerview.widget.DividerItemDecoration.VERTICAL))
compositeSubscription.add(socialRepository.getGroupMemberships()
.map { it.map { membership -> membership.groupID } }
.subscribeWithErrorHandler { viewAdapter.setMemberGuildIDs(it) })
viewAdapter.socialRepository = socialRepository
binding?.recyclerView?.adapter = viewAdapter
binding?.recyclerView?.itemAnimator = SafeDefaultItemAnimator()
binding?.refreshLayout?.setOnRefreshListener(this)
if (onlyShowUsersGuilds) {
compositeSubscription.add(socialRepository.getUserGroups("guild").subscribe({ viewAdapter.setUnfilteredData(it) }, RxErrorHandler.handleEmptyError()))
} else {
compositeSubscription.add(this.socialRepository.getPublicGuilds()
.subscribe({ groups ->
this@GuildListFragment.viewAdapter.setUnfilteredData(groups)
}, RxErrorHandler.handleEmptyError()))
}
this.fetchGuilds()
}
@ -52,26 +62,14 @@ class PublicGuildsFragment : BaseMainFragment<FragmentRecyclerviewBinding>(), Se
super.onDestroy()
}
private fun fetchGuilds() {
compositeSubscription.add(this.socialRepository.getPublicGuilds()
.subscribe({ groups ->
this@PublicGuildsFragment.viewAdapter.setUnfilteredData(groups)
internal fun fetchGuilds() {
compositeSubscription.add(this.socialRepository.retrieveGroups(if (onlyShowUsersGuilds) "guilds" else "publicGuilds")
.subscribe({
binding?.refreshLayout?.isRefreshing = false
}, RxErrorHandler.handleEmptyError()))
compositeSubscription.add(this.socialRepository.retrieveGroups("publicGuilds").subscribe({ }, RxErrorHandler.handleEmptyError()))
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_public_guild, menu)
val searchItem = menu.findItem(R.id.action_guild_search)
val guildSearchView = searchItem?.actionView as? SearchView
val theTextArea = guildSearchView?.findViewById<SearchView.SearchAutoComplete>(R.id.search_src_text)
context?.let { theTextArea?.setHintTextColor(ContextCompat.getColor(it, R.color.white)) }
guildSearchView?.queryHint = getString(R.string.guild_search_hint)
guildSearchView?.setOnQueryTextListener(this)
}
override fun onQueryTextSubmit(s: String): Boolean {
override fun onQueryTextSubmit(s: String?): Boolean {
viewAdapter.filter.filter(s)
activity?.let {
KeyboardUtil.dismissKeyboard(it)
@ -79,9 +77,13 @@ class PublicGuildsFragment : BaseMainFragment<FragmentRecyclerviewBinding>(), Se
return true
}
override fun onQueryTextChange(s: String): Boolean {
override fun onQueryTextChange(s: String?): Boolean {
viewAdapter.filter.filter(s)
return true
}
override fun onRefresh() {
fetchGuilds()
}
}

View file

@ -0,0 +1,144 @@
package com.habitrpg.android.habitica.ui.fragments.social.guilds
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.*
import android.widget.RelativeLayout
import android.widget.TextView
import androidx.appcompat.widget.SearchView
import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentStatePagerAdapter
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.data.SocialRepository
import com.habitrpg.android.habitica.databinding.FragmentViewpagerBinding
import com.habitrpg.android.habitica.extensions.addCloseButton
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
import javax.inject.Inject
class GuildOverviewFragment : BaseMainFragment<FragmentViewpagerBinding>(), SearchView.OnQueryTextListener {
@Inject
internal lateinit var socialRepository: SocialRepository
override var binding: FragmentViewpagerBinding? = null
override fun createBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentViewpagerBinding {
return FragmentViewpagerBinding.inflate(inflater, container, false)
}
private var statePagerAdapter: FragmentStatePagerAdapter? = null
private var userGuildsFragment: GuildListFragment? = GuildListFragment(true)
private var publicGuildsFragment: GuildListFragment? = GuildListFragment(false)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
this.usesTabLayout = true
this.hidesToolbar = true
return super.onCreateView(inflater, container, savedInstanceState)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setViewPagerAdapter()
}
override fun injectFragment(component: UserComponent) {
component.inject(this)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_public_guild, menu)
val searchItem = menu.findItem(R.id.action_search)
val guildSearchView = searchItem?.actionView as? SearchView
val theTextArea = guildSearchView?.findViewById<SearchView.SearchAutoComplete>(R.id.search_src_text)
context?.let { theTextArea?.setHintTextColor(ContextCompat.getColor(it, R.color.white)) }
guildSearchView?.queryHint = getString(R.string.guild_search_hint)
guildSearchView?.setOnQueryTextListener(this)
}
@Suppress("ReturnCount")
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.menu_create_item -> {
showCreationDialog()
return true
}
R.id.action_reload -> {
getActiveFragment()?.fetchGuilds()
return true
}
}
return super.onOptionsItemSelected(item)
}
private fun showCreationDialog() {
val context = context ?: return
val dialog = HabiticaAlertDialog(context)
dialog.setTitle(R.string.create_guild)
dialog.setMessage(R.string.create_guild_description)
dialog.addButton(R.string.open_website, true, false) { _, _ ->
val uriUrl = "https://habitica.com/groups/myGuilds".toUri()
val launchBrowser = Intent(Intent.ACTION_VIEW, uriUrl)
val l = context.packageManager.queryIntentActivities(launchBrowser, PackageManager.MATCH_DEFAULT_ONLY)
val notHabitica = l.first { !it.activityInfo.processName.contains("habitica") }
launchBrowser.setPackage(notHabitica.activityInfo.processName)
startActivity(launchBrowser)
}
dialog.addCloseButton()
dialog.show()
}
private fun getActiveFragment(): GuildListFragment? {
return if (binding?.viewPager?.currentItem == 0) {
userGuildsFragment
} else {
publicGuildsFragment
}
}
private fun setViewPagerAdapter() {
val fragmentManager = childFragmentManager
statePagerAdapter = object : FragmentStatePagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
override fun getItem(position: Int): Fragment {
return if (position == 0) {
userGuildsFragment
} else {
publicGuildsFragment
} ?: Fragment()
}
override fun getCount(): Int {
return 2
}
override fun getPageTitle(position: Int): CharSequence {
return when (position) {
0 -> getString(R.string.my_guilds)
1 -> getString(R.string.discover)
else -> ""
}
}
}
binding?.viewPager?.adapter = statePagerAdapter
tabLayout?.setupWithViewPager(binding?.viewPager)
statePagerAdapter?.notifyDataSetChanged()
}
override fun onQueryTextSubmit(query: String?): Boolean {
return getActiveFragment()?.onQueryTextSubmit(query) ?: false
}
override fun onQueryTextChange(newText: String?): Boolean {
return getActiveFragment()?.onQueryTextChange(newText) ?: false
}
}

View file

@ -244,13 +244,9 @@ class TasksFragment : BaseMainFragment<FragmentViewpagerBinding>(), SearchView.O
}
private fun updateFilterIcon() {
if (filterMenuItem == null) {
return
}
var filterCount = 0
if (activeFragment != null) {
filterCount = taskFilterHelper.howMany(activeFragment?.classType)
}
val filterCount = taskFilterHelper.howMany(activeFragment?.classType)
filterMenuItem?.isVisible = activeFragment?.classType != Task.TYPE_REWARD
if (filterCount == 0) {
filterMenuItem?.setIcon(R.drawable.ic_action_filter_list)
context?.let {

View file

@ -77,7 +77,9 @@ class ChatRecyclerMessageViewHolder(itemView: View, private var userId: String,
if(it.uuid != userId) {
onLikeMessage?.invoke(it)
} else {
val event = ShowSnackbarEvent(context.getString(R.string.cant_like_own_message), HabiticaSnackbar.SnackbarDisplayType.FAILURE)
val event = ShowSnackbarEvent()
event.text = context.getString(R.string.cant_like_own_message)
event.type = HabiticaSnackbar.SnackbarDisplayType.FAILURE
EventBus.getDefault().post(event)
}
}

View file

@ -10336,7 +10336,7 @@ public class HabiticaIcons {
labelTextPaint.setColor(crestColor5);
GlobalCache.helveticaNeue.setTtcIndex(0);
labelTextPaint.setTypeface(GlobalCache.helveticaNeue.build());
labelTextPaint.setTextSize(12f);
labelTextPaint.setTextSize(10f);
StaticLayout labelStaticLayout = CacheForGuildCrest.labelStaticLayout.get((int) labelRect.width(), Layout.Alignment.ALIGN_CENTER, memberCountLabel, labelTextPaint);
canvas.save();
canvas.clipRect(labelRect);

View file

@ -834,4 +834,31 @@ public class HabiticaIconsHelper {
return imageOfSpookyGemPromoBG;
}
public static Bitmap imageOfGuildCrest(Context context, boolean isOwner, boolean isPublic, float memberCount, String memberCountLabel) {
Bitmap imageOfGuildCrest = Bitmap.createBitmap(scaleSize(40), scaleSize(38), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(imageOfGuildCrest);
canvas.scale(displayDensity, displayDensity);
HabiticaIcons.drawGuildCrest(canvas, context, isOwner, isPublic, memberCount, memberCountLabel);
return imageOfGuildCrest;
}
public static Bitmap imageOfGuildCrestMedium(float memberCount) {
Bitmap imageOfGuildCrestMedium = Bitmap.createBitmap(scaleSize(30), scaleSize(34), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(imageOfGuildCrestMedium);
canvas.scale(displayDensity, displayDensity);
HabiticaIcons.drawGuildCrestMedium(canvas, memberCount);
return imageOfGuildCrestMedium;
}
public static Bitmap imageOfGuildCrestSmall(float memberCount) {
Bitmap imageOfGuildCrestSmall = Bitmap.createBitmap(scaleSize(16), scaleSize(16), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(imageOfGuildCrestSmall);
canvas.scale(displayDensity, displayDensity);
HabiticaIcons.drawGuildCrestSmall(canvas, memberCount);
return imageOfGuildCrestSmall;
}
}

View file

@ -288,7 +288,9 @@ open class HabiticaAlertDialog(context: Context) : AlertDialog(context, R.style.
if ((dialogQueue[0].context as? Activity)?.isFinishing != true) {
GlobalScope.launch(context = Dispatchers.Main) {
delay(500L)
dialogQueue[0].show()
if (dialogQueue.size > 0) {
dialogQueue[0].show()
}
}
}
}

View file

@ -10,8 +10,8 @@
{
"type": "SINGLE",
"filters": [],
"versionCode": 2685,
"versionName": "3.1.1",
"versionCode": 2803,
"versionName": "3.2",
"outputFile": "Habitica-staff-release.apk"
}
]

View file

@ -13,7 +13,7 @@ buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:4.1.2'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
classpath 'com.google.gms:google-services:4.3.4'
classpath 'com.google.gms:google-services:4.3.5'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.4.1'
classpath "io.realm:realm-gradle-plugin:10.3.0"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"