- //
- // This is where the reply goes.
- val body = bodyForReply(eventReplied.getLastMessageContent())
+
+ val body = bodyForReply(eventReplied.getLastMessageContent(), eventReplied.root.getClearContent().toModel())
val replyFormatted = REPLY_PATTERN.format(
permalink,
stringProvider.getString(R.string.message_reply_to_prefix),
@@ -260,8 +291,22 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials
//
// > <@alice:example.org> This is the original body
//
+ val replyFallback = buildReplyFallback(body, userId, replyText)
+
+ val eventId = eventReplied.root.eventId ?: return null
+ val content = MessageTextContent(
+ type = MessageType.MSGTYPE_TEXT,
+ format = MessageType.FORMAT_MATRIX_HTML,
+ body = replyFallback,
+ formattedBody = replyFormatted,
+ relatesTo = RelationDefaultContent(null, null, ReplyToContent(eventId))
+ )
+ return createEvent(roomId, content)
+ }
+
+ private fun buildReplyFallback(body: TextContent, originalSenderId: String?, newBodyText: String): String {
val lines = body.text.split("\n")
- val replyFallback = StringBuffer("><$userId>")
+ val replyFallback = StringBuffer("><$originalSenderId>")
lines.forEachIndexed { index, s ->
if (index == 0) {
replyFallback.append(" $s")
@@ -269,23 +314,16 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials
replyFallback.append("\n>$s")
}
}
- replyFallback.append("\n\n").append(replyText)
-
- val eventId = eventReplied.root.eventId ?: return null
- val content = MessageTextContent(
- type = MessageType.MSGTYPE_TEXT,
- format = MessageType.FORMAT_MATRIX_HTML,
- body = replyFallback.toString(),
- formattedBody = replyFormatted,
- relatesTo = RelationDefaultContent(null, null, ReplyToContent(eventId))
- )
- return createEvent(roomId, content)
+ replyFallback.append("\n\n").append(newBodyText)
+ return replyFallback.toString()
}
/**
* Returns a TextContent used for the fallback event representation in a reply message.
+ * We also pass the original content, because in case of an edit of a reply the last content is not
+ * himself a reply, but it will contain the fallbacks, so we have to trim them.
*/
- private fun bodyForReply(content: MessageContent?): TextContent {
+ private fun bodyForReply(content: MessageContent?, originalContent: MessageContent?): TextContent {
when (content?.type) {
MessageType.MSGTYPE_EMOTE,
MessageType.MSGTYPE_TEXT,
@@ -296,7 +334,7 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials
formattedText = content.formattedBody
}
}
- val isReply = content.relatesTo?.inReplyTo?.eventId != null
+ val isReply = content.isReply() || originalContent.isReply()
return if (isReply)
TextContent(content.body, formattedText).removeInReplyFallbacks()
else
@@ -353,7 +391,16 @@ internal class LocalEchoEventFactory @Inject constructor(private val credentials
companion object {
const val LOCAL_ID_PREFIX = "local."
- // No whitespace
+
+ //
+ //
Apache License
diff --git a/vector/src/main/java/im/vector/riotx/core/di/ScreenComponent.kt b/vector/src/main/java/im/vector/riotx/core/di/ScreenComponent.kt
index 06f65129..a42eec49 100644
--- a/vector/src/main/java/im/vector/riotx/core/di/ScreenComponent.kt
+++ b/vector/src/main/java/im/vector/riotx/core/di/ScreenComponent.kt
@@ -32,13 +32,14 @@ import im.vector.riotx.features.crypto.keysbackup.setup.KeysBackupSetupStep1Frag
import im.vector.riotx.features.crypto.keysbackup.setup.KeysBackupSetupStep2Fragment
import im.vector.riotx.features.crypto.keysbackup.setup.KeysBackupSetupStep3Fragment
import im.vector.riotx.features.crypto.verification.SASVerificationIncomingFragment
-import im.vector.riotx.features.home.*
+import im.vector.riotx.features.home.HomeActivity
+import im.vector.riotx.features.home.HomeDetailFragment
+import im.vector.riotx.features.home.HomeDrawerFragment
+import im.vector.riotx.features.home.HomeModule
import im.vector.riotx.features.home.group.GroupListFragment
import im.vector.riotx.features.home.room.detail.RoomDetailFragment
-import im.vector.riotx.features.home.room.detail.timeline.action.MessageActionsBottomSheet
-import im.vector.riotx.features.home.room.detail.timeline.action.MessageMenuFragment
-import im.vector.riotx.features.home.room.detail.timeline.action.QuickReactionFragment
-import im.vector.riotx.features.home.room.detail.timeline.action.ViewReactionBottomSheet
+import im.vector.riotx.features.home.room.detail.timeline.action.*
+import im.vector.riotx.features.home.room.filtered.FilteredRoomsActivity
import im.vector.riotx.features.home.room.list.RoomListFragment
import im.vector.riotx.features.invite.VectorInviteView
import im.vector.riotx.features.login.LoginActivity
@@ -50,6 +51,7 @@ import im.vector.riotx.features.rageshake.RageShake
import im.vector.riotx.features.reactions.EmojiReactionPickerActivity
import im.vector.riotx.features.roomdirectory.PublicRoomsFragment
import im.vector.riotx.features.roomdirectory.RoomDirectoryActivity
+import im.vector.riotx.features.roomdirectory.createroom.CreateRoomActivity
import im.vector.riotx.features.roomdirectory.createroom.CreateRoomFragment
import im.vector.riotx.features.roomdirectory.picker.RoomDirectoryPickerFragment
import im.vector.riotx.features.roomdirectory.roompreview.RoomPreviewNoPreviewFragment
@@ -93,6 +95,8 @@ interface ScreenComponent {
fun inject(viewReactionBottomSheet: ViewReactionBottomSheet)
+ fun inject(viewEditHistoryBottomSheet: ViewEditHistoryBottomSheet)
+
fun inject(messageMenuFragment: MessageMenuFragment)
fun inject(vectorSettingsActivity: VectorSettingsActivity)
@@ -131,6 +135,10 @@ interface ScreenComponent {
fun inject(imageMediaViewerActivity: ImageMediaViewerActivity)
+ fun inject(filteredRoomsActivity: FilteredRoomsActivity)
+
+ fun inject(createRoomActivity: CreateRoomActivity)
+
fun inject(vectorInviteView: VectorInviteView)
fun inject(videoMediaViewerActivity: VideoMediaViewerActivity)
diff --git a/vector/src/main/java/im/vector/riotx/core/di/ViewModelModule.kt b/vector/src/main/java/im/vector/riotx/core/di/ViewModelModule.kt
index 234d4a0c..534a346a 100644
--- a/vector/src/main/java/im/vector/riotx/core/di/ViewModelModule.kt
+++ b/vector/src/main/java/im/vector/riotx/core/di/ViewModelModule.kt
@@ -29,11 +29,7 @@ import im.vector.riotx.features.crypto.keysbackup.settings.KeysBackupSettingsVie
import im.vector.riotx.features.crypto.keysbackup.settings.KeysBackupSettingsViewModel_AssistedFactory
import im.vector.riotx.features.crypto.keysbackup.setup.KeysBackupSetupSharedViewModel
import im.vector.riotx.features.crypto.verification.SasVerificationViewModel
-import im.vector.riotx.features.home.HomeActivityViewModel
-import im.vector.riotx.features.home.HomeActivityViewModel_AssistedFactory
-import im.vector.riotx.features.home.HomeDetailViewModel
-import im.vector.riotx.features.home.HomeDetailViewModel_AssistedFactory
-import im.vector.riotx.features.home.HomeNavigationViewModel
+import im.vector.riotx.features.home.*
import im.vector.riotx.features.home.group.GroupListViewModel
import im.vector.riotx.features.home.group.GroupListViewModel_AssistedFactory
import im.vector.riotx.features.home.room.detail.RoomDetailViewModel
@@ -59,11 +55,17 @@ import im.vector.riotx.features.workers.signout.SignOutViewModel
@Module
interface ViewModelModule {
-
+
+ /**
+ * ViewModels with @IntoMap will be injected by this factory
+ */
@Binds
fun bindViewModelFactory(factory: VectorViewModelFactory): ViewModelProvider.Factory
+ /**
+ * Below are bindings for the androidx view models (which extend ViewModel). Will be converted to MvRx ViewModel in the future.
+ */
@Binds
@IntoMap
@ViewModelKey(SignOutViewModel::class)
@@ -114,6 +116,10 @@ interface ViewModelModule {
@ViewModelKey(ConfigurationViewModel::class)
fun bindConfigurationViewModel(viewModel: ConfigurationViewModel): ViewModel
+ /**
+ * Below are bindings for the MvRx view models (which extend VectorViewModel). Will be the only usage in the future.
+ */
+
@Binds
fun bindHomeActivityViewModelFactory(factory: HomeActivityViewModel_AssistedFactory): HomeActivityViewModel.Factory
@@ -156,6 +162,9 @@ interface ViewModelModule {
@Binds
fun bindViewReactionViewModelFactory(factory: ViewReactionViewModel_AssistedFactory): ViewReactionViewModel.Factory
+ @Binds
+ fun bindViewEditHistoryViewModelFactory(factory: ViewEditHistoryViewModel_AssistedFactory): ViewEditHistoryViewModel.Factory
+
@Binds
fun bindCreateRoomViewModelFactory(factory: CreateRoomViewModel_AssistedFactory): CreateRoomViewModel.Factory
diff --git a/vector/src/main/java/im/vector/riotx/core/extensions/Activity.kt b/vector/src/main/java/im/vector/riotx/core/extensions/Activity.kt
index 75817779..b4afb569 100644
--- a/vector/src/main/java/im/vector/riotx/core/extensions/Activity.kt
+++ b/vector/src/main/java/im/vector/riotx/core/extensions/Activity.kt
@@ -23,8 +23,8 @@ fun AppCompatActivity.addFragment(fragment: Fragment, frameId: Int) {
supportFragmentManager.inTransaction { add(frameId, fragment) }
}
-fun AppCompatActivity.replaceFragment(fragment: Fragment, frameId: Int) {
- supportFragmentManager.inTransaction { replace(frameId, fragment) }
+fun AppCompatActivity.replaceFragment(fragment: Fragment, frameId: Int, tag: String? = null) {
+ supportFragmentManager.inTransaction { replace(frameId, fragment, tag) }
}
fun AppCompatActivity.addFragmentToBackstack(fragment: Fragment, frameId: Int, tag: String? = null) {
diff --git a/vector/src/main/java/im/vector/riotx/core/extensions/Session.kt b/vector/src/main/java/im/vector/riotx/core/extensions/Session.kt
index b03686d4..95e17a4b 100644
--- a/vector/src/main/java/im/vector/riotx/core/extensions/Session.kt
+++ b/vector/src/main/java/im/vector/riotx/core/extensions/Session.kt
@@ -16,14 +16,20 @@
package im.vector.riotx.core.extensions
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.ProcessLifecycleOwner
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.sync.FilterService
import im.vector.riotx.features.notifications.PushRuleTriggerListener
+import timber.log.Timber
fun Session.configureAndStart(pushRuleTriggerListener: PushRuleTriggerListener) {
open()
setFilter(FilterService.FilterPreset.RiotFilter)
- startSync()
+ Timber.i("Configure and start session for ${this.myUserId}")
+ val isAtLeastStarted = ProcessLifecycleOwner.get().lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)
+ Timber.v("--> is at least started? $isAtLeastStarted")
+ startSync(isAtLeastStarted)
refreshPushers()
pushRuleTriggerListener.startWithSession(this)
fetchPushRules()
diff --git a/vector/src/main/java/im/vector/riotx/core/extensions/TimelineEvent.kt b/vector/src/main/java/im/vector/riotx/core/extensions/TimelineEvent.kt
index c82631c0..db171300 100644
--- a/vector/src/main/java/im/vector/riotx/core/extensions/TimelineEvent.kt
+++ b/vector/src/main/java/im/vector/riotx/core/extensions/TimelineEvent.kt
@@ -21,5 +21,5 @@ import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
fun TimelineEvent.canReact(): Boolean {
// Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
- return root.getClearType() == EventType.MESSAGE && sendState.isSent()
+ return root.getClearType() == EventType.MESSAGE && sendState.isSent() && !root.isRedacted()
}
diff --git a/vector/src/main/java/im/vector/riotx/core/platform/VectorViewModel.kt b/vector/src/main/java/im/vector/riotx/core/platform/VectorViewModel.kt
index 7a79bf37..1570a7f8 100644
--- a/vector/src/main/java/im/vector/riotx/core/platform/VectorViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/core/platform/VectorViewModel.kt
@@ -18,7 +18,18 @@ package im.vector.riotx.core.platform
import com.airbnb.mvrx.BaseMvRxViewModel
import com.airbnb.mvrx.MvRxState
+import im.vector.matrix.android.api.util.CancelableBag
import im.vector.riotx.BuildConfig
abstract class VectorViewModel(initialState: S)
- : BaseMvRxViewModel(initialState, false)
\ No newline at end of file
+ : BaseMvRxViewModel(initialState, false) {
+
+ protected val cancelableBag = CancelableBag()
+
+ override fun onCleared() {
+ super.onCleared()
+ cancelableBag.cancel()
+ }
+
+
+}
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotx/core/preference/UserAvatarPreference.kt b/vector/src/main/java/im/vector/riotx/core/preference/UserAvatarPreference.kt
index e5e84514..b0747e7e 100755
--- a/vector/src/main/java/im/vector/riotx/core/preference/UserAvatarPreference.kt
+++ b/vector/src/main/java/im/vector/riotx/core/preference/UserAvatarPreference.kt
@@ -58,10 +58,10 @@ open class UserAvatarPreference : Preference {
open fun refreshAvatar() {
val session = mSession ?: return
val view = mAvatarView ?: return
- session.getUser(session.sessionParams.credentials.userId)?.let {
+ session.getUser(session.myUserId)?.let {
avatarRenderer.render(it, view)
} ?: run {
- avatarRenderer.render(null, session.sessionParams.credentials.userId, null, view)
+ avatarRenderer.render(null, session.myUserId, null, view)
}
}
diff --git a/vector/src/main/java/im/vector/riotx/core/ui/list/GenericItem.kt b/vector/src/main/java/im/vector/riotx/core/ui/list/GenericItem.kt
index ebaeb2d3..f0a62ccd 100644
--- a/vector/src/main/java/im/vector/riotx/core/ui/list/GenericItem.kt
+++ b/vector/src/main/java/im/vector/riotx/core/ui/list/GenericItem.kt
@@ -51,7 +51,7 @@ abstract class GenericItem : VectorEpoxyModel() {
var title: String? = null
@EpoxyAttribute
- var description: String? = null
+ var description: CharSequence? = null
@EpoxyAttribute
var style: STYLE = STYLE.NORMAL_TEXT
diff --git a/vector/src/main/java/im/vector/riotx/core/ui/list/GenericItemHeader.kt b/vector/src/main/java/im/vector/riotx/core/ui/list/GenericItemHeader.kt
new file mode 100644
index 00000000..3c9ce20d
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/core/ui/list/GenericItemHeader.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.core.ui.list
+
+import android.widget.TextView
+import com.airbnb.epoxy.EpoxyAttribute
+import com.airbnb.epoxy.EpoxyModelClass
+import im.vector.riotx.R
+import im.vector.riotx.core.epoxy.VectorEpoxyHolder
+import im.vector.riotx.core.epoxy.VectorEpoxyModel
+import im.vector.riotx.core.extensions.setTextOrHide
+
+/**
+ * A generic list item header left aligned with notice color.
+ */
+@EpoxyModelClass(layout = R.layout.item_generic_header)
+abstract class GenericItemHeader : VectorEpoxyModel() {
+
+ @EpoxyAttribute
+ var text: String? = null
+
+ override fun bind(holder: Holder) {
+ holder.text.setTextOrHide(text)
+ }
+
+ class Holder : VectorEpoxyHolder() {
+ val text by bind(R.id.itemGenericHeaderText)
+ }
+}
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotx/core/ui/list/GenericLoaderItem.kt b/vector/src/main/java/im/vector/riotx/core/ui/list/GenericLoaderItem.kt
new file mode 100644
index 00000000..56daca22
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/core/ui/list/GenericLoaderItem.kt
@@ -0,0 +1,20 @@
+package im.vector.riotx.core.ui.list
+
+import com.airbnb.epoxy.EpoxyModelClass
+import im.vector.riotx.R
+import im.vector.riotx.core.epoxy.VectorEpoxyHolder
+import im.vector.riotx.core.epoxy.VectorEpoxyModel
+
+
+/**
+ * A generic list item header left aligned with notice color.
+ */
+@EpoxyModelClass(layout = R.layout.item_generic_loader)
+abstract class GenericLoaderItem : VectorEpoxyModel() {
+
+ //Maybe/Later add some style configuration, SMALL/BIG ?
+
+ override fun bind(holder: Holder) {}
+
+ class Holder : VectorEpoxyHolder()
+}
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotx/core/utils/SystemUtils.kt b/vector/src/main/java/im/vector/riotx/core/utils/SystemUtils.kt
index f387b296..9c7b7938 100644
--- a/vector/src/main/java/im/vector/riotx/core/utils/SystemUtils.kt
+++ b/vector/src/main/java/im/vector/riotx/core/utils/SystemUtils.kt
@@ -28,6 +28,7 @@ import android.os.Build
import android.os.PowerManager
import android.provider.Settings
import android.widget.Toast
+import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import im.vector.riotx.R
@@ -81,11 +82,11 @@ fun requestDisablingBatteryOptimization(activity: Activity, fragment: Fragment?,
* @param context the context
* @param text the text to copy
*/
-fun copyToClipboard(context: Context, text: CharSequence, showToast: Boolean = true) {
+fun copyToClipboard(context: Context, text: CharSequence, showToast: Boolean = true, @StringRes toastMessage : Int = R.string.copied_to_clipboard) {
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
clipboard.primaryClip = ClipData.newPlainText("", text)
if (showToast) {
- context.toast(R.string.copied_to_clipboard)
+ context.toast(toastMessage)
}
}
diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyViewModel.kt b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyViewModel.kt
index c4da3038..1d7a6a35 100644
--- a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyViewModel.kt
@@ -21,8 +21,8 @@ import androidx.lifecycle.ViewModel
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.listeners.StepProgressListener
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService
-import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult
+import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
import im.vector.riotx.R
import im.vector.riotx.core.platform.WaitingViewData
import im.vector.riotx.core.ui.views.KeysBackupBanner
@@ -57,7 +57,7 @@ class KeysBackupRestoreFromKeyViewModel @Inject constructor() : ViewModel() {
keysBackup.restoreKeysWithRecoveryKey(keysVersionResult,
recoveryKey,
null,
- session.sessionParams.credentials.userId,
+ session.myUserId,
object : StepProgressListener {
override fun onStepProgress(step: StepProgressListener.Step) {
when (step) {
diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseViewModel.kt b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseViewModel.kt
index 3a4a9528..45995434 100644
--- a/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseViewModel.kt
@@ -21,8 +21,8 @@ import androidx.lifecycle.ViewModel
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.listeners.StepProgressListener
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService
-import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult
+import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
import im.vector.riotx.R
import im.vector.riotx.core.platform.WaitingViewData
import im.vector.riotx.core.ui.views.KeysBackupBanner
@@ -58,7 +58,7 @@ class KeysBackupRestoreFromPassphraseViewModel @Inject constructor() : ViewModel
keysBackup.restoreKeyBackupWithPassword(keysVersionResult,
passphrase.value!!,
null,
- sharedViewModel.session.sessionParams.credentials.userId,
+ sharedViewModel.session.myUserId,
object : StepProgressListener {
override fun onStepProgress(step: StepProgressListener.Step) {
when (step) {
diff --git a/vector/src/main/java/im/vector/riotx/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/riotx/features/home/HomeActivity.kt
index 67d1af4e..4ec2c0ad 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/HomeActivity.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/HomeActivity.kt
@@ -41,6 +41,7 @@ import im.vector.riotx.core.platform.ToolbarConfigurable
import im.vector.riotx.core.platform.VectorBaseActivity
import im.vector.riotx.core.pushers.PushersManager
import im.vector.riotx.features.disclaimer.showDisclaimerDialog
+import im.vector.riotx.features.navigation.Navigator
import im.vector.riotx.features.notifications.NotificationDrawerManager
import im.vector.riotx.features.rageshake.VectorUncaughtExceptionHandler
import im.vector.riotx.features.workers.signout.SignOutViewModel
@@ -64,6 +65,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
@Inject lateinit var activeSessionHolder: ActiveSessionHolder
@Inject lateinit var homeActivityViewModelFactory: HomeActivityViewModel.Factory
@Inject lateinit var homeNavigator: HomeNavigator
+ @Inject lateinit var navigator: Navigator
@Inject lateinit var vectorUncaughtExceptionHandler: VectorUncaughtExceptionHandler
@Inject lateinit var pushManager: PushersManager
@Inject lateinit var notificationDrawerManager: NotificationDrawerManager
@@ -192,6 +194,10 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
bugReporter.openBugReportScreen(this, false)
return true
}
+ R.id.menu_home_filter -> {
+ navigator.openRoomsFiltering(this)
+ return true
+ }
}
return true
diff --git a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt
index db8ae359..bd4b2ca4 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt
@@ -209,7 +209,7 @@ class HomeDetailFragment : VectorBaseFragment(), KeysBackupBanner.Delegate {
unreadCounterBadgeViews[INDEX_PEOPLE].render(UnreadCounterBadgeView.State(it.notificationCountPeople, it.notificationHighlightPeople))
unreadCounterBadgeViews[INDEX_ROOMS].render(UnreadCounterBadgeView.State(it.notificationCountRooms, it.notificationHighlightRooms))
syncProgressBar.visibility = when (it.syncState) {
- is SyncState.RUNNING -> if (it.syncState.catchingUp) View.VISIBLE else View.GONE
+ is SyncState.RUNNING -> if (it.syncState.afterPause) View.VISIBLE else View.GONE
else -> View.GONE
}
syncProgressBarWrap.visibility = syncProgressBar.visibility
diff --git a/vector/src/main/java/im/vector/riotx/features/home/HomeDrawerFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/HomeDrawerFragment.kt
index 832e8a5e..ac4cc08d 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/HomeDrawerFragment.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/HomeDrawerFragment.kt
@@ -52,7 +52,7 @@ class HomeDrawerFragment : VectorBaseFragment() {
replaceChildFragment(groupListFragment, R.id.homeDrawerGroupListContainer)
}
- session.observeUser(session.sessionParams.credentials.userId).observeK(this) { user ->
+ session.observeUser(session.myUserId).observeK(this) { user ->
if (user != null) {
avatarRenderer.render(user.avatarUrl, user.userId, user.displayName, homeDrawerHeaderAvatarView)
homeDrawerUsernameView.text = user.displayName
diff --git a/vector/src/main/java/im/vector/riotx/features/home/HomeRoomListObservableStore.kt b/vector/src/main/java/im/vector/riotx/features/home/HomeRoomListObservableStore.kt
index 4e0b5b70..df8cd411 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/HomeRoomListObservableStore.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/HomeRoomListObservableStore.kt
@@ -18,10 +18,6 @@ package im.vector.riotx.features.home
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.riotx.core.utils.RxStore
-import im.vector.riotx.features.home.room.list.RoomListDisplayModeFilter
-import im.vector.riotx.features.home.room.list.RoomListFragment
-import io.reactivex.Observable
-import io.reactivex.schedulers.Schedulers
import javax.inject.Inject
import javax.inject.Singleton
diff --git a/vector/src/main/java/im/vector/riotx/features/home/group/GroupListViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/group/GroupListViewModel.kt
index 229652b0..513379bd 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/group/GroupListViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/group/GroupListViewModel.kt
@@ -93,7 +93,7 @@ class GroupListViewModel @AssistedInject constructor(@Assisted initialState: Gro
.rx()
.liveGroupSummaries()
.map {
- val myUser = session.getUser(session.sessionParams.credentials.userId)
+ val myUser = session.getUser(session.myUserId)
val allCommunityGroup = GroupSummary(
groupId = ALL_COMMUNITIES_GROUP_ID,
displayName = stringProvider.getString(R.string.group_all_communities),
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailActions.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailActions.kt
index d52b16ca..ace0802e 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailActions.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailActions.kt
@@ -32,7 +32,6 @@ sealed class RoomDetailActions {
data class RedactAction(val targetEventId: String, val reason: String? = "") : RoomDetailActions()
data class UndoReaction(val targetEventId: String, val key: String, val reason: String? = "") : RoomDetailActions()
data class UpdateQuickReactAction(val targetEventId: String, val selectedReaction: String, val add: Boolean) : RoomDetailActions()
- data class ShowEditHistoryAction(val event: String, val editAggregatedSummary: EditAggregatedSummary) : RoomDetailActions()
data class NavigateToEvent(val eventId: String, val position: Int?) : RoomDetailActions()
data class DownloadFile(val eventId: String, val messageFileContent: MessageFileContent) : RoomDetailActions()
object AcceptInvite : RoomDetailActions()
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailActivity.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailActivity.kt
index 9108f5d0..6ad9a61f 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailActivity.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailActivity.kt
@@ -35,7 +35,7 @@ class RoomDetailActivity : VectorBaseActivity(), ToolbarConfigurable {
super.onCreate(savedInstanceState)
if (isFirstCreation()) {
val roomDetailArgs: RoomDetailArgs = intent?.extras?.getParcelable(EXTRA_ROOM_DETAIL_ARGS)
- ?: return
+ ?: return
val roomDetailFragment = RoomDetailFragment.newInstance(roomDetailArgs)
replaceFragment(roomDetailFragment, R.id.roomDetailContainer)
}
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt
index f611fe99..b7491ae6 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt
@@ -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,8 +59,10 @@ 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
import im.vector.matrix.android.api.session.user.model.User
import im.vector.riotx.R
import im.vector.riotx.core.di.ScreenComponent
@@ -85,12 +89,9 @@ import im.vector.riotx.features.home.room.detail.composer.TextComposerView
import im.vector.riotx.features.home.room.detail.composer.TextComposerViewModel
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.ActionsHandler
-import im.vector.riotx.features.home.room.detail.timeline.action.MessageActionsBottomSheet
-import im.vector.riotx.features.home.room.detail.timeline.action.MessageMenuViewModel
-import im.vector.riotx.features.home.room.detail.timeline.action.ViewReactionBottomSheet
+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
@@ -261,7 +262,7 @@ class RoomDetailFragment :
composerLayout.composerRelatedMessageContent.text = formattedBody
?: nonFormattedBody
- composerLayout.composerEditText.setText(if (useText) nonFormattedBody else "")
+ composerLayout.composerEditText.setText(if (useText) event.getTextEditableContent() else "")
composerLayout.composerRelatedMessageActionIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), iconRes))
avatarRenderer.render(event.senderAvatar, event.root.senderId
@@ -269,8 +270,10 @@ class RoomDetailFragment :
composerLayout.composerEditText.setSelection(composerLayout.composerEditText.text.length)
composerLayout.expand {
+ //need to do it here also when not using quick reply
focusComposerAndShowKeyboard()
}
+ focusComposerAndShowKeyboard()
}
override fun onResume() {
@@ -326,6 +329,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() {
@@ -489,7 +518,7 @@ class RoomDetailFragment :
timelineEventController.setTimeline(state.timeline, state.eventId)
inviteView.visibility = View.GONE
- val uid = session.sessionParams.credentials.userId
+ val uid = session.myUserId
val meMember = session.getRoom(state.roomId)?.getRoomMember(uid)
avatarRenderer.render(meMember?.avatarUrl, uid, meMember?.displayName, composerLayout.composerAvatarImageView)
@@ -575,7 +604,7 @@ class RoomDetailFragment :
override fun onUrlLongClicked(url: String): Boolean {
// Copy the url to the clipboard
- copyToClipboard(requireContext(), url)
+ copyToClipboard(requireContext(), url, true, R.string.link_copied_to_clipboard)
return true
}
@@ -666,10 +695,8 @@ class RoomDetailFragment :
}
override fun onEditedDecorationClicked(informationData: MessageInformationData, editAggregatedSummary: EditAggregatedSummary?) {
- editAggregatedSummary?.also {
- roomDetailViewModel.process(RoomDetailActions.ShowEditHistoryAction(informationData.eventId, it))
- }
-
+ ViewEditHistoryBottomSheet.newInstance(roomDetailArgs.roomId, informationData)
+ .show(requireActivity().supportFragmentManager, "DISPLAY_EDITS")
}
// AutocompleteUserPresenter.Callback
@@ -785,7 +812,7 @@ class RoomDetailFragment :
if (null != text) {
// var vibrate = false
- val myDisplayName = session.getUser(session.sessionParams.credentials.userId)?.displayName
+ val myDisplayName = session.getUser(session.myUserId)?.displayName
if (TextUtils.equals(myDisplayName, text)) {
// current user
if (TextUtils.isEmpty(composerLayout.composerEditText.text)) {
@@ -833,10 +860,12 @@ class RoomDetailFragment :
// VectorInviteView.Callback
override fun onAcceptInvite() {
+ notificationDrawerManager.clearMemberShipNotificationForRoom(roomDetailArgs.roomId)
roomDetailViewModel.process(RoomDetailActions.AcceptInvite)
}
override fun onRejectInvite() {
+ notificationDrawerManager.clearMemberShipNotificationForRoom(roomDetailArgs.roomId)
roomDetailViewModel.process(RoomDetailActions.RejectInvite)
}
}
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt
index 2a19914a..d38561fa 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt
@@ -38,8 +38,8 @@ import im.vector.matrix.android.api.session.room.model.message.MessageType
import im.vector.matrix.android.api.session.room.model.message.getFileUrl
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.internal.crypto.attachments.toElementToDecrypt
+import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent
import im.vector.matrix.rx.rx
-import im.vector.riotx.R
import im.vector.riotx.core.intent.getFilenameFromUri
import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.resources.UserPreferencesProvider
@@ -52,8 +52,6 @@ import org.commonmark.parser.Parser
import org.commonmark.renderer.html.HtmlRenderer
import timber.log.Timber
import java.io.File
-import java.text.SimpleDateFormat
-import java.util.*
import java.util.concurrent.TimeUnit
@@ -97,7 +95,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
observeRoomSummary()
observeEventDisplayedActions()
observeInvitationState()
- room.loadRoomMembersIfNeeded()
+ cancelableBag += room.loadRoomMembersIfNeeded()
timeline.start()
setState { copy(timeline = this@RoomDetailViewModel.timeline) }
}
@@ -114,7 +112,6 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
is RoomDetailActions.RedactAction -> handleRedactEvent(action)
is RoomDetailActions.UndoReaction -> handleUndoReact(action)
is RoomDetailActions.UpdateQuickReactAction -> handleUpdateQuickReaction(action)
- is RoomDetailActions.ShowEditHistoryAction -> handleShowEditHistoryReaction(action)
is RoomDetailActions.EnterEditMode -> handleEditAction(action)
is RoomDetailActions.EnterQuoteMode -> handleQuoteAction(action)
is RoomDetailActions.EnterReplyMode -> handleReplyAction(action)
@@ -230,16 +227,27 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
}
}
is SendMode.EDIT -> {
- val messageContent: MessageContent? =
- state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
- ?: state.sendMode.timelineEvent.root.getClearContent().toModel()
- val nonFormattedBody = messageContent?.body ?: ""
- if (nonFormattedBody != action.text) {
- room.editTextMessage(state.sendMode.timelineEvent.root.eventId
- ?: "", messageContent?.type ?: MessageType.MSGTYPE_TEXT, action.text, action.autoMarkdown)
+ //is original event a reply?
+ val inReplyTo = state.sendMode.timelineEvent.root.getClearContent().toModel()?.relatesTo?.inReplyTo?.eventId
+ ?: state.sendMode.timelineEvent.root.content.toModel()?.relatesTo?.inReplyTo?.eventId
+ if (inReplyTo != null) {
+ //TODO check if same content?
+ room.getTimeLineEvent(inReplyTo)?.let {
+ room.editReply(state.sendMode.timelineEvent, it, action.text)
+ }
} else {
- Timber.w("Same message content, do not send edition")
+ val messageContent: MessageContent? =
+ state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
+ ?: state.sendMode.timelineEvent.root.getClearContent().toModel()
+ val existingBody = messageContent?.body ?: ""
+ if (existingBody != action.text) {
+ room.editTextMessage(state.sendMode.timelineEvent.root.eventId
+ ?: "", messageContent?.type
+ ?: MessageType.MSGTYPE_TEXT, action.text, action.autoMarkdown)
+ } else {
+ Timber.w("Same message content, do not send edition")
+ }
}
setState {
copy(
@@ -309,22 +317,6 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
return finalText
}
- private fun handleShowEditHistoryReaction(action: RoomDetailActions.ShowEditHistoryAction) {
- //TODO temporary implementation
- val lastReplace = action.editAggregatedSummary.sourceEvents.lastOrNull()?.let {
- room.getTimeLineEvent(it)
- } ?: return
-
- val dateFormat = SimpleDateFormat("EEE, d MMM yyyy HH:mm", Locale.getDefault())
- _nonBlockingPopAlert.postValue(LiveEvent(
- Pair(R.string.last_edited_info_message, listOf(
- lastReplace.getDisambiguatedDisplayName(),
- dateFormat.format(Date(lastReplace.root.originServerTs ?: 0)))
- ))
- )
- }
-
-
private fun handleChangeTopicSlashCommand(changeTopic: ParsedCommand.ChangeTopic) {
_sendMessageResultLiveData.postValue(LiveEvent(SendMessageResult.SlashCommandHandled))
@@ -364,7 +356,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
}
private fun handleUndoReact(action: RoomDetailActions.UndoReaction) {
- room.undoReaction(action.key, action.targetEventId, session.sessionParams.credentials.userId)
+ room.undoReaction(action.key, action.targetEventId, session.myUserId)
}
@@ -372,7 +364,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
if (action.add) {
room.sendReaction(action.selectedReaction, action.targetEventId)
} else {
- room.undoReaction(action.selectedReaction, action.targetEventId, session.sessionParams.credentials.userId)
+ room.undoReaction(action.selectedReaction, action.targetEventId, session.myUserId)
}
}
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomMessageTouchHelperCallback.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomMessageTouchHelperCallback.kt
new file mode 100644
index 00000000..cb283511
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomMessageTouchHelperCallback.kt
@@ -0,0 +1,214 @@
+/*
+ * 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
+ )
+ }
+
+}
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/TextComposerView.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/TextComposerView.kt
index 488ecbd3..4a3fc3ff 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/TextComposerView.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/TextComposerView.kt
@@ -57,6 +57,7 @@ class TextComposerView @JvmOverloads constructor(context: Context, attrs: Attrib
var currentConstraintSetId: Int = -1
+ private val animationDuration = 100L
init {
inflate(context, R.layout.merge_composer_layout, this)
@@ -73,7 +74,7 @@ class TextComposerView @JvmOverloads constructor(context: Context, attrs: Attrib
currentConstraintSetId = R.layout.constraint_set_composer_layout_compact
if (animate) {
val transition = AutoTransition()
-// transition.duration = 5000
+ transition.duration = animationDuration
transition.addListener(object : Transition.TransitionListener {
override fun onTransitionEnd(transition: Transition) {
@@ -105,7 +106,7 @@ class TextComposerView @JvmOverloads constructor(context: Context, attrs: Attrib
currentConstraintSetId = R.layout.constraint_set_composer_layout_expanded
if (animate) {
val transition = AutoTransition()
-// transition.duration = 5000
+ transition.duration = animationDuration
transition.addListener(object : Transition.TransitionListener {
override fun onTransitionEnd(transition: Transition) {
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/TimelineEventController.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/TimelineEventController.kt
index b6cf00a3..0b78f815 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/TimelineEventController.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/TimelineEventController.kt
@@ -18,7 +18,6 @@ package im.vector.riotx.features.home.room.detail.timeline
import android.os.Handler
import android.os.Looper
-import android.util.LongSparseArray
import android.view.View
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListUpdateCallback
@@ -84,7 +83,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Tim
}
private val collapsedEventIds = linkedSetOf()
- private val mergeItemCollapseStates = HashMap()
+ private val mergeItemCollapseStates = HashMap()
private val modelCache = arrayListOf()
private var currentSnapshot: List = emptyList()
@@ -178,16 +177,19 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Tim
}
override fun buildModels() {
- LoadingItem_()
+ val loaderAdded = LoadingItem_()
.id("forward_loading_item")
.addWhen(Timeline.Direction.FORWARDS)
val timelineModels = getModels()
add(timelineModels)
- LoadingItem_()
- .id("backward_loading_item")
- .addWhen(Timeline.Direction.BACKWARDS)
+ // Avoid displaying two loaders if there is no elements between them
+ if (!loaderAdded || timelineModels.isNotEmpty()) {
+ LoadingItem_()
+ .id("backward_loading_item")
+ .addWhen(Timeline.Direction.BACKWARDS)
+ }
}
// Timeline.LISTENER ***************************************************************************
@@ -310,9 +312,13 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Tim
}
}
- private fun LoadingItem_.addWhen(direction: Timeline.Direction) {
+ /**
+ * Return true if added
+ */
+ private fun LoadingItem_.addWhen(direction: Timeline.Direction): Boolean {
val shouldAdd = timeline?.hasMoreToLoad(direction) ?: false
addIf(shouldAdd, this@TimelineEventController)
+ return shouldAdd
}
fun searchPositionOfEvent(eventId: String): Int? {
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageMenuViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageMenuViewModel.kt
index 21f5da52..5b0dbdfe 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageMenuViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageMenuViewModel.kt
@@ -126,51 +126,55 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M
}
//TODO is downloading attachement?
- 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 (!event.root.isRedacted()) {
- if (canReply(event, messageContent)) {
- this.add(SimpleAction(ACTION_REPLY, R.string.reply, R.drawable.ic_reply, eventId))
- }
-
- if (canEdit(event, session.sessionParams.credentials.userId)) {
- this.add(SimpleAction(ACTION_EDIT, R.string.edit, R.drawable.ic_edit, eventId))
- }
-
- if (canRedact(event, session.sessionParams.credentials.userId)) {
- this.add(SimpleAction(ACTION_DELETE, R.string.delete, R.drawable.ic_delete, eventId))
- }
-
- if (canQuote(event, messageContent)) {
- this.add(SimpleAction(ACTION_QUOTE, R.string.quote, R.drawable.ic_quote, eventId))
- }
-
- if (canViewReactions(event)) {
- this.add(SimpleAction(ACTION_VIEW_REACTIONS, R.string.message_view_reaction, R.drawable.ic_view_reactions, informationData))
- }
-
- if (canShare(type)) {
- if (messageContent is MessageImageContent) {
- this.add(
- SimpleAction(ACTION_SHARE,
- R.string.share, R.drawable.ic_share,
- session.contentUrlResolver().resolveFullSize(messageContent.url))
- )
+ if (canReply(event, messageContent)) {
+ this.add(SimpleAction(ACTION_REPLY, R.string.reply, R.drawable.ic_reply, eventId))
+ }
+
+ if (canEdit(event, session.myUserId)) {
+ this.add(SimpleAction(ACTION_EDIT, R.string.edit, R.drawable.ic_edit, eventId))
+ }
+
+ if (canRedact(event, session.myUserId)) {
+ 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))
+ }
+
+ if (canViewReactions(event)) {
+ this.add(SimpleAction(ACTION_VIEW_REACTIONS, R.string.message_view_reaction, R.drawable.ic_view_reactions, informationData))
+ }
+
+ if (canShare(type)) {
+ if (messageContent is MessageImageContent) {
+ this.add(
+ SimpleAction(ACTION_SHARE,
+ R.string.share, R.drawable.ic_share,
+ session.contentUrlResolver().resolveFullSize(messageContent.url))
+ )
+ }
+ //TODO
}
- //TODO
- }
- if (event.sendState == SendState.SENT) {
+ if (event.sendState == SendState.SENT) {
- //TODO Can be redacted
+ //TODO Can be redacted
- //TODO sent by me or sufficient power level
+ //TODO sent by me or sufficient power level
+ }
}
this.add(SimpleAction(VIEW_SOURCE, R.string.view_source, R.drawable.ic_view_source, event.root.toContentStringWithIndent()))
@@ -181,7 +185,7 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M
}
this.add(SimpleAction(ACTION_COPY_PERMALINK, R.string.permalink, R.drawable.ic_permalink, event.root.eventId))
- if (session.sessionParams.credentials.userId != event.root.senderId && event.root.getClearType() == EventType.MESSAGE) {
+ if (session.myUserId != event.root.senderId && event.root.getClearType() == EventType.MESSAGE) {
//not sent by me
this.add(SimpleAction(ACTION_FLAG, R.string.report_content, R.drawable.ic_flag, event.root.eventId))
}
@@ -240,7 +244,7 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M
//Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
if (event.root.getClearType() != EventType.MESSAGE) return false
//TODO if user is admin or moderator
- val messageContent = event.root.content.toModel()
+ val messageContent = event.root.getClearContent().toModel()
return event.root.senderId == myUserId && (
messageContent?.type == MessageType.MSGTYPE_TEXT
|| messageContent?.type == MessageType.MSGTYPE_EMOTE
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/ViewEditHistoryBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/ViewEditHistoryBottomSheet.kt
new file mode 100644
index 00000000..aefbde43
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/ViewEditHistoryBottomSheet.kt
@@ -0,0 +1,93 @@
+/*
+ * 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.timeline.action
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.LinearLayout
+import androidx.recyclerview.widget.DividerItemDecoration
+import butterknife.BindView
+import butterknife.ButterKnife
+import com.airbnb.epoxy.EpoxyRecyclerView
+import com.airbnb.mvrx.MvRx
+import com.airbnb.mvrx.fragmentViewModel
+import com.airbnb.mvrx.withState
+import im.vector.riotx.R
+import im.vector.riotx.core.di.ScreenComponent
+import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
+import im.vector.riotx.features.html.EventHtmlRenderer
+import kotlinx.android.synthetic.main.bottom_sheet_epoxylist_with_title.*
+import javax.inject.Inject
+
+
+/**
+ * Bottom sheet displaying list of edits for a given event ordered by timestamp
+ */
+class ViewEditHistoryBottomSheet : VectorBaseBottomSheetDialogFragment() {
+
+ private val viewModel: ViewEditHistoryViewModel by fragmentViewModel(ViewEditHistoryViewModel::class)
+
+ @Inject lateinit var viewEditHistoryViewModelFactory: ViewEditHistoryViewModel.Factory
+ @Inject lateinit var eventHtmlRenderer: EventHtmlRenderer
+
+ @BindView(R.id.bottom_sheet_display_reactions_list)
+ lateinit var epoxyRecyclerView: EpoxyRecyclerView
+
+ private val epoxyController by lazy {
+ ViewEditHistoryEpoxyController(requireContext(), viewModel.timelineDateFormatter, eventHtmlRenderer)
+ }
+
+ override fun injectWith(screenComponent: ScreenComponent) {
+ screenComponent.inject(this)
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ val view = inflater.inflate(R.layout.bottom_sheet_epoxylist_with_title, container, false)
+ ButterKnife.bind(this, view)
+ return view
+ }
+
+ override fun onActivityCreated(savedInstanceState: Bundle?) {
+ super.onActivityCreated(savedInstanceState)
+ epoxyRecyclerView.setController(epoxyController)
+ val dividerItemDecoration = DividerItemDecoration(epoxyRecyclerView.context,
+ LinearLayout.VERTICAL)
+ epoxyRecyclerView.addItemDecoration(dividerItemDecoration)
+ bottomSheetTitle.text = context?.getString(R.string.message_edits)
+ }
+
+
+ override fun invalidate() = withState(viewModel) {
+ epoxyController.setData(it)
+ }
+
+ companion object {
+ fun newInstance(roomId: String, informationData: MessageInformationData): ViewEditHistoryBottomSheet {
+ val args = Bundle()
+ val parcelableArgs = TimelineEventFragmentArgs(
+ informationData.eventId,
+ roomId,
+ informationData
+ )
+ args.putParcelable(MvRx.KEY_ARG, parcelableArgs)
+ return ViewEditHistoryBottomSheet().apply { arguments = args }
+
+ }
+ }
+}
+
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/ViewEditHistoryEpoxyController.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/ViewEditHistoryEpoxyController.kt
new file mode 100644
index 00000000..fc11f255
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/ViewEditHistoryEpoxyController.kt
@@ -0,0 +1,157 @@
+/*
+ * 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.timeline.action
+
+import android.content.Context
+import android.text.Spannable
+import android.text.format.DateUtils
+import androidx.core.content.ContextCompat
+import com.airbnb.epoxy.TypedEpoxyController
+import com.airbnb.mvrx.Fail
+import com.airbnb.mvrx.Incomplete
+import com.airbnb.mvrx.Success
+import im.vector.matrix.android.api.session.events.model.Event
+import im.vector.matrix.android.api.session.events.model.toModel
+import im.vector.matrix.android.api.session.room.model.message.MessageTextContent
+import im.vector.matrix.android.api.util.ContentUtils.extractUsefulTextFromReply
+import im.vector.riotx.R
+import im.vector.riotx.core.extensions.localDateTime
+import im.vector.riotx.core.ui.list.genericFooterItem
+import im.vector.riotx.core.ui.list.genericItem
+import im.vector.riotx.core.ui.list.genericItemHeader
+import im.vector.riotx.core.ui.list.genericLoaderItem
+import im.vector.riotx.features.home.room.detail.timeline.helper.TimelineDateFormatter
+import im.vector.riotx.features.html.EventHtmlRenderer
+import me.gujun.android.span.span
+import name.fraser.neil.plaintext.diff_match_patch
+import timber.log.Timber
+import java.util.*
+
+/**
+ * Epoxy controller for reaction event list
+ */
+class ViewEditHistoryEpoxyController(private val context: Context,
+ val timelineDateFormatter: TimelineDateFormatter,
+ val eventHtmlRenderer: EventHtmlRenderer) : TypedEpoxyController() {
+
+ override fun buildModels(state: ViewEditHistoryViewState) {
+ when (state.editList) {
+ is Incomplete -> {
+ genericLoaderItem {
+ id("Spinner")
+ }
+ }
+ is Fail -> {
+ genericFooterItem {
+ id("failure")
+ text(context.getString(R.string.unknown_error))
+ }
+ }
+ is Success -> {
+ state.editList()?.let { renderEvents(it, state.isOriginalAReply) }
+ }
+
+ }
+ }
+
+ private fun renderEvents(sourceEvents: List, isOriginalReply: Boolean) {
+ if (sourceEvents.isEmpty()) {
+ genericItem {
+ id("footer")
+ title(context.getString(R.string.no_message_edits_found))
+ }
+ } else {
+ var lastDate: Calendar? = null
+ sourceEvents.forEachIndexed { index, timelineEvent ->
+
+ val evDate = Calendar.getInstance().apply {
+ timeInMillis = timelineEvent.originServerTs
+ ?: System.currentTimeMillis()
+ }
+ if (lastDate?.get(Calendar.DAY_OF_YEAR) != evDate.get(Calendar.DAY_OF_YEAR)) {
+ //need to display header with day
+ val dateString = if (DateUtils.isToday(evDate.timeInMillis)) context.getString(R.string.today)
+ else timelineDateFormatter.formatMessageDay(timelineEvent.localDateTime())
+ genericItemHeader {
+ id(evDate.hashCode())
+ text(dateString)
+ }
+ }
+ lastDate = evDate
+ val cContent = getCorrectContent(timelineEvent, isOriginalReply)
+ val body = cContent.second?.let { eventHtmlRenderer.render(it) }
+ ?: cContent.first
+
+ val nextEvent = if (index + 1 <= sourceEvents.lastIndex) sourceEvents[index + 1] else null
+
+ var spannedDiff: Spannable? = null
+ if (nextEvent != null && cContent.second == null /*No diff for html*/) {
+ //compares the body
+ val nContent = getCorrectContent(nextEvent, isOriginalReply)
+ val nextBody = nContent.second?.let { eventHtmlRenderer.render(it) }
+ ?: nContent.first
+ val dmp = diff_match_patch()
+ val diff = dmp.diff_main(nextBody.toString(), body.toString())
+ Timber.e("#### Diff: $diff")
+ dmp.diff_cleanupSemantic(diff)
+ Timber.e("#### Diff: $diff")
+ spannedDiff = span {
+ diff.map {
+ when (it.operation) {
+ diff_match_patch.Operation.DELETE -> {
+ span {
+ text = it.text
+ textColor = ContextCompat.getColor(context, R.color.vector_error_color)
+ textDecorationLine = "line-through"
+ }
+ }
+ diff_match_patch.Operation.INSERT -> {
+ span {
+ text = it.text
+ textColor = ContextCompat.getColor(context, R.color.vector_success_color)
+ }
+ }
+ else -> {
+ span {
+ text = it.text
+ }
+ }
+ }
+ }
+
+ }
+ }
+ genericItem {
+ id(timelineEvent.eventId)
+ title(timelineDateFormatter.formatMessageHour(timelineEvent.localDateTime()))
+ description(spannedDiff ?: body)
+ }
+ }
+ }
+ }
+
+ private fun getCorrectContent(event: Event, isOriginalReply: Boolean): Pair {
+ val clearContent = event.getClearContent().toModel()
+ val newContent = clearContent
+ ?.newContent
+ ?.toModel()
+ if (isOriginalReply) {
+ return extractUsefulTextFromReply(newContent?.body ?: clearContent?.body ?: "") to null
+ }
+ return (newContent?.body ?: clearContent?.body ?: "") to (newContent?.formattedBody
+ ?: clearContent?.formattedBody)
+ }
+}
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/ViewEditHistoryViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/ViewEditHistoryViewModel.kt
new file mode 100644
index 00000000..6ad17210
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/ViewEditHistoryViewModel.kt
@@ -0,0 +1,123 @@
+/*
+ * 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.timeline.action
+
+import com.airbnb.mvrx.*
+import com.squareup.inject.assisted.Assisted
+import com.squareup.inject.assisted.AssistedInject
+import im.vector.matrix.android.api.MatrixCallback
+import im.vector.matrix.android.api.session.Session
+import im.vector.matrix.android.api.session.crypto.MXCryptoError
+import im.vector.matrix.android.api.session.events.model.Event
+import im.vector.matrix.android.api.session.events.model.toModel
+import im.vector.matrix.android.api.session.room.model.message.MessageContent
+import im.vector.matrix.android.api.session.room.model.message.isReply
+import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult
+import im.vector.riotx.core.platform.VectorViewModel
+import im.vector.riotx.features.home.room.detail.timeline.helper.TimelineDateFormatter
+import timber.log.Timber
+import java.util.*
+
+
+data class ViewEditHistoryViewState(
+ val eventId: String,
+ val roomId: String,
+ val isOriginalAReply: Boolean = false,
+ val editList: Async> = Uninitialized)
+ : MvRxState {
+
+ constructor(args: TimelineEventFragmentArgs) : this(roomId = args.roomId, eventId = args.eventId)
+
+}
+
+class ViewEditHistoryViewModel @AssistedInject constructor(@Assisted
+ initialState: ViewEditHistoryViewState,
+ val session: Session,
+ val timelineDateFormatter: TimelineDateFormatter
+) : VectorViewModel(initialState) {
+
+ private val roomId = initialState.roomId
+ private val eventId = initialState.eventId
+ private val room = session.getRoom(roomId)
+ ?: throw IllegalStateException("Shouldn't use this ViewModel without a room")
+
+ @AssistedInject.Factory
+ interface Factory {
+ fun create(initialState: ViewEditHistoryViewState): ViewEditHistoryViewModel
+ }
+
+
+ companion object : MvRxViewModelFactory {
+
+ override fun create(viewModelContext: ViewModelContext, state: ViewEditHistoryViewState): ViewEditHistoryViewModel? {
+ val fragment: ViewEditHistoryBottomSheet = (viewModelContext as FragmentViewModelContext).fragment()
+ return fragment.viewEditHistoryViewModelFactory.create(state)
+ }
+
+ }
+
+ init {
+ loadHistory()
+ }
+
+ private fun loadHistory() {
+ setState { copy(editList = Loading()) }
+ room.fetchEditHistory(eventId, object : MatrixCallback> {
+ override fun onFailure(failure: Throwable) {
+ setState {
+ copy(editList = Fail(failure))
+ }
+ }
+
+ override fun onSuccess(data: List) {
+ var originalIsReply = false
+
+ val events = data.map { event ->
+ val timelineID = event.roomId + UUID.randomUUID().toString()
+ event.also {
+ //We need to check encryption
+ if (it.isEncrypted() && it.mxDecryptionResult == null) {
+ //for now decrypt sync
+ try {
+ val result = session.decryptEvent(it, timelineID)
+ it.mxDecryptionResult = OlmDecryptionResult(
+ payload = result.clearEvent,
+ senderKey = result.senderCurve25519Key,
+ keysClaimed = result.claimedEd25519Key?.let { k -> mapOf("ed25519" to k) },
+ forwardingCurve25519KeyChain = result.forwardingCurve25519KeyChain
+ )
+ } catch (e: MXCryptoError) {
+ Timber.w("Failed to decrypt event in history")
+ }
+ }
+
+ if (event.eventId == it.eventId) {
+ originalIsReply = it.getClearContent().toModel().isReply()
+ }
+ }
+
+ }
+ setState {
+ copy(
+ editList = Success(events),
+ isOriginalAReply = originalIsReply
+ )
+ }
+ }
+ })
+ }
+
+}
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/ViewReactionBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/ViewReactionBottomSheet.kt
index 760b74da..d7e41784 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/ViewReactionBottomSheet.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/ViewReactionBottomSheet.kt
@@ -21,7 +21,6 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
-import androidx.core.view.isVisible
import androidx.recyclerview.widget.DividerItemDecoration
import butterknife.BindView
import butterknife.ButterKnife
@@ -33,7 +32,7 @@ import im.vector.riotx.EmojiCompatFontProvider
import im.vector.riotx.R
import im.vector.riotx.core.di.ScreenComponent
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
-import kotlinx.android.synthetic.main.bottom_sheet_display_reactions.*
+import kotlinx.android.synthetic.main.bottom_sheet_epoxylist_with_title.*
import javax.inject.Inject
/**
@@ -49,14 +48,16 @@ class ViewReactionBottomSheet : VectorBaseBottomSheetDialogFragment() {
@BindView(R.id.bottom_sheet_display_reactions_list)
lateinit var epoxyRecyclerView: EpoxyRecyclerView
- private val epoxyController by lazy { ViewReactionsEpoxyController(emojiCompatFontProvider.typeface) }
+ private val epoxyController by lazy {
+ ViewReactionsEpoxyController(requireContext(), emojiCompatFontProvider.typeface)
+ }
override fun injectWith(screenComponent: ScreenComponent) {
screenComponent.inject(this)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- val view = inflater.inflate(R.layout.bottom_sheet_display_reactions, container, false)
+ val view = inflater.inflate(R.layout.bottom_sheet_epoxylist_with_title, container, false)
ButterKnife.bind(this, view)
return view
}
@@ -67,16 +68,11 @@ class ViewReactionBottomSheet : VectorBaseBottomSheetDialogFragment() {
val dividerItemDecoration = DividerItemDecoration(epoxyRecyclerView.context,
LinearLayout.VERTICAL)
epoxyRecyclerView.addItemDecoration(dividerItemDecoration)
+ bottomSheetTitle.text = context?.getString(R.string.reactions)
}
override fun invalidate() = withState(viewModel) {
- if (it.mapReactionKeyToMemberList() == null) {
- bottomSheetViewReactionSpinner.isVisible = true
- bottomSheetViewReactionSpinner.animate()
- } else {
- bottomSheetViewReactionSpinner.isVisible = false
- }
epoxyController.setData(it)
}
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/ViewReactionsEpoxyController.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/ViewReactionsEpoxyController.kt
index 57c3d265..74b3f492 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/ViewReactionsEpoxyController.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/ViewReactionsEpoxyController.kt
@@ -16,24 +16,47 @@
package im.vector.riotx.features.home.room.detail.timeline.action
+import android.content.Context
import android.graphics.Typeface
import com.airbnb.epoxy.TypedEpoxyController
+import com.airbnb.mvrx.Fail
+import com.airbnb.mvrx.Incomplete
+import com.airbnb.mvrx.Success
+import im.vector.riotx.R
+import im.vector.riotx.core.ui.list.genericFooterItem
+import im.vector.riotx.core.ui.list.genericLoaderItem
/**
* Epoxy controller for reaction event list
*/
-class ViewReactionsEpoxyController(private val emojiCompatTypeface: Typeface?) : TypedEpoxyController() {
+class ViewReactionsEpoxyController(private val context: Context, private val emojiCompatTypeface: Typeface?)
+ : TypedEpoxyController() {
override fun buildModels(state: DisplayReactionsViewState) {
- val map = state.mapReactionKeyToMemberList() ?: return
- map.forEach {
- reactionInfoSimpleItem {
- id(it.eventId)
- emojiTypeFace(emojiCompatTypeface)
- timeStamp(it.timestamp)
- reactionKey(it.reactionKey)
- authorDisplayName(it.authorName ?: it.authorId)
+ when (state.mapReactionKeyToMemberList) {
+ is Incomplete -> {
+ genericLoaderItem {
+ id("Spinner")
+ }
+ }
+ is Fail -> {
+ genericFooterItem {
+ id("failure")
+ text(context.getString(R.string.unknown_error))
+ }
+ }
+ is Success -> {
+ state.mapReactionKeyToMemberList()?.forEach {
+ reactionInfoSimpleItem {
+ id(it.eventId)
+ emojiTypeFace(emojiCompatTypeface)
+ timeStamp(it.timestamp)
+ reactionKey(it.reactionKey)
+ authorDisplayName(it.authorName ?: it.authorId)
+ }
+ }
}
}
+
}
}
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt
index 2a4a0c0f..080565cd 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt
@@ -68,6 +68,7 @@ class EncryptedItemFactory @Inject constructor(private val messageInformationDat
return MessageTextItem_()
.message(spannableStr)
.avatarRenderer(avatarRenderer)
+ .colorProvider(colorProvider)
.informationData(informationData)
.highlighted(highlight)
.avatarCallback(callback)
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt
index d8f1c602..3a0d2d1d 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/MessageItemFactory.kt
@@ -27,12 +27,14 @@ import dagger.Lazy
import im.vector.matrix.android.api.permalinks.MatrixLinkify
import im.vector.matrix.android.api.permalinks.MatrixPermalinkSpan
import im.vector.matrix.android.api.session.events.model.RelationType
+import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.EditAggregatedSummary
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.internal.crypto.attachments.toElementToDecrypt
+import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent
import im.vector.riotx.EmojiCompatFontProvider
import im.vector.riotx.R
import im.vector.riotx.core.epoxy.VectorEpoxyModel
@@ -83,7 +85,9 @@ class MessageItemFactory @Inject constructor(
?: //Malformed content, we should echo something on screen
return DefaultItem_().text(stringProvider.getString(R.string.malformed_message))
- if (messageContent.relatesTo?.type == RelationType.REPLACE) {
+ if (messageContent.relatesTo?.type == RelationType.REPLACE
+ || event.isEncrypted() && event.root.content.toModel()?.relatesTo?.type == RelationType.REPLACE
+ ) {
// ignore replace event, the targeted id is already edited
return BlankItem_()
}
@@ -117,6 +121,7 @@ class MessageItemFactory @Inject constructor(
callback: TimelineEventController.Callback?): MessageFileItem? {
return MessageFileItem_()
.avatarRenderer(avatarRenderer)
+ .colorProvider(colorProvider)
.informationData(informationData)
.highlighted(highlight)
.avatarCallback(callback)
@@ -144,6 +149,7 @@ class MessageItemFactory @Inject constructor(
callback: TimelineEventController.Callback?): MessageFileItem? {
return MessageFileItem_()
.avatarRenderer(avatarRenderer)
+ .colorProvider(colorProvider)
.informationData(informationData)
.highlighted(highlight)
.avatarCallback(callback)
@@ -195,6 +201,7 @@ class MessageItemFactory @Inject constructor(
)
return MessageImageVideoItem_()
.avatarRenderer(avatarRenderer)
+ .colorProvider(colorProvider)
.imageContentRenderer(imageContentRenderer)
.contentUploadStateTrackerBinder(contentUploadStateTrackerBinder)
.playable(messageContent.info?.mimeType == "image/gif")
@@ -226,7 +233,8 @@ class MessageItemFactory @Inject constructor(
val (maxWidth, maxHeight) = timelineMediaSizeProvider.getMaxSize()
val thumbnailData = ImageContentRenderer.Data(
filename = messageContent.body,
- url = messageContent.videoInfo?.thumbnailFile?.url ?: messageContent.videoInfo?.thumbnailUrl,
+ url = messageContent.videoInfo?.thumbnailFile?.url
+ ?: messageContent.videoInfo?.thumbnailUrl,
elementToDecrypt = messageContent.videoInfo?.thumbnailFile?.toElementToDecrypt(),
height = messageContent.videoInfo?.height,
maxHeight = maxHeight,
@@ -246,6 +254,7 @@ class MessageItemFactory @Inject constructor(
.imageContentRenderer(imageContentRenderer)
.contentUploadStateTrackerBinder(contentUploadStateTrackerBinder)
.avatarRenderer(avatarRenderer)
+ .colorProvider(colorProvider)
.playable(true)
.informationData(informationData)
.highlighted(highlight)
@@ -288,6 +297,7 @@ class MessageItemFactory @Inject constructor(
}
.avatarRenderer(avatarRenderer)
.informationData(informationData)
+ .colorProvider(colorProvider)
.highlighted(highlight)
.avatarCallback(callback)
.urlClickCallback(callback)
@@ -353,6 +363,7 @@ class MessageItemFactory @Inject constructor(
return MessageTextItem_()
.avatarRenderer(avatarRenderer)
.message(message)
+ .colorProvider(colorProvider)
.informationData(informationData)
.highlighted(highlight)
.avatarCallback(callback)
@@ -393,6 +404,7 @@ class MessageItemFactory @Inject constructor(
}
}
.avatarRenderer(avatarRenderer)
+ .colorProvider(colorProvider)
.informationData(informationData)
.highlighted(highlight)
.avatarCallback(callback)
@@ -414,9 +426,18 @@ class MessageItemFactory @Inject constructor(
callback: TimelineEventController.Callback?): RedactedMessageItem? {
return RedactedMessageItem_()
.avatarRenderer(avatarRenderer)
+ .colorProvider(colorProvider)
.informationData(informationData)
.highlighted(highlight)
.avatarCallback(callback)
+ .cellClickListener(
+ DebouncedClickListener(View.OnClickListener { view ->
+ callback?.onEventCellClicked(informationData, null, view)
+ }))
+ .longClickListener { view ->
+ return@longClickListener callback?.onEventLongClicked(informationData, null, view)
+ ?: false
+ }
}
private fun linkifyBody(body: CharSequence, callback: TimelineEventController.Callback?): CharSequence {
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt
index 688cac3d..ca796667 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/helper/ContentUploadStateTrackerBinder.kt
@@ -23,12 +23,16 @@ import android.widget.ProgressBar
import android.widget.TextView
import androidx.core.view.isVisible
import im.vector.matrix.android.api.session.content.ContentUploadStateTracker
+import im.vector.matrix.android.api.session.room.send.SendState
import im.vector.riotx.R
import im.vector.riotx.core.di.ActiveSessionHolder
+import im.vector.riotx.core.resources.ColorProvider
import im.vector.riotx.features.media.ImageContentRenderer
+import im.vector.riotx.features.ui.getMessageTextColor
import javax.inject.Inject
-class ContentUploadStateTrackerBinder @Inject constructor(private val activeSessionHolder: ActiveSessionHolder) {
+class ContentUploadStateTrackerBinder @Inject constructor(private val activeSessionHolder: ActiveSessionHolder,
+ private val colorProvider: ColorProvider) {
private val updateListeners = mutableMapOf()
@@ -38,7 +42,7 @@ class ContentUploadStateTrackerBinder @Inject constructor(private val activeSess
activeSessionHolder.getActiveSession().also { session ->
val uploadStateTracker = session.contentUploadProgressTracker()
- val updateListener = ContentMediaProgressUpdater(progressLayout, mediaData)
+ val updateListener = ContentMediaProgressUpdater(progressLayout, mediaData, colorProvider)
updateListeners[eventId] = updateListener
uploadStateTracker.track(eventId, updateListener)
}
@@ -56,7 +60,8 @@ class ContentUploadStateTrackerBinder @Inject constructor(private val activeSess
}
private class ContentMediaProgressUpdater(private val progressLayout: ViewGroup,
- private val mediaData: ImageContentRenderer.Data) : ContentUploadStateTracker.UpdateListener {
+ private val mediaData: ImageContentRenderer.Data,
+ private val colorProvider: ColorProvider) : ContentUploadStateTracker.UpdateListener {
override fun onUpdate(state: ContentUploadStateTracker.State) {
when (state) {
@@ -79,6 +84,7 @@ private class ContentMediaProgressUpdater(private val progressLayout: ViewGroup,
progressBar?.isIndeterminate = true
progressBar?.progress = 0
progressTextView?.text = progressLayout.context.getString(R.string.send_file_step_idle)
+ progressTextView?.setTextColor(colorProvider.getMessageTextColor(SendState.UNSENT))
} else {
progressLayout.isVisible = false
}
@@ -106,6 +112,7 @@ private class ContentMediaProgressUpdater(private val progressLayout: ViewGroup,
val progressTextView = progressLayout.findViewById(R.id.mediaProgressTextView)
progressBar?.isIndeterminate = true
progressTextView?.text = progressLayout.context.getString(resId)
+ progressTextView?.setTextColor(colorProvider.getMessageTextColor(SendState.ENCRYPTING))
}
private fun doHandleProgress(resId: Int, current: Long, total: Long) {
@@ -119,6 +126,7 @@ private class ContentMediaProgressUpdater(private val progressLayout: ViewGroup,
progressTextView?.text = progressLayout.context.getString(resId,
Formatter.formatShortFileSize(progressLayout.context, current),
Formatter.formatShortFileSize(progressLayout.context, total))
+ progressTextView?.setTextColor(colorProvider.getMessageTextColor(SendState.SENDING))
}
private fun handleFailure(state: ContentUploadStateTracker.State.Failure) {
@@ -126,8 +134,8 @@ private class ContentMediaProgressUpdater(private val progressLayout: ViewGroup,
val progressBar = progressLayout.findViewById(R.id.mediaProgressBar)
val progressTextView = progressLayout.findViewById(R.id.mediaProgressTextView)
progressBar?.isVisible = false
- // TODO Red text
progressTextView?.text = state.throwable.localizedMessage
+ progressTextView?.setTextColor(colorProvider.getMessageTextColor(SendState.UNDELIVERED))
}
private fun handleSuccess(state: ContentUploadStateTracker.State.Success) {
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/AbsMessageItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/AbsMessageItem.kt
index 7a66c6ad..88697db4 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/AbsMessageItem.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/AbsMessageItem.kt
@@ -23,24 +23,32 @@ import android.view.ViewGroup
import android.view.ViewStub
import android.widget.ImageView
import android.widget.TextView
+import androidx.annotation.IdRes
import androidx.constraintlayout.helper.widget.Flow
import androidx.core.view.children
import androidx.core.view.isGone
import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute
import im.vector.riotx.R
+import im.vector.riotx.core.resources.ColorProvider
import im.vector.riotx.core.utils.DebouncedClickListener
import im.vector.riotx.core.utils.DimensionUtils.dpToPx
import im.vector.riotx.features.home.AvatarRenderer
import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController
import im.vector.riotx.features.reactions.widget.ReactionButton
+import im.vector.riotx.features.ui.getMessageTextColor
abstract class AbsMessageItem : BaseEventItem() {
- abstract val informationData: MessageInformationData
+ @EpoxyAttribute
+ lateinit var informationData: MessageInformationData
- abstract val avatarRenderer: AvatarRenderer
+ @EpoxyAttribute
+ lateinit var avatarRenderer: AvatarRenderer
+
+ @EpoxyAttribute
+ lateinit var colorProvider: ColorProvider
@EpoxyAttribute
var longClickListener: View.OnLongClickListener? = null
@@ -153,13 +161,12 @@ abstract class AbsMessageItem : BaseEventItem() {
return true
}
- protected fun View.renderSendState() {
- isClickable = informationData.sendState.isSent()
- alpha = if (informationData.sendState.isSent()) 1f else 0.5f
+ protected fun renderSendState(root: View, textView: TextView?) {
+ root.isClickable = informationData.sendState.isSent()
+ textView?.setTextColor(colorProvider.getMessageTextColor(informationData.sendState))
}
- abstract class Holder : BaseHolder() {
-
+ abstract class Holder(@IdRes stubId: Int) : BaseHolder(stubId) {
val avatarImageView by bind(R.id.messageAvatarImageView)
val memberNameView by bind(R.id.messageMemberNameView)
val timeView by bind(R.id.messageTimeView)
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/BaseEventItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/BaseEventItem.kt
index 3a7d09e2..843f52b3 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/BaseEventItem.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/BaseEventItem.kt
@@ -26,6 +26,9 @@ import im.vector.riotx.core.epoxy.VectorEpoxyModel
import im.vector.riotx.core.platform.CheckableView
import im.vector.riotx.core.utils.DimensionUtils.dpToPx
+/**
+ * Children must override getViewType()
+ */
abstract class BaseEventItem : VectorEpoxyModel() {
var avatarStyle: AvatarStyle = AvatarStyle.SMALL
@@ -43,31 +46,18 @@ abstract class BaseEventItem : VectorEpoxyModel
holder.checkableBackground.isChecked = highlighted
}
-
- override fun getViewType(): Int {
- return getStubType()
- }
-
- abstract fun getStubType(): Int
-
-
- abstract class BaseHolder : VectorEpoxyHolder() {
-
+ abstract class BaseHolder(@IdRes val stubId: Int) : VectorEpoxyHolder() {
val leftGuideline by bind(R.id.messageStartGuideline)
val checkableBackground by bind(R.id.messageSelectedBackground)
- @IdRes
- abstract fun getStubId(): Int
-
override fun bindView(itemView: View) {
super.bindView(itemView)
inflateStub()
}
private fun inflateStub() {
- view.findViewById(getStubId()).inflate()
+ view.findViewById(stubId).inflate()
}
-
}
companion object {
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/DefaultItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/DefaultItem.kt
index cc50493a..0b30facf 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/DefaultItem.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/DefaultItem.kt
@@ -31,11 +31,9 @@ abstract class DefaultItem : BaseEventItem() {
holder.messageView.text = text
}
- override fun getStubType(): Int = STUB_ID
-
- class Holder : BaseHolder() {
- override fun getStubId(): Int = STUB_ID
+ override fun getViewType() = STUB_ID
+ class Holder : BaseHolder(STUB_ID) {
val messageView by bind(R.id.stateMessageView)
}
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MergedHeaderItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MergedHeaderItem.kt
index 0ad13fcf..4f26f9bb 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MergedHeaderItem.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MergedHeaderItem.kt
@@ -46,7 +46,7 @@ data class MergedHeaderItem(private val isCollapsed: Boolean,
return Holder()
}
- override fun getStubType(): Int = STUB_ID
+ override fun getViewType() = STUB_ID
override fun bind(holder: Holder) {
super.bind(holder)
@@ -84,8 +84,7 @@ data class MergedHeaderItem(private val isCollapsed: Boolean,
val avatarUrl: String?
)
- class Holder : BaseHolder() {
- override fun getStubId(): Int = STUB_ID
+ class Holder : BaseHolder(STUB_ID) {
val expandView by bind(R.id.itemMergedExpandTextView)
val summaryView by bind(R.id.itemMergedSummaryTextView)
@@ -95,6 +94,6 @@ data class MergedHeaderItem(private val isCollapsed: Boolean,
}
companion object {
- private val STUB_ID = R.id.messageContentMergedheaderStub
+ private const val STUB_ID = R.id.messageContentMergedheaderStub
}
}
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessageFileItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessageFileItem.kt
index 66b368df..45e57b59 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessageFileItem.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessageFileItem.kt
@@ -25,7 +25,6 @@ import androidx.annotation.DrawableRes
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.riotx.R
-import im.vector.riotx.features.home.AvatarRenderer
@EpoxyModelClass(layout = R.layout.item_timeline_event_base)
abstract class MessageFileItem : AbsMessageItem() {
@@ -36,34 +35,27 @@ abstract class MessageFileItem : AbsMessageItem() {
@DrawableRes
var iconRes: Int = 0
@EpoxyAttribute
- override lateinit var informationData: MessageInformationData
- @EpoxyAttribute
- override lateinit var avatarRenderer: AvatarRenderer
- @EpoxyAttribute
var clickListener: View.OnClickListener? = null
override fun bind(holder: Holder) {
super.bind(holder)
- holder.fileLayout.renderSendState()
+ renderSendState(holder.fileLayout, holder.filenameView)
holder.filenameView.text = filename
holder.fileImageView.setImageResource(iconRes)
holder.filenameView.setOnClickListener(clickListener)
holder.filenameView.paintFlags = (holder.filenameView.paintFlags or Paint.UNDERLINE_TEXT_FLAG)
}
- override fun getStubType(): Int = STUB_ID
-
- class Holder : AbsMessageItem.Holder() {
- override fun getStubId(): Int = STUB_ID
+ override fun getViewType() = STUB_ID
+ class Holder : AbsMessageItem.Holder(STUB_ID) {
val fileLayout by bind(R.id.messageFileLayout)
val fileImageView by bind(R.id.messageFileImageView)
val filenameView by bind(R.id.messageFilenameView)
-
}
companion object {
- private val STUB_ID = R.id.messageContentFileStub
+ private const val STUB_ID = R.id.messageContentFileStub
}
}
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessageImageVideoItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessageImageVideoItem.kt
index 67f0ed7b..d551e44c 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessageImageVideoItem.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessageImageVideoItem.kt
@@ -22,7 +22,6 @@ import android.widget.ImageView
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.riotx.R
-import im.vector.riotx.features.home.AvatarRenderer
import im.vector.riotx.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder
import im.vector.riotx.features.media.ImageContentRenderer
@@ -32,10 +31,6 @@ abstract class MessageImageVideoItem : AbsMessageItem(R.id.messageMediaUploadProgressLayout)
val imageView by bind(R.id.messageThumbnailView)
val playContentView by bind(R.id.messageMediaPlayView)
val mediaContentView by bind(R.id.messageContentMedia)
-
}
-
companion object {
- private val STUB_ID = R.id.messageContentMediaStub
+ private const val STUB_ID = R.id.messageContentMediaStub
}
-
}
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessageTextItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessageTextItem.kt
index 3ab428e0..fc867b12 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessageTextItem.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/MessageTextItem.kt
@@ -16,6 +16,7 @@
package im.vector.riotx.features.home.room.detail.timeline.item
+import android.view.MotionEvent
import androidx.appcompat.widget.AppCompatTextView
import androidx.core.text.PrecomputedTextCompat
import androidx.core.text.toSpannable
@@ -24,7 +25,6 @@ import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.riotx.R
import im.vector.riotx.core.utils.containsOnlyEmojis
-import im.vector.riotx.features.home.AvatarRenderer
import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController
import im.vector.riotx.features.html.PillImageSpan
import kotlinx.coroutines.Dispatchers
@@ -39,20 +39,25 @@ abstract class MessageTextItem : AbsMessageItem() {
@EpoxyAttribute
var message: CharSequence? = null
@EpoxyAttribute
- override lateinit var avatarRenderer: AvatarRenderer
- @EpoxyAttribute
- override lateinit var informationData: MessageInformationData
- @EpoxyAttribute
var urlClickCallback: TimelineEventController.UrlClickCallback? = null
+ // Better link movement methods fixes the issue when
+ // long pressing to open the context menu on a TextView also triggers an autoLink click.
private val mvmtMethod = BetterLinkMovementMethod.newInstance().also {
it.setOnLinkClickListener { _, url ->
//Return false to let android manage the click on the link, or true if the link is handled by the application
urlClickCallback?.onUrlClicked(url) == true
}
- it.setOnLinkLongClickListener { _, url ->
+ //We need also to fix the case when long click on link will trigger long click on cell
+ it.setOnLinkLongClickListener { tv, url ->
//Long clicks are handled by parent, return true to block android to do something with url
- urlClickCallback?.onUrlLongClicked(url) == true
+ if (urlClickCallback?.onUrlLongClicked(url) == true) {
+ tv.dispatchTouchEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0f, 0f, 0))
+ true
+ } else {
+ false
+ }
+
}
}
@@ -73,7 +78,7 @@ abstract class MessageTextItem : AbsMessageItem() {
null)
holder.messageView.setTextFuture(textFuture)
- holder.messageView.renderSendState()
+ renderSendState(holder.messageView, holder.messageView)
holder.messageView.setOnClickListener(cellClickListener)
holder.messageView.setOnLongClickListener(longClickListener)
findPillsAndProcess { it.bind(holder.messageView) }
@@ -90,12 +95,13 @@ abstract class MessageTextItem : AbsMessageItem() {
}
}
- override fun getStubType(): Int = R.id.messageContentTextStub
+ override fun getViewType() = STUB_ID
- class Holder : AbsMessageItem.Holder() {
+ class Holder : AbsMessageItem.Holder(STUB_ID) {
val messageView by bind(R.id.messageTextView)
- override fun getStubId(): Int = R.id.messageContentTextStub
-
}
+ companion object {
+ private const val STUB_ID = R.id.messageContentTextStub
+ }
}
\ No newline at end of file
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/NoticeItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/NoticeItem.kt
index 08645357..2879073f 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/NoticeItem.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/NoticeItem.kt
@@ -57,10 +57,9 @@ abstract class NoticeItem : BaseEventItem() {
holder.view.setOnLongClickListener(longClickListener)
}
- override fun getStubType(): Int = STUB_ID
+ override fun getViewType() = STUB_ID
- class Holder : BaseHolder() {
- override fun getStubId(): Int = STUB_ID
+ class Holder : BaseHolder(STUB_ID) {
val avatarImageView by bind(R.id.itemNoticeAvatarView)
val noticeTextView by bind(R.id.itemNoticeTextView)
}
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/RedactedMessageItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/RedactedMessageItem.kt
index 05967f00..88e2be2b 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/RedactedMessageItem.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/item/RedactedMessageItem.kt
@@ -16,28 +16,19 @@
package im.vector.riotx.features.home.room.detail.timeline.item
-import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.riotx.R
-import im.vector.riotx.features.home.AvatarRenderer
@EpoxyModelClass(layout = R.layout.item_timeline_event_base)
abstract class RedactedMessageItem : AbsMessageItem() {
- @EpoxyAttribute
- override lateinit var informationData: MessageInformationData
- @EpoxyAttribute
- override lateinit var avatarRenderer: AvatarRenderer
-
- override fun getStubType(): Int = STUB_ID
+ override fun getViewType() = STUB_ID
override fun shouldShowReactionAtBottom() = false
- class Holder : AbsMessageItem.Holder() {
- override fun getStubId(): Int = STUB_ID
- }
+ class Holder : AbsMessageItem.Holder(STUB_ID)
companion object {
- private val STUB_ID = R.id.messageContentRedactedStub
+ private const val STUB_ID = R.id.messageContentRedactedStub
}
-}
\ No newline at end of file
+}
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/filtered/FilteredRoomFooterItem.kt b/vector/src/main/java/im/vector/riotx/features/home/room/filtered/FilteredRoomFooterItem.kt
new file mode 100644
index 00000000..777f0d32
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/filtered/FilteredRoomFooterItem.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.filtered
+
+import android.widget.Button
+import com.airbnb.epoxy.EpoxyAttribute
+import com.airbnb.epoxy.EpoxyModelClass
+import im.vector.riotx.R
+import im.vector.riotx.core.epoxy.VectorEpoxyHolder
+import im.vector.riotx.core.epoxy.VectorEpoxyModel
+import im.vector.riotx.features.home.room.list.widget.FabMenuView
+
+@EpoxyModelClass(layout = R.layout.item_room_filter_footer)
+abstract class FilteredRoomFooterItem : VectorEpoxyModel() {
+
+ @EpoxyAttribute
+ var listener: FilteredRoomFooterItemListener? = null
+
+ @EpoxyAttribute
+ var currentFilter: String = ""
+
+ override fun bind(holder: Holder) {
+ holder.createRoomButton.setOnClickListener { listener?.createRoom(currentFilter) }
+ holder.createDirectChat.setOnClickListener { listener?.createDirectChat() }
+ holder.openRoomDirectory.setOnClickListener { listener?.openRoomDirectory(currentFilter) }
+ }
+
+ class Holder : VectorEpoxyHolder() {
+ val createRoomButton by bindErrore bat gertatu da konfiantzazko informazioa jasotzeanErrore bat gertatu da gakoen babes-kopiaren datuak jasotzean
+ Ongi etorri betara!
+ RiotX oraindik garapenean dagoenez, ezaugarri batzuk faltan bota ditzakezu eta akatsen bat aurkitu dezakezu.
+ Azken ezaugarrien zerrenda hemen dago beti : %1$s, eta akatsak aurkitzen badituzu bidali mesedez akatsen txosten bat Hasiera pantailako goi ezkerreko menua erabiliz, eta ahalik eta lasterren konponduko dugu.
+ Play Store-ko deskripzioa
+ Akatsak aurkitzen badituzu bidali mesedez akatsen txosten bat Hasiera pantailako goi ezkerreko menua erabiliz, eta ahalik eta lasterren konponduko dugu.
+
+ "Inportatu e2e gakoak \"%1$s\" fitxategitik."
+
+ Matrix SDK bertsioa
+ Beste hirugarrengoen adierazpenak
+ Gela hau ikusten ari zara dagoeneko!
+
+ Erreakzio azkarrak
+
+ Orokorra
+ Hobespenak
+ Segurtasuna eta pribatutasuna
+ Aditua
+ Push arauak
+ Ez da push araurik zehaztu
+ Ez dago push arauentzako erregistratutako atebiderik
+
+ app_id:
+ push_gakoa:
+ gailu_izena:
+ Url-a:
+ Formatua:
+
+ Ahotsa eta bideoa
+ Laguntza eta honi buruz
+
+
+ Erregistratu token-a
+
+ Egin iradokizun bat
+ Idatzi zure iradokizuna azpian.
+ Deskribatu zure iradokizuna hemen
+ Eskerrik asko, iradokizuna ongi bidali da
+ Huts egin su iradokizuna bidaltzean (%s)
+
+ Erakutsi ezkutatutako gertaerak denbora lerroan
+
+ RiotX - Hurrengo belaunaldiko Matrix bezeroa
+ Matrix-erako bezero azkarrago eta arinago bat azken Android tresnak erabiliz eginak
+ RiotX bezero berri bat da Matrix protokoloarentzako (Matrix.org): komunikazioa seguru eta deszentralizatuarentzako sare libre bat. RiotX Android plataformarako Riot bezeroaren berridazketa oso bat da, erabat berridatzitako Android SDK-n oinarritua.
+\n
+\nAbisua: hau beta bertsio bat da. RiotX garapen aktiboan dago eta baditu mugak zein akatsak (gehiegi ez espero dugu). Iruzkin guztiak ongi etorriak dira!
+\n
+\nRiotX bezeroak honakoa ahalbidetzen du: • Badagoen kontu batean saioa hasi • Gelak sortu eta gela publikoetara elkartu • Gonbidapenak onartu edo ukatu • Erabiltzailearen gelak zerrendatu • Gelaren xehetasunak ikusi • Testuzko mezuak bidali • Eranskinak bidali • Zifratutako geletan mezuak irakurri eta idatzi • Zifratzea: E2Egakoen babeskopia, gailuaren egiaztaketa aurreratua, gakoa partekatzeko eskaria eta erantzuna • Push jakinarazpena • Gai argia, iluna eta beltza
+\n
+\nEz dira oraindik Riot bezeroaren ezaugarri guztiak ezarri RiotX bezeroan. Falta diren (eta laster etorriko direnen) artean nabarmenak dira: • Kontua sortzea • Gelaren ezarpenak (gelako kideak zerrendatzea, eta abar.) • Txat zuzenerako gelak sortzea • Deiak • Trepetak • …
+
+ app_display_name:
diff --git a/vector/src/main/res/values-fi/strings.xml b/vector/src/main/res/values-fi/strings.xml
index d84267a0..88553114 100644
--- a/vector/src/main/res/values-fi/strings.xml
+++ b/vector/src/main/res/values-fi/strings.xml
@@ -768,10 +768,10 @@
Varoitus!
- Konferenssipuhelu on yhtä kehitysvaiheessa eikä välttämättä ole luotettava.
+ Konferenssipuhelut ovat kehitysvaiheessa eivätkä välttämättä luotettavia.
- Virheellinen komento
+ KomentovirheTuntematon komento: %s
@@ -1166,7 +1166,7 @@ Haluatko lisätä paketteja?Käytä näppäimistön rivinvaihtopainiketta viestin lähettämiseenTämä vaihtoehto vaatii kolmannen osapuolen sovelluksen viestien tallennukseen.
- Komento ”%s” vaatii enemmän parametreja, tai jotkinparametrit ovat väärin.
+ Komento ”%s” vaatii enemmän parametreja, tai jotkin parametrit ovat virheellisiä.Näyttää toiminnonEstää käyttäjän annetulla id:lläSallii käyttäjän annetulla id:llä
@@ -1520,4 +1520,17 @@ Jotta et menetä mitään, automaattiset päivitykset kannattaa pitää käytös
Luottamustietoa haettaessa tapahtui virheAvainten varmuuskopioinnin tietoja haettaessa tapahtui virhe
+ Tuntematon laite pyytää salausavaimia
+\nLaitteen nimi: %1$s
+\nViimeksi nähty: %2$s
+\nJos et kirjautunut toisella laitteella, voit jättää tämän pyynnön huomiotta.
+
+ Tervetuloa beetaan!
+ RiotX on varhaisessa kehitysvaiheessa, mistä johtuen osa ominaisuuksista puuttuu ja saatat kohdata virheitä.
+ Matrix SDK:n versio
+ Muut kolmansien osapuolten huomautukset
+ Pikareaktiot
+
+ Asetukset
+ Tietoturva ja yksityisyys
diff --git a/vector/src/main/res/values-fr/strings.xml b/vector/src/main/res/values-fr/strings.xml
index cd91650b..d0f39271 100644
--- a/vector/src/main/res/values-fr/strings.xml
+++ b/vector/src/main/res/values-fr/strings.xml
@@ -1527,4 +1527,57 @@ Si vous n’avez pas configuré de nouvelle méthode de récupération, un attaq
Une erreur est survenue lors de la récupération des informations de confianceUne erreur est survenue lors de la récupération des données de sauvegarde de clés
+ Bienvenue à la bêta !
+ Comme RiotX est au début de son développement, il se peut que certaines fonctionnalités soient manquantes et que vous rencontriez quelques anomalies.
+ La dernière liste de fonctionnalités est toujours dans la %1$s, et si vous rencontrez des anomalies, envoyez un rapport dans le menu en haut à gauche de l’accueil et nous les règlerons aussi vite que possible.
+ description du Play Store
+ Si vous rencontrez des anomalies, envoyez un rapport dans le menu en haut à gauche de l’accueil et nous les règlerons aussi vite que possible.
+
+ Importer les clés de chiffrement depuis le fichier « %1$s ».
+
+ Version du SDK de Matrix
+ Licences tierces
+ Vous êtes déjà en train de visualiser ce salon !
+
+ Réactions rapides
+
+ Général
+ Préférences
+ Sécurité & vie privée
+ Expert
+ Règles de notification
+ Aucune règle de notification définie
+ Aucune passerelle de notification enregistrée
+
+ app_id :
+ push_key :
+ app_display_name :
+ device_name :
+ URL :
+ Format :
+
+ Voix & vidéo
+ Aide & à propos
+
+
+ Inscrire le jeton
+
+ Faire une suggestion
+ Saisissez votre suggestion ci-dessous.
+ Décrivez votre suggestion ici
+ Merci, la suggestion a bien été envoyée
+ Échec d’envoi de la suggestion (%s)
+
+ Afficher les évènements cachés dans l’historique
+
+ RiotX − Client Matrix nouvelle génération
+ Un client pour Matrix plus rapide et plus léger utilisant les derniers frameworks Android
+ RiotX est un nouveau client pour le protocole Matrix (Matrix.org) : un réseau ouvert pour des communications sécurisées et décentralisées. RiotX est une réécriture complète du client Android Riot, basée sur une réécriture complète du SDK Android de Matrix.
+\n
+\nMise en garde : Ceci est une version bêta. RiotX est actuellement en plein développement et a des limites et (peu, nous l’espérons) des anomalies. Tout commentaire est le bienvenu !
+\n
+\nRiotX prend en charge : • Se connecter à un compte existant • Créer de salons et rejoindre des salons publics • Accepter et refuser des invitations • Lister les salons des utilisateurs • Voir les informations des salons • Envoyer des messages texte • Envoyer des pièces jointes • Lire et écrire des messages dans les salons chiffrés • Chiffrement : sauvegarde des clés de chiffrement, vérification avancée des appareils, demande et réponse de partage de clé • Notifications • Thèmes clair, sombre et noir
+\n
+\nToutes les fonctionnalités de Riot ne sont pas encore implémentées dans RiotX. Principales fonctionnalités manquantes (et qui arrivent bientôt !) : • Création de compte • Réglages des salons (lister les membres du salon etc.) • Création de salons de discussion directe • Appels • Widgets • …
+
diff --git a/vector/src/main/res/values-hu/strings.xml b/vector/src/main/res/values-hu/strings.xml
index b8e19998..035cae08 100644
--- a/vector/src/main/res/values-hu/strings.xml
+++ b/vector/src/main/res/values-hu/strings.xml
@@ -1526,4 +1526,57 @@ Ha nem te állítottad be a visszaállítási metódust, akkor egy támadó pró
A megbízhatósági információ beszerzésekor hiba történtA kulcs mentés adatainak beszerzésekor hiba történt
+ Üdv a béta verzióban!
+ Amíg a RiotX újdonsült fejlesztés, néhány funkció hiányozhat és találkozhatsz hibákkal.
+ A legfrissebb funkciók listáját itt találod: %1$s, és ha hibát találsz kérlek küldj egy jelentést róla a Kezdő oldal bal felső sarkában lévő menüvel és ahogy tudjuk javítjuk.
+ Play áruház leírás
+ Ha hibát találsz kérlek küldj egy jelentést róla a Kezdő oldal bal felső sarkában lévő menüvel és ahogy tudjuk javítjuk.
+
+ Végponttól végpontig titkosítás kulcsainak betöltése ebből a fájlból: \"%1$s\".
+
+ Matrix SDK Verzió
+ Harmadik fél megjegyzések
+ Már nézed ezt a szobát!
+
+ Gyors Reakció
+
+ Általános
+ Beállítások
+ Biztonság & Adatvédelem
+ Haladó
+ „Push” szabályok
+ „Push” szabályok nincsenek
+ „Push” átjárók nincsenek regisztrálva
+
+ app_id:
+ push_key:
+ app_display_name:
+ device_name:
+ Url:
+ Formátum:
+
+ Hang & Videó
+ Segítség & Névjegy
+
+
+ „Token” regisztrálás
+
+ Javaslat tétel
+ A javaslatodat kérlek ír le alulra.
+ Ír le a javaslatodat ide
+ Köszönjük, a javaslatodat sikeresen elküldtük
+ A javaslatot nem sikerült elküldeni (%s)
+
+ Rejtett események megjelenítése az idővonalon
+
+ RiotX - Matrix Kliens Új Nemzedéke
+ A gyorsabb és kisebb Matrix kliens ami a legfrissebb Android keretrendszert használja
+ RiotX a Matrix protokollhoz (Matrix.org) készült új kliens: nyílt hálózat biztonságos és decentralizált kommunikációhoz. RiotX a Riot Android kliens teljesen újraírt változata ami a teljesen újraírt Matrix Android SDK-ra épül.
+\n
+\nFigyelmeztetés: Ez egy béta verzió. RiotX aktív fejlesztés alatt áll és vannak korlátai és (reméljük nem olyan sok) hibái. Minden visszajelzést szívesen fogadunk!
+\n
+\nRiotX ezeket támogatja: • Bejelentkezés létező fiókba • Szoba készítés és nyilvános szobába való belépés • Meghívók fogadása és elutasítás • Felhasználók szobáinak listázása • Szoba adatainak megtekintése • Szöveges üzenet küldése • Csatolmány küldése • Titkosított szobákban üzenetek olvasása és írása • Titkosítás: Végponttól végpontig titkosító kulcsok mentése, fejlett eszköz ellenőrzés, kulcs megosztás kérése és válasz • „Push” értesítések • Világos, sötét és fekete téma
+\n
+\nNem minden Riot funkció támogatott a RiotX-ben jelenleg. A fő hiányzó (és hamarosan elérhető!) funkciók: • Felhasználói fiók létrehozása • Szoba beállítások (szoba tagság mutatása, stb…) • Közvetlen beszélgetések indítása • Hívások • Kisalkalmazások • …
+
diff --git a/vector/src/main/res/values-it/strings.xml b/vector/src/main/res/values-it/strings.xml
index fd717864..cb5141ce 100644
--- a/vector/src/main/res/values-it/strings.xml
+++ b/vector/src/main/res/values-it/strings.xml
@@ -1570,4 +1570,57 @@ Per essere certo di non perdere nulla, mantieni gli aggiornamenti attivi."Si è verificato un errore nell\'ottenere informazioni sulla fiducia
Si è verificato un errore nell\'ottenere i dati del backup chiavi
+ Benvenuti nella beta!
+ Mentre RiotX si trova nella fase iniziale dello sviluppo, potrebbero mancare alcune funzioni e potrebbero verificarsi errori.
+ L\'elenco di funzioni più recenti è sempre nel %1$s, se trovi errori ti preghiamo segnalarli nel menu in alto a sinistra della Pagina Iniziale e noi li correggeremo appena possibile.
+ Descrizione nel Play Store
+ Se trovi errori ti preghiamo segnalarli nel menu in alto a sinistra della Pagina Iniziale e noi li correggeremo appena possibile.
+
+ Importa chiavi e2e dal file \"%1$s\".
+
+ Versione SDK Matrix
+ Altre note di terze parti
+ Stai già visualizzando questa stanza!
+
+ Reazioni rapide
+
+ Generali
+ Preferenze
+ Sicurezza e privacy
+ Esperto
+ Regole di push
+ Nessuna regola di push definita
+ Nessun gateway di push registrato
+
+ id_app:
+ chiave_push:
+ nome_visualizzato_app:
+ nome_dispositivo:
+ Url:
+ Formato:
+
+ Voce e video
+ Aiuto e informazioni
+
+
+ Registra token
+
+ Suggerisci qualcosa
+ Scrivi qua sotto il tuo suggerimento.
+ Descrivi qui il tuo suggerimento
+ Grazie, il suggerimento è stato inviato correttamente
+ L\'invio del suggerimento è fallito (%s)
+
+ Mostra eventi nascosti nella cronologia
+
+ RiotX - Client Matrix di nuova generazione
+ Un client per Matrix più veloce e leggero usando gli ultimi framework di Android
+ RiotX è un nuovo client per il protocollo Matrix (Matrix.org): una rete aperta per comunicazioni sicure e decentralizzate. RiotX è una riscrittura totale del client Riot Android, basata su una riscrittura completa dell\'SDK Android di Matrix.
+\n
+\nDisclaimer: questa è una versione beta. RiotX è attualmente in uno sviluppo attivo e contiene limitazioni ed errori (speriamo non troppi). I suggerimenti sono ben accetti!
+\n
+\nRiotX supporta: • Accesso ad account esistente • Crea stanze ed entra in stanze pubbliche • Accetta e rifiuta inviti • Elenca stanze utenti • Vedi dettagli stanza • Invia messaggi di testo • Invia allegati • Leggi e scrivi messaggi in stanze cifrate • Crypto: backup chiavi E2E, verifica avanzata dispositivi, richiesta e risposta condivisione chiavi • Notifiche push • Tema chiaro, scuro e nero
+\n
+\nNon tutte le funzioni di Riot sono già implementate in RiotX. Principali funzioni mancanti (prossimamente!): • Creazione account • Impostazioni stanza (elenca membri stanza, ecc.) • Creazione di stanze di chat diretta • Chiamate • Widget • …
+
diff --git a/vector/src/main/res/values-ko/strings.xml b/vector/src/main/res/values-ko/strings.xml
index eeabcc7a..5e357eb6 100644
--- a/vector/src/main/res/values-ko/strings.xml
+++ b/vector/src/main/res/values-ko/strings.xml
@@ -7,7 +7,7 @@
어두운 테마검정 테마
- 동기화
+ 동기화하는중…메시지방설정
@@ -26,7 +26,7 @@
보내기복사다시 보내기
- 삭제하기
+ 지우기인용받기공유
@@ -38,7 +38,7 @@
컨텐츠 신고음성영상
- 전화를 걸 수 없습니다, 나중에 다시 시도해주세요.
+ 전화를 걸 수 없습니다, 나중에 다시 시도해주세요권한이 없어졌기 때문에, 일부 기능이 빠졌을 수 있습니다…권한이 없어졌기 때문에, 이 행동을 할 수 없습니다.전화를 걸 수 없습니다
@@ -80,13 +80,13 @@
로그 보내기충돌 로그 보내기스크린샷 보내기
- 버그 보고서
+ 버그 보고하기버그에 대해 설명해주세요. 무엇을 했나요? 어떤 일이 일어나길 바라고 한 건가요? 실제로는 어떤 일이 일어났나요?가능하다면, 영어로 설명해주세요.여기에 문제를 설명해주세요버그 보고서를 보내는데 성공했습니다버그 보고서를 보내는데 실패했습니다 (%s)
- 등록하기
+ 계정 만들기로그인로그아웃홈서버 URL
@@ -108,7 +108,7 @@
영상 찍기로그인
- 등록하기
+ 계정 만들기제출하기건너뛰기이메일이나 이용자 이름
@@ -117,4 +117,65 @@
이용자 이름이용자가 당신을 찾고 재설정 비밀번호를 보낼 수 있게 이메일 주소를 적어주세요.이용자가 당신을 찾을 수 있게 계정에 전화번호를 추가해주세요.
+ Status.im 테마
+
+ 서비스 초기화 중
+ 소리로 알림
+ 소리 없이 알림
+
+ 구성원 정보
+ 커뮤니티 정보
+ 키 백업
+ 키 백업하기
+ 인증 기기
+
+ 키 백업이 끝나지 않았습니다, 기다려주세요…
+ 지금 로그아웃하면 암호화된 메세지가 사라집니다
+ 키 백업하기
+ 정말이세요\?
+ 백업
+ 머물기
+ 넘기기
+ 완료
+ 정말 로그아웃하시겠어요\?
+ 읽었다고 표시
+ Riot이 주소록에 접근할 수 없게 되어 있습니다
+
+ %d 사람들
+
+
+ 초대
+ 커뮤니티
+ 최근에 애플리케이션이 충돌한 것 같습니다. 충돌 보고서를 열까요\?
+ 읽기
+
+ 사용자 이름
+ 음성 보내기
+
+ 스티커팩이 하나도 없습니다.
+\n
+\n뭐라도 추가할까요\?
+
+ 로그인 화면으로 돌아가기
+ 이메일 주소
+ 이메일 주소 (선택)
+ 전화번호
+ 전화번호 (선택)
+ 비밀번호 확인
+ 새 비밀번호 확인
+ 알맞지 않은 사용자 이름이나 비밀번호
+ 비밀번호가 너무 짧아요 (최소 6자)
+ 유효한 이메일 주소가 아닌 것 같아요
+ 유효한 전화번호가 아닌 것 같아요
+ 비밀번호가 맞지 않아요
+ 비밀번호를 잊어버리셨나요\?
+ 사용중인 사용자 이름입니다
+ 홈서버:
+ 비밀번호를 초기화하려면, 계정에 이메일을 등록해야합니다:
+ 새 비밀번호를 입력해야합니다.
+ 비밀번호가 초기화되었습니다.
+\n
+\n모든 기기에서 로그아웃되고 알림도 가지 않을 거에요. 다시 알림을 받으려면, 각 기기에 다시 로그인하세요.
+ 모바일
+
diff --git a/vector/src/main/res/values-sq/strings.xml b/vector/src/main/res/values-sq/strings.xml
index 82f4423e..8ab796fc 100644
--- a/vector/src/main/res/values-sq/strings.xml
+++ b/vector/src/main/res/values-sq/strings.xml
@@ -1484,4 +1484,51 @@ Që të garantoni se s’ju shpëton gjë, thjesht mbajeni të aktivizuar mekani
Ndodhi një gabim gjatë marrjes së të dhënave të besueshmërisëNdodhi një gabim teksa merreshin të dhëna kopjeruajtjeje kyçesh
+ Mirë se vini në beta!
+ Teksa RiotX është në fillimet e zhvillimit, disa veçori mund të mos jenë gati dhe mund të hasni të meta.
+ Lista e veçorive më të reja gjendet te %1$s, dhe nëse gjeni të meta, ju lutemi, parashtroni një njoftim te menuja majtas sipër te Home, dhe do t’i ndreqim sa më shpejt të mundemi.
+ Përshkrim në Play Store
+ Nëse gjeni të meta, ju lutemi, parashtroni një njoftim te menuja majtas sipër te Home, dhe do t’i ndreqim sa më shpejt të mundemi.
+
+ Importo kyçe e2e prej kartelës \"%1$s\".
+
+ Versioni Matrix SDK
+ Shënime të tjera palësh të treta
+ Po e shihni tashmë këtë dhomë!
+
+ Reagime të Shpejta
+
+ Të përgjithshme
+ Parapëlqime
+ Siguri & Privatësi
+ Ekspert
+ Rregulla për Push
+ S’ka të përcaktuara rregulla për push
+ Pa kanale push të regjistruar
+
+ Url:
+ Format:
+
+ Zë & Video
+ Ndihmë & Mbi
+
+
+ Bëni një sugjerim
+ Ju lutemi, shkruajeni sugjerimin tuaj më poshtë.
+ Përshkruani këtu sugjerimin tuaj
+ Faleminderit, sugjerimi u dërgua me sukses
+ S’u arrit të dërgohej sugjerimi (%s)
+
+ Shfaq te rrjedha kohore akte të fshehura
+
+ RiotX - Klient Matrix i Brezit të Ardhshëm
+ Një klient më i shpejtë dhe më i lehtë për Matrix, që përdor mekanizmat më të rinj Android
+ RiotX është një klient i ri për protokollin Matrix (Matrix.org): një rrjet i hapur për komunikim të sigurt, të centralizuar. RiotX është një rishkrim i plotë i klientit Riot Android, bazuar në një rishkrim të plotë të Matrix Android SDK-së.
+\n
+\nKlauzolë: Ky është një version beta. RiotX-i është ende nën zhivillim aktiv dhe përmban kufizime dhe (shpresojmë të mos jenë shumë) të meta. Mirëpresim krejt përshtypjet dhe sugjerimet!
+\n
+\nRiotX-i mbulon: • Hyrje në një llogari ekzistuese • Krijim dhome dhe pjesëmarrje në dhoma publike • Pranim dhe hedhje poshtë ftesash • Njohje të dhomave të përdoruesve • Parje hollësish dhome • Dërgim mesazhesh tekst • Dërgim bashkëngjitjesh • Lexim dhe shkrim mesazhesh në dhoma të fshehtëzuara • Kriptografi: kopjeruajtje kyçesh E2E, verifikim i thelluar pajisjesh, kërkesa dhe përgjigje për ndarje kyçesh • Njoftime push • Tema të Çelëta, të Errëta dhe të Zeza
+\n
+\nNë RiotX s’janë sendërtuar ende krejt veçoritë e Riot-it. Veçori kryesore që mungojnë (dhe që do të vijnë së shpejti!): • Krijim llogarish • Rregullime dhome (shfaqje anëtarësh dhome, etj.) • Krijim dhomash fjalosjeje të drejtpërdrejtë • Thirrje • Widget-es • …
+
diff --git a/vector/src/main/res/values-zh-rTW/strings.xml b/vector/src/main/res/values-zh-rTW/strings.xml
index a7152c10..3f755c20 100644
--- a/vector/src/main/res/values-zh-rTW/strings.xml
+++ b/vector/src/main/res/values-zh-rTW/strings.xml
@@ -1479,4 +1479,57 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意
取得信任資訊時發生錯誤取得金鑰備份資料時發生錯誤
+ 歡迎使用測試版!
+ RiotX 還在早期開發階段,可能會缺少某些功能,您可能會遇到臭蟲。
+ 最新的功能清單一直都會在 %1$s,如果您發現臭蟲,請在首頁左上角的選單中遞交回報,我們將會盡快修復。
+ Play 商店描述
+ 如果您發現臭蟲,請在首頁左上角的選單中遞交回報,我們將會盡快修復。
+
+ 從檔案「%1$s」匯入 e2e 金鑰。
+
+ Matrix SDK 版本
+ 其他第三方提醒
+ 您已在檢視此聊天室了!
+
+ 快速反應
+
+ 一般
+ 偏好設定
+ 安全與隱私
+ 專家
+ 推送規則
+ 未定義通送規則
+ 沒有已註冊的推送閘道
+
+ app_id:
+ push_key:
+ app_display_name:
+ device_name:
+ Url:
+ 格式:
+
+ 音訊與視訊
+ 說明與關於
+
+
+ 註冊代符
+
+ 建議
+ 請在下面編寫您的建議。
+ 在此描述您的建議
+ 感謝,建議已成功傳送
+ 建議傳送失敗 (%s)
+
+ 在時間軸中顯示隱藏的活動
+
+ RiotX - 下一代的 Matrix 客戶端
+ 使用最新 Android 框架的 Matrix 較快且輕量的客戶端
+ RiotX 是 Matrix 協定 (Matrix.org) 的新客戶端:安全、去中心化通訊的開放網路。RiotX 是 Riot Android 客戶端的完全重寫,以 Matrix Android SDK 的完全重寫為基礎。
+\n
+\n免責聲明:這是測試版。RiotX 目前仍在積極開發中,包含限制與(我們希望不多的)臭蟲。所有的回饋都很歡迎!
+\n
+\nRiotX 支援:• 登入到既有的帳號 • 建立聊天室與加入公開聊天室 • 接受與回絕邀請 • 列出使用者聊天室 • 檢視聊天室詳細資訊 • 傳送文字訊息 • 傳送附件 • 讀取與編寫已加密的聊天室 • 加密:E2E 金鑰備份、進階裝置驗證、金鑰分享請求與回應 • 推送通知 • 亮、暗與黑色主題
+\n
+\n不是所有 Riot 的功能都已在 RiotX 中實作。主要缺少(會在稍後到來!)的功能:• 建立帳號 • 聊天室設定(列出聊天室成員等) • 直接聊天室建立 • 通話 • 小工具 • …
+
diff --git a/vector/src/main/res/values/config.xml b/vector/src/main/res/values/config.xml
index f3904499..508b942a 100755
--- a/vector/src/main/res/values/config.xml
+++ b/vector/src/main/res/values/config.xml
@@ -1,7 +1,7 @@
- "RiotX"
+
https://vector.im
diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml
index 96404fe6..daab4259 100644
--- a/vector/src/main/res/values/strings.xml
+++ b/vector/src/main/res/values/strings.xml
@@ -1522,7 +1522,7 @@ Why choose Riot.im?
"An error occurred getting keys backup data"Welcome to the beta!
- "While Riot X is in early development, some features may be missing and you may experience bugs."
+ "While RiotX is in early development, some features may be missing and you may experience bugs.""The latest feature list is always in the %1$s, and if you find bugs please submit a report in the top left menu of Home, and we’ll fix them as quickly as we can.""Play Store description"
@@ -1594,4 +1594,38 @@ Not all features in Riot are implemented in RiotX yet. Main missing (and coming
• Widgets
• …"
+ Direct Messages
+
+ Waiting…
+ Encrypting thumbnail…
+ Sending thumbnail (%1$s / %2$s)
+ Encrypting file…
+ Sending file (%1$s / %2$s)
+
+ Downloading file %1$s…
+ File %1$s has been downloaded!
+
+ "(edited)"
+
+
+ %1$s to create an account.
+ Use the old app
+
+
+ Message Edits
+ No edits found
+
+
+ Filter conversations…
+ Can’t find what you’re looking for?
+ Create a new room
+ Send a new direct message
+ View the room directory
+
+ Name or ID (#example:matrix.org)
+
+ Enable swipe to reply in timeline
+
+ Link copied to clipboard
+
diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml
index 7c1d39c4..118fde84 100644
--- a/vector/src/main/res/values/strings_riotX.xml
+++ b/vector/src/main/res/values/strings_riotX.xml
@@ -3,22 +3,4 @@
- Direct Messages
-
- Waiting…
- Encrypting thumbnail…
- Sending thumbnail (%1$s / %2$s)
- Encrypting file…
- Sending file (%1$s / %2$s)
-
- Downloading file %1$s…
- File %1$s has been downloaded!
-
- "(edited)"
-
-
- %1$s to create an account.
- Use the old app
-
-
\ No newline at end of file
diff --git a/vector/src/main/res/values/styles_riot.xml b/vector/src/main/res/values/styles_riot.xml
index 78907193..91753271 100644
--- a/vector/src/main/res/values/styles_riot.xml
+++ b/vector/src/main/res/values/styles_riot.xml
@@ -9,6 +9,7 @@
@style/Vector.Toolbar.Title@style/Vector.Toolbar.SubTitle?riotx_background
+ 0dp
+
+
diff --git a/vector/src/main/res/values/theme_dark.xml b/vector/src/main/res/values/theme_dark.xml
index 76cd365e..11184539 100644
--- a/vector/src/main/res/values/theme_dark.xml
+++ b/vector/src/main/res/values/theme_dark.xml
@@ -65,7 +65,7 @@
@color/primary_color_dark
- #FFE9EDF1
+ #FF15171b#55555555
@@ -93,7 +93,7 @@
@android:color/white@color/riot_primary_text_color_dark@color/accent_color_dark
- ?android:textColorSecondary
+ @color/riotx_text_secondary_dark@color/vector_fuchsia_color@color/primary_color_light#CCC3C3C3
diff --git a/vector/src/main/res/values/theme_light.xml b/vector/src/main/res/values/theme_light.xml
index f7f9a260..d55148e7 100644
--- a/vector/src/main/res/values/theme_light.xml
+++ b/vector/src/main/res/values/theme_light.xml
@@ -93,7 +93,7 @@
@color/riot_primary_text_color_light#FF61708b@color/accent_color_light
- ?android:textColorSecondary
+ @color/riotx_text_secondary_light@color/vector_fuchsia_color@color/primary_color_light#333C3C3C
diff --git a/vector/src/main/res/xml/vector_settings_labs.xml b/vector/src/main/res/xml/vector_settings_labs.xml
index 66eb3f39..56205172 100644
--- a/vector/src/main/res/xml/vector_settings_labs.xml
+++ b/vector/src/main/res/xml/vector_settings_labs.xml
@@ -1,45 +1,49 @@
-
+
-
-
+
+
-
+
-
-
-
+
+
+
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
-
+
+
+
+