Impelement new report view. Fixes #355

This commit is contained in:
Phillip Thelen 2019-04-18 11:54:32 +02:00
parent fbeaef460c
commit fbf6ac031d
32 changed files with 438 additions and 111 deletions

View file

@ -229,6 +229,11 @@
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
</intent-filter>
</activity>
<activity
android:name=".ui.activities.ReportMessageActivity"
android:theme="@style/BottomSheetTheme"
android:windowSoftInputMode="stateHidden|adjustResize">
</activity>
<service
android:name=".helpers.notifications.HabiticaFirebaseMessagingService"
android:exported="false">

View file

@ -152,7 +152,7 @@ android {
buildConfigField "String", "STORE", "\"google\""
multiDexEnabled true
versionCode 2091
versionCode 2092
versionName "1.9"
}

View file

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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"
xmlns:tools="http://schemas.android.com/tools">
<View
android:id="@+id/touch_outside"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<FrameLayout
android:id="@+id/bottom_sheet"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
app:behavior_peekHeight="350dp"
app:behavior_hideable="true"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/content_container"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:elevation="0dp">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
android:theme="@style/Toolbar"
android:background="@color/white"
app:layout_scrollFlags="scroll|enterAlways"
app:title="@string/report"
app:titleTextColor="@color/gray_50"
app:theme="@style/ReportMessageToolbar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Dark"/>
</com.google.android.material.appbar.AppBarLayout>
<LinearLayout
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="16dp"
android:paddingLeft="24dp"
android:paddingRight="24dp"
android:paddingBottom="25dp">
<TextView
android:id="@+id/title_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Report NAME for violation:"
android:textColor="@color/gray_10"
android:textSize="14sp"
android:textStyle="bold"
android:layout_marginBottom="@dimen/spacing_medium"
android:layout_marginStart="12dp"/>
<TextView
android:id="@+id/message_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/border_1f000000"
android:textColor="@color/gray_300"
android:padding="@dimen/spacing_medium"
tools:text="This is the message that is reported" />
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/TextInputLayoutAppearance"
android:layout_marginTop="@dimen/spacing_large"
android:layout_marginBottom="@dimen/spacing_large">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/additional_info_edittext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/reason_for_report"
android:textColor="@color/gray_50"/>
</com.google.android.material.textfield.TextInputLayout>
<TextView
android:id="@+id/report_explanation_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
tools:text="@string/report_explanation"
android:textSize="12sp"
android:textColor="@color/gray_200"
/>
</LinearLayout>
</LinearLayout>
</FrameLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="24dp"
android:paddingRight="24dp"
android:paddingBottom="25dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Report NAME for violation:" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/border_1f000000"
android:textColor="@color/gray_300"
android:padding="@dimen/spacing_medium"
android:text="This is the message that is reported" />
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/report_explanation"
android:textSize="12sp"
android:textColor="@color/gray_200"
/>
</LinearLayout>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/menu_report" android:title="@string/report" app:showAsAction="always" />
</menu>

View file

@ -2,7 +2,7 @@
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/navigation"
android:id="@+id/main_nav"
app:startDestination="@id/tasksFragment">
<fragment
@ -261,4 +261,37 @@
android:id="@+id/groupFormActivity"
android:name="com.habitrpg.android.habitica.ui.activities.GroupFormActivity"
android:label="GroupFormActivity" />
<activity
android:id="@+id/classSelectionActivity"
android:name="com.habitrpg.android.habitica.ui.activities.ClassSelectionActivity"
android:label="ClassSelectionActivity" >
<argument
android:name="isInitialSelection"
app:argType="boolean"
android:defaultValue="false" />
<argument
android:name="className"
app:argType="string"
android:defaultValue="warrior" />
</activity>
<activity
android:id="@+id/reportMessageActivity"
android:name="com.habitrpg.android.habitica.ui.activities.ReportMessageActivity"
android:label="ReportMessageActivity" >
<argument
android:name="text"
app:argType="string" />
<argument
android:name="profileName"
app:argType="string" />
<argument
android:name="messageID"
app:argType="string" />
</activity>
<action
android:id="@+id/action_global_reportMessageActivity"
app:destination="@id/reportMessageActivity" />
<action
android:id="@+id/action_global_classSelectionActivity"
app:destination="@id/classSelectionActivity" />
</navigation>

View file

