forked from GitHub-Mirror/riotX-android
Merge pull request #382 from vector-im/feature/better_long_tap_menu
Feature/better long tap menu
This commit is contained in:
commit
4d5bdecec6
@ -7,6 +7,8 @@ Features:
|
||||
|
||||
Improvements:
|
||||
- Handle click on redacted events: view source and create permalink
|
||||
- Improve long tap menu: reply on top, more compact (#368)
|
||||
- Quick reply in timeline with swipe gesture
|
||||
- Improve edit of replies
|
||||
|
||||
Other changes:
|
||||
|
@ -37,9 +37,11 @@ import androidx.annotation.DrawableRes
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import butterknife.BindView
|
||||
import com.airbnb.epoxy.EpoxyModel
|
||||
import com.airbnb.epoxy.EpoxyVisibilityTracker
|
||||
import com.airbnb.mvrx.args
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
@ -57,6 +59,7 @@ import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.room.model.EditAggregatedSummary
|
||||
import im.vector.matrix.android.api.session.room.model.Membership
|
||||
import im.vector.matrix.android.api.session.room.model.message.*
|
||||
import im.vector.matrix.android.api.session.room.send.SendState
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.matrix.android.api.session.room.timeline.getLastMessageContent
|
||||
import im.vector.matrix.android.api.session.room.timeline.getTextEditableContent
|
||||
@ -88,7 +91,7 @@ import im.vector.riotx.features.home.room.detail.composer.TextComposerViewState
|
||||
import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController
|
||||
import im.vector.riotx.features.home.room.detail.timeline.action.*
|
||||
import im.vector.riotx.features.home.room.detail.timeline.helper.EndlessRecyclerViewScrollListener
|
||||
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
|
||||
import im.vector.riotx.features.home.room.detail.timeline.item.*
|
||||
import im.vector.riotx.features.html.EventHtmlRenderer
|
||||
import im.vector.riotx.features.html.PillImageSpan
|
||||
import im.vector.riotx.features.invite.VectorInviteView
|
||||
@ -324,6 +327,32 @@ class RoomDetailFragment :
|
||||
})
|
||||
recyclerView.setController(timelineEventController)
|
||||
timelineEventController.callback = this
|
||||
|
||||
if (VectorPreferences.swipeToReplyIsEnabled(requireContext())) {
|
||||
val swipeCallback = RoomMessageTouchHelperCallback(requireContext(),
|
||||
R.drawable.ic_reply,
|
||||
object : RoomMessageTouchHelperCallback.QuickReplayHandler {
|
||||
override fun performQuickReplyOnHolder(model: EpoxyModel<*>) {
|
||||
(model as? AbsMessageItem)?.informationData?.let {
|
||||
val eventId = it.eventId
|
||||
roomDetailViewModel.process(RoomDetailActions.EnterReplyMode(eventId))
|
||||
}
|
||||
}
|
||||
|
||||
override fun canSwipeModel(model: EpoxyModel<*>): Boolean {
|
||||
return when (model) {
|
||||
is MessageFileItem,
|
||||
is MessageImageVideoItem,
|
||||
is MessageTextItem -> {
|
||||
return (model as AbsMessageItem).informationData.sendState == SendState.SYNCED
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
})
|
||||
val touchHelper = ItemTouchHelper(swipeCallback)
|
||||
touchHelper.attachToRecyclerView(recyclerView)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupComposer() {
|
||||
|
@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright 2019 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.riotx.features.home.room.detail
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.util.TypedValue
|
||||
import android.view.HapticFeedbackConstants
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.ItemTouchHelper.ACTION_STATE_SWIPE
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.airbnb.epoxy.EpoxyModel
|
||||
import com.airbnb.epoxy.EpoxyTouchHelperCallback
|
||||
import com.airbnb.epoxy.EpoxyViewHolder
|
||||
import timber.log.Timber
|
||||
|
||||
|
||||
class RoomMessageTouchHelperCallback(private val context: Context,
|
||||
@DrawableRes actionIcon: Int,
|
||||
private val handler: QuickReplayHandler) : EpoxyTouchHelperCallback() {
|
||||
|
||||
interface QuickReplayHandler {
|
||||
fun performQuickReplyOnHolder(model: EpoxyModel<*>)
|
||||
fun canSwipeModel(model: EpoxyModel<*>): Boolean
|
||||
}
|
||||
|
||||
private var swipeBack: Boolean = false
|
||||
private var dX = 0f
|
||||
private var startTracking = false
|
||||
private var isVibrate = false
|
||||
|
||||
private var replyButtonProgress: Float = 0F
|
||||
private var lastReplyButtonAnimationTime: Long = 0
|
||||
|
||||
private var imageDrawable: Drawable = ContextCompat.getDrawable(context, actionIcon)!!
|
||||
|
||||
|
||||
private val triggerDistance = convertToPx(100)
|
||||
private val minShowDistance = convertToPx(20)
|
||||
private val triggerDelta = convertToPx(20)
|
||||
|
||||
override fun onSwiped(viewHolder: EpoxyViewHolder?, direction: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun onMove(recyclerView: RecyclerView?, viewHolder: EpoxyViewHolder?, target: EpoxyViewHolder?): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: EpoxyViewHolder): Int {
|
||||
if (handler.canSwipeModel(viewHolder.model)) {
|
||||
return ItemTouchHelper.Callback.makeMovementFlags(0, ItemTouchHelper.START) //Should we use Left?
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//We never let items completely go out
|
||||
override fun convertToAbsoluteDirection(flags: Int, layoutDirection: Int): Int {
|
||||
if (swipeBack) {
|
||||
swipeBack = false;
|
||||
return 0;
|
||||
}
|
||||
return super.convertToAbsoluteDirection(flags, layoutDirection);
|
||||
}
|
||||
|
||||
override fun onChildDraw(c: Canvas, recyclerView: RecyclerView, viewHolder: EpoxyViewHolder, dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean) {
|
||||
if (actionState == ACTION_STATE_SWIPE) {
|
||||
setTouchListener(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
|
||||
}
|
||||
val size = triggerDistance
|
||||
if (Math.abs(viewHolder.itemView.translationX) < size || dX > this.dX /*going back*/) {
|
||||
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
|
||||
this.dX = dX
|
||||
startTracking = true
|
||||
}
|
||||
drawReplyButton(c, viewHolder.itemView)
|
||||
}
|
||||
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private fun setTouchListener(c: Canvas,
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: EpoxyViewHolder,
|
||||
dX: Float, dY: Float,
|
||||
actionState: Int, isCurrentlyActive: Boolean) {
|
||||
//TODO can this interfer with other interactions? should i remove it
|
||||
recyclerView.setOnTouchListener { v, event ->
|
||||
swipeBack = event.action == MotionEvent.ACTION_CANCEL || event.action == MotionEvent.ACTION_UP
|
||||
if (swipeBack) {
|
||||
if (Math.abs(dX) >= triggerDistance) {
|
||||
try {
|
||||
viewHolder.model?.let { handler.performQuickReplyOnHolder(it) }
|
||||
} catch (e: IllegalStateException) {
|
||||
Timber.e(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun drawReplyButton(canvas: Canvas, itemView: View) {
|
||||
|
||||
Timber.v("drawReplyButton")
|
||||
val translationX = Math.abs(itemView.translationX)
|
||||
val newTime = System.currentTimeMillis()
|
||||
val dt = Math.min(17, newTime - lastReplyButtonAnimationTime)
|
||||
lastReplyButtonAnimationTime = newTime
|
||||
val showing = translationX >= minShowDistance
|
||||
if (showing) {
|
||||
if (replyButtonProgress < 1.0f) {
|
||||
replyButtonProgress += dt / 180.0f
|
||||
if (replyButtonProgress > 1.0f) {
|
||||
replyButtonProgress = 1.0f
|
||||
} else {
|
||||
itemView.invalidate()
|
||||
}
|
||||
}
|
||||
} else if (translationX <= 0.0f) {
|
||||
replyButtonProgress = 0f
|
||||
startTracking = false
|
||||
isVibrate = false
|
||||
} else {
|
||||
if (replyButtonProgress > 0.0f) {
|
||||
replyButtonProgress -= dt / 180.0f
|
||||
if (replyButtonProgress < 0.1f) {
|
||||
replyButtonProgress = 0f
|
||||
} else {
|
||||
itemView.invalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
val alpha: Int
|
||||
val scale: Float
|
||||
if (showing) {
|
||||
scale = if (replyButtonProgress <= 0.8f) {
|
||||
1.2f * (replyButtonProgress / 0.8f)
|
||||
} else {
|
||||
1.2f - 0.2f * ((replyButtonProgress - 0.8f) / 0.2f)
|
||||
}
|
||||
alpha = Math.min(255f, 255 * (replyButtonProgress / 0.8f)).toInt()
|
||||
} else {
|
||||
scale = replyButtonProgress
|
||||
alpha = Math.min(255f, 255 * replyButtonProgress).toInt()
|
||||
}
|
||||
|
||||
imageDrawable.alpha = alpha
|
||||
if (startTracking) {
|
||||
if (!isVibrate && translationX >= triggerDistance) {
|
||||
itemView.performHapticFeedback(
|
||||
HapticFeedbackConstants.LONG_PRESS
|
||||
// , HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING
|
||||
)
|
||||
isVibrate = true
|
||||
}
|
||||
}
|
||||
|
||||
val x: Int = itemView.width - if (translationX > triggerDistance + triggerDelta) {
|
||||
(convertToPx(130) / 2).toInt()
|
||||
} else {
|
||||
(translationX / 2).toInt()
|
||||
}
|
||||
|
||||
val y = (itemView.top + itemView.measuredHeight / 2).toFloat()
|
||||
//magic numbers?
|
||||
imageDrawable.setBounds(
|
||||
(x - convertToPx(12) * scale).toInt(),
|
||||
(y - convertToPx(11) * scale).toInt(),
|
||||
(x + convertToPx(12) * scale).toInt(),
|
||||
(y + convertToPx(10) * scale).toInt()
|
||||
)
|
||||
imageDrawable.draw(canvas)
|
||||
imageDrawable.alpha = 255
|
||||
}
|
||||
|
||||
private fun convertToPx(dp: Int): Float {
|
||||
return TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP,
|
||||
dp.toFloat(),
|
||||
context.resources.displayMetrics
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -127,13 +127,6 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M
|
||||
//TODO is downloading attachement?
|
||||
|
||||
if (!event.root.isRedacted()) {
|
||||
if (event.canReact()) {
|
||||
this.add(SimpleAction(ACTION_ADD_REACTION, R.string.message_add_reaction, R.drawable.ic_add_reaction, eventId))
|
||||
}
|
||||
if (canCopy(type)) {
|
||||
//TODO copy images? html? see ClipBoard
|
||||
this.add(SimpleAction(ACTION_COPY, R.string.copy, R.drawable.ic_copy, messageContent!!.body))
|
||||
}
|
||||
|
||||
if (canReply(event, messageContent)) {
|
||||
this.add(SimpleAction(ACTION_REPLY, R.string.reply, R.drawable.ic_reply, eventId))
|
||||
@ -147,6 +140,15 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M
|
||||
this.add(SimpleAction(ACTION_DELETE, R.string.delete, R.drawable.ic_delete, eventId))
|
||||
}
|
||||
|
||||
if (canCopy(type)) {
|
||||
//TODO copy images? html? see ClipBoard
|
||||
this.add(SimpleAction(ACTION_COPY, R.string.copy, R.drawable.ic_copy, messageContent!!.body))
|
||||
}
|
||||
|
||||
if (event.canReact()) {
|
||||
this.add(SimpleAction(ACTION_ADD_REACTION, R.string.message_add_reaction, R.drawable.ic_add_reaction, eventId))
|
||||
}
|
||||
|
||||
if (canQuote(event, messageContent)) {
|
||||
this.add(SimpleAction(ACTION_QUOTE, R.string.quote, R.drawable.ic_quote, eventId))
|
||||
}
|
||||
|
@ -148,6 +148,7 @@ object VectorPreferences {
|
||||
private const val SETTINGS_ENABLE_SEND_VOICE_FEATURE_PREFERENCE_KEY = "SETTINGS_ENABLE_SEND_VOICE_FEATURE_PREFERENCE_KEY"
|
||||
|
||||
private const val SETTINGS_LABS_SHOW_HIDDEN_EVENTS_PREFERENCE_KEY = "SETTINGS_LABS_SHOW_HIDDEN_EVENTS_PREFERENCE_KEY"
|
||||
private const val SETTINGS_LABS_ENABLE_SWIPE_TO_REPLY = "SETTINGS_LABS_ENABLE_SWIPE_TO_REPLY"
|
||||
|
||||
// analytics
|
||||
const val SETTINGS_USE_ANALYTICS_KEY = "SETTINGS_USE_ANALYTICS_KEY"
|
||||
@ -249,6 +250,10 @@ object VectorPreferences {
|
||||
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(SETTINGS_LABS_SHOW_HIDDEN_EVENTS_PREFERENCE_KEY, false)
|
||||
}
|
||||
|
||||
fun swipeToReplyIsEnabled(context: Context): Boolean {
|
||||
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(SETTINGS_LABS_ENABLE_SWIPE_TO_REPLY, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if we have already asked the user to disable battery optimisations on android >= M devices.
|
||||
*
|
||||
|
@ -7,18 +7,6 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/quickReactionTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:text="@string/quick_reactions"
|
||||
android:textColor="?riotx_text_secondary"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/quickReaction0"
|
||||
android:layout_width="wrap_content"
|
||||
@ -104,16 +92,14 @@
|
||||
android:id="@+id/reactionsFlowHelper"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:constraint_referenced_ids="quickReaction0,quickReaction1,quickReaction2,quickReaction3,quickReaction4,quickReaction5,quickReaction6,quickReaction7"
|
||||
app:flow_horizontalGap="8dp"
|
||||
app:flow_horizontalGap="0dp"
|
||||
app:flow_horizontalStyle="spread"
|
||||
app:flow_verticalBias="0"
|
||||
app:flow_verticalGap="4dp"
|
||||
app:flow_wrapMode="chain"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/quickReactionTitle" />
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
@ -28,6 +28,7 @@
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0"
|
||||
tools:src="@tools:sample/avatars" />
|
||||
|
||||
<TextView
|
||||
|
@ -1534,7 +1534,6 @@ Why choose Riot.im?
|
||||
<string name="settings_other_third_party_notices">Other third party notices</string>
|
||||
<string name="navigate_to_room_when_already_in_the_room">You are already viewing this room!</string>
|
||||
|
||||
<string name="quick_reactions">Quick Reactions</string>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="settings_general_title">General</string>
|
||||
|
@ -35,4 +35,5 @@
|
||||
<string name="room_directory_search_hint">Name or ID (#example:matrix.org)</string>
|
||||
|
||||
|
||||
<string name="labs_swipe_to_reply_in_timeline">Enable swipe to reply in timeline</string>
|
||||
</resources>
|
@ -1,45 +1,49 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.preference.PreferenceScreen 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">
|
||||
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<!--<im.vector.riotx.core.preference.VectorPreferenceCategory-->
|
||||
<!--android:key="SETTINGS_LABS_PREFERENCE_KEY"-->
|
||||
<!--android:title="@string/room_settings_labs_pref_title">-->
|
||||
<!--android:key="SETTINGS_LABS_PREFERENCE_KEY"-->
|
||||
<!--android:title="@string/room_settings_labs_pref_title">-->
|
||||
|
||||
<im.vector.riotx.core.preference.VectorPreference
|
||||
android:focusable="false"
|
||||
android:key="labs_warning"
|
||||
android:summary="@string/room_settings_labs_warning_message" />
|
||||
<im.vector.riotx.core.preference.VectorPreference
|
||||
android:focusable="false"
|
||||
android:key="labs_warning"
|
||||
android:summary="@string/room_settings_labs_warning_message" />
|
||||
|
||||
<!--<im.vector.riotx.core.preference.VectorSwitchPreference-->
|
||||
<!--android:key="SETTINGS_ROOM_SETTINGS_LABS_END_TO_END_PREFERENCE_KEY"-->
|
||||
<!--android:title="@string/room_settings_labs_end_to_end" />-->
|
||||
<!--<im.vector.riotx.core.preference.VectorSwitchPreference-->
|
||||
<!--android:key="SETTINGS_ROOM_SETTINGS_LABS_END_TO_END_PREFERENCE_KEY"-->
|
||||
<!--android:title="@string/room_settings_labs_end_to_end" />-->
|
||||
|
||||
<!--<im.vector.riotx.core.preference.VectorPreference-->
|
||||
<!--android:focusable="false"-->
|
||||
<!--android:key="SETTINGS_ROOM_SETTINGS_LABS_END_TO_END_IS_ACTIVE_PREFERENCE_KEY"-->
|
||||
<!--android:title="@string/room_settings_labs_end_to_end_is_active" />-->
|
||||
<!--<im.vector.riotx.core.preference.VectorPreference-->
|
||||
<!--android:focusable="false"-->
|
||||
<!--android:key="SETTINGS_ROOM_SETTINGS_LABS_END_TO_END_IS_ACTIVE_PREFERENCE_KEY"-->
|
||||
<!--android:title="@string/room_settings_labs_end_to_end_is_active" />-->
|
||||
|
||||
<!--<im.vector.riotx.core.preference.VectorSwitchPreference-->
|
||||
<!--android:key="SETTINGS_DATA_SAVE_MODE_PREFERENCE_KEY"-->
|
||||
<!--android:summary="@string/settings_data_save_mode_summary"-->
|
||||
<!--android:title="@string/settings_data_save_mode" />-->
|
||||
<!--<im.vector.riotx.core.preference.VectorSwitchPreference-->
|
||||
<!--android:key="SETTINGS_DATA_SAVE_MODE_PREFERENCE_KEY"-->
|
||||
<!--android:summary="@string/settings_data_save_mode_summary"-->
|
||||
<!--android:title="@string/settings_data_save_mode" />-->
|
||||
|
||||
<!--<im.vector.riotx.core.preference.VectorSwitchPreference-->
|
||||
<!--android:defaultValue="true"-->
|
||||
<!--android:key="SETTINGS_USE_JITSI_CONF_PREFERENCE_KEY"-->
|
||||
<!--android:title="@string/settings_labs_create_conference_with_jitsi" />-->
|
||||
<!--<im.vector.riotx.core.preference.VectorSwitchPreference-->
|
||||
<!--android:defaultValue="true"-->
|
||||
<!--android:key="SETTINGS_USE_JITSI_CONF_PREFERENCE_KEY"-->
|
||||
<!--android:title="@string/settings_labs_create_conference_with_jitsi" />-->
|
||||
|
||||
<!--<im.vector.riotx.core.preference.VectorSwitchPreference-->
|
||||
<!--android:key="SETTINGS_ENABLE_SEND_VOICE_FEATURE_PREFERENCE_KEY"-->
|
||||
<!--android:summary="@string/settings_labs_enable_send_voice_summary"-->
|
||||
<!--android:title="@string/settings_labs_enable_send_voice" />-->
|
||||
<!--<im.vector.riotx.core.preference.VectorSwitchPreference-->
|
||||
<!--android:key="SETTINGS_ENABLE_SEND_VOICE_FEATURE_PREFERENCE_KEY"-->
|
||||
<!--android:summary="@string/settings_labs_enable_send_voice_summary"-->
|
||||
<!--android:title="@string/settings_labs_enable_send_voice" />-->
|
||||
|
||||
<im.vector.riotx.core.preference.VectorSwitchPreference
|
||||
android:key="SETTINGS_LABS_SHOW_HIDDEN_EVENTS_PREFERENCE_KEY"
|
||||
android:defaultValue="false"
|
||||
android:title="@string/settings_labs_show_hidden_events_in_timeline" />
|
||||
<im.vector.riotx.core.preference.VectorSwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="SETTINGS_LABS_SHOW_HIDDEN_EVENTS_PREFERENCE_KEY"
|
||||
android:title="@string/settings_labs_show_hidden_events_in_timeline" />
|
||||
|
||||
|
||||
<im.vector.riotx.core.preference.VectorSwitchPreference
|
||||
android:defaultValue="true"
|
||||
android:key="SETTINGS_LABS_ENABLE_SWIPE_TO_REPLY"
|
||||
android:title="@string/labs_swipe_to_reply_in_timeline" />
|
||||
|
||||
<!--</im.vector.riotx.core.preference.VectorPreferenceCategory>-->
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user