Implement party creation

This commit is contained in:
Phillip Thelen 2019-03-05 13:14:42 +01:00
parent 81a9de01b9
commit 2edf68b474
21 changed files with 336 additions and 230 deletions

View file

@ -122,7 +122,7 @@
</activity>
<activity
android:name=".ui.activities.GroupFormActivity"
android:theme="@style/AppThemeWithActionBarBlackText"
android:theme="@style/AppTheme.NoActionBar.Transparent"
android:parentActivityName=".ui.activities.MainActivity"
android:screenOrientation="portrait"
tools:ignore="UnusedAttribute">

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:top="18dp">
<shape android:shape="rectangle">
<solid android:color="@color/gray_500" />
<corners android:radius="4dp" android:topLeftRadius="8dp" android:topRightRadius="8dp" />
</shape>
</item>
<item android:bottom="2dp" android:top="18dp">
<shape android:shape="rectangle">
<solid android:color="@color/gray_700" />
<corners android:topLeftRadius="4dp" android:topRightRadius="4dp" android:bottomLeftRadius="2dp" android:bottomRightRadius="2dp" />
</shape>
</item>
</layer-list>

View file

@ -2,62 +2,130 @@
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
android:background="@color/gray_700">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:paddingTop="30dp"
android:background="@color/white">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageButton
android:id="@+id/cancel_button"
android:layout_width="50dp"
android:layout_height="wrap_content"
android:src="@drawable/ic_close_purple_300_24dp"
style="@style/Base.Widget.AppCompat.Button.Borderless"
android:contentDescription="@string/cancel" />
<Space
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<Button
android:id="@+id/save_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/create"
style="@style/HabiticaButton"
android:textColor="@color/brand_400"
android:paddingEnd="@dimen/spacing_large"
android:paddingStart="@dimen/spacing_large"
android:minWidth="20dp"/>
</LinearLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:hintTextAppearance="@style/TextAppearance.AppCompat">
<EditText
app:hintTextAppearance="@style/TextAppearance.AppCompat"
android:layout_marginTop="@dimen/spacing_small"
android:layout_marginLeft="@dimen/spacing_large"
android:layout_marginRight="@dimen/spacing_large"
android:background="@drawable/edittext"
android:textColorHint="@color/gray_50">
<MultiAutoCompleteTextView
android:id="@+id/group_name_edittext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:hint="@string/name"
android:textColor="@android:color/black"
android:inputType="textCapSentences" />
android:inputType="textCapSentences"
android:background="@color/transparent"
android:paddingStart="@dimen/spacing_large"
android:paddingEnd="@dimen/spacing_large"
android:paddingBottom="@dimen/spacing_large"
android:paddingTop="20dp"
android:textColorHint="@color/gray_50"
/>
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="72dp"
android:orientation="horizontal" >
<ImageButton
android:id="@+id/emoji.toggle.btn"
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/ic_emoticon_grey600_24dp"
android:background="@drawable/md_transparent"
android:contentDescription="Toogle Emoji"
android:focusable="true"
android:focusableInTouchMode="true" />
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:hintTextAppearance="@style/TextAppearance.AppCompat">
app:hintTextAppearance="@style/TextAppearance.AppCompat"
android:layout_marginTop="@dimen/spacing_large"
android:layout_marginLeft="@dimen/spacing_large"
android:layout_marginRight="@dimen/spacing_large"
android:background="@drawable/edittext"
android:textColorHint="@color/gray_50">
<net.pherth.android.emoji_library.EmojiEditText
<MultiAutoCompleteTextView
android:id="@+id/group_description_edittext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:hint="@string/description"
android:textColor="@android:color/black"
android:inputType="textCapSentences|textMultiLine" />
android:inputType="textCapSentences|textMultiLine"
android:background="@color/transparent"
android:paddingStart="@dimen/spacing_large"
android:paddingEnd="@dimen/spacing_large"
android:paddingBottom="@dimen/spacing_large"
android:paddingTop="20dp"
android:textColorHint="@color/gray_50"
android:minLines="3"
android:gravity="top" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_large"
android:layout_marginLeft="@dimen/spacing_large"
android:layout_marginRight="@dimen/spacing_large">
<TextView
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="@string/only_leader_create_challenge"/>
<Switch
android:id="@+id/leader_create_challenge_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
<View
android:id="@+id/privacy_separator"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#1e000000"
android:layout_marginTop="@dimen/spacing_medium"
android:layout_marginBottom="@dimen/spacing_medium"
tools:visibility="gone" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/privacyWrapper">
android:id="@+id/privacyWrapper"
android:layout_marginLeft="@dimen/spacing_large"
android:layout_marginRight="@dimen/spacing_large"
tools:visibility="gone">
<TextView
android:layout_width="wrap_content"
@ -70,5 +138,10 @@
android:layout_height="wrap_content"
android:id="@+id/privacySpinner" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#1e000000"
android:layout_marginTop="@dimen/spacing_medium" />
</LinearLayout>
</ScrollView>