@ -887,4 +887,7 @@
<string name="need_more_help">Need more help?</string>
<string name="become_x">Become a %s</string>
<string name="x_class">%s Class</string>
<string name="report_explanation">**Only** report a post that violates the [Community Guidelines](https://habitica.com/static/community-guidelines) and/or [Terms of Service](https://habitica.com/static/terms). Inappropriately reporting a post may give you an infraction.</string>
<string name="report_message_title">Report %s for violation:</string>
<string name="reason_for_report">Reason for report (optional)</string>
</resources>

View file

@ -28,6 +28,17 @@
<item name="alertDialogTheme">@style/AlertDialogTheme</item>
</style>
<style name="BottomSheetTheme" parent="Theme.AppCompat.NoActionBar">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:backgroundDimEnabled">true</item>
<item name="actionBarStyle">@color/transparent</item>
<item name="colorPrimary">@color/brand</item>
<item name="colorPrimaryDark">@color/brand_50</item>
<item name="colorAccent">@color/brand</item>
<item name="android:windowContentOverlay">@null</item>
</style>
<style name="AppTheme.ActionBar" parent="AppTheme">
<item name="windowActionBar">true</item>
<item name="windowNoTitle">false</item>
@ -537,5 +548,9 @@
<item name="android:actionMenuTextColor">@color/gray_50</item>
</style>
<style name="ReportMessageToolbar">
<item name="android:actionMenuTextColor">@color/red_50</item>
</style>
<color name="taskform_gray">#99edecee</color>
</resources>

View file

@ -35,8 +35,6 @@ import com.habitrpg.android.habitica.models.user.Items;
import com.habitrpg.android.habitica.models.user.Stats;
import com.habitrpg.android.habitica.models.user.User;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Map;
@ -236,7 +234,7 @@ public interface ApiService {
Flowable<HabitResponse<ChatMessage>> likeMessage(@Path("gid") String groupId, @Path("mid") String mid);
@POST("groups/{gid}/chat/{mid}/flag")
Flowable<HabitResponse<Void>> flagMessage(@Path("gid") String groupId, @Path("mid") String mid);
Flowable<HabitResponse<Void>> flagMessage(@Path("gid") String groupId, @Path("mid") String mid, @Body Map<String, String> data);
@POST("groups/{gid}/chat/seen")
Flowable<HabitResponse<Void>> seenMessages(@Path("gid") String groupId);

View file

@ -30,6 +30,7 @@ import com.habitrpg.android.habitica.ui.activities.MainActivity;
import com.habitrpg.android.habitica.ui.activities.MaintenanceActivity;
import com.habitrpg.android.habitica.ui.activities.PartyInviteActivity;
import com.habitrpg.android.habitica.ui.activities.PrefsActivity;
import com.habitrpg.android.habitica.ui.activities.ReportMessageActivity;
import com.habitrpg.android.habitica.ui.activities.SetupActivity;
import com.habitrpg.android.habitica.ui.activities.SkillMemberActivity;
import com.habitrpg.android.habitica.ui.activities.SkillTasksActivity;
@ -314,4 +315,6 @@ public interface AppComponent {
void inject(@NotNull ChatBarView chatBarView);
void inject(@NotNull TaskFormActivity taskFormActivity);
void inject(@NotNull ReportMessageActivity reportMessageActivity);
}

View file

@ -20,7 +20,6 @@ import com.habitrpg.android.habitica.models.user.Stats
import com.habitrpg.android.habitica.models.user.User
import io.reactivex.Flowable
import io.reactivex.FlowableTransformer
import io.reactivex.ObservableTransformer
import retrofit2.HttpException
@ -150,7 +149,7 @@ interface ApiClient {
// Like returns the full chat list
fun likeMessage(groupId: String, mid: String): Flowable<ChatMessage>
fun flagMessage(groupId: String, mid: String): Flowable<Void>
fun flagMessage(groupId: String, mid: String, data: MutableMap<String, String>): Flowable<Void>
fun seenMessages(groupId: String): Flowable<Void>

View file

@ -20,7 +20,7 @@ interface SocialRepository : BaseRepository {
fun markMessagesSeen(seenGroupId: String)
fun flagMessage(chatMessage: ChatMessage): Flowable<Void>
fun flagMessage(chatMessage: ChatMessage, additionalInfo: String): Flowable<Void>
fun likeMessage(chatMessage: ChatMessage): Flowable<ChatMessage>
@ -79,4 +79,5 @@ interface SocialRepository : BaseRepository {
fun getGroupMembership(id: String): Flowable<GroupMembership>
fun getGroupMemberships(): Flowable<RealmResults<GroupMembership>>
fun getChatmessage(messageID: String): Flowable<ChatMessage>
}

View file

@ -524,8 +524,8 @@ class ApiClientImpl//private OnHabitsAPIResult mResultListener;
return apiService.likeMessage(groupId, mid).compose(configureApiCallObserver())
}
override fun flagMessage(groupId: String, mid: String): Flowable<Void> {
return apiService.flagMessage(groupId, mid).compose(configureApiCallObserver())
override fun flagMessage(groupId: String, mid: String, data: MutableMap<String, String>): Flowable<Void> {
return apiService.flagMessage(groupId, mid, data).compose(configureApiCallObserver())
}
override fun seenMessages(groupId: String): Flowable<Void> {

View file

@ -6,12 +6,10 @@ import com.habitrpg.android.habitica.data.local.SocialLocalRepository
import com.habitrpg.android.habitica.extensions.notNull
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.AchievementResult
import com.habitrpg.android.habitica.models.auth.LocalAuthentication
import com.habitrpg.android.habitica.models.inventory.Quest
import com.habitrpg.android.habitica.models.members.Member
import com.habitrpg.android.habitica.models.responses.PostChatMessageResult
import com.habitrpg.android.habitica.models.social.*
import com.habitrpg.android.habitica.models.user.Authentication
import com.habitrpg.android.habitica.models.user.User
import io.reactivex.Flowable
import io.reactivex.Single
@ -20,7 +18,9 @@ import io.reactivex.functions.Consumer
import io.realm.RealmResults
class SocialRepositoryImpl(localRepository: SocialLocalRepository, apiClient: ApiClient, userID: String) : BaseRepositoryImpl<SocialLocalRepository>(localRepository, apiClient, userID), SocialRepository {
override fun getChatmessage(messageID: String): Flowable<ChatMessage> {
return localRepository.getChatMessage(messageID)
}
override fun getGroupMembership(id: String): Flowable<GroupMembership> {
return localRepository.getGroupMembership(userID, id)
@ -49,10 +49,14 @@ class SocialRepositoryImpl(localRepository: SocialLocalRepository, apiClient: Ap
apiClient.seenMessages(seenGroupId).subscribe(Consumer { }, RxErrorHandler.handleEmptyError())
}
override fun flagMessage(chatMessage: ChatMessage): Flowable<Void> {
override fun flagMessage(chatMessage: ChatMessage, additionalInfo: String): Flowable<Void> {
return if (chatMessage.id == "") {
Flowable.empty()
} else apiClient.flagMessage(chatMessage.groupId ?: "", chatMessage.id)
} else {
val data = mutableMapOf<String, String>()
data["comment"] = additionalInfo
apiClient.flagMessage(chatMessage.groupId ?: "", chatMessage.id, data)
}
}
override fun likeMessage(chatMessage: ChatMessage): Flowable<ChatMessage> {

View file

@ -47,4 +47,5 @@ interface SocialLocalRepository : BaseLocalRepository {
fun getInboxOverviewList(userId: String): Flowable<RealmResults<ChatMessage>>
fun saveGroupMemberships(userID: String?, memberships: List<GroupMembership>)
fun saveInboxMessages(userID: String, messages: List<ChatMessage>)
fun getChatMessage(messageID: String): Flowable<ChatMessage>
}

View file

@ -15,6 +15,12 @@ import java.util.*
class RealmSocialLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm), SocialLocalRepository {
override fun getChatMessage(messageID: String): Flowable<ChatMessage> = realm.where(ChatMessage::class.java)
.equalTo("id", messageID)
.findAll()
.asFlowable()
.filter { it.isLoaded && it.isNotEmpty() }
.map { it.first() }
override fun getGroupMembership(userId: String, id: String): Flowable<GroupMembership> = realm.where(GroupMembership::class.java)
.equalTo("userID", userId)

View file

@ -15,6 +15,7 @@ import android.widget.Button
import android.widget.TextView
import androidx.appcompat.widget.Toolbar
import androidx.core.content.ContextCompat
import androidx.navigation.navArgs
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.AppComponent
import com.habitrpg.android.habitica.data.UserRepository
@ -86,9 +87,9 @@ class ClassSelectionActivity : BaseActivity(), Consumer<User> {
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true)
val intent = intent
val bundle = intent.extras
isInitialSelection = bundle?.getBoolean("isInitialSelection") ?: false
val args = navArgs<ClassSelectionActivityArgs>().value
isInitialSelection = args.isInitialSelection
currentClass = args.className
compositeSubscription.add(userRepository.getUser().firstElement().subscribe(Consumer {
it.preferences?.let {preferences ->
@ -97,9 +98,6 @@ class ClassSelectionActivity : BaseActivity(), Consumer<User> {
setAvatarViews(unmanagedPrefs)
}
}, RxErrorHandler.handleEmptyError()))
bundle.notNull { thisBundle ->
currentClass = thisBundle.getString("currentClass")
}
if (!isInitialSelection) {
compositeSubscription.add(userRepository.changeClass()

View file

@ -30,10 +30,7 @@ import com.habitrpg.android.habitica.models.user.Stats
import com.habitrpg.android.habitica.ui.AvatarView
import com.habitrpg.android.habitica.ui.AvatarWithBarsViewModel
import com.habitrpg.android.habitica.ui.adapter.social.AchievementAdapter
import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
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 com.habitrpg.android.habitica.ui.helpers.*
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar.SnackbarDisplayType
import io.reactivex.Flowable
@ -63,8 +60,8 @@ class FullProfileActivity : BaseActivity() {
private val equipmentTableLayout: TableLayout by bindView(R.id.equipment_table)
private val costumeTableLayout: TableLayout by bindView(R.id.costume_table)
private val costumeCard: androidx.cardview.widget.CardView by bindView(R.id.profile_costume_card)
private val avatar_with_bars: View by bindView(R.id.avatar_with_bars)
private val fullprofile_scrollview: ScrollView by bindView(R.id.fullprofile_scrollview)
private val avatarWithStatsView: View by bindView(R.id.avatar_with_bars)
private val scrollView: ScrollView by bindView(R.id.fullprofile_scrollview)
private val petsFoundCount: TextView by bindView(R.id.profile_pets_found_count)
private val mountsTamedCount: TextView by bindView(R.id.profile_mounts_tamed_count)
private val currentPetDrawee: SimpleDraweeView by bindView(R.id.current_pet_drawee)
@ -101,12 +98,12 @@ class FullProfileActivity : BaseActivity() {
avatarWithBars?.valueBarLabelsToBlack()
avatar_with_bars.setBackgroundColor(ContextCompat.getColor(this, R.color.transparent))
avatarWithStatsView.setBackgroundColor(ContextCompat.getColor(this, R.color.transparent))
attributeRows.clear()
attributesCardView.setOnClickListener { toggleAttributeDetails() }
avatarWithBars = AvatarWithBarsViewModel(this, avatar_with_bars)
avatarWithBars = AvatarWithBarsViewModel(this, avatarWithStatsView)
}
override fun onDestroy() {
@ -144,13 +141,13 @@ class FullProfileActivity : BaseActivity() {
.setPositiveButton(android.R.string.ok) { _, _ ->
socialRepository.postPrivateMessage(userID, emojiEditText.text.toString())
.subscribe(Consumer {
HabiticaSnackbar.showSnackbar(this@FullProfileActivity.fullprofile_scrollview.getChildAt(0) as ViewGroup,
HabiticaSnackbar.showSnackbar(this@FullProfileActivity.scrollView.getChildAt(0) as ViewGroup,
String.format(getString(R.string.profile_message_sent_to), userName), SnackbarDisplayType.NORMAL)
}, RxErrorHandler.handleEmptyError())
KeyboardUtil.dismissKeyboard(this)
dismissKeyboard()
}
.setNegativeButton(android.R.string.cancel) { _, _ -> KeyboardUtil.dismissKeyboard(this) }
.setNegativeButton(android.R.string.cancel) { _, _ -> dismissKeyboard() }
.create()
@ -352,12 +349,10 @@ class FullProfileActivity : BaseActivity() {
addLevelAttributes(user)
for (row in statsRows) {
if (row.javaClass == UserStatComputer.EquipmentRow::class.java) {
val equipmentRow = row as UserStatComputer.EquipmentRow
addEquipmentRow(equipmentTableLayout, equipmentRow.gearKey, equipmentRow.text, equipmentRow.stats)
} else if (row.javaClass == UserStatComputer.AttributeRow::class.java) {
val attributeRow2 = row as UserStatComputer.AttributeRow
addAttributeRow(getString(attributeRow2.labelId), attributeRow2.strVal, attributeRow2.intVal, attributeRow2.conVal, attributeRow2.perVal, attributeRow2.roundDown, attributeRow2.isSummary)
if (row is UserStatComputer.EquipmentRow) {
addEquipmentRow(equipmentTableLayout, row.gearKey, row.text, row.stats)
} else if (row is UserStatComputer.AttributeRow) {
addAttributeRow(getString(row.labelId), row.strVal, row.intVal, row.conVal, row.perVal, row.roundDown, row.isSummary)
}
}

View file

@ -60,7 +60,7 @@ class GroupFormActivity : BaseActivity() {
cancelButton.setOnClickListener {
finish()
KeyboardUtil.dismissKeyboard(this)
dismissKeyboard()
}
saveButton.setOnClickListener {
@ -92,13 +92,13 @@ class GroupFormActivity : BaseActivity() {
override fun onSupportNavigateUp(): Boolean {
finish()
KeyboardUtil.dismissKeyboard(this)
dismissKeyboard()
return true
}
override fun onBackPressed() {
finish()
KeyboardUtil.dismissKeyboard(this)
dismissKeyboard()
}
private fun finishActivitySuccessfuly() {
@ -116,7 +116,7 @@ class GroupFormActivity : BaseActivity() {
resultIntent.putExtras(bundle)
setResult(Activity.RESULT_OK, resultIntent)
finish()
KeyboardUtil.dismissKeyboard(this)
dismissKeyboard()
}
companion object {

View file

@ -44,6 +44,7 @@ import com.habitrpg.android.habitica.models.auth.UserAuthResponse
import com.habitrpg.android.habitica.prefs.scanner.IntentIntegrator
import com.habitrpg.android.habitica.ui.helpers.KeyboardUtil
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.ui.helpers.dismissKeyboard
import com.habitrpg.android.habitica.ui.views.login.LockableScrollView
import com.habitrpg.android.habitica.ui.views.login.LoginBackgroundView
import io.reactivex.Flowable
@ -555,7 +556,7 @@ class LoginActivity : BaseActivity(), Consumer<UserAuthResponse> {
showAnimation.play(newGameAlphaAnimation).after(scrollViewAlphaAnimation)
showAnimation.play(showLoginAlphaAnimation).after(scrollViewAlphaAnimation)
showAnimation.start()
KeyboardUtil.dismissKeyboard(this)
dismissKeyboard()
}
private fun onForgotPasswordClicked() {

View file

@ -10,18 +10,14 @@ import android.content.res.Configuration
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.drawable.BitmapDrawable
import android.net.Uri
import android.os.Build
import android.os.Build.VERSION.SDK_INT
import android.os.Bundle
import android.os.Trace
import android.preference.PreferenceManager
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.appbar.CollapsingToolbarLayout
import com.google.android.material.tabs.TabLayout
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import androidx.drawerlayout.widget.DrawerLayout
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.Toolbar
@ -32,11 +28,11 @@ import android.widget.FrameLayout
import android.widget.TextView
import androidx.core.content.edit
import androidx.navigation.findNavController
import androidx.navigation.fragment.NavHostFragment
import com.facebook.drawee.view.SimpleDraweeView
import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.perf.FirebasePerformance
import com.habitrpg.android.habitica.HabiticaBaseApplication
import com.habitrpg.android.habitica.MainNavDirections
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.api.HostConfig
import com.habitrpg.android.habitica.api.MaintenanceApiService
@ -51,6 +47,7 @@ import com.habitrpg.android.habitica.interactors.*
import com.habitrpg.android.habitica.models.TutorialStep
import com.habitrpg.android.habitica.models.responses.MaintenanceResponse
import com.habitrpg.android.habitica.models.responses.TaskScoringResult
import com.habitrpg.android.habitica.models.social.Group
import com.habitrpg.android.habitica.models.tasks.Task
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.proxy.CrashlyticsProxy
@ -82,7 +79,6 @@ import io.reactivex.schedulers.Schedulers
import io.realm.Realm
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import java.lang.IllegalStateException
import java.lang.ref.WeakReference
import java.util.*
import javax.inject.Inject

View file

@ -23,6 +23,7 @@ import com.habitrpg.android.habitica.prefs.scanner.IntentIntegrator
import com.habitrpg.android.habitica.ui.fragments.social.party.PartyInviteFragment
import com.habitrpg.android.habitica.ui.helpers.KeyboardUtil
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.ui.helpers.dismissKeyboard
import io.reactivex.functions.Consumer
import java.util.*
import javax.inject.Inject
@ -74,7 +75,7 @@ class PartyInviteActivity : BaseActivity() {
if (id == R.id.action_send_invites) {
setResult(Activity.RESULT_OK, createResultIntent())
KeyboardUtil.dismissKeyboard(this)
dismissKeyboard()
finish()
return true
}

View file

@ -0,0 +1,162 @@
package com.habitrpg.android.habitica.ui.activities
import android.annotation.SuppressLint
import android.graphics.Color
import android.os.Build
import android.os.Build.VERSION_CODES
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.TextView
import androidx.annotation.RequiresApi
import androidx.appcompat.widget.Toolbar
import androidx.core.content.ContextCompat
import androidx.navigation.navArgs
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.bottomsheet.BottomSheetBehavior
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.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.social.ChatMessage
import com.habitrpg.android.habitica.ui.helpers.MarkdownParser
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.ui.helpers.dismissKeyboard
import io.reactivex.functions.Consumer
import javax.inject.Inject
class ReportMessageActivity : BaseActivity() {
@Inject
lateinit var socialRepository: SocialRepository
private val toolbar: Toolbar by bindView(R.id.toolbar)
private val appBar: AppBarLayout by bindView(R.id.app_bar)
private val bottomSheetView: View by bindView(R.id.bottom_sheet)
private val contentContainer: ViewGroup by bindView(R.id.content_container)
private val dismissTouchView: View by bindView(R.id.touch_outside)
private val titleTextView: TextView by bindView(R.id.title_text_view)
private val messageTextView: TextView by bindView(R.id.message_text_view)
private val additionInfoEditText: EditText by bindView(R.id.additional_info_edittext)
private val reportExplanationTextView: TextView by bindView(R.id.report_explanation_textview)
private var raisedElevation = 0f
private var messageID: String? = null
private var isReporting: Boolean = false
override fun getLayoutResId(): Int {
return R.layout.activity_report_message
}
override fun injectActivity(component: AppComponent?) {
component?.inject(this)
}
private var chatMessage: ChatMessage? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setSupportActionBar(toolbar)
raisedElevation = appBar.elevation
setStatusBarDim(true)
contentContainer.setOnTouchListener { _, _ -> true }
additionInfoEditText.setOnTouchListener { _, _ -> true }
dismissTouchView.setOnClickListener { finish() }
reportExplanationTextView.text = MarkdownParser.parseMarkdown(getString(R.string.report_explanation))
BottomSheetBehavior.from<View>(bottomSheetView)
.setBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
@SuppressLint("SwitchIntDef")
override fun onStateChanged(bottomSheet: View, newState: Int) {
when (newState) {
BottomSheetBehavior.STATE_HIDDEN -> finish()
BottomSheetBehavior.STATE_EXPANDED -> setStatusBarDim(false)
else -> setStatusBarDim(true)
}
}
override fun onSlide(bottomSheet: View, slideOffset: Float) {
// no op
}
})
val args = navArgs<ReportMessageActivityArgs>().value
messageID = args.messageID
titleTextView.text = getString(R.string.report_message_title, args.profileName)
messageTextView.text = args.text
messageID?.let {messageID ->
compositeSubscription.add(socialRepository.getChatmessage(messageID).subscribe(Consumer {
chatMessage = it
}, RxErrorHandler.handleEmptyError()))
}
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.report_message, menu)
return true
}
@Suppress("ReturnCount")
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val id = item.itemId
when (id) {
R.id.menu_report -> {
reportMessage()
return true
}
}
return super.onOptionsItemSelected(item)
}
private fun reportMessage() {
if (isReporting) {
return
}
chatMessage?.let {
isReporting = true
socialRepository.flagMessage(it, additionInfoEditText.text.toString())
.doOnError { isReporting = false }
.subscribe(Consumer {
finish()
}, RxErrorHandler.handleEmptyError())
}
}
private fun setStatusBarDim(dim: Boolean) {
if (dim) {
appBar.elevation = 0f
window.statusBarColor = Color.TRANSPARENT
supportActionBar?.setDisplayHomeAsUpEnabled(false)
supportActionBar?.setHomeAsUpIndicator(null)
} else {
appBar.elevation = 8f
window.statusBarColor = ContextCompat.getColor(this, R.color.gray_600)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_close)
}
if (Build.VERSION.SDK_INT >= VERSION_CODES.M) {
setSystemBarTheme(dim)
}
}
override fun finish() {
dismissKeyboard()
super.finish()
}
@RequiresApi(api = Build.VERSION_CODES.M)
fun setSystemBarTheme(isDark: Boolean) {
// Fetch the current flags.
val lFlags = window.decorView.systemUiVisibility
// Update the SystemUiVisibility dependening on whether we want a Light or Dark theme.
window.decorView.systemUiVisibility = if (isDark) lFlags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv() else lFlags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
}
}

View file

@ -242,7 +242,6 @@ class ChatRecyclerViewAdapter(data: OrderedRealmCollection<ChatMessage>?, autoUp
buttonsWrapper.visibility = View.VISIBLE
deleteButton.visibility = if (shouldShowDelete()) View.VISIBLE else View.GONE
replyButton.visibility = if (chatMessage?.isInboxMessage == true) View.GONE else View.VISIBLE
reportButton.visibility = if (chatMessage?.uuid == userId) View.GONE else View.VISIBLE
} else {
buttonsWrapper.visibility = View.GONE
}

View file

@ -25,6 +25,7 @@ import com.habitrpg.android.habitica.ui.activities.GiftIAPActivity
import com.habitrpg.android.habitica.ui.helpers.KeyboardUtil
import com.habitrpg.android.habitica.ui.helpers.bindOptionalView
import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.ui.helpers.dismissKeyboard
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
import com.habitrpg.android.habitica.ui.views.subscriptions.SubscriptionDetailsView
import com.habitrpg.android.habitica.ui.views.subscriptions.SubscriptionOptionView
@ -293,7 +294,7 @@ class SubscriptionFragment : BaseFragment(), GemPurchaseActivity.CheckoutFragmen
startActivity(intent)
}
.setNeutralButton(getString(R.string.action_cancel)) { dialog, _ ->
KeyboardUtil.dismissKeyboard(thisActivity)
thisActivity.dismissKeyboard()
dialog.cancel()
}
.create()

View file

@ -15,9 +15,11 @@ import androidx.core.net.toUri
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
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.RemoteConfigManager
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.social.ChatMessage
@ -197,21 +199,8 @@ class ChatFragment constructor() : BaseFragment(), SwipeRefreshLayout.OnRefreshL
}
private fun showFlagConfirmationDialog(chatMessage: ChatMessage) {
val context = context
if (context != null) {
val builder = AlertDialog.Builder(context)
builder.setMessage(R.string.chat_flag_confirmation)
.setPositiveButton(R.string.flag_confirm) { _, _ ->
viewModel.flagMessage(chatMessage) {
val activity = activity as? MainActivity
activity?.floatingMenuWrapper.notNull {
showSnackbar(it, "Flagged message by " + chatMessage.user, SnackbarDisplayType.NORMAL)
}
}
}
.setNegativeButton(R.string.action_cancel) { _, _ -> }
builder.show()
}
val directions = MainNavDirections.actionGlobalReportMessageActivity(chatMessage.text ?: "", chatMessage.user ?: "", chatMessage.id)
MainNavigationController.navigate(directions)
}
private fun showDeleteConfirmationDialog(chatMessage: ChatMessage) {

View file

@ -15,11 +15,13 @@ import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.core.net.toUri
import com.habitrpg.android.habitica.MainNavDirections
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.notNull
import com.habitrpg.android.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.helpers.RemoteConfigManager
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.social.ChatMessage
@ -257,22 +259,8 @@ class ChatListFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener {
}
private fun showFlagConfirmationDialog(chatMessage: ChatMessage) {
val context = context
if (context != null) {
val builder = AlertDialog.Builder(context)
builder.setMessage(R.string.chat_flag_confirmation)
.setPositiveButton(R.string.flag_confirm) { _, _ ->
socialRepository.flagMessage(chatMessage)
.subscribe(Consumer {
val activity = activity as? MainActivity
activity?.floatingMenuWrapper.notNull {
showSnackbar(it, "Flagged message by " + chatMessage.user, SnackbarDisplayType.NORMAL)
}
}, RxErrorHandler.handleEmptyError())
}
.setNegativeButton(R.string.action_cancel) { _, _ -> }
builder.show()
}
val directions = MainNavDirections.actionGlobalReportMessageActivity(chatMessage.text ?: "", chatMessage.user ?: "", chatMessage.id)
MainNavigationController.navigate(directions)
}
private fun showDeleteConfirmationDialog(chatMessage: ChatMessage) {

View file

@ -18,6 +18,7 @@ import com.habitrpg.android.habitica.modules.AppModule
import com.habitrpg.android.habitica.ui.AvatarView
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
import com.habitrpg.android.habitica.ui.helpers.KeyboardUtil
import com.habitrpg.android.habitica.ui.helpers.dismissKeyboard
import com.habitrpg.android.habitica.ui.views.social.UsernameLabel
import io.reactivex.functions.Consumer
import io.realm.RealmResults
@ -94,7 +95,7 @@ class InboxFragment : BaseMainFragment(), androidx.swiperefreshlayout.widget.Swi
openInboxMessages(uuidEditText?.text?.toString() ?: "", "")
}
.setNeutralButton(getString(R.string.action_cancel)) { dialog, _ ->
KeyboardUtil.dismissKeyboard(thisActivity)
thisActivity.dismissKeyboard()
dialog.cancel()
}
.create()

View file

@ -8,10 +8,12 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import com.habitrpg.android.habitica.MainNavDirections
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.MainNavigationController
import com.habitrpg.android.habitica.helpers.RemoteConfigManager
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.social.ChatMessage
@ -146,19 +148,8 @@ class InboxMessageListFragment : BaseMainFragment(), androidx.swiperefreshlayout
}
private fun showFlagConfirmationDialog(chatMessage: ChatMessage) {
val activity = getActivity() as? MainActivity ?: return
val builder = AlertDialog.Builder(activity)
builder.setMessage(R.string.chat_flag_confirmation)
.setPositiveButton(R.string.flag_confirm) { _, _ ->
socialRepository.flagMessage(chatMessage)
.subscribe(Consumer { _ ->
activity.floatingMenuWrapper.notNull {
showSnackbar(it, "Flagged message by " + chatMessage.user, HabiticaSnackbar.SnackbarDisplayType.NORMAL)
}
}, RxErrorHandler.handleEmptyError())
}
.setNegativeButton(R.string.action_cancel) { _, _ -> }
builder.show()
val directions = MainNavDirections.actionGlobalReportMessageActivity(chatMessage.text ?: "", chatMessage.user ?: "", chatMessage.id)
MainNavigationController.navigate(directions)
}
private fun showDeleteConfirmationDialog(chatMessage: ChatMessage) {

View file

@ -79,4 +79,9 @@ class KeyboardUtil(activity: Activity, private val contentView: View) {
}
}
}
}
fun Activity.dismissKeyboard() {
KeyboardUtil.dismissKeyboard(this)
}

View file

@ -3,21 +3,15 @@ package com.habitrpg.android.habitica.ui.viewmodels
import android.os.Bundle
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
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.*
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.social.ChatMessage
import com.habitrpg.android.habitica.models.social.Group
import com.habitrpg.android.habitica.ui.activities.MainActivity
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar
import io.reactivex.BackpressureStrategy
import io.reactivex.Flowable
import io.reactivex.Maybe
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.functions.Consumer
import io.reactivex.subjects.BehaviorSubject
import io.realm.RealmResults
@ -135,13 +129,6 @@ open class GroupViewModel : BaseViewModel() {
disposable.add(socialRepository.likeMessage(message).subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
}
fun flagMessage(chatMessage: ChatMessage, function: () -> Unit) {
disposable.add(socialRepository.flagMessage(chatMessage)
.subscribe(Consumer {
function()
}, RxErrorHandler.handleEmptyError()))
}
fun deleteMessage(chatMessage: ChatMessage) {
disposable.add(socialRepository.deleteMessage(chatMessage).subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
}

View file

@ -1 +1,5 @@
This update brings username and emoji autocomplete, deep linking (open https://habitica.com urls from anywhere on your device with the app), bigger emojis, party creation and many bugfixes.
- Redesigned Class selection
- Redesigned Task Form
- Optimized user and content loading
- Improved FAQ and settings