Unverified Commit b06bd403 authored by Benoît Marty's avatar Benoît Marty Committed by GitHub
Browse files

Merge pull request #3322 from vector-im/feature/bca/spaces_feedback

Add option to send beta feedback
parents 03f2b516 72e343ad
......@@ -161,7 +161,7 @@ Formatter\.formatShortFileSize===1
# android\.text\.TextUtils
### This is not a rule, but a warning: the number of "enum class" has changed. For Json classes, it is mandatory that they have `@JsonClass(generateAdapter = false)`. If the enum is not used as a Json class, change the value in file forbidden_strings_in_code.txt
enum class===99
enum class===100
### Do not import temporary legacy classes
import org.matrix.android.sdk.internal.legacy.riot===3
......
......@@ -16,12 +16,14 @@
package im.vector.app.core.ui.list
import android.widget.TextView
import androidx.annotation.ColorInt
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.features.themes.ThemeUtils
/**
* A generic list item header left aligned with notice color.
......@@ -32,9 +34,18 @@ abstract class GenericItemHeader : VectorEpoxyModel<GenericItemHeader.Holder>()
@EpoxyAttribute
var text: String? = null
@EpoxyAttribute
@ColorInt
var textColor: Int? = null
override fun bind(holder: Holder) {
super.bind(holder)
holder.text.setTextOrHide(text)
if (textColor != null) {
holder.text.setTextColor(textColor!!)
} else {
holder.text.setTextColor(ThemeUtils.getColor(holder.view.context, R.attr.vctr_notice_text_color))
}
}
class Holder : VectorEpoxyHolder() {
......
......@@ -55,6 +55,7 @@ import im.vector.app.features.permalink.PermalinkHandler
import im.vector.app.features.popup.DefaultVectorAlert
import im.vector.app.features.popup.PopupAlertManager
import im.vector.app.features.popup.VerificationVectorAlert
import im.vector.app.features.rageshake.ReportType
import im.vector.app.features.rageshake.VectorUncaughtExceptionHandler
import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.settings.VectorSettingsActivity
......@@ -216,6 +217,9 @@ class HomeActivity :
SpaceInviteBottomSheet.newInstance(sharedAction.spaceId)
.show(supportFragmentManager, "SPACE_INVITE")
}
HomeActivitySharedAction.SendSpaceFeedBack -> {
bugReporter.openBugReportScreen(this, ReportType.SPACE_BETA_FEEDBACK)
}
}.exhaustive
}
.disposeOnDestroy()
......@@ -449,11 +453,11 @@ class HomeActivity :
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.menu_home_suggestion -> {
bugReporter.openBugReportScreen(this, true)
bugReporter.openBugReportScreen(this, ReportType.SUGGESTION)
return true
}
R.id.menu_home_report_bug -> {
bugReporter.openBugReportScreen(this, false)
bugReporter.openBugReportScreen(this, ReportType.BUG_REPORT)
return true
}
R.id.menu_home_init_sync_legacy -> {
......
......@@ -29,4 +29,5 @@ sealed class HomeActivitySharedAction : VectorSharedAction {
data class OpenSpacePreview(val spaceId: String) : HomeActivitySharedAction()
data class OpenSpaceInvite(val spaceId: String) : HomeActivitySharedAction()
data class ShowSpaceSettings(val spaceId: String) : HomeActivitySharedAction()
object SendSpaceFeedBack : HomeActivitySharedAction()
}
......@@ -16,6 +16,8 @@
package im.vector.app.features.rageshake
import android.content.Context
import android.content.Intent
import android.view.Menu
import android.view.MenuItem
import android.widget.Toast
......@@ -27,6 +29,7 @@ import im.vector.app.R
import im.vector.app.core.di.ScreenComponent
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivityBugReportBinding
import org.matrix.android.sdk.api.extensions.tryOrNull
import timber.log.Timber
import javax.inject.Inject
......@@ -46,7 +49,7 @@ class BugReportActivity : VectorBaseActivity<ActivityBugReportBinding>() {
private val viewModel: BugReportViewModel by viewModel()
private var forSuggestion: Boolean = false
private var reportType: ReportType = ReportType.BUG_REPORT
override fun initUiAndData() {
configureToolbar(views.bugReportToolbar)
......@@ -60,30 +63,48 @@ class BugReportActivity : VectorBaseActivity<ActivityBugReportBinding>() {
views.bugReportButtonIncludeScreenshot.isEnabled = false
}
forSuggestion = intent.getBooleanExtra("FOR_SUGGESTION", false)
reportType = intent.getStringExtra(REPORT_TYPE_EXTRA)?.let {
tryOrNull { ReportType.valueOf(it) }
} ?: ReportType.BUG_REPORT
// Default screen is for bug report, so modify it for suggestion
if (forSuggestion) {
supportActionBar?.setTitle(R.string.send_suggestion)
when (reportType) {
ReportType.BUG_REPORT -> {
supportActionBar?.setTitle(R.string.title_activity_bug_report)
views.bugReportButtonContactMe.isVisible = true
}
ReportType.SUGGESTION -> {
supportActionBar?.setTitle(R.string.send_suggestion)
views.bugReportFirstText.setText(R.string.send_suggestion_content)
views.bugReportTextInputLayout.hint = getString(R.string.send_suggestion_report_placeholder)
views.bugReportFirstText.setText(R.string.send_suggestion_content)
views.bugReportTextInputLayout.hint = getString(R.string.send_suggestion_report_placeholder)
views.bugReportButtonContactMe.isVisible = true
views.bugReportLogsDescription.isVisible = false
hideBugReportOptions()
}
ReportType.SPACE_BETA_FEEDBACK -> {
supportActionBar?.setTitle(R.string.send_feedback_space_title)
views.bugReportButtonIncludeLogs.isChecked = false
views.bugReportButtonIncludeLogs.isVisible = false
views.bugReportFirstText.setText(R.string.send_feedback_space_info)
views.bugReportTextInputLayout.hint = getString(R.string.feedback)
views.bugReportButtonContactMe.isVisible = true
views.bugReportButtonIncludeCrashLogs.isChecked = false
views.bugReportButtonIncludeCrashLogs.isVisible = false
hideBugReportOptions()
}
}
}
views.bugReportButtonIncludeKeyShareHistory.isChecked = false
views.bugReportButtonIncludeKeyShareHistory.isVisible = false
private fun hideBugReportOptions() {
views.bugReportLogsDescription.isVisible = false
// Keep the screenshot
} else {
supportActionBar?.setTitle(R.string.title_activity_bug_report)
}
views.bugReportButtonIncludeLogs.isChecked = false
views.bugReportButtonIncludeLogs.isVisible = false
views.bugReportButtonIncludeCrashLogs.isChecked = false
views.bugReportButtonIncludeCrashLogs.isVisible = false
views.bugReportButtonIncludeKeyShareHistory.isChecked = false
views.bugReportButtonIncludeKeyShareHistory.isVisible = false
}
private fun setupViews() {
......@@ -134,23 +155,31 @@ class BugReportActivity : VectorBaseActivity<ActivityBugReportBinding>() {
views.bugReportProgressView.progress = 0
bugReporter.sendBugReport(this,
forSuggestion,
reportType,
views.bugReportButtonIncludeLogs.isChecked,
views.bugReportButtonIncludeCrashLogs.isChecked,
views.bugReportButtonIncludeKeyShareHistory.isChecked,
views.bugReportButtonIncludeScreenshot.isChecked,
views.bugReportEditText.text.toString(),
state.serverVersion,
views.bugReportButtonContactMe.isChecked,
object : BugReporter.IMXBugReportListener {
override fun onUploadFailed(reason: String?) {
try {
if (!reason.isNullOrEmpty()) {
if (forSuggestion) {
Toast.makeText(this@BugReportActivity,
getString(R.string.send_suggestion_failed, reason), Toast.LENGTH_LONG).show()
} else {
Toast.makeText(this@BugReportActivity,
getString(R.string.send_bug_report_failed, reason), Toast.LENGTH_LONG).show()
when (reportType) {
ReportType.BUG_REPORT -> {
Toast.makeText(this@BugReportActivity,
getString(R.string.send_bug_report_failed, reason), Toast.LENGTH_LONG).show()
}
ReportType.SUGGESTION -> {
Toast.makeText(this@BugReportActivity,
getString(R.string.send_suggestion_failed, reason), Toast.LENGTH_LONG).show()
}
ReportType.SPACE_BETA_FEEDBACK -> {
Toast.makeText(this@BugReportActivity,
getString(R.string.feedback_failed, reason), Toast.LENGTH_LONG).show()
}
}
}
} catch (e: Exception) {
......@@ -178,10 +207,16 @@ class BugReportActivity : VectorBaseActivity<ActivityBugReportBinding>() {
override fun onUploadSucceed() {
try {
if (forSuggestion) {
Toast.makeText(this@BugReportActivity, R.string.send_suggestion_sent, Toast.LENGTH_LONG).show()
} else {
Toast.makeText(this@BugReportActivity, R.string.send_bug_report_sent, Toast.LENGTH_LONG).show()
when (reportType) {
ReportType.BUG_REPORT -> {
Toast.makeText(this@BugReportActivity, R.string.send_bug_report_sent, Toast.LENGTH_LONG).show()
}
ReportType.SUGGESTION -> {
Toast.makeText(this@BugReportActivity, R.string.send_suggestion_sent, Toast.LENGTH_LONG).show()
}
ReportType.SPACE_BETA_FEEDBACK -> {
Toast.makeText(this@BugReportActivity, R.string.feedback_sent, Toast.LENGTH_LONG).show()
}
}
} catch (e: Exception) {
Timber.e(e, "## onUploadSucceed() : failed to dismiss the toast")
......@@ -214,4 +249,14 @@ class BugReportActivity : VectorBaseActivity<ActivityBugReportBinding>() {
super.onBackPressed()
}
companion object {
private const val REPORT_TYPE_EXTRA = "REPORT_TYPE_EXTRA"
fun intent(context: Context, reportType: ReportType): Intent {
return Intent(context, BugReportActivity::class.java).apply {
putExtra(REPORT_TYPE_EXTRA, reportType.name)
}
}
}
}
......@@ -18,7 +18,6 @@ package im.vector.app.features.rageshake
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Canvas
import android.os.Build
......@@ -149,7 +148,7 @@ class BugReporter @Inject constructor(
* Send a bug report.
*
* @param context the application context
* @param forSuggestion true to send a suggestion
* @param reportType The report type (bug, suggestion, feedback)
* @param withDevicesLogs true to include the device log
* @param withCrashLogs true to include the crash logs
* @param withKeyRequestHistory true to include the crash logs
......@@ -159,13 +158,14 @@ class BugReporter @Inject constructor(
*/
@SuppressLint("StaticFieldLeak")
fun sendBugReport(context: Context,
forSuggestion: Boolean,
reportType: ReportType,
withDevicesLogs: Boolean,
withCrashLogs: Boolean,
withKeyRequestHistory: Boolean,
withScreenshot: Boolean,
theBugDescription: String,
serverVersion: String,
canContact: Boolean = false,
listener: IMXBugReportListener?) {
// enumerate files to delete
val mBugReportFiles: MutableList<File> = ArrayList()
......@@ -246,13 +246,11 @@ class BugReporter @Inject constructor(
}
if (!mIsCancelled) {
val text = "[Element] " +
if (forSuggestion) {
"[Suggestion] "
} else {
""
} +
bugDescription
val text = when (reportType) {
ReportType.BUG_REPORT -> "[Element] $bugDescription"
ReportType.SUGGESTION -> "[Element] [Suggestion] $bugDescription"
ReportType.SPACE_BETA_FEEDBACK -> "[Element] [spaces-feedback] $bugDescription"
}
// build the multi part request
val builder = BugReporterMultipartBody.Builder()
......@@ -260,6 +258,7 @@ class BugReporter @Inject constructor(
.addFormDataPart("app", "riot-android")
.addFormDataPart("user_agent", Matrix.getInstance(context).getUserAgent())
.addFormDataPart("user_id", userId)
.addFormDataPart("can_contact", canContact.toString())
.addFormDataPart("device_id", deviceId)
.addFormDataPart("version", versionProvider.getVersion(longFormat = true, useBuildNumber = false))
.addFormDataPart("branch_name", context.getString(R.string.git_branch_name))
......@@ -321,9 +320,12 @@ class BugReporter @Inject constructor(
// Special for RiotX
builder.addFormDataPart("label", "[Element]")
// Suggestion
if (forSuggestion) {
builder.addFormDataPart("label", "[Suggestion]")
when (reportType) {
ReportType.BUG_REPORT -> {
/* nop */
}
ReportType.SUGGESTION -> builder.addFormDataPart("label", "[Suggestion]")
ReportType.SPACE_BETA_FEEDBACK -> builder.addFormDataPart("label", "spaces-feedback")
}
if (getCrashFile(context).exists()) {
......@@ -447,16 +449,14 @@ class BugReporter @Inject constructor(
/**
* Send a bug report either with email or with Vector.
*/
fun openBugReportScreen(activity: FragmentActivity, forSuggestion: Boolean = false) {
fun openBugReportScreen(activity: FragmentActivity, reportType: ReportType = ReportType.BUG_REPORT) {
screenshot = takeScreenshot(activity)
activeSessionHolder.getSafeActiveSession()?.let {
it.logDbUsageInfo()
it.cryptoService().logDbUsageInfo()
}
val intent = Intent(activity, BugReportActivity::class.java)
intent.putExtra("FOR_SUGGESTION", forSuggestion)
activity.startActivity(intent)
activity.startActivity(BugReportActivity.intent(activity, reportType))
}
// ==============================================================================================================
......
/*
* Copyright (c) 2021 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.rageshake
enum class ReportType {
BUG_REPORT,
SUGGESTION,
SPACE_BETA_FEEDBACK
}
......@@ -16,13 +16,28 @@
package im.vector.app.features.spaces
import android.view.View
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.utils.DebouncedClickListener
@EpoxyModelClass(layout = R.layout.item_space_beta_header)
abstract class SpaceBetaHeaderItem : VectorEpoxyModel<SpaceBetaHeaderItem.Holder>() {
class Holder : VectorEpoxyHolder()
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
var clickAction: View.OnClickListener? = null
override fun bind(holder: Holder) {
super.bind(holder)
holder.feedBackAction.setOnClickListener(DebouncedClickListener({
clickAction?.onClick(it)
}))
}
class Holder : VectorEpoxyHolder() {
val feedBackAction by bind<View>(R.id.spaceBetaFeedbackAction)
}
}
......@@ -101,4 +101,8 @@ class SpaceListFragment @Inject constructor(
override fun onGroupSelected(groupSummary: GroupSummary?) {
viewModel.handle(SpaceListAction.SelectLegacyGroup(groupSummary))
}
override fun sendFeedBack() {
sharedActionViewModel.post(HomeActivitySharedAction.SendSpaceFeedBack)
}
}
......@@ -33,6 +33,8 @@ import im.vector.app.databinding.BottomSheetSpaceSettingsBinding
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.navigation.Navigator
import im.vector.app.features.powerlevel.PowerLevelsObservableFactory
import im.vector.app.features.rageshake.BugReporter
import im.vector.app.features.rageshake.ReportType
import im.vector.app.features.roomprofile.RoomProfileActivity
import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.spaces.manage.ManageType
......@@ -58,6 +60,7 @@ class SpaceSettingsMenuBottomSheet : VectorBaseBottomSheetDialogFragment<BottomS
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
@Inject lateinit var avatarRenderer: AvatarRenderer
@Inject lateinit var vectorPreferences: VectorPreferences
@Inject lateinit var bugReporter: BugReporter
private val spaceArgs: SpaceBottomSheetSettingsArgs by args()
......@@ -106,6 +109,10 @@ class SpaceSettingsMenuBottomSheet : VectorBaseBottomSheetDialogFragment<BottomS
views.addRooms.isVisible = canAddChild
}.disposeOnDestroyView()
views.spaceBetaTag.setOnClickListener {
bugReporter.openBugReportScreen(requireActivity(), ReportType.SPACE_BETA_FEEDBACK)
}
views.invitePeople.views.bottomSheetActionClickableZone.debouncedClicks {
dismiss()
interactionListener?.onShareSpaceSelected(spaceArgs.spaceId)
......
......@@ -16,9 +16,11 @@
package im.vector.app.features.spaces
import android.view.View
import com.airbnb.epoxy.EpoxyController
import im.vector.app.R
import im.vector.app.RoomGroupingMethod
import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider
import im.vector.app.core.ui.list.genericFooterItem
import im.vector.app.core.ui.list.genericItemHeader
......@@ -38,6 +40,7 @@ import javax.inject.Inject
class SpaceSummaryController @Inject constructor(
private val avatarRenderer: AvatarRenderer,
private val colorProvider: ColorProvider,
private val stringProvider: StringProvider) : EpoxyController() {
var callback: Callback? = null
......@@ -72,6 +75,7 @@ class SpaceSummaryController @Inject constructor(
genericItemHeader {
id("legacy_groups")
text(stringProvider.getString(R.string.groups_header))
textColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_primary))
}
// add home for communities
......@@ -104,13 +108,13 @@ class SpaceSummaryController @Inject constructor(
rootSpaces: List<RoomSummary>?,
expandedStates: Map<String, Boolean>,
homeCount: RoomAggregateNotificationCount) {
genericItemHeader {
id("spaces")
text(stringProvider.getString(R.string.spaces_header))
spaceBetaHeaderItem {
id("beta_header")
clickAction(View.OnClickListener {
callback?.sendFeedBack()
})
}
spaceBetaHeaderItem { id("beta_header") }
// show invites on top
summaries?.filter { it.membership == Membership.INVITE }
......@@ -221,7 +225,7 @@ class SpaceSummaryController @Inject constructor(
fun onSpaceSettings(spaceSummary: RoomSummary)
fun onToggleExpand(spaceSummary: RoomSummary)
fun onAddSpaceSelected()
fun onGroupSelected(groupSummary: GroupSummary?)
fun sendFeedBack()
}
}
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:viewportWidth="16"
android:viewportHeight="16">
<path
android:pathData="M6.2887,10.748C8.7036,10.748 10.6612,8.7897 10.6612,6.374C10.6612,3.9583 8.7036,2 6.2887,2C3.8739,2 1.9163,3.9583 1.9163,6.374C1.9163,7.038 2.0642,7.6674 2.3288,8.2311L1.6,9.7683C1.2011,10.6099 2.0682,11.492 2.9165,11.1074L4.5258,10.378C5.0651,10.6159 5.6615,10.748 6.2887,10.748Z"
android:fillColor="#368BD6"
android:fillType="evenOdd"/>
<path
android:pathData="M12.6608,7.3739C12.6608,9.7896 10.7032,11.7479 8.2883,11.7479C7.8421,11.7479 7.4114,11.681 7.0059,11.5568C7.7732,12.2953 8.8076,12.7479 9.9456,12.7479C10.5605,12.7479 11.1451,12.6158 11.6737,12.3778L13.211,13.0887C14.0564,13.4796 14.9301,12.6043 14.5376,11.7597L13.8272,10.2308C14.0865,9.6672 14.2315,9.0378 14.2315,8.3739C14.2315,6.4535 13.0188,4.8221 11.3323,4.2339C12.1516,5.0289 12.6608,6.1419 12.6608,7.3739Z"
android:fillColor="#368BD6"
android:fillType="evenOdd"/>
</vector>
......@@ -133,6 +133,15 @@
android:checked="false"
android:text="@string/send_bug_report_include_key_share_history" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/bug_report_button_contact_me"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:checked="false"
android:text="@string/you_may_contact_me" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/bug_report_button_include_screenshot"
android:layout_width="match_parent"
......
......@@ -30,7 +30,6 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/layout_horizontal_margin"
android:layout_marginEnd="8dp"
android:duplicateParentState="true"
android:ellipsize="end"
android:maxLines="1"
......@@ -39,12 +38,22 @@
android:textStyle="bold"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toTopOf="@+id/spaceDescription"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintEnd_toStartOf="@id/spaceBetaTag"
app:layout_constraintStart_toEndOf="@id/spaceAvatarImageView"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
tools:text="@sample/matrix.json/data/displayName" />
<ImageView
android:id="@+id/spaceBetaTag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:contentDescription="@string/a11y_beta"
android:src="@drawable/ic_beta_pill"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/spaceDescription"
android:layout_width="0dp"
......@@ -97,7 +106,7 @@
app:actionTitle="@string/settings"
app:leftIcon="@drawable/ic_settings_root_general"
app:tint="?attr/riotx_text_primary"
app:titleTextColor="?attr/riotx_text_primary"/>
app:titleTextColor="?attr/riotx_text_primary" />
<im.vector.app.core.ui.views.BottomSheetActionButton
android:id="@+id/exploreRooms"
......
......@@ -33,7 +33,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_vertical_margin_big"
android:importantForAccessibility="no"
android:contentDescription="@string/a11y_beta"
android:src="@drawable/ic_beta_pill"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
......
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout 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="wrap_content"
android:padding="16dp">
android:orientation="vertical"
android:paddingStart="16dp"