View file

@ -111,7 +111,7 @@
style="@style/HabiticaButton.Gray.600"
android:background="@drawable/layout_rounded_bg_gray_700"
android:textSize="16sp"
android:text="@string/create_party_website"
android:text="@string/create_party"
android:layout_marginTop="@dimen/spacing_large"/>
</LinearLayout>
<View

View file

@ -10,4 +10,9 @@
android:orderInCategory="1"
app:showAsAction="never"
android:title="@string/action_edit" />
<item
android:id="@+id/menu.guild.leave"
android:orderInCategory="1"
app:showAsAction="never"
android:title="@string/leave" />
</menu>

View file

@ -256,4 +256,8 @@
android:name="userID"
app:argType="string" />
</activity>
<activity
android:id="@+id/groupFormActivity"
android:name="com.habitrpg.android.habitica.ui.activities.GroupFormActivity"
android:label="GroupFormActivity" />
</navigation>

View file

@ -860,5 +860,8 @@
<string name="discover">Discover</string>
<string name="damage_paused">Damage paused</string>
<string name="preference_push_important_announcements">Important Announcements</string>
<string name="create">Create</string>
<string name="only_leader_create_challenge">Only leader can create Challenges</string>
<string name="create_party">Create Party</string>
</resources>

View file

@ -201,6 +201,9 @@ public interface ApiService {
@GET("groups/{gid}")
Flowable<HabitResponse<Group>> getGroup(@Path("gid") String groupId);
@POST("groups")
Flowable<HabitResponse<Group>> createGroup(@Body Group item);
@PUT("groups/{id}")
Flowable<HabitResponse<Void>> updateGroup(@Path("id") String id, @Body Group item);

View file

@ -129,6 +129,7 @@ interface ApiClient {
fun getGroup(groupId: String): Flowable<Group>
fun createGroup(group: Group): Flowable<Group>
fun updateGroup(id: String, item: Group): Flowable<Void>
fun listGroupChat(groupId: String): Flowable<List<ChatMessage>>

View file

@ -36,7 +36,8 @@ interface SocialRepository : BaseRepository {
fun joinGroup(id: String?): Flowable<Group>
fun updateGroup(group: Group?, name: String?, description: String?, leader: String?, privacy: String?): Flowable<Void>
fun createGroup(name: String?, description: String?, leader: String?, type: String?, privacy: String?, leaderCreateChallenge: Boolean?): Flowable<Group>
fun updateGroup(group: Group?, name: String?, description: String?, leader: String?, leaderCreateChallenge: Boolean?): Flowable<Void>
fun retrieveGroups(type: String): Flowable<List<Group>>
fun getGroups(type: String): Flowable<RealmResults<Group>>

View file

@ -63,6 +63,7 @@ class ApiClientImpl//private OnHabitsAPIResult mResultListener;
//private HostConfig mConfig;
(private val gsonConverter: GsonConverterFactory, override val hostConfig: HostConfig, private val crashlyticsProxy: CrashlyticsProxy, private val popupNotificationsManager: PopupNotificationsManager, private val context: Context) : Consumer<Throwable>, ApiClient {
private lateinit var retrofitAdapter: Retrofit
// I think we don't need the ApiClientImpl anymore we could just use ApiService
@ -476,6 +477,10 @@ class ApiClientImpl//private OnHabitsAPIResult mResultListener;
return apiService.getGroup(groupId).compose(configureApiCallObserver())
}
override fun createGroup(group: Group): Flowable<Group> {
return apiService.createGroup(group).compose(configureApiCallObserver())
}
override fun updateGroup(id: String, item: Group): Flowable<Void> {
return apiService.updateGroup(id, item).compose(configureApiCallObserver())
}

View file

@ -19,6 +19,7 @@ import io.realm.RealmResults
class SocialRepositoryImpl(localRepository: SocialLocalRepository, apiClient: ApiClient, userID: String) : BaseRepositoryImpl<SocialLocalRepository>(localRepository, apiClient, userID), SocialRepository {
override fun getGroupMembership(id: String): Flowable<GroupMembership> {
return localRepository.getGroupMembership(userID, id)
}
@ -131,7 +132,19 @@ class SocialRepositoryImpl(localRepository: SocialLocalRepository, apiClient: Ap
}
}
override fun updateGroup(group: Group?, name: String?, description: String?, leader: String?, privacy: String?): Flowable<Void> {
override fun createGroup(name: String?, description: String?, leader: String?, type: String?, privacy: String?, leaderCreateChallenge: Boolean?): Flowable<Group> {
val group = Group()
group.name = name
group.description = description
group.type = type
group.leaderID = leader
group.privacy = privacy
return apiClient.createGroup(group).doOnNext {
localRepository.save(it)
}
}
override fun updateGroup(group: Group?, name: String?, description: String?, leader: String?, leaderCreateChallenge: Boolean?): Flowable<Void> {
if (group == null) {
return Flowable.empty()
}
@ -139,7 +152,7 @@ class SocialRepositoryImpl(localRepository: SocialLocalRepository, apiClient: Ap
copiedGroup.name = name
copiedGroup.description = description
copiedGroup.leaderID = leader
copiedGroup.privacy = privacy
copiedGroup.leaderOnlyChallenges = leaderCreateChallenge ?: false
localRepository.save(copiedGroup)
return apiClient.updateGroup(copiedGroup.id, copiedGroup)
}

View file

@ -13,34 +13,22 @@ open class Group : RealmObject() {
@SerializedName("_id")
@PrimaryKey
var id: String = ""
var balance: Double = 0.toDouble()
var description: String? = null
var summary: String? = null
var leaderID: String? = null
var leaderName: String? = null
var name: String? = null
var memberCount: Int = 0
var type: String? = null
var logo: String? = null
var quest: Quest? = null
var privacy: String? = null
var members: RealmList<User>? = null
var challengeCount: Int = 0
var leaderMessage: String? = null
var leaderOnlyChallenges: Boolean = false
var leaderOnlyGetGems: Boolean = false
override fun equals(other: Any?): Boolean {
if (this === other) {

View file

@ -1,45 +1,35 @@
package com.habitrpg.android.habitica.ui.activities
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.core.content.ContextCompat
import android.view.KeyEvent
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.ImageButton
import android.widget.LinearLayout
import android.widget.Spinner
import android.widget.*
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.AppComponent
import com.habitrpg.android.habitica.ui.helpers.KeyboardUtil
import com.habitrpg.android.habitica.ui.helpers.MarkdownParser
import com.habitrpg.android.habitica.ui.helpers.bindView
import net.pherth.android.emoji_library.EmojiEditText
import net.pherth.android.emoji_library.EmojiPopup
import com.habitrpg.android.habitica.extensions.notNull
import com.habitrpg.android.habitica.ui.helpers.*
class GroupFormActivity : BaseActivity() {
private var groupID: String? = null
private var groupType: String? = null
private var groupName: String? = null
private var groupDescription: String? = null
private var groupPrivacy: String? = null
private var groupLeader: String? = null
private var leaderCreateChallenge = false
private val groupNameEditText: EditText by bindView(R.id.group_name_edittext)
private val groupDescriptionEditText: EmojiEditText by bindView(R.id.group_description_edittext)
internal val emojiButton: ImageButton by bindView(R.id.emoji_toggle_btn)
internal val privacyWrapper: LinearLayout by bindView(R.id.privacyWrapper)
private val cancelButton: ImageButton by bindView(R.id.cancel_button)
private val saveButton: Button by bindView(R.id.save_button)
private val groupNameEditText: MultiAutoCompleteTextView by bindView(R.id.group_name_edittext)
private val groupDescriptionEditText: MultiAutoCompleteTextView by bindView(R.id.group_description_edittext)
private val leaderCreateChallengeSwitch: Switch by bindView(R.id.leader_create_challenge_switch)
private val privacyWrapper: LinearLayout by bindView(R.id.privacyWrapper)
internal val privacySpinner: Spinner by bindView(R.id.privacySpinner)
private val privacySeparator: View by bindView(R.id.privacy_separator)
private val popup: EmojiPopup by lazy {
EmojiPopup(emojiButton.rootView, this, ContextCompat.getColor(this, R.color.brand))
}
private var autocompleteAdapter: AutocompleteAdapter? = null
override fun getLayoutResId(): Int {
return R.layout.activity_group_form
@ -48,61 +38,34 @@ class GroupFormActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val intent = intent
val bundle = intent.extras
this.groupID = bundle!!.getString("groupID")
this.groupName = bundle.getString("name")
this.groupDescription = bundle.getString("description")
this.groupPrivacy = bundle.getString("privacy")
this.groupLeader = bundle.getString("leader")
// Emoji keyboard stuff
popup
popup.setSizeForSoftKeyboard()
popup.setOnDismissListener { changeEmojiKeyboardIcon(false) }
popup.setOnSoftKeyboardOpenCloseListener(object : EmojiPopup.OnSoftKeyboardOpenCloseListener {
override fun onKeyboardOpen(keyBoardHeight: Int) {
}
override fun onKeyboardClose() {
if (popup.isShowing) {
popup.dismiss()
}
}
})
popup.setOnEmojiconClickedListener { emojicon ->
if (currentFocus == null || !isEmojiEditText(currentFocus) || emojicon == null) {
return@setOnEmojiconClickedListener
}
val emojiEditText = currentFocus as? EmojiEditText
val start = emojiEditText?.selectionStart ?: 0
val end = emojiEditText?.selectionEnd ?: 0
if (start < 0) {
emojiEditText?.append(emojicon.emoji)
} else {
emojiEditText?.text?.replace(Math.min(start, end),
Math.max(start, end), emojicon.emoji, 0,
emojicon.emoji.length)
}
intent.extras.notNull {bundle ->
groupID = bundle.getString("groupID")
groupType = bundle.getString("groupType")
groupName = bundle.getString("name")
groupDescription = bundle.getString("description")
groupPrivacy = bundle.getString("privacy")
groupLeader = bundle.getString("leader")
leaderCreateChallenge = bundle.getBoolean("leaderCreateChallenge", false)
}
popup.setOnEmojiconBackspaceClickedListener { _ ->
if (isEmojiEditText(currentFocus)) {
val event = KeyEvent(
0, 0, 0, KeyEvent.KEYCODE_DEL, 0, 0, 0, 0, KeyEvent.KEYCODE_ENDCALL)
currentFocus?.dispatchKeyEvent(event)
}
if (groupType == "party") {
privacyWrapper.visibility = View.GONE
privacySeparator.visibility = View.GONE
}
emojiButton.setOnClickListener(EmojiClickListener(groupDescriptionEditText))
if (this.groupID != null) {
this.fillForm()
if (groupID != null) {
fillForm()
}
cancelButton.setOnClickListener {
finish()
KeyboardUtil.dismissKeyboard(this)
}
saveButton.setOnClickListener {
finishActivitySuccessfuly()
}
}
@ -111,43 +74,11 @@ class GroupFormActivity : BaseActivity() {
}
private fun fillForm() {
this.groupNameEditText.setText(this.groupName)
this.groupDescriptionEditText.setText(this.groupDescription)
this.privacyWrapper.visibility = View.GONE
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.menu_save, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
val id = item.itemId
if (id == R.id.action_save_changes) {
finishActivitySuccessfuly()
return true
}
return super.onOptionsItemSelected(item)
}
private fun isEmojiEditText(view: View?): Boolean {
return view is EmojiEditText
}
private fun changeEmojiKeyboardIcon(keyboardOpened: Boolean) {
if (keyboardOpened) {
emojiButton.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_keyboard_grey600_24dp))
} else {
emojiButton.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_emoticon_grey600_24dp))
}
groupNameEditText.setText(groupName)
groupDescriptionEditText.setText(groupDescription)
leaderCreateChallengeSwitch.isChecked = leaderCreateChallenge
privacyWrapper.visibility = View.GONE
saveButton.text = getString(R.string.save)
}
override fun onSupportNavigateUp(): Boolean {
@ -165,7 +96,9 @@ class GroupFormActivity : BaseActivity() {
val resultIntent = Intent()
val bundle = Bundle()
bundle.putString("name", this.groupNameEditText.text.toString())
bundle.putString("groupType", groupType)
bundle.putString("description", MarkdownParser.parseCompiled(this.groupDescriptionEditText.text))
bundle.putBoolean("leaderCreateChallenge", leaderCreateChallengeSwitch.isActivated)
bundle.putString("leader", this.groupLeader)
resultIntent.putExtras(bundle)
setResult(Activity.RESULT_OK, resultIntent)
@ -173,28 +106,6 @@ class GroupFormActivity : BaseActivity() {
KeyboardUtil.dismissKeyboard(this)
}
private inner class EmojiClickListener(internal var view: EmojiEditText) : View.OnClickListener {
override fun onClick(v: View) {
if (!popup.isShowing) {
if (popup.isKeyBoardOpen == true) {
popup.showAtBottom()
changeEmojiKeyboardIcon(true)
} else {
view.isFocusableInTouchMode = true
view.requestFocus()
popup.showAtBottomPending()
val inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT)
changeEmojiKeyboardIcon(true)
}
} else {
popup.dismiss()
changeEmojiKeyboardIcon(false)
}
}
}
companion object {
const val GROUP_FORM_ACTIVITY = 11

View file

@ -1,5 +1,6 @@
package com.habitrpg.android.habitica.ui.fragments.social
import android.app.Activity
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
@ -7,24 +8,24 @@ import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Shader
import android.graphics.drawable.BitmapDrawable
import android.net.Uri
import android.os.Bundle
import android.text.method.LinkMovementMethod
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.net.toUri
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.extensions.backgroundCompat
import com.habitrpg.android.habitica.extensions.notNull
import com.habitrpg.android.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.invitations.PartyInvite
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.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.DataBindingUtils
@ -71,7 +72,7 @@ class GroupInformationFragment : BaseFragment() {
updateGroup(group)
buttonPartyInviteAccept.setOnClickListener { _ ->
buttonPartyInviteAccept.setOnClickListener {
val userId = user?.invitations?.party?.id
if (userId != null) {
socialRepository.joinGroup(userId)
@ -83,7 +84,7 @@ class GroupInformationFragment : BaseFragment() {
}
}
buttonPartyInviteReject.setOnClickListener { _ ->
buttonPartyInviteReject.setOnClickListener {
val userId = user?.invitations?.party?.id
if (userId != null) {
socialRepository.rejectGroupInvite(userId)
@ -91,7 +92,7 @@ class GroupInformationFragment : BaseFragment() {
}
}
username_textview.setOnClickListener { _ ->
username_textview.setOnClickListener {
val clipboard = context?.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager
val clip = ClipData.newPlainText(context?.getString(R.string.username), user?.username)
clipboard?.primaryClip = clip
@ -102,16 +103,21 @@ class GroupInformationFragment : BaseFragment() {
}
craetePartyButton.setOnClickListener {
val browserIntent = Intent(Intent.ACTION_VIEW, "https://habitica.com/party".toUri())
startActivity(browserIntent)
val bundle = Bundle()
bundle.putString("groupType", "party")
bundle.putString("leader", user?.id)
val intent = Intent(activity, GroupFormActivity::class.java)
intent.putExtras(bundle)
intent.flags = Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
startActivityForResult(intent, GroupFormActivity.GROUP_FORM_ACTIVITY)
}
context.notNull { context ->
DataBindingUtils.loadImage("timeTravelersShop_background_fall") {
val aspectRatio = it.width / it.height.toFloat()
DataBindingUtils.loadImage("timeTravelersShop_background_fall") {bitmap ->
val aspectRatio = bitmap.width / bitmap.height.toFloat()
val height = context.resources.getDimension(R.dimen.shop_height).toInt()
val width = Math.round(height * aspectRatio)
val drawable = BitmapDrawable(context.resources, Bitmap.createScaledBitmap(it, width, height, false))
val drawable = BitmapDrawable(context.resources, Bitmap.createScaledBitmap(bitmap, width, height, false))
drawable.tileModeX = Shader.TileMode.REPEAT
Observable.just(drawable)
.observeOn(AndroidSchedulers.mainThread())
@ -125,6 +131,37 @@ class GroupInformationFragment : BaseFragment() {
groupSummaryView.movementMethod = LinkMovementMethod.getInstance()
}
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
if (bundle?.getString("groupType") == "party") {
socialRepository.createGroup(bundle.getString("name"),
bundle.getString("description"),
bundle.getString("leader"),
"party",
bundle.getString("privacy"),
bundle.getBoolean("leaderCreateChallenge"))
.flatMap {
userRepository.retrieveUser(false)
}
.subscribe(Consumer {
}, RxErrorHandler.handleEmptyError())
} else {
this.socialRepository.updateGroup(this.group,
bundle?.getString("name"),
bundle?.getString("description"),
bundle?.getString("leader"),
bundle?.getBoolean("leaderCreateChallenge"))
.subscribe(Consumer { }, RxErrorHandler.handleEmptyError())
}
}
}
}
}
private fun refresh() {
if (group != null) {
compositeSubscription.add(socialRepository.retrieveGroup(group?.id ?: "").subscribe(Consumer {}, RxErrorHandler.handleEmptyError()))

View file

@ -180,11 +180,13 @@ class GuildFragment : BaseMainFragment() {
private fun displayEditForm() {
val bundle = Bundle()
bundle.putString("groupID", this.guild?.id)
bundle.putString("name", this.guild?.name)
bundle.putString("description", this.guild?.description)
bundle.putString("privacy", this.guild?.privacy)
bundle.putString("leader", this.guild?.leaderID)
bundle.putString("groupID", guild?.id)
bundle.putString("name", guild?.name)
bundle.putString("description", guild?.description)
bundle.putString("privacy", guild?.privacy)
bundle.putString("leader", guild?.leaderID)
bundle.putBoolean("leaderCreateChallenge", guild?.leaderOnlyChallenges ?: true)
val intent = Intent(activity, GroupFormActivity::class.java)
intent.putExtras(bundle)
@ -202,7 +204,7 @@ class GuildFragment : BaseMainFragment() {
bundle?.getString("name"),
bundle?.getString("description"),
bundle?.getString("leader"),
bundle?.getString("privacy"))
bundle?.getBoolean("leaderCreateChallenge"))
.subscribe(Consumer { }, RxErrorHandler.handleEmptyError())
}
}

View file

@ -195,7 +195,7 @@ class PartyDetailFragment constructor(private val viewModel: PartyViewModel) : B
val builder = AlertDialog.Builder(activity)
.setMessage(R.string.leave_party_confirmation)
.setPositiveButton(R.string.yes) { _, _ ->
viewModel.leaveGroup { activity?.supportFragmentManager?.beginTransaction()?.remove(this)?.commit() }
viewModel.leaveGroup { }
}.setNegativeButton(R.string.no) { _, _ -> }
builder.show()
}

View file

@ -12,8 +12,7 @@ import androidx.lifecycle.ViewModelProviders
import androidx.viewpager.widget.ViewPager
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.RemoteConfigManager
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.activities.PartyInviteActivity
@ -24,15 +23,14 @@ import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.ui.helpers.resetViews
import com.habitrpg.android.habitica.ui.viewmodels.GroupViewType
import com.habitrpg.android.habitica.ui.viewmodels.PartyViewModel
import io.reactivex.functions.Consumer
import java.util.*
import javax.inject.Inject
class PartyFragment : BaseMainFragment() {
@Inject
lateinit var configRepository: RemoteConfigManager
private val viewPager: ViewPager? by bindView(R.id.viewPager)
private var firstFragment: Fragment? = null
private var partyMemberListFragment: PartyMemberListFragment? = null
private var chatFragment: ChatFragment? = null
private var viewPagerAdapter: androidx.fragment.app.FragmentPagerAdapter? = null
@ -62,10 +60,26 @@ class PartyFragment : BaseMainFragment() {
viewModel.loadPartyID()
// Get the full group data
if (userHasParty()) {
viewModel.retrieveGroup {}
}
compositeSubscription.add(userRepository.getUser()
.map {
it.hasParty()
}
.distinctUntilChanged()
.subscribe(Consumer {
val fragment = firstFragment
if (fragment != null) {
childFragmentManager.beginTransaction().remove(fragment).commit()
}
viewPager?.adapter?.notifyDataSetChanged()
if (it) {
viewModel.retrieveGroup {}
tabLayout?.visibility = View.VISIBLE
} else {
tabLayout?.visibility = View.GONE
}
}, RxErrorHandler.handleEmptyError()))
viewPager?.currentItem = 0
@ -74,10 +88,6 @@ class PartyFragment : BaseMainFragment() {
this.tutorialText = getString(R.string.tutorial_party)
}
private fun userHasParty(): Boolean {
return user?.party?.id?.isNotEmpty() == true
}
override fun injectFragment(component: AppComponent) {
component.inject(this)
}
@ -102,6 +112,9 @@ class PartyFragment : BaseMainFragment() {
if (group != null && this.user != null) {
if (group.leaderID == this.user?.id) {
inflater.inflate(R.menu.menu_party_admin, menu)
if (group.memberCount > 1) {
menu.findItem(R.id.menu_guild_leave).isVisible = false
}
} else {
inflater.inflate(R.menu.menu_party, menu)
}
@ -128,9 +141,6 @@ class PartyFragment : BaseMainFragment() {
.setMessage(context?.getString(R.string.leave_party_confirmation))
.setPositiveButton(context?.getString(R.string.yes)) { _, _ ->
viewModel.leaveGroup {
parentFragment.notNull { fragment ->
activity?.supportFragmentManager?.beginTransaction()?.remove(fragment)?.commit()
}
}
}
.setNegativeButton(context?.getString(R.string.no)) { dialog, _ -> dialog.dismiss() }
@ -147,8 +157,10 @@ class PartyFragment : BaseMainFragment() {
val group = viewModel.getGroupData().value
bundle.putString("groupID", group?.id)
bundle.putString("name", group?.name)
bundle.putString("groupType", group?.type)
bundle.putString("description", group?.description)
bundle.putString("leader", group?.leaderID)
bundle.putBoolean("leaderCreateChallenge", group?.leaderOnlyChallenges ?: false)
val intent = Intent(activity, GroupFormActivity::class.java)
intent.putExtras(bundle)
@ -161,7 +173,7 @@ class PartyFragment : BaseMainFragment() {
when (requestCode) {
GroupFormActivity.GROUP_FORM_ACTIVITY -> {
if (resultCode == Activity.RESULT_OK) {
viewModel.updateGroup(data?.extras)
viewModel.updateOrCreateGroup(data?.extras)
}
}
PartyInviteActivity.RESULT_SEND_INVITES -> {
@ -201,18 +213,19 @@ class PartyFragment : BaseMainFragment() {
override fun getItem(position: Int): androidx.fragment.app.Fragment {
return when (position) {
0 -> {
if (user?.hasParty() == true) {
firstFragment = if (user?.hasParty() == true) {
val detailFragment = PartyDetailFragment(viewModel)
detailFragment
} else {
GroupInformationFragment.newInstance(null, user)
}
firstFragment
}
1 -> {
if (chatFragment == null) {
chatFragment = ChatFragment(viewModel)
}
chatFragment ?: Fragment()
chatFragment
}
2 -> {
if (partyMemberListFragment == null) {
@ -221,15 +234,15 @@ class PartyFragment : BaseMainFragment() {
partyMemberListFragment?.setPartyId(user?.party?.id ?: "")
}
}
partyMemberListFragment ?: Fragment()
partyMemberListFragment
}
else -> Fragment()
}
} ?: Fragment()
}
override fun getCount(): Int {
return if (viewModel.getGroupData().value == null) {
return if (user?.hasParty() != true) {
1
} else {
3
@ -244,6 +257,14 @@ class PartyFragment : BaseMainFragment() {
else -> ""
} ?: ""
}
override fun getItemPosition(fragment: Any): Int {
return if ((fragment is GroupInformationFragment && user?.hasParty() == true) || (fragment is PartyDetailFragment && user?.hasParty() != true)) {
POSITION_NONE
} else {
POSITION_UNCHANGED
}
}
}
this.viewPager?.adapter = viewPagerAdapter
@ -266,6 +287,4 @@ class PartyFragment : BaseMainFragment() {
})
tabLayout?.setupWithViewPager(viewPager)
}
}

View file

@ -27,7 +27,7 @@ class AutocompleteAdapter(val context: Context, val socialRepository: SocialRepo
override fun performFiltering(constraint: CharSequence?): FilterResults {
val filterResults = FilterResults()
if (constraint != null && constraint.isNotEmpty()) {
if (constraint[0] == '@' && socialRepository != null) {
if (constraint[0] == '@' && constraint.length >= 3 && socialRepository != null) {
isAutocompletingUsers = true
userResults = socialRepository.findUsernames(constraint.toString().drop(1), autocompleteContext, groupID).blockingFirst(arrayListOf())
filterResults.values = userResults

View file

@ -89,15 +89,23 @@ open class GroupViewModel: BaseViewModel() {
.subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
}
fun updateGroup(bundle: Bundle?) {
fun updateOrCreateGroup(bundle: Bundle?) {
if (group.value == null) {
return
socialRepository.createGroup(bundle?.getString("name"),
bundle?.getString("description"),
bundle?.getString("leader"),
bundle?.getString("groupType"),
bundle?.getString("privacy"),
bundle?.getBoolean("leaderCreateChallenge"))
} else {
disposable.add(socialRepository.updateGroup(group.value, bundle?.getString("name"),
bundle?.getString("description"),
bundle?.getString("leader"),
bundle?.getBoolean("leaderCreateChallenge"))
.subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
}
disposable.add(socialRepository.updateGroup(group.value, bundle?.getString("name"),
bundle?.getString("description"),
bundle?.getString("leader"),
bundle?.getString("privacy"))
.subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
}
fun leaveGroup(function: () -> Unit) {
@ -148,10 +156,13 @@ open class GroupViewModel: BaseViewModel() {
}
fun retrieveGroupChat(onComplete: () -> Unit) {
groupIDSubject.value?.value.notNull {
disposable.add(socialRepository.retrieveGroupChat(it).subscribe(Consumer {
onComplete()
}, RxErrorHandler.handleEmptyError()))
val groupID = groupIDSubject.value?.value
if (groupID.isNullOrEmpty()) {
onComplete()
return
}
disposable.add(socialRepository.retrieveGroupChat(groupID).subscribe(Consumer {
onComplete()
}, RxErrorHandler.handleEmptyError()))
}
}

View file

@ -106,6 +106,16 @@ class GroupSerialization : JsonDeserializer<Group>, JsonSerializer<Group> {
}
}
}
if (obj.has("leaderOnly")) {
val leaderOnly = obj.getAsJsonObject("leaderOnly")
if (leaderOnly.has("challenges")) {
group.leaderOnlyChallenges = leaderOnly.get("challenges").asBoolean
}
if (leaderOnly.has("getGems")) {
group.leaderOnlyGetGems = leaderOnly.get("getGems").asBoolean
}
}
return group
}
@ -118,6 +128,10 @@ class GroupSerialization : JsonDeserializer<Group>, JsonSerializer<Group> {
obj.addProperty("type", src.type)
obj.addProperty("type", src.type)
obj.addProperty("leader", src.leaderID)
val leaderOnly = JsonObject()
leaderOnly.addProperty("challenges", src.leaderOnlyChallenges)
leaderOnly.addProperty("getGems", src.leaderOnlyGetGems)
obj.add("leaderOnly", leaderOnly)
return obj
}
}