Merge pull request #746 from vector-im/feature/fix_various_issues
Fix 2 crashes reported by the PlayStore
This commit is contained in:
commit
79ef055bfb
@ -12,12 +12,14 @@ Other changes:
|
|||||||
|
|
||||||
Bugfix 🐛:
|
Bugfix 🐛:
|
||||||
- When automardown is ON, pills are sent as MD in body (#739)
|
- When automardown is ON, pills are sent as MD in body (#739)
|
||||||
|
- "ban" event are not rendered correctly (#716)
|
||||||
|
- Fix crash when rotating screen in Room timeline
|
||||||
|
|
||||||
Translations 🗣:
|
Translations 🗣:
|
||||||
-
|
-
|
||||||
|
|
||||||
Build 🧱:
|
Build 🧱:
|
||||||
- "ban" event are not rendered correctly (#716)
|
-
|
||||||
|
|
||||||
Changes in RiotX 0.9.1 (2019-12-05)
|
Changes in RiotX 0.9.1 (2019-12-05)
|
||||||
===================================================
|
===================================================
|
||||||
|
@ -22,6 +22,8 @@ interface ContentUploadStateTracker {
|
|||||||
|
|
||||||
fun untrack(key: String, updateListener: UpdateListener)
|
fun untrack(key: String, updateListener: UpdateListener)
|
||||||
|
|
||||||
|
fun clear()
|
||||||
|
|
||||||
interface UpdateListener {
|
interface UpdateListener {
|
||||||
fun onUpdate(state: State)
|
fun onUpdate(state: State)
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,7 @@ interface RelationService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a reaction (emoji) to the targetedEvent.
|
* Sends a reaction (emoji) to the targetedEvent.
|
||||||
|
* It has no effect if the user has already added the same reaction to the event.
|
||||||
* @param targetEventId the id of the event being reacted
|
* @param targetEventId the id of the event being reacted
|
||||||
* @param reaction the reaction (preferably emoji)
|
* @param reaction the reaction (preferably emoji)
|
||||||
*/
|
*/
|
||||||
|
@ -42,6 +42,10 @@ internal class DefaultContentUploadStateTracker @Inject constructor() : ContentU
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun clear() {
|
||||||
|
listeners.clear()
|
||||||
|
}
|
||||||
|
|
||||||
internal fun setFailure(key: String, throwable: Throwable) {
|
internal fun setFailure(key: String, throwable: Throwable) {
|
||||||
val failure = ContentUploadStateTracker.State.Failure(throwable)
|
val failure = ContentUploadStateTracker.State.Failure(throwable)
|
||||||
updateState(key, failure)
|
updateState(key, failure)
|
||||||
|
@ -30,10 +30,13 @@ import im.vector.matrix.android.api.session.room.model.message.MessageType
|
|||||||
import im.vector.matrix.android.api.session.room.model.relation.RelationService
|
import im.vector.matrix.android.api.session.room.model.relation.RelationService
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
|
import im.vector.matrix.android.api.util.NoOpCancellable
|
||||||
import im.vector.matrix.android.api.util.Optional
|
import im.vector.matrix.android.api.util.Optional
|
||||||
import im.vector.matrix.android.api.util.toOptional
|
import im.vector.matrix.android.api.util.toOptional
|
||||||
|
import im.vector.matrix.android.internal.database.mapper.TimelineEventMapper
|
||||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
import im.vector.matrix.android.internal.database.mapper.asDomain
|
||||||
import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryEntity
|
import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryEntity
|
||||||
|
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
import im.vector.matrix.android.internal.di.UserId
|
import im.vector.matrix.android.internal.di.UserId
|
||||||
import im.vector.matrix.android.internal.session.room.send.EncryptEventWorker
|
import im.vector.matrix.android.internal.session.room.send.EncryptEventWorker
|
||||||
@ -44,6 +47,7 @@ import im.vector.matrix.android.internal.session.room.timeline.TimelineSendEvent
|
|||||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||||
import im.vector.matrix.android.internal.task.configureWith
|
import im.vector.matrix.android.internal.task.configureWith
|
||||||
import im.vector.matrix.android.internal.util.CancelableWork
|
import im.vector.matrix.android.internal.util.CancelableWork
|
||||||
|
import im.vector.matrix.android.internal.util.fetchCopyMap
|
||||||
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
@ -54,6 +58,7 @@ internal class DefaultRelationService @AssistedInject constructor(@Assisted priv
|
|||||||
private val cryptoService: CryptoService,
|
private val cryptoService: CryptoService,
|
||||||
private val findReactionEventForUndoTask: FindReactionEventForUndoTask,
|
private val findReactionEventForUndoTask: FindReactionEventForUndoTask,
|
||||||
private val fetchEditHistoryTask: FetchEditHistoryTask,
|
private val fetchEditHistoryTask: FetchEditHistoryTask,
|
||||||
|
private val timelineEventMapper: TimelineEventMapper,
|
||||||
private val monarchy: Monarchy,
|
private val monarchy: Monarchy,
|
||||||
private val taskExecutor: TaskExecutor)
|
private val taskExecutor: TaskExecutor)
|
||||||
: RelationService {
|
: RelationService {
|
||||||
@ -64,11 +69,27 @@ internal class DefaultRelationService @AssistedInject constructor(@Assisted priv
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun sendReaction(targetEventId: String, reaction: String): Cancelable {
|
override fun sendReaction(targetEventId: String, reaction: String): Cancelable {
|
||||||
|
return if (monarchy
|
||||||
|
.fetchCopyMap(
|
||||||
|
{ realm ->
|
||||||
|
TimelineEventEntity.where(realm, roomId, targetEventId).findFirst()
|
||||||
|
},
|
||||||
|
{ entity, _ ->
|
||||||
|
timelineEventMapper.map(entity)
|
||||||
|
})
|
||||||
|
?.annotations
|
||||||
|
?.reactionsSummary
|
||||||
|
.orEmpty()
|
||||||
|
.none { it.addedByMe && it.key == reaction }) {
|
||||||
val event = eventFactory.createReactionEvent(roomId, targetEventId, reaction)
|
val event = eventFactory.createReactionEvent(roomId, targetEventId, reaction)
|
||||||
.also { saveLocalEcho(it) }
|
.also { saveLocalEcho(it) }
|
||||||
val sendRelationWork = createSendEventWork(event, true)
|
val sendRelationWork = createSendEventWork(event, true)
|
||||||
TimelineSendEventWorkCommon.postWork(context, roomId, sendRelationWork)
|
TimelineSendEventWorkCommon.postWork(context, roomId, sendRelationWork)
|
||||||
return CancelableWork(context, sendRelationWork.id)
|
CancelableWork(context, sendRelationWork.id)
|
||||||
|
} else {
|
||||||
|
Timber.w("Reaction already added")
|
||||||
|
NoOpCancellable
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun undoReaction(targetEventId: String, reaction: String): Cancelable {
|
override fun undoReaction(targetEventId: String, reaction: String): Cancelable {
|
||||||
|
@ -289,6 +289,9 @@ internal class DefaultTimeline(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun addListener(listener: Timeline.Listener) = synchronized(listeners) {
|
override fun addListener(listener: Timeline.Listener) = synchronized(listeners) {
|
||||||
|
if (listeners.contains(listener)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
listeners.add(listener).also {
|
listeners.add(listener).also {
|
||||||
postSnapshot()
|
postSnapshot()
|
||||||
}
|
}
|
||||||
|
@ -82,3 +82,6 @@ layout_constraintLeft_
|
|||||||
### Will crash on API < 21. Use ?colorAccent instead
|
### Will crash on API < 21. Use ?colorAccent instead
|
||||||
\?android:colorAccent
|
\?android:colorAccent
|
||||||
\?android:attr/colorAccent
|
\?android:attr/colorAccent
|
||||||
|
|
||||||
|
### Use androidx.recyclerview.widget.RecyclerView because EpoxyRecyclerViews add behavior we do not want to
|
||||||
|
<com\.airbnb\.epoxy\.EpoxyRecyclerView
|
||||||
|
@ -20,16 +20,22 @@ import android.os.Bundle
|
|||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import im.vector.matrix.android.api.crypto.getAllVerificationEmojis
|
import im.vector.matrix.android.api.crypto.getAllVerificationEmojis
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import kotlinx.android.synthetic.main.fragment_generic_recycler_epoxy.*
|
import im.vector.riotx.core.extensions.cleanup
|
||||||
|
import im.vector.riotx.core.extensions.configureWith
|
||||||
|
import kotlinx.android.synthetic.main.fragment_generic_recycler.*
|
||||||
|
|
||||||
class DebugSasEmojiActivity : AppCompatActivity() {
|
class DebugSasEmojiActivity : AppCompatActivity() {
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.fragment_generic_recycler_epoxy)
|
setContentView(R.layout.fragment_generic_recycler)
|
||||||
|
|
||||||
val controller = SasEmojiController()
|
val controller = SasEmojiController()
|
||||||
epoxyRecyclerView.setController(controller)
|
recyclerView.configureWith(controller)
|
||||||
controller.setData(SasState(getAllVerificationEmojis()))
|
controller.setData(SasState(getAllVerificationEmojis()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
recyclerView.cleanup()
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ import im.vector.riotx.core.di.DaggerVectorComponent
|
|||||||
import im.vector.riotx.core.di.HasVectorInjector
|
import im.vector.riotx.core.di.HasVectorInjector
|
||||||
import im.vector.riotx.core.di.VectorComponent
|
import im.vector.riotx.core.di.VectorComponent
|
||||||
import im.vector.riotx.core.extensions.configureAndStart
|
import im.vector.riotx.core.extensions.configureAndStart
|
||||||
import im.vector.riotx.core.utils.initKnownEmojiHashSet
|
import im.vector.riotx.core.rx.setupRxPlugin
|
||||||
import im.vector.riotx.features.configuration.VectorConfiguration
|
import im.vector.riotx.features.configuration.VectorConfiguration
|
||||||
import im.vector.riotx.features.lifecycle.VectorActivityLifecycleCallbacks
|
import im.vector.riotx.features.lifecycle.VectorActivityLifecycleCallbacks
|
||||||
import im.vector.riotx.features.notifications.NotificationDrawerManager
|
import im.vector.riotx.features.notifications.NotificationDrawerManager
|
||||||
@ -55,8 +55,7 @@ import im.vector.riotx.features.version.VersionProvider
|
|||||||
import im.vector.riotx.push.fcm.FcmHelper
|
import im.vector.riotx.push.fcm.FcmHelper
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Date
|
import java.util.*
|
||||||
import java.util.Locale
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.Provider, androidx.work.Configuration.Provider {
|
class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.Provider, androidx.work.Configuration.Provider {
|
||||||
@ -79,14 +78,13 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.
|
|||||||
lateinit var vectorComponent: VectorComponent
|
lateinit var vectorComponent: VectorComponent
|
||||||
private var fontThreadHandler: Handler? = null
|
private var fontThreadHandler: Handler? = null
|
||||||
|
|
||||||
// var slowMode = false
|
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
appContext = this
|
appContext = this
|
||||||
vectorComponent = DaggerVectorComponent.factory().create(this)
|
vectorComponent = DaggerVectorComponent.factory().create(this)
|
||||||
vectorComponent.inject(this)
|
vectorComponent.inject(this)
|
||||||
vectorUncaughtExceptionHandler.activate(this)
|
vectorUncaughtExceptionHandler.activate(this)
|
||||||
|
setupRxPlugin()
|
||||||
|
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
Timber.plant(Timber.DebugTree())
|
Timber.plant(Timber.DebugTree())
|
||||||
@ -138,7 +136,7 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.
|
|||||||
})
|
})
|
||||||
ProcessLifecycleOwner.get().lifecycle.addObserver(appStateHandler)
|
ProcessLifecycleOwner.get().lifecycle.addObserver(appStateHandler)
|
||||||
// This should be done as early as possible
|
// This should be done as early as possible
|
||||||
initKnownEmojiHashSet(appContext)
|
// initKnownEmojiHashSet(appContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun providesMatrixConfiguration() = MatrixConfiguration(BuildConfig.FLAVOR_DESCRIPTION)
|
override fun providesMatrixConfiguration() = MatrixConfiguration(BuildConfig.FLAVOR_DESCRIPTION)
|
||||||
|
@ -38,6 +38,7 @@ import im.vector.riotx.features.home.room.detail.RoomDetailFragment
|
|||||||
import im.vector.riotx.features.home.room.list.RoomListFragment
|
import im.vector.riotx.features.home.room.list.RoomListFragment
|
||||||
import im.vector.riotx.features.login.*
|
import im.vector.riotx.features.login.*
|
||||||
import im.vector.riotx.features.login.terms.LoginTermsFragment
|
import im.vector.riotx.features.login.terms.LoginTermsFragment
|
||||||
|
import im.vector.riotx.features.reactions.EmojiChooserFragment
|
||||||
import im.vector.riotx.features.reactions.EmojiSearchResultFragment
|
import im.vector.riotx.features.reactions.EmojiSearchResultFragment
|
||||||
import im.vector.riotx.features.roomdirectory.PublicRoomsFragment
|
import im.vector.riotx.features.roomdirectory.PublicRoomsFragment
|
||||||
import im.vector.riotx.features.roomdirectory.createroom.CreateRoomFragment
|
import im.vector.riotx.features.roomdirectory.createroom.CreateRoomFragment
|
||||||
@ -255,4 +256,9 @@ interface FragmentModule {
|
|||||||
@IntoMap
|
@IntoMap
|
||||||
@FragmentKey(BreadcrumbsFragment::class)
|
@FragmentKey(BreadcrumbsFragment::class)
|
||||||
fun bindBreadcrumbsFragment(fragment: BreadcrumbsFragment): Fragment
|
fun bindBreadcrumbsFragment(fragment: BreadcrumbsFragment): Fragment
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@FragmentKey(EmojiChooserFragment::class)
|
||||||
|
fun bindEmojiChooserFragment(fragment: EmojiChooserFragment): Fragment
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* 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.extensions
|
||||||
|
|
||||||
|
import androidx.recyclerview.widget.DividerItemDecoration
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.airbnb.epoxy.EpoxyController
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply a Vertical LinearLayout Manager to the recyclerView and set the adapter from the epoxy controller
|
||||||
|
*/
|
||||||
|
fun RecyclerView.configureWith(epoxyController: EpoxyController,
|
||||||
|
itemAnimator: RecyclerView.ItemAnimator? = null,
|
||||||
|
showDivider: Boolean = false,
|
||||||
|
hasFixedSize: Boolean = true) {
|
||||||
|
layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
|
||||||
|
itemAnimator?.let { this.itemAnimator = it }
|
||||||
|
if (showDivider) {
|
||||||
|
addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL))
|
||||||
|
}
|
||||||
|
setHasFixedSize(hasFixedSize)
|
||||||
|
adapter = epoxyController.adapter
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To call from Fragment.onDestroyView()
|
||||||
|
*/
|
||||||
|
fun RecyclerView.cleanup() {
|
||||||
|
adapter = null
|
||||||
|
}
|
@ -51,7 +51,7 @@ class StateView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
View.inflate(context, R.layout.view_state, this)
|
View.inflate(context, R.layout.view_state, this)
|
||||||
layoutParams = LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
|
layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
||||||
errorRetryView.setOnClickListener {
|
errorRetryView.setOnClickListener {
|
||||||
eventCallback?.onRetryClicked()
|
eventCallback?.onRetryClicked()
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ abstract class VectorBaseBottomSheetDialogFragment : BottomSheetDialogFragment()
|
|||||||
injectWith(screenComponent)
|
injectWith(screenComponent)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun injectWith(screenComponent: ScreenComponent) = Unit
|
protected open fun injectWith(injector: ScreenComponent) = Unit
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
mvrxViewIdProperty.restoreFrom(savedInstanceState)
|
mvrxViewIdProperty.restoreFrom(savedInstanceState)
|
||||||
|
@ -135,6 +135,7 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector {
|
|||||||
override fun onSaveInstanceState(outState: Bundle) {
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
super.onSaveInstanceState(outState)
|
super.onSaveInstanceState(outState)
|
||||||
restorables.forEach { it.onSaveInstanceState(outState) }
|
restorables.forEach { it.onSaveInstanceState(outState) }
|
||||||
|
restorables.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewStateRestored(savedInstanceState: Bundle?) {
|
override fun onViewStateRestored(savedInstanceState: Bundle?) {
|
||||||
|
35
vector/src/main/java/im/vector/riotx/core/rx/Rx.kt
Normal file
35
vector/src/main/java/im/vector/riotx/core/rx/Rx.kt
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* 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.rx
|
||||||
|
|
||||||
|
import im.vector.riotx.BuildConfig
|
||||||
|
import io.reactivex.plugins.RxJavaPlugins
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sure unhandled Rx error does not crash the app in production
|
||||||
|
*/
|
||||||
|
fun setupRxPlugin() {
|
||||||
|
RxJavaPlugins.setErrorHandler { throwable ->
|
||||||
|
Timber.e(throwable, "RxError")
|
||||||
|
|
||||||
|
// Avoid crash in production
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
throw throwable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -16,13 +16,6 @@
|
|||||||
|
|
||||||
package im.vector.riotx.core.utils
|
package im.vector.riotx.core.utils
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import com.squareup.moshi.Moshi
|
|
||||||
import im.vector.riotx.R
|
|
||||||
import im.vector.riotx.features.reactions.EmojiDataSource
|
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import timber.log.Timber
|
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
private val emojisPattern = Pattern.compile("((?:[\uD83C\uDF00-\uD83D\uDDFF]" +
|
private val emojisPattern = Pattern.compile("((?:[\uD83C\uDF00-\uD83D\uDDFF]" +
|
||||||
@ -49,6 +42,7 @@ private val emojisPattern = Pattern.compile("((?:[\uD83C\uDF00-\uD83D\uDDFF]" +
|
|||||||
"|\uD83C\uDCCF\uFE0F?" +
|
"|\uD83C\uDCCF\uFE0F?" +
|
||||||
"|[\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA]\uFE0F?))")
|
"|[\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA]\uFE0F?))")
|
||||||
|
|
||||||
|
/*
|
||||||
// A hashset from all supported emoji
|
// A hashset from all supported emoji
|
||||||
private var knownEmojiSet: HashSet<String>? = null
|
private var knownEmojiSet: HashSet<String>? = null
|
||||||
|
|
||||||
@ -56,7 +50,7 @@ fun initKnownEmojiHashSet(context: Context, done: (() -> Unit)? = null) {
|
|||||||
GlobalScope.launch {
|
GlobalScope.launch {
|
||||||
context.resources.openRawResource(R.raw.emoji_picker_datasource).use { input ->
|
context.resources.openRawResource(R.raw.emoji_picker_datasource).use { input ->
|
||||||
val moshi = Moshi.Builder().build()
|
val moshi = Moshi.Builder().build()
|
||||||
val jsonAdapter = moshi.adapter(EmojiDataSource.EmojiData::class.java)
|
val jsonAdapter = moshi.adapter(EmojiData::class.java)
|
||||||
val inputAsString = input.bufferedReader().use { it.readText() }
|
val inputAsString = input.bufferedReader().use { it.readText() }
|
||||||
val source = jsonAdapter.fromJson(inputAsString)
|
val source = jsonAdapter.fromJson(inputAsString)
|
||||||
knownEmojiSet = HashSet<String>().also {
|
knownEmojiSet = HashSet<String>().also {
|
||||||
@ -77,6 +71,7 @@ fun isSingleEmoji(string: String): Boolean {
|
|||||||
}
|
}
|
||||||
return knownEmojiSet?.contains(string) ?: false
|
return knownEmojiSet?.contains(string) ?: false
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test if a string contains emojis.
|
* Test if a string contains emojis.
|
||||||
|
@ -21,6 +21,8 @@ import androidx.appcompat.app.AlertDialog
|
|||||||
import com.airbnb.mvrx.activityViewModel
|
import com.airbnb.mvrx.activityViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
|
import im.vector.riotx.core.extensions.cleanup
|
||||||
|
import im.vector.riotx.core.extensions.configureWith
|
||||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||||
import im.vector.riotx.features.crypto.keysbackup.restore.KeysBackupRestoreActivity
|
import im.vector.riotx.features.crypto.keysbackup.restore.KeysBackupRestoreActivity
|
||||||
import im.vector.riotx.features.crypto.keysbackup.setup.KeysBackupSetupActivity
|
import im.vector.riotx.features.crypto.keysbackup.setup.KeysBackupSetupActivity
|
||||||
@ -37,12 +39,16 @@ class KeysBackupSettingsFragment @Inject constructor(private val keysBackupSetti
|
|||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
keysBackupSettingsRecyclerView.configureWith(keysBackupSettingsRecyclerViewController)
|
||||||
keysBackupSettingsRecyclerView.setController(keysBackupSettingsRecyclerViewController)
|
|
||||||
|
|
||||||
keysBackupSettingsRecyclerViewController.listener = this
|
keysBackupSettingsRecyclerViewController.listener = this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
keysBackupSettingsRecyclerViewController.listener = null
|
||||||
|
keysBackupSettingsRecyclerView.cleanup()
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
override fun invalidate() = withState(viewModel) { state ->
|
override fun invalidate() = withState(viewModel) { state ->
|
||||||
keysBackupSettingsRecyclerViewController.setData(state)
|
keysBackupSettingsRecyclerViewController.setData(state)
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,6 @@ import com.airbnb.mvrx.fragmentViewModel
|
|||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
import com.google.android.material.bottomnavigation.BottomNavigationItemView
|
import com.google.android.material.bottomnavigation.BottomNavigationItemView
|
||||||
import com.google.android.material.bottomnavigation.BottomNavigationMenuView
|
import com.google.android.material.bottomnavigation.BottomNavigationMenuView
|
||||||
import im.vector.matrix.android.api.session.Session
|
|
||||||
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState
|
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState
|
||||||
import im.vector.matrix.android.api.session.group.model.GroupSummary
|
import im.vector.matrix.android.api.session.group.model.GroupSummary
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
@ -46,7 +45,6 @@ private const val INDEX_PEOPLE = 1
|
|||||||
private const val INDEX_ROOMS = 2
|
private const val INDEX_ROOMS = 2
|
||||||
|
|
||||||
class HomeDetailFragment @Inject constructor(
|
class HomeDetailFragment @Inject constructor(
|
||||||
private val session: Session,
|
|
||||||
val homeDetailViewModelFactory: HomeDetailViewModel.Factory,
|
val homeDetailViewModelFactory: HomeDetailViewModel.Factory,
|
||||||
private val avatarRenderer: AvatarRenderer
|
private val avatarRenderer: AvatarRenderer
|
||||||
) : VectorBaseFragment(), KeysBackupBanner.Delegate {
|
) : VectorBaseFragment(), KeysBackupBanner.Delegate {
|
||||||
@ -56,9 +54,7 @@ class HomeDetailFragment @Inject constructor(
|
|||||||
private val viewModel: HomeDetailViewModel by fragmentViewModel()
|
private val viewModel: HomeDetailViewModel by fragmentViewModel()
|
||||||
private lateinit var sharedActionViewModel: HomeSharedActionViewModel
|
private lateinit var sharedActionViewModel: HomeSharedActionViewModel
|
||||||
|
|
||||||
override fun getLayoutResId(): Int {
|
override fun getLayoutResId() = R.layout.fragment_home_detail
|
||||||
return R.layout.fragment_home_detail
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
@ -23,9 +23,7 @@ import com.airbnb.mvrx.withState
|
|||||||
import com.jakewharton.rxbinding3.widget.textChanges
|
import com.jakewharton.rxbinding3.widget.textChanges
|
||||||
import im.vector.matrix.android.api.session.user.model.User
|
import im.vector.matrix.android.api.session.user.model.User
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.extensions.hideKeyboard
|
import im.vector.riotx.core.extensions.*
|
||||||
import im.vector.riotx.core.extensions.setupAsSearch
|
|
||||||
import im.vector.riotx.core.extensions.showKeyboard
|
|
||||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||||
import kotlinx.android.synthetic.main.fragment_create_direct_room_directory_users.*
|
import kotlinx.android.synthetic.main.fragment_create_direct_room_directory_users.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -48,10 +46,15 @@ class CreateDirectRoomDirectoryUsersFragment @Inject constructor(
|
|||||||
setupCloseView()
|
setupCloseView()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
recyclerView.cleanup()
|
||||||
|
directRoomController.callback = null
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
private fun setupRecyclerView() {
|
private fun setupRecyclerView() {
|
||||||
recyclerView.setHasFixedSize(true)
|
|
||||||
directRoomController.callback = this
|
directRoomController.callback = this
|
||||||
recyclerView.setController(directRoomController)
|
recyclerView.configureWith(directRoomController)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupSearchByMatrixIdView() {
|
private fun setupSearchByMatrixIdView() {
|
||||||
|
@ -31,9 +31,7 @@ import com.google.android.material.chip.ChipGroup
|
|||||||
import com.jakewharton.rxbinding3.widget.textChanges
|
import com.jakewharton.rxbinding3.widget.textChanges
|
||||||
import im.vector.matrix.android.api.session.user.model.User
|
import im.vector.matrix.android.api.session.user.model.User
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.extensions.hideKeyboard
|
import im.vector.riotx.core.extensions.*
|
||||||
import im.vector.riotx.core.extensions.observeEvent
|
|
||||||
import im.vector.riotx.core.extensions.setupAsSearch
|
|
||||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||||
import im.vector.riotx.core.utils.DimensionConverter
|
import im.vector.riotx.core.utils.DimensionConverter
|
||||||
import kotlinx.android.synthetic.main.fragment_create_direct_room.*
|
import kotlinx.android.synthetic.main.fragment_create_direct_room.*
|
||||||
@ -67,6 +65,12 @@ class CreateDirectRoomKnownUsersFragment @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
knownUsersController.callback = null
|
||||||
|
recyclerView.cleanup()
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onPrepareOptionsMenu(menu: Menu) {
|
override fun onPrepareOptionsMenu(menu: Menu) {
|
||||||
withState(viewModel) {
|
withState(viewModel) {
|
||||||
val createMenuItem = menu.findItem(R.id.action_create_direct_room)
|
val createMenuItem = menu.findItem(R.id.action_create_direct_room)
|
||||||
@ -94,11 +98,10 @@ class CreateDirectRoomKnownUsersFragment @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setupRecyclerView() {
|
private fun setupRecyclerView() {
|
||||||
recyclerView.setHasFixedSize(true)
|
|
||||||
// Don't activate animation as we might have way to much item animation when filtering
|
// Don't activate animation as we might have way to much item animation when filtering
|
||||||
recyclerView.itemAnimator = null
|
recyclerView.itemAnimator = null
|
||||||
knownUsersController.callback = this
|
knownUsersController.callback = this
|
||||||
recyclerView.setController(knownUsersController)
|
recyclerView.configureWith(knownUsersController)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupFilterView() {
|
private fun setupFilterView() {
|
||||||
|
@ -23,11 +23,13 @@ import com.airbnb.mvrx.Success
|
|||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import im.vector.matrix.android.api.session.group.model.GroupSummary
|
import im.vector.matrix.android.api.session.group.model.GroupSummary
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
|
import im.vector.riotx.core.extensions.cleanup
|
||||||
|
import im.vector.riotx.core.extensions.configureWith
|
||||||
import im.vector.riotx.core.extensions.observeEvent
|
import im.vector.riotx.core.extensions.observeEvent
|
||||||
import im.vector.riotx.core.platform.StateView
|
import im.vector.riotx.core.platform.StateView
|
||||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||||
import im.vector.riotx.features.home.HomeSharedActionViewModel
|
|
||||||
import im.vector.riotx.features.home.HomeActivitySharedAction
|
import im.vector.riotx.features.home.HomeActivitySharedAction
|
||||||
|
import im.vector.riotx.features.home.HomeSharedActionViewModel
|
||||||
import kotlinx.android.synthetic.main.fragment_group_list.*
|
import kotlinx.android.synthetic.main.fragment_group_list.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -45,14 +47,20 @@ class GroupListFragment @Inject constructor(
|
|||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
sharedActionViewModel = activityViewModelProvider.get(HomeSharedActionViewModel::class.java)
|
sharedActionViewModel = activityViewModelProvider.get(HomeSharedActionViewModel::class.java)
|
||||||
groupController.callback = this
|
groupController.callback = this
|
||||||
stateView.contentView = groupListEpoxyRecyclerView
|
stateView.contentView = groupListView
|
||||||
groupListEpoxyRecyclerView.setController(groupController)
|
groupListView.configureWith(groupController)
|
||||||
viewModel.subscribe { renderState(it) }
|
viewModel.subscribe { renderState(it) }
|
||||||
viewModel.openGroupLiveData.observeEvent(this) {
|
viewModel.openGroupLiveData.observeEvent(this) {
|
||||||
sharedActionViewModel.post(HomeActivitySharedAction.OpenGroup)
|
sharedActionViewModel.post(HomeActivitySharedAction.OpenGroup)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
groupController.callback = null
|
||||||
|
groupListView.cleanup()
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
private fun renderState(state: GroupListViewState) {
|
private fun renderState(state: GroupListViewState) {
|
||||||
when (state.asyncGroups) {
|
when (state.asyncGroups) {
|
||||||
is Incomplete -> stateView.state = StateView.State.Loading
|
is Incomplete -> stateView.state = StateView.State.Loading
|
||||||
|
@ -18,9 +18,10 @@ package im.vector.riotx.features.home.room.breadcrumbs
|
|||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
|
import im.vector.riotx.core.extensions.cleanup
|
||||||
|
import im.vector.riotx.core.extensions.configureWith
|
||||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||||
import im.vector.riotx.features.home.room.detail.RoomDetailSharedAction
|
import im.vector.riotx.features.home.room.detail.RoomDetailSharedAction
|
||||||
import im.vector.riotx.features.home.room.detail.RoomDetailSharedActionViewModel
|
import im.vector.riotx.features.home.room.detail.RoomDetailSharedActionViewModel
|
||||||
@ -46,16 +47,13 @@ class BreadcrumbsFragment @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
|
breadcrumbsRecyclerView.cleanup()
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
breadcrumbsRecyclerView.adapter = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupRecyclerView() {
|
private fun setupRecyclerView() {
|
||||||
val layoutManager = LinearLayoutManager(context)
|
breadcrumbsRecyclerView.configureWith(breadcrumbsController, BreadcrumbsAnimator(), hasFixedSize = false)
|
||||||
breadcrumbsRecyclerView.layoutManager = layoutManager
|
|
||||||
breadcrumbsRecyclerView.itemAnimator = BreadcrumbsAnimator()
|
|
||||||
breadcrumbsController.listener = this
|
breadcrumbsController.listener = this
|
||||||
breadcrumbsRecyclerView.setController(breadcrumbsController)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderState(state: BreadcrumbsViewState) {
|
private fun renderState(state: BreadcrumbsViewState) {
|
||||||
|
@ -46,6 +46,7 @@ import androidx.recyclerview.widget.RecyclerView
|
|||||||
import butterknife.BindView
|
import butterknife.BindView
|
||||||
import com.airbnb.epoxy.EpoxyModel
|
import com.airbnb.epoxy.EpoxyModel
|
||||||
import com.airbnb.epoxy.EpoxyVisibilityTracker
|
import com.airbnb.epoxy.EpoxyVisibilityTracker
|
||||||
|
import com.airbnb.epoxy.OnModelBuildFinishedListener
|
||||||
import com.airbnb.mvrx.*
|
import com.airbnb.mvrx.*
|
||||||
import com.github.piasy.biv.BigImageViewer
|
import com.github.piasy.biv.BigImageViewer
|
||||||
import com.github.piasy.biv.loader.ImageLoader
|
import com.github.piasy.biv.loader.ImageLoader
|
||||||
@ -69,10 +70,7 @@ import im.vector.riotx.R
|
|||||||
import im.vector.riotx.core.dialogs.withColoredButton
|
import im.vector.riotx.core.dialogs.withColoredButton
|
||||||
import im.vector.riotx.core.epoxy.LayoutManagerStateRestorer
|
import im.vector.riotx.core.epoxy.LayoutManagerStateRestorer
|
||||||
import im.vector.riotx.core.error.ErrorFormatter
|
import im.vector.riotx.core.error.ErrorFormatter
|
||||||
import im.vector.riotx.core.extensions.hideKeyboard
|
import im.vector.riotx.core.extensions.*
|
||||||
import im.vector.riotx.core.extensions.observeEvent
|
|
||||||
import im.vector.riotx.core.extensions.setTextOrHide
|
|
||||||
import im.vector.riotx.core.extensions.showKeyboard
|
|
||||||
import im.vector.riotx.core.files.addEntryToDownloadManager
|
import im.vector.riotx.core.files.addEntryToDownloadManager
|
||||||
import im.vector.riotx.core.glide.GlideApp
|
import im.vector.riotx.core.glide.GlideApp
|
||||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||||
@ -193,6 +191,8 @@ class RoomDetailFragment @Inject constructor(
|
|||||||
|
|
||||||
private lateinit var sharedActionViewModel: MessageSharedActionViewModel
|
private lateinit var sharedActionViewModel: MessageSharedActionViewModel
|
||||||
private lateinit var layoutManager: LinearLayoutManager
|
private lateinit var layoutManager: LinearLayoutManager
|
||||||
|
private var modelBuildListener: OnModelBuildFinishedListener? = null
|
||||||
|
|
||||||
private lateinit var attachmentsHelper: AttachmentsHelper
|
private lateinit var attachmentsHelper: AttachmentsHelper
|
||||||
private lateinit var keyboardStateUtils: KeyboardStateUtils
|
private lateinit var keyboardStateUtils: KeyboardStateUtils
|
||||||
|
|
||||||
@ -286,13 +286,16 @@ class RoomDetailFragment @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
|
timelineEventController.callback = null
|
||||||
|
timelineEventController.removeModelBuildListener(modelBuildListener)
|
||||||
|
modelBuildListener = null
|
||||||
|
debouncer.cancelAll()
|
||||||
|
recyclerView.cleanup()
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
recyclerView.adapter = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
roomDetailViewModel.handle(RoomDetailAction.ExitTrackingUnreadMessagesState)
|
roomDetailViewModel.handle(RoomDetailAction.ExitTrackingUnreadMessagesState)
|
||||||
debouncer.cancelAll()
|
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -447,11 +450,7 @@ class RoomDetailFragment @Inject constructor(
|
|||||||
if (!hasBeenHandled && resultCode == RESULT_OK && data != null) {
|
if (!hasBeenHandled && resultCode == RESULT_OK && data != null) {
|
||||||
when (requestCode) {
|
when (requestCode) {
|
||||||
REACTION_SELECT_REQUEST_CODE -> {
|
REACTION_SELECT_REQUEST_CODE -> {
|
||||||
val eventId = data.getStringExtra(EmojiReactionPickerActivity.EXTRA_EVENT_ID)
|
val (eventId, reaction) = EmojiReactionPickerActivity.getOutput(data) ?: return
|
||||||
?: return
|
|
||||||
val reaction = data.getStringExtra(EmojiReactionPickerActivity.EXTRA_REACTION_RESULT)
|
|
||||||
?: return
|
|
||||||
// TODO check if already reacted with that?
|
|
||||||
roomDetailViewModel.handle(RoomDetailAction.SendReaction(eventId, reaction))
|
roomDetailViewModel.handle(RoomDetailAction.SendReaction(eventId, reaction))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -470,13 +469,14 @@ class RoomDetailFragment @Inject constructor(
|
|||||||
recyclerView.layoutManager = layoutManager
|
recyclerView.layoutManager = layoutManager
|
||||||
recyclerView.itemAnimator = null
|
recyclerView.itemAnimator = null
|
||||||
recyclerView.setHasFixedSize(true)
|
recyclerView.setHasFixedSize(true)
|
||||||
timelineEventController.addModelBuildListener {
|
modelBuildListener = OnModelBuildFinishedListener {
|
||||||
it.dispatchTo(stateRestorer)
|
it.dispatchTo(stateRestorer)
|
||||||
it.dispatchTo(scrollOnNewMessageCallback)
|
it.dispatchTo(scrollOnNewMessageCallback)
|
||||||
it.dispatchTo(scrollOnHighlightedEventCallback)
|
it.dispatchTo(scrollOnHighlightedEventCallback)
|
||||||
updateJumpToReadMarkerViewVisibility()
|
updateJumpToReadMarkerViewVisibility()
|
||||||
updateJumpToBottomViewVisibility()
|
updateJumpToBottomViewVisibility()
|
||||||
}
|
}
|
||||||
|
timelineEventController.addModelBuildListener(modelBuildListener)
|
||||||
recyclerView.adapter = timelineEventController.adapter
|
recyclerView.adapter = timelineEventController.adapter
|
||||||
|
|
||||||
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||||
@ -521,7 +521,8 @@ class RoomDetailFragment @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateJumpToReadMarkerViewVisibility() = jumpToReadMarkerView.post {
|
private fun updateJumpToReadMarkerViewVisibility() {
|
||||||
|
jumpToReadMarkerView?.post {
|
||||||
withState(roomDetailViewModel) {
|
withState(roomDetailViewModel) {
|
||||||
val showJumpToUnreadBanner = when (it.unreadState) {
|
val showJumpToUnreadBanner = when (it.unreadState) {
|
||||||
UnreadState.Unknown,
|
UnreadState.Unknown,
|
||||||
@ -544,6 +545,7 @@ class RoomDetailFragment @Inject constructor(
|
|||||||
jumpToReadMarkerView.isVisible = showJumpToUnreadBanner
|
jumpToReadMarkerView.isVisible = showJumpToUnreadBanner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateJumpToBottomViewVisibility() {
|
private fun updateJumpToBottomViewVisibility() {
|
||||||
debouncer.debounce("jump_to_bottom_visibility", 250, Runnable {
|
debouncer.debounce("jump_to_bottom_visibility", 250, Runnable {
|
||||||
|
@ -863,7 +863,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
|||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
timeline.dispose()
|
timeline.dispose()
|
||||||
timeline.removeAllListeners()
|
timeline.removeListener(this)
|
||||||
super.onCleared()
|
super.onCleared()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,6 @@ import android.os.Parcelable
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import butterknife.BindView
|
import butterknife.BindView
|
||||||
import butterknife.ButterKnife
|
import butterknife.ButterKnife
|
||||||
@ -29,6 +28,8 @@ import com.airbnb.mvrx.MvRx
|
|||||||
import com.airbnb.mvrx.args
|
import com.airbnb.mvrx.args
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.di.ScreenComponent
|
import im.vector.riotx.core.di.ScreenComponent
|
||||||
|
import im.vector.riotx.core.extensions.cleanup
|
||||||
|
import im.vector.riotx.core.extensions.configureWith
|
||||||
import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment
|
import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.item.ReadReceiptData
|
import im.vector.riotx.features.home.room.detail.timeline.item.ReadReceiptData
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
||||||
@ -52,8 +53,8 @@ class DisplayReadReceiptsBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||||||
|
|
||||||
private val displayReadReceiptArgs: DisplayReadReceiptArgs by args()
|
private val displayReadReceiptArgs: DisplayReadReceiptArgs by args()
|
||||||
|
|
||||||
override fun injectWith(screenComponent: ScreenComponent) {
|
override fun injectWith(injector: ScreenComponent) {
|
||||||
screenComponent.inject(this)
|
injector.inject(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
@ -64,12 +65,16 @@ class DisplayReadReceiptsBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
recyclerView.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
|
recyclerView.configureWith(epoxyController, hasFixedSize = false)
|
||||||
recyclerView.adapter = epoxyController.adapter
|
|
||||||
bottomSheetTitle.text = getString(R.string.seen_by)
|
bottomSheetTitle.text = getString(R.string.seen_by)
|
||||||
epoxyController.setData(displayReadReceiptArgs.readReceipts)
|
epoxyController.setData(displayReadReceiptArgs.readReceipts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
recyclerView.cleanup()
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
// we are not using state for this one as it's static, so no need to override invalidate()
|
// we are not using state for this one as it's static, so no need to override invalidate()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -25,7 +25,6 @@ import androidx.recyclerview.widget.RecyclerView
|
|||||||
import com.airbnb.epoxy.EpoxyController
|
import com.airbnb.epoxy.EpoxyController
|
||||||
import com.airbnb.epoxy.EpoxyModel
|
import com.airbnb.epoxy.EpoxyModel
|
||||||
import com.airbnb.epoxy.VisibilityState
|
import com.airbnb.epoxy.VisibilityState
|
||||||
import im.vector.matrix.android.api.session.Session
|
|
||||||
import im.vector.matrix.android.api.session.room.model.message.*
|
import im.vector.matrix.android.api.session.room.model.message.*
|
||||||
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
||||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||||
@ -44,7 +43,7 @@ import org.threeten.bp.LocalDateTime
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class TimelineEventController @Inject constructor(private val dateFormatter: VectorDateFormatter,
|
class TimelineEventController @Inject constructor(private val dateFormatter: VectorDateFormatter,
|
||||||
private val session: Session,
|
private val contentUploadStateTrackerBinder: ContentUploadStateTrackerBinder,
|
||||||
private val timelineItemFactory: TimelineItemFactory,
|
private val timelineItemFactory: TimelineItemFactory,
|
||||||
private val timelineMediaSizeProvider: TimelineMediaSizeProvider,
|
private val timelineMediaSizeProvider: TimelineMediaSizeProvider,
|
||||||
private val mergedHeaderItemFactory: MergedHeaderItemFactory,
|
private val mergedHeaderItemFactory: MergedHeaderItemFactory,
|
||||||
@ -209,6 +208,13 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
|||||||
timelineMediaSizeProvider.recyclerView = recyclerView
|
timelineMediaSizeProvider.recyclerView = recyclerView
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
|
||||||
|
timelineMediaSizeProvider.recyclerView = null
|
||||||
|
contentUploadStateTrackerBinder.clear()
|
||||||
|
timeline?.removeListener(this)
|
||||||
|
super.onDetachedFromRecyclerView(recyclerView)
|
||||||
|
}
|
||||||
|
|
||||||
override fun buildModels() {
|
override fun buildModels() {
|
||||||
val timestamp = System.currentTimeMillis()
|
val timestamp = System.currentTimeMillis()
|
||||||
showingForwardLoader = LoadingItem_()
|
showingForwardLoader = LoadingItem_()
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* 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 androidx.recyclerview.widget.DefaultItemAnimator
|
||||||
|
|
||||||
|
private const val ANIM_DURATION_IN_MILLIS = 300L
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We only want to animate the expand of the "Report content" submenu
|
||||||
|
*/
|
||||||
|
class MessageActionsAnimator : DefaultItemAnimator() {
|
||||||
|
|
||||||
|
init {
|
||||||
|
addDuration = ANIM_DURATION_IN_MILLIS
|
||||||
|
removeDuration = 0
|
||||||
|
moveDuration = 0
|
||||||
|
changeDuration = 0
|
||||||
|
}
|
||||||
|
}
|
@ -19,7 +19,6 @@ import android.os.Bundle
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import butterknife.BindView
|
import butterknife.BindView
|
||||||
import butterknife.ButterKnife
|
import butterknife.ButterKnife
|
||||||
@ -27,6 +26,8 @@ import com.airbnb.mvrx.fragmentViewModel
|
|||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.di.ScreenComponent
|
import im.vector.riotx.core.di.ScreenComponent
|
||||||
|
import im.vector.riotx.core.extensions.cleanup
|
||||||
|
import im.vector.riotx.core.extensions.configureWith
|
||||||
import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment
|
import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
|
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -48,8 +49,8 @@ class MessageActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), Message
|
|||||||
|
|
||||||
private lateinit var sharedActionViewModel: MessageSharedActionViewModel
|
private lateinit var sharedActionViewModel: MessageSharedActionViewModel
|
||||||
|
|
||||||
override fun injectWith(screenComponent: ScreenComponent) {
|
override fun injectWith(injector: ScreenComponent) {
|
||||||
screenComponent.inject(this)
|
injector.inject(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
@ -61,13 +62,17 @@ class MessageActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), Message
|
|||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java)
|
sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java)
|
||||||
recyclerView.layoutManager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false)
|
recyclerView.configureWith(messageActionsEpoxyController, hasFixedSize = false)
|
||||||
recyclerView.adapter = messageActionsEpoxyController.adapter
|
|
||||||
// Disable item animation
|
// Disable item animation
|
||||||
recyclerView.itemAnimator = null
|
recyclerView.itemAnimator = null
|
||||||
messageActionsEpoxyController.listener = this
|
messageActionsEpoxyController.listener = this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
recyclerView.cleanup()
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onUrlClicked(url: String): Boolean {
|
override fun onUrlClicked(url: String): Boolean {
|
||||||
sharedActionViewModel.post(EventSharedAction.OnUrlClicked(url))
|
sharedActionViewModel.post(EventSharedAction.OnUrlClicked(url))
|
||||||
// Always consume
|
// Always consume
|
||||||
@ -83,6 +88,10 @@ class MessageActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), Message
|
|||||||
override fun didSelectMenuAction(eventAction: EventSharedAction) {
|
override fun didSelectMenuAction(eventAction: EventSharedAction) {
|
||||||
if (eventAction is EventSharedAction.ReportContent) {
|
if (eventAction is EventSharedAction.ReportContent) {
|
||||||
// Toggle report menu
|
// Toggle report menu
|
||||||
|
// Enable item animation
|
||||||
|
if (recyclerView.itemAnimator == null) {
|
||||||
|
recyclerView.itemAnimator = MessageActionsAnimator()
|
||||||
|
}
|
||||||
viewModel.handle(MessageActionsAction.ToggleReportMenu)
|
viewModel.handle(MessageActionsAction.ToggleReportMenu)
|
||||||
} else {
|
} else {
|
||||||
sharedActionViewModel.post(eventAction)
|
sharedActionViewModel.post(eventAction)
|
||||||
|
@ -19,9 +19,6 @@ import android.os.Bundle
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.LinearLayout
|
|
||||||
import androidx.recyclerview.widget.DividerItemDecoration
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import butterknife.BindView
|
import butterknife.BindView
|
||||||
import butterknife.ButterKnife
|
import butterknife.ButterKnife
|
||||||
@ -30,6 +27,8 @@ import com.airbnb.mvrx.fragmentViewModel
|
|||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.di.ScreenComponent
|
import im.vector.riotx.core.di.ScreenComponent
|
||||||
|
import im.vector.riotx.core.extensions.cleanup
|
||||||
|
import im.vector.riotx.core.extensions.configureWith
|
||||||
import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment
|
import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.action.TimelineEventFragmentArgs
|
import im.vector.riotx.features.home.room.detail.timeline.action.TimelineEventFragmentArgs
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
|
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
|
||||||
@ -54,8 +53,8 @@ class ViewEditHistoryBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||||||
ViewEditHistoryEpoxyController(requireContext(), viewModel.dateFormatter, eventHtmlRenderer)
|
ViewEditHistoryEpoxyController(requireContext(), viewModel.dateFormatter, eventHtmlRenderer)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun injectWith(screenComponent: ScreenComponent) {
|
override fun injectWith(injector: ScreenComponent) {
|
||||||
screenComponent.inject(this)
|
injector.inject(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
@ -66,13 +65,18 @@ class ViewEditHistoryBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
recyclerView.adapter = epoxyController.adapter
|
recyclerView.configureWith(
|
||||||
recyclerView.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
|
epoxyController,
|
||||||
val dividerItemDecoration = DividerItemDecoration(requireContext(), LinearLayout.VERTICAL)
|
showDivider = true,
|
||||||
recyclerView.addItemDecoration(dividerItemDecoration)
|
hasFixedSize = false)
|
||||||
bottomSheetTitle.text = context?.getString(R.string.message_edits)
|
bottomSheetTitle.text = context?.getString(R.string.message_edits)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
recyclerView.cleanup()
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
override fun invalidate() = withState(viewModel) {
|
override fun invalidate() = withState(viewModel) {
|
||||||
epoxyController.setData(it)
|
epoxyController.setData(it)
|
||||||
super.invalidate()
|
super.invalidate()
|
||||||
|
@ -28,17 +28,16 @@ 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.session.room.model.message.MessageTextContent
|
||||||
import im.vector.matrix.android.api.util.ContentUtils.extractUsefulTextFromReply
|
import im.vector.matrix.android.api.util.ContentUtils.extractUsefulTextFromReply
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
|
import im.vector.riotx.core.date.VectorDateFormatter
|
||||||
import im.vector.riotx.core.extensions.localDateTime
|
import im.vector.riotx.core.extensions.localDateTime
|
||||||
import im.vector.riotx.core.ui.list.genericFooterItem
|
import im.vector.riotx.core.ui.list.genericFooterItem
|
||||||
import im.vector.riotx.core.ui.list.genericItem
|
import im.vector.riotx.core.ui.list.genericItem
|
||||||
import im.vector.riotx.core.ui.list.genericItemHeader
|
import im.vector.riotx.core.ui.list.genericItemHeader
|
||||||
import im.vector.riotx.core.ui.list.genericLoaderItem
|
import im.vector.riotx.core.ui.list.genericLoaderItem
|
||||||
import im.vector.riotx.core.date.VectorDateFormatter
|
|
||||||
import im.vector.riotx.features.html.EventHtmlRenderer
|
import im.vector.riotx.features.html.EventHtmlRenderer
|
||||||
import me.gujun.android.span.span
|
import me.gujun.android.span.span
|
||||||
import name.fraser.neil.plaintext.diff_match_patch
|
import name.fraser.neil.plaintext.diff_match_patch
|
||||||
import timber.log.Timber
|
import java.util.*
|
||||||
import java.util.Calendar
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Epoxy controller for edit history list
|
* Epoxy controller for edit history list
|
||||||
@ -104,9 +103,7 @@ class ViewEditHistoryEpoxyController(private val context: Context,
|
|||||||
?: nContent.first
|
?: nContent.first
|
||||||
val dmp = diff_match_patch()
|
val dmp = diff_match_patch()
|
||||||
val diff = dmp.diff_main(nextBody.toString(), body.toString())
|
val diff = dmp.diff_main(nextBody.toString(), body.toString())
|
||||||
Timber.e("#### Diff: $diff")
|
|
||||||
dmp.diff_cleanupSemantic(diff)
|
dmp.diff_cleanupSemantic(diff)
|
||||||
Timber.e("#### Diff: $diff")
|
|
||||||
spannedDiff = span {
|
spannedDiff = span {
|
||||||
diff.map {
|
diff.map {
|
||||||
when (it.operation) {
|
when (it.operation) {
|
||||||
|
@ -25,12 +25,14 @@ import im.vector.matrix.android.api.session.content.ContentUploadStateTracker
|
|||||||
import im.vector.matrix.android.api.session.room.send.SendState
|
import im.vector.matrix.android.api.session.room.send.SendState
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.di.ActiveSessionHolder
|
import im.vector.riotx.core.di.ActiveSessionHolder
|
||||||
|
import im.vector.riotx.core.di.ScreenScope
|
||||||
import im.vector.riotx.core.error.ErrorFormatter
|
import im.vector.riotx.core.error.ErrorFormatter
|
||||||
import im.vector.riotx.core.resources.ColorProvider
|
import im.vector.riotx.core.resources.ColorProvider
|
||||||
import im.vector.riotx.core.utils.TextUtils
|
import im.vector.riotx.core.utils.TextUtils
|
||||||
import im.vector.riotx.features.ui.getMessageTextColor
|
import im.vector.riotx.features.ui.getMessageTextColor
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ScreenScope
|
||||||
class ContentUploadStateTrackerBinder @Inject constructor(private val activeSessionHolder: ActiveSessionHolder,
|
class ContentUploadStateTrackerBinder @Inject constructor(private val activeSessionHolder: ActiveSessionHolder,
|
||||||
private val colorProvider: ColorProvider,
|
private val colorProvider: ColorProvider,
|
||||||
private val errorFormatter: ErrorFormatter) {
|
private val errorFormatter: ErrorFormatter) {
|
||||||
@ -40,7 +42,7 @@ class ContentUploadStateTrackerBinder @Inject constructor(private val activeSess
|
|||||||
fun bind(eventId: String,
|
fun bind(eventId: String,
|
||||||
isLocalFile: Boolean,
|
isLocalFile: Boolean,
|
||||||
progressLayout: ViewGroup) {
|
progressLayout: ViewGroup) {
|
||||||
activeSessionHolder.getActiveSession().also { session ->
|
activeSessionHolder.getSafeActiveSession()?.also { session ->
|
||||||
val uploadStateTracker = session.contentUploadProgressTracker()
|
val uploadStateTracker = session.contentUploadProgressTracker()
|
||||||
val updateListener = ContentMediaProgressUpdater(progressLayout, isLocalFile, colorProvider, errorFormatter)
|
val updateListener = ContentMediaProgressUpdater(progressLayout, isLocalFile, colorProvider, errorFormatter)
|
||||||
updateListeners[eventId] = updateListener
|
updateListeners[eventId] = updateListener
|
||||||
@ -49,13 +51,19 @@ class ContentUploadStateTrackerBinder @Inject constructor(private val activeSess
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun unbind(eventId: String) {
|
fun unbind(eventId: String) {
|
||||||
activeSessionHolder.getActiveSession().also { session ->
|
activeSessionHolder.getSafeActiveSession()?.also { session ->
|
||||||
val uploadStateTracker = session.contentUploadProgressTracker()
|
val uploadStateTracker = session.contentUploadProgressTracker()
|
||||||
updateListeners[eventId]?.also {
|
updateListeners[eventId]?.also {
|
||||||
uploadStateTracker.untrack(eventId, it)
|
uploadStateTracker.untrack(eventId, it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun clear() {
|
||||||
|
activeSessionHolder.getSafeActiveSession()?.also {
|
||||||
|
it.contentUploadProgressTracker().clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ContentMediaProgressUpdater(private val progressLayout: ViewGroup,
|
private class ContentMediaProgressUpdater(private val progressLayout: ViewGroup,
|
||||||
|
@ -19,11 +19,12 @@ package im.vector.riotx.features.home.room.detail.timeline.helper
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import im.vector.riotx.core.di.ScreenScope
|
import im.vector.riotx.core.di.ScreenScope
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
@ScreenScope
|
@ScreenScope
|
||||||
class TimelineMediaSizeProvider @Inject constructor() {
|
class TimelineMediaSizeProvider @Inject constructor() {
|
||||||
|
|
||||||
lateinit var recyclerView: RecyclerView
|
var recyclerView: RecyclerView? = null
|
||||||
private var cachedSize: Pair<Int, Int>? = null
|
private var cachedSize: Pair<Int, Int>? = null
|
||||||
|
|
||||||
fun getMaxSize(): Pair<Int, Int> {
|
fun getMaxSize(): Pair<Int, Int> {
|
||||||
@ -31,17 +32,17 @@ class TimelineMediaSizeProvider @Inject constructor() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun computeMaxSize(): Pair<Int, Int> {
|
private fun computeMaxSize(): Pair<Int, Int> {
|
||||||
val width = recyclerView.width
|
val width = recyclerView?.width ?: 0
|
||||||
val height = recyclerView.height
|
val height = recyclerView?.height ?: 0
|
||||||
val maxImageWidth: Int
|
val maxImageWidth: Int
|
||||||
val maxImageHeight: Int
|
val maxImageHeight: Int
|
||||||
// landscape / portrait
|
// landscape / portrait
|
||||||
if (width < height) {
|
if (width < height) {
|
||||||
maxImageWidth = Math.round(width * 0.7f)
|
maxImageWidth = (width * 0.7f).roundToInt()
|
||||||
maxImageHeight = Math.round(height * 0.5f)
|
maxImageHeight = (height * 0.5f).roundToInt()
|
||||||
} else {
|
} else {
|
||||||
maxImageWidth = Math.round(width * 0.5f)
|
maxImageWidth = (width * 0.5f).roundToInt()
|
||||||
maxImageHeight = Math.round(height * 0.7f)
|
maxImageHeight = (height * 0.7f).roundToInt()
|
||||||
}
|
}
|
||||||
return Pair(maxImageWidth, maxImageHeight)
|
return Pair(maxImageWidth, maxImageHeight)
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@ import android.os.Bundle
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import butterknife.BindView
|
import butterknife.BindView
|
||||||
import butterknife.ButterKnife
|
import butterknife.ButterKnife
|
||||||
@ -29,6 +28,8 @@ import com.airbnb.mvrx.fragmentViewModel
|
|||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.di.ScreenComponent
|
import im.vector.riotx.core.di.ScreenComponent
|
||||||
|
import im.vector.riotx.core.extensions.cleanup
|
||||||
|
import im.vector.riotx.core.extensions.configureWith
|
||||||
import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment
|
import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.action.TimelineEventFragmentArgs
|
import im.vector.riotx.features.home.room.detail.timeline.action.TimelineEventFragmentArgs
|
||||||
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
|
import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
|
||||||
@ -49,8 +50,8 @@ class ViewReactionsBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||||||
|
|
||||||
@Inject lateinit var epoxyController: ViewReactionsEpoxyController
|
@Inject lateinit var epoxyController: ViewReactionsEpoxyController
|
||||||
|
|
||||||
override fun injectWith(screenComponent: ScreenComponent) {
|
override fun injectWith(injector: ScreenComponent) {
|
||||||
screenComponent.inject(this)
|
injector.inject(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
@ -61,11 +62,15 @@ class ViewReactionsBottomSheet : VectorBaseBottomSheetDialogFragment() {
|
|||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
recyclerView.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
|
recyclerView.configureWith(epoxyController, hasFixedSize = false)
|
||||||
recyclerView.adapter = epoxyController.adapter
|
|
||||||
bottomSheetTitle.text = context?.getString(R.string.reactions)
|
bottomSheetTitle.text = context?.getString(R.string.reactions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
recyclerView.cleanup()
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
override fun invalidate() = withState(viewModel) {
|
override fun invalidate() = withState(viewModel) {
|
||||||
epoxyController.setData(it)
|
epoxyController.setData(it)
|
||||||
super.invalidate()
|
super.invalidate()
|
||||||
|
@ -36,9 +36,7 @@ class FilteredRoomsActivity : VectorBaseActivity() {
|
|||||||
return supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as? RoomListFragment
|
return supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as? RoomListFragment
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getLayoutRes(): Int {
|
override fun getLayoutRes() = R.layout.activity_filtered_rooms
|
||||||
return R.layout.activity_filtered_rooms
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun injectWith(injector: ScreenComponent) {
|
override fun injectWith(injector: ScreenComponent) {
|
||||||
injector.inject(this)
|
injector.inject(this)
|
||||||
|
@ -26,6 +26,7 @@ import androidx.core.content.ContextCompat
|
|||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.airbnb.epoxy.OnModelBuildFinishedListener
|
||||||
import com.airbnb.mvrx.*
|
import com.airbnb.mvrx.*
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import im.vector.matrix.android.api.failure.Failure
|
import im.vector.matrix.android.api.failure.Failure
|
||||||
@ -35,13 +36,13 @@ import im.vector.matrix.android.api.session.room.notification.RoomNotificationSt
|
|||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.epoxy.LayoutManagerStateRestorer
|
import im.vector.riotx.core.epoxy.LayoutManagerStateRestorer
|
||||||
import im.vector.riotx.core.error.ErrorFormatter
|
import im.vector.riotx.core.error.ErrorFormatter
|
||||||
|
import im.vector.riotx.core.extensions.cleanup
|
||||||
import im.vector.riotx.core.platform.OnBackPressed
|
import im.vector.riotx.core.platform.OnBackPressed
|
||||||
import im.vector.riotx.core.platform.StateView
|
import im.vector.riotx.core.platform.StateView
|
||||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||||
|
|
||||||
import im.vector.riotx.features.home.RoomListDisplayMode
|
import im.vector.riotx.features.home.RoomListDisplayMode
|
||||||
import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsSharedAction
|
|
||||||
import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsBottomSheet
|
import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsBottomSheet
|
||||||
|
import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsSharedAction
|
||||||
import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel
|
import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel
|
||||||
import im.vector.riotx.features.home.room.list.widget.FabMenuView
|
import im.vector.riotx.features.home.room.list.widget.FabMenuView
|
||||||
import im.vector.riotx.features.notifications.NotificationDrawerManager
|
import im.vector.riotx.features.notifications.NotificationDrawerManager
|
||||||
@ -65,6 +66,7 @@ class RoomListFragment @Inject constructor(
|
|||||||
|
|
||||||
) : VectorBaseFragment(), RoomSummaryController.Listener, OnBackPressed, FabMenuView.Listener {
|
) : VectorBaseFragment(), RoomSummaryController.Listener, OnBackPressed, FabMenuView.Listener {
|
||||||
|
|
||||||
|
private var modelBuildListener: OnModelBuildFinishedListener? = null
|
||||||
private lateinit var sharedActionViewModel: RoomListQuickActionsSharedActionViewModel
|
private lateinit var sharedActionViewModel: RoomListQuickActionsSharedActionViewModel
|
||||||
private val roomListParams: RoomListParams by args()
|
private val roomListParams: RoomListParams by args()
|
||||||
private val roomListViewModel: RoomListViewModel by fragmentViewModel()
|
private val roomListViewModel: RoomListViewModel by fragmentViewModel()
|
||||||
@ -118,8 +120,12 @@ class RoomListFragment @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
|
roomController.removeModelBuildListener(modelBuildListener)
|
||||||
|
modelBuildListener = null
|
||||||
|
roomListView.cleanup()
|
||||||
|
roomController.listener = null
|
||||||
|
createChatFabMenu.listener = null
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
roomListView.adapter = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun openSelectedRoom(event: RoomListViewEvents.SelectRoom) {
|
private fun openSelectedRoom(event: RoomListViewEvents.SelectRoom) {
|
||||||
@ -198,7 +204,8 @@ class RoomListFragment @Inject constructor(
|
|||||||
roomListView.layoutManager = layoutManager
|
roomListView.layoutManager = layoutManager
|
||||||
roomListView.itemAnimator = RoomListAnimator()
|
roomListView.itemAnimator = RoomListAnimator()
|
||||||
roomController.listener = this
|
roomController.listener = this
|
||||||
roomController.addModelBuildListener { it.dispatchTo(stateRestorer) }
|
modelBuildListener = OnModelBuildFinishedListener { it.dispatchTo(stateRestorer) }
|
||||||
|
roomController.addModelBuildListener(modelBuildListener)
|
||||||
roomListView.adapter = roomController.adapter
|
roomListView.adapter = roomController.adapter
|
||||||
stateView.contentView = roomListView
|
stateView.contentView = roomListView
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,6 @@ import android.os.Parcelable
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import butterknife.BindView
|
import butterknife.BindView
|
||||||
import butterknife.ButterKnife
|
import butterknife.ButterKnife
|
||||||
@ -29,6 +28,8 @@ import com.airbnb.mvrx.fragmentViewModel
|
|||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.di.ScreenComponent
|
import im.vector.riotx.core.di.ScreenComponent
|
||||||
|
import im.vector.riotx.core.extensions.cleanup
|
||||||
|
import im.vector.riotx.core.extensions.configureWith
|
||||||
import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment
|
import im.vector.riotx.core.platform.VectorBaseBottomSheetDialogFragment
|
||||||
import im.vector.riotx.features.navigation.Navigator
|
import im.vector.riotx.features.navigation.Navigator
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
||||||
@ -56,8 +57,8 @@ class RoomListQuickActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), R
|
|||||||
|
|
||||||
override val showExpanded = true
|
override val showExpanded = true
|
||||||
|
|
||||||
override fun injectWith(screenComponent: ScreenComponent) {
|
override fun injectWith(injector: ScreenComponent) {
|
||||||
screenComponent.inject(this)
|
injector.inject(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
@ -69,13 +70,17 @@ class RoomListQuickActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), R
|
|||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
sharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java)
|
sharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java)
|
||||||
recyclerView.layoutManager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false)
|
recyclerView.configureWith(roomListActionsEpoxyController, hasFixedSize = false)
|
||||||
recyclerView.adapter = roomListActionsEpoxyController.adapter
|
|
||||||
// Disable item animation
|
// Disable item animation
|
||||||
recyclerView.itemAnimator = null
|
recyclerView.itemAnimator = null
|
||||||
roomListActionsEpoxyController.listener = this
|
roomListActionsEpoxyController.listener = this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
recyclerView.cleanup()
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
override fun invalidate() = withState(viewModel) {
|
override fun invalidate() = withState(viewModel) {
|
||||||
roomListActionsEpoxyController.setData(it)
|
roomListActionsEpoxyController.setData(it)
|
||||||
super.invalidate()
|
super.invalidate()
|
||||||
|
@ -24,6 +24,8 @@ import butterknife.OnClick
|
|||||||
import com.airbnb.mvrx.args
|
import com.airbnb.mvrx.args
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.error.ErrorFormatter
|
import im.vector.riotx.core.error.ErrorFormatter
|
||||||
|
import im.vector.riotx.core.extensions.cleanup
|
||||||
|
import im.vector.riotx.core.extensions.configureWith
|
||||||
import im.vector.riotx.core.utils.openUrlInExternalBrowser
|
import im.vector.riotx.core.utils.openUrlInExternalBrowser
|
||||||
import im.vector.riotx.features.login.AbstractLoginFragment
|
import im.vector.riotx.features.login.AbstractLoginFragment
|
||||||
import im.vector.riotx.features.login.LoginAction
|
import im.vector.riotx.features.login.LoginAction
|
||||||
@ -55,8 +57,7 @@ class LoginTermsFragment @Inject constructor(
|
|||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
loginTermsPolicyList.configureWith(policyController)
|
||||||
loginTermsPolicyList.setController(policyController)
|
|
||||||
policyController.listener = this
|
policyController.listener = this
|
||||||
|
|
||||||
val list = ArrayList<LocalizedFlowDataLoginTermsChecked>()
|
val list = ArrayList<LocalizedFlowDataLoginTermsChecked>()
|
||||||
@ -69,6 +70,12 @@ class LoginTermsFragment @Inject constructor(
|
|||||||
loginTermsViewState = LoginTermsViewState(list)
|
loginTermsViewState = LoginTermsViewState(list)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
loginTermsPolicyList.cleanup()
|
||||||
|
policyController.listener = null
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
private fun renderState() {
|
private fun renderState() {
|
||||||
policyController.setData(loginTermsViewState.localizedFlowDataLoginTermsChecked)
|
policyController.setData(loginTermsViewState.localizedFlowDataLoginTermsChecked)
|
||||||
|
|
||||||
|
@ -17,12 +17,18 @@ package im.vector.riotx.features.reactions
|
|||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.lifecycle.observe
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
|
import im.vector.riotx.core.extensions.cleanup
|
||||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||||
|
import kotlinx.android.synthetic.main.emoji_chooser_fragment.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class EmojiChooserFragment @Inject constructor() : VectorBaseFragment() {
|
class EmojiChooserFragment @Inject constructor(
|
||||||
|
private val emojiRecyclerAdapter: EmojiRecyclerAdapter
|
||||||
|
) : VectorBaseFragment(),
|
||||||
|
EmojiRecyclerAdapter.InteractionListener,
|
||||||
|
ReactionClickListener {
|
||||||
|
|
||||||
override fun getLayoutResId() = R.layout.emoji_chooser_fragment
|
override fun getLayoutResId() = R.layout.emoji_chooser_fragment
|
||||||
|
|
||||||
@ -31,10 +37,29 @@ class EmojiChooserFragment @Inject constructor() : VectorBaseFragment() {
|
|||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
viewModel = activityViewModelProvider.get(EmojiChooserViewModel::class.java)
|
viewModel = activityViewModelProvider.get(EmojiChooserViewModel::class.java)
|
||||||
viewModel.initWithContext(context!!)
|
|
||||||
(view as? RecyclerView)?.let {
|
emojiRecyclerAdapter.reactionClickListener = this
|
||||||
it.adapter = viewModel.adapter
|
emojiRecyclerAdapter.interactionListener = this
|
||||||
it.adapter?.notifyDataSetChanged()
|
|
||||||
|
emojiRecyclerView.adapter = emojiRecyclerAdapter
|
||||||
|
|
||||||
|
viewModel.moveToSection.observe(viewLifecycleOwner) { section ->
|
||||||
|
emojiRecyclerAdapter.scrollToSection(section)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun firstVisibleSectionChange(section: Int) {
|
||||||
|
viewModel.setCurrentSection(section)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onReactionSelected(reaction: String) {
|
||||||
|
viewModel.onReactionSelected(reaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
emojiRecyclerView.cleanup()
|
||||||
|
emojiRecyclerAdapter.reactionClickListener = null
|
||||||
|
emojiRecyclerAdapter.interactionListener = null
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package im.vector.riotx.features.reactions
|
package im.vector.riotx.features.reactions
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import im.vector.riotx.core.utils.LiveEvent
|
import im.vector.riotx.core.utils.LiveEvent
|
||||||
@ -23,36 +22,26 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
class EmojiChooserViewModel @Inject constructor() : ViewModel() {
|
class EmojiChooserViewModel @Inject constructor() : ViewModel() {
|
||||||
|
|
||||||
var adapter: EmojiRecyclerAdapter? = null
|
|
||||||
val emojiSourceLiveData: MutableLiveData<EmojiDataSource> = MutableLiveData()
|
|
||||||
|
|
||||||
val navigateEvent: MutableLiveData<LiveEvent<String>> = MutableLiveData()
|
val navigateEvent: MutableLiveData<LiveEvent<String>> = MutableLiveData()
|
||||||
var selectedReaction: String? = null
|
var selectedReaction: String? = null
|
||||||
var eventId: String? = null
|
var eventId: String? = null
|
||||||
|
|
||||||
val currentSection: MutableLiveData<Int> = MutableLiveData()
|
val currentSection: MutableLiveData<Int> = MutableLiveData()
|
||||||
|
val moveToSection: MutableLiveData<Int> = MutableLiveData()
|
||||||
|
|
||||||
var reactionClickListener = object : ReactionClickListener {
|
fun onReactionSelected(reaction: String) {
|
||||||
override fun onReactionSelected(reaction: String) {
|
|
||||||
selectedReaction = reaction
|
selectedReaction = reaction
|
||||||
navigateEvent.value = LiveEvent(NAVIGATE_FINISH)
|
navigateEvent.value = LiveEvent(NAVIGATE_FINISH)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fun initWithContext(context: Context) {
|
// Called by the Fragment, when the List is scrolled
|
||||||
// TODO load async
|
fun setCurrentSection(section: Int) {
|
||||||
val emojiDataSource = EmojiDataSource(context)
|
|
||||||
emojiSourceLiveData.value = emojiDataSource
|
|
||||||
adapter = EmojiRecyclerAdapter(emojiDataSource, reactionClickListener)
|
|
||||||
adapter?.interactionListener = object : EmojiRecyclerAdapter.InteractionListener {
|
|
||||||
override fun firstVisibleSectionChange(section: Int) {
|
|
||||||
currentSection.value = section
|
currentSection.value = section
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun scrollToSection(sectionIndex: Int) {
|
// Called by the Activity, when a tab item is clicked
|
||||||
adapter?.scrollToSection(sectionIndex)
|
fun scrollToSection(section: Int) {
|
||||||
|
moveToSection.value = section
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -1,91 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.reactions
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import com.squareup.moshi.Json
|
|
||||||
import com.squareup.moshi.JsonClass
|
|
||||||
import com.squareup.moshi.Moshi
|
|
||||||
import im.vector.riotx.R
|
|
||||||
|
|
||||||
class EmojiDataSource(val context: Context) {
|
|
||||||
|
|
||||||
var rawData: EmojiData? = null
|
|
||||||
|
|
||||||
init {
|
|
||||||
context.resources.openRawResource(R.raw.emoji_picker_datasource).use { input ->
|
|
||||||
val moshi = Moshi.Builder().build()
|
|
||||||
val jsonAdapter = moshi.adapter(EmojiData::class.java)
|
|
||||||
val inputAsString = input.bufferedReader().use { it.readText() }
|
|
||||||
this.rawData = jsonAdapter.fromJson(inputAsString)
|
|
||||||
// this.rawData = mb.fr(InputStreamReader(it), EmojiData::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@JsonClass(generateAdapter = true)
|
|
||||||
data class EmojiData(val categories: List<EmojiCategory>,
|
|
||||||
val emojis: Map<String, EmojiItem>,
|
|
||||||
val aliases: Map<String, String>)
|
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
|
||||||
data class EmojiCategory(val id: String, val name: String, val emojis: List<String>)
|
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
|
||||||
data class EmojiItem(
|
|
||||||
@Json(name = "a") val name: String,
|
|
||||||
@Json(name = "b") val unicode: String,
|
|
||||||
@Json(name = "j") val keywords: List<String>?,
|
|
||||||
val k: List<String>?) {
|
|
||||||
|
|
||||||
var _emojiText: String? = null
|
|
||||||
|
|
||||||
fun emojiString() : String {
|
|
||||||
if (_emojiText == null) {
|
|
||||||
val utf8Text = unicode.split("-").joinToString("") { "\\u$it" } // "\u0048\u0065\u006C\u006C\u006F World"
|
|
||||||
_emojiText = fromUnicode(utf8Text)
|
|
||||||
}
|
|
||||||
return _emojiText!!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun fromUnicode(unicode: String): String {
|
|
||||||
val str = unicode.replace("\\", "")
|
|
||||||
val arr = str.split("u".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
|
||||||
val text = StringBuffer()
|
|
||||||
for (i in 1 until arr.size) {
|
|
||||||
val hexVal = Integer.parseInt(arr[i], 16)
|
|
||||||
text.append(Character.toChars(hexVal))
|
|
||||||
}
|
|
||||||
return text.toString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// name: 'a',
|
|
||||||
// unified: 'b',
|
|
||||||
// non_qualified: 'c',
|
|
||||||
// has_img_apple: 'd',
|
|
||||||
// has_img_google: 'e',
|
|
||||||
// has_img_twitter: 'f',
|
|
||||||
// has_img_emojione: 'g',
|
|
||||||
// has_img_facebook: 'h',
|
|
||||||
// has_img_messenger: 'i',
|
|
||||||
// keywords: 'j',
|
|
||||||
// sheet: 'k',
|
|
||||||
// emoticons: 'l',
|
|
||||||
// text: 'm',
|
|
||||||
// short_names: 'n',
|
|
||||||
// added_in: 'o',
|
|
||||||
}
|
|
@ -35,6 +35,7 @@ import im.vector.riotx.R
|
|||||||
import im.vector.riotx.core.di.ScreenComponent
|
import im.vector.riotx.core.di.ScreenComponent
|
||||||
import im.vector.riotx.core.extensions.observeEvent
|
import im.vector.riotx.core.extensions.observeEvent
|
||||||
import im.vector.riotx.core.platform.VectorBaseActivity
|
import im.vector.riotx.core.platform.VectorBaseActivity
|
||||||
|
import im.vector.riotx.features.reactions.data.EmojiDataSource
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
import kotlinx.android.synthetic.main.activity_emoji_reaction_picker.*
|
import kotlinx.android.synthetic.main.activity_emoji_reaction_picker.*
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
@ -44,7 +45,6 @@ import javax.inject.Inject
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* TODO: Loading indicator while getting emoji data source?
|
* TODO: Loading indicator while getting emoji data source?
|
||||||
* TODO: migrate to MvRx
|
|
||||||
* TODO: Finish Refactor to vector base activity
|
* TODO: Finish Refactor to vector base activity
|
||||||
*/
|
*/
|
||||||
class EmojiReactionPickerActivity : VectorBaseActivity(),
|
class EmojiReactionPickerActivity : VectorBaseActivity(),
|
||||||
@ -54,13 +54,15 @@ class EmojiReactionPickerActivity : VectorBaseActivity(),
|
|||||||
|
|
||||||
lateinit var viewModel: EmojiChooserViewModel
|
lateinit var viewModel: EmojiChooserViewModel
|
||||||
|
|
||||||
override fun getMenuRes(): Int = R.menu.menu_emoji_reaction_picker
|
override fun getMenuRes() = R.menu.menu_emoji_reaction_picker
|
||||||
|
|
||||||
override fun getLayoutRes(): Int = R.layout.activity_emoji_reaction_picker
|
override fun getLayoutRes() = R.layout.activity_emoji_reaction_picker
|
||||||
|
|
||||||
override fun getTitleRes(): Int = R.string.title_activity_emoji_reaction_picker
|
override fun getTitleRes() = R.string.title_activity_emoji_reaction_picker
|
||||||
|
|
||||||
|
@Inject lateinit var emojiSearchResultViewModelFactory: EmojiSearchResultViewModel.Factory
|
||||||
@Inject lateinit var emojiCompatFontProvider: EmojiCompatFontProvider
|
@Inject lateinit var emojiCompatFontProvider: EmojiCompatFontProvider
|
||||||
|
@Inject lateinit var emojiDataSource: EmojiDataSource
|
||||||
|
|
||||||
private val searchResultViewModel: EmojiSearchResultViewModel by viewModel()
|
private val searchResultViewModel: EmojiSearchResultViewModel by viewModel()
|
||||||
|
|
||||||
@ -93,13 +95,11 @@ class EmojiReactionPickerActivity : VectorBaseActivity(),
|
|||||||
|
|
||||||
viewModel.eventId = intent.getStringExtra(EXTRA_EVENT_ID)
|
viewModel.eventId = intent.getStringExtra(EXTRA_EVENT_ID)
|
||||||
|
|
||||||
viewModel.emojiSourceLiveData.observe(this, Observer {
|
emojiDataSource.rawData.categories.forEach { category ->
|
||||||
it.rawData?.categories?.let { categories ->
|
|
||||||
for (category in categories) {
|
|
||||||
val s = category.emojis[0]
|
val s = category.emojis[0]
|
||||||
tabLayout.newTab()
|
tabLayout.newTab()
|
||||||
.also { tab ->
|
.also { tab ->
|
||||||
tab.text = it.rawData!!.emojis[s]!!.emojiString()
|
tab.text = emojiDataSource.rawData.emojis[s]!!.emoji
|
||||||
tab.contentDescription = category.name
|
tab.contentDescription = category.name
|
||||||
}
|
}
|
||||||
.also { tab ->
|
.also { tab ->
|
||||||
@ -107,8 +107,6 @@ class EmojiReactionPickerActivity : VectorBaseActivity(),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
tabLayout.addOnTabSelectedListener(tabLayoutSelectionListener)
|
tabLayout.addOnTabSelectedListener(tabLayoutSelectionListener)
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
viewModel.currentSection.observe(this, Observer { section ->
|
viewModel.currentSection.observe(this, Observer { section ->
|
||||||
section?.let {
|
section?.let {
|
||||||
@ -136,7 +134,6 @@ class EmojiReactionPickerActivity : VectorBaseActivity(),
|
|||||||
|
|
||||||
override fun compatibilityFontUpdate(typeface: Typeface?) {
|
override fun compatibilityFontUpdate(typeface: Typeface?) {
|
||||||
EmojiDrawView.configureTextPaint(this, typeface)
|
EmojiDrawView.configureTextPaint(this, typeface)
|
||||||
searchResultViewModel.dataSource
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
@ -206,13 +203,19 @@ class EmojiReactionPickerActivity : VectorBaseActivity(),
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
const val EXTRA_EVENT_ID = "EXTRA_EVENT_ID"
|
private const val EXTRA_EVENT_ID = "EXTRA_EVENT_ID"
|
||||||
const val EXTRA_REACTION_RESULT = "EXTRA_REACTION_RESULT"
|
private const val EXTRA_REACTION_RESULT = "EXTRA_REACTION_RESULT"
|
||||||
|
|
||||||
fun intent(context: Context, eventId: String): Intent {
|
fun intent(context: Context, eventId: String): Intent {
|
||||||
val intent = Intent(context, EmojiReactionPickerActivity::class.java)
|
val intent = Intent(context, EmojiReactionPickerActivity::class.java)
|
||||||
intent.putExtra(EXTRA_EVENT_ID, eventId)
|
intent.putExtra(EXTRA_EVENT_ID, eventId)
|
||||||
return intent
|
return intent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getOutput(data: Intent): Pair<String, String>? {
|
||||||
|
val eventId = data.getStringExtra(EXTRA_EVENT_ID) ?: return null
|
||||||
|
val reaction = data.getStringExtra(EXTRA_REACTION_RESULT) ?: return null
|
||||||
|
return eventId to reaction
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,22 +30,25 @@ import androidx.recyclerview.widget.RecyclerView
|
|||||||
import androidx.transition.AutoTransition
|
import androidx.transition.AutoTransition
|
||||||
import androidx.transition.TransitionManager
|
import androidx.transition.TransitionManager
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
|
import im.vector.riotx.features.reactions.data.EmojiDataSource
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import javax.inject.Inject
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* TODO: Configure Span using available width and emoji size
|
* TODO: Configure Span using available width and emoji size
|
||||||
* TODO: Search
|
|
||||||
* TODO: Performances
|
* TODO: Performances
|
||||||
* TODO: Scroll to section - Find a way to snap section to the top
|
* TODO: Scroll to section - Find a way to snap section to the top
|
||||||
*/
|
*/
|
||||||
class EmojiRecyclerAdapter(private val dataSource: EmojiDataSource? = null,
|
class EmojiRecyclerAdapter @Inject constructor(
|
||||||
private var reactionClickListener: ReactionClickListener?) :
|
private val dataSource: EmojiDataSource
|
||||||
|
) :
|
||||||
RecyclerView.Adapter<EmojiRecyclerAdapter.ViewHolder>() {
|
RecyclerView.Adapter<EmojiRecyclerAdapter.ViewHolder>() {
|
||||||
|
|
||||||
|
var reactionClickListener: ReactionClickListener? = null
|
||||||
var interactionListener: InteractionListener? = null
|
var interactionListener: InteractionListener? = null
|
||||||
private var mRecyclerView: RecyclerView? = null
|
private var mRecyclerView: RecyclerView? = null
|
||||||
|
|
||||||
@ -66,13 +69,12 @@ class EmojiRecyclerAdapter(private val dataSource: EmojiDataSource? = null,
|
|||||||
private val itemClickListener = View.OnClickListener { view ->
|
private val itemClickListener = View.OnClickListener { view ->
|
||||||
mRecyclerView?.getChildLayoutPosition(view)?.let { itemPosition ->
|
mRecyclerView?.getChildLayoutPosition(view)?.let { itemPosition ->
|
||||||
if (itemPosition != RecyclerView.NO_POSITION) {
|
if (itemPosition != RecyclerView.NO_POSITION) {
|
||||||
val categories = dataSource?.rawData?.categories ?: return@OnClickListener
|
|
||||||
val sectionNumber = getSectionForAbsoluteIndex(itemPosition)
|
val sectionNumber = getSectionForAbsoluteIndex(itemPosition)
|
||||||
if (!isSection(itemPosition)) {
|
if (!isSection(itemPosition)) {
|
||||||
val sectionMojis = categories[sectionNumber].emojis
|
val sectionMojis = dataSource.rawData.categories[sectionNumber].emojis
|
||||||
val sectionOffset = getSectionOffset(sectionNumber)
|
val sectionOffset = getSectionOffset(sectionNumber)
|
||||||
val emoji = sectionMojis[itemPosition - sectionOffset]
|
val emoji = sectionMojis[itemPosition - sectionOffset]
|
||||||
val item = dataSource.rawData!!.emojis.getValue(emoji).emojiString()
|
val item = dataSource.rawData.emojis.getValue(emoji).emoji
|
||||||
reactionClickListener?.onReactionSelected(item)
|
reactionClickListener?.onReactionSelected(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -113,7 +115,7 @@ class EmojiRecyclerAdapter(private val dataSource: EmojiDataSource? = null,
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun scrollToSection(section: Int) {
|
fun scrollToSection(section: Int) {
|
||||||
if (section < 0 || section >= dataSource?.rawData?.categories?.size ?: 0) {
|
if (section < 0 || section >= dataSource.rawData.categories.size) {
|
||||||
// ignore
|
// ignore
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -145,15 +147,13 @@ class EmojiRecyclerAdapter(private val dataSource: EmojiDataSource? = null,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun isSection(position: Int): Boolean {
|
private fun isSection(position: Int): Boolean {
|
||||||
dataSource?.rawData?.categories?.let { categories ->
|
|
||||||
var sectionOffset = 1
|
var sectionOffset = 1
|
||||||
var lastItemInSection: Int
|
var lastItemInSection: Int
|
||||||
for (category in categories) {
|
dataSource.rawData.categories.forEach { category ->
|
||||||
lastItemInSection = sectionOffset + category.emojis.size - 1
|
lastItemInSection = sectionOffset + category.emojis.size - 1
|
||||||
if (position == sectionOffset - 1) return true
|
if (position == sectionOffset - 1) return true
|
||||||
sectionOffset = lastItemInSection + 2
|
sectionOffset = lastItemInSection + 2
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,14 +161,12 @@ class EmojiRecyclerAdapter(private val dataSource: EmojiDataSource? = null,
|
|||||||
var sectionOffset = 1
|
var sectionOffset = 1
|
||||||
var lastItemInSection: Int
|
var lastItemInSection: Int
|
||||||
var index = 0
|
var index = 0
|
||||||
dataSource?.rawData?.categories?.let {
|
dataSource.rawData.categories.forEach { category ->
|
||||||
for (category in it) {
|
|
||||||
lastItemInSection = sectionOffset + category.emojis.size - 1
|
lastItemInSection = sectionOffset + category.emojis.size - 1
|
||||||
if (position <= lastItemInSection) return index
|
if (position <= lastItemInSection) return index
|
||||||
sectionOffset = lastItemInSection + 2
|
sectionOffset = lastItemInSection + 2
|
||||||
index++
|
index++
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return index
|
return index
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,27 +174,24 @@ class EmojiRecyclerAdapter(private val dataSource: EmojiDataSource? = null,
|
|||||||
// Todo cache this for fast access
|
// Todo cache this for fast access
|
||||||
var sectionOffset = 1
|
var sectionOffset = 1
|
||||||
var lastItemInSection: Int
|
var lastItemInSection: Int
|
||||||
dataSource?.rawData?.categories?.let {
|
dataSource.rawData.categories.forEachIndexed { index, category ->
|
||||||
for ((index, category) in it.withIndex()) {
|
|
||||||
lastItemInSection = sectionOffset + category.emojis.size - 1
|
lastItemInSection = sectionOffset + category.emojis.size - 1
|
||||||
if (section == index) return sectionOffset
|
if (section == index) return sectionOffset
|
||||||
sectionOffset = lastItemInSection + 2
|
sectionOffset = lastItemInSection + 2
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return sectionOffset
|
return sectionOffset
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
beginTraceSession("MyAdapter.onBindViewHolder")
|
beginTraceSession("MyAdapter.onBindViewHolder")
|
||||||
dataSource?.rawData?.categories?.let { categories ->
|
|
||||||
val sectionNumber = getSectionForAbsoluteIndex(position)
|
val sectionNumber = getSectionForAbsoluteIndex(position)
|
||||||
if (isSection(position)) {
|
if (isSection(position)) {
|
||||||
holder.bind(categories[sectionNumber].name)
|
holder.bind(dataSource.rawData.categories[sectionNumber].name)
|
||||||
} else {
|
} else {
|
||||||
val sectionMojis = categories[sectionNumber].emojis
|
val sectionMojis = dataSource.rawData.categories[sectionNumber].emojis
|
||||||
val sectionOffset = getSectionOffset(sectionNumber)
|
val sectionOffset = getSectionOffset(sectionNumber)
|
||||||
val emoji = sectionMojis[position - sectionOffset]
|
val emoji = sectionMojis[position - sectionOffset]
|
||||||
val item = dataSource.rawData!!.emojis[emoji]!!.emojiString()
|
val item = dataSource.rawData.emojis[emoji]!!.emoji
|
||||||
(holder as EmojiViewHolder).data = item
|
(holder as EmojiViewHolder).data = item
|
||||||
if (scrollState != ScrollState.SETTLING || !isFastScroll) {
|
if (scrollState != ScrollState.SETTLING || !isFastScroll) {
|
||||||
// Log.i("PERF","Bind with draw at position:$position")
|
// Log.i("PERF","Bind with draw at position:$position")
|
||||||
@ -207,7 +202,6 @@ class EmojiRecyclerAdapter(private val dataSource: EmojiDataSource? = null,
|
|||||||
holder.bind(null)
|
holder.bind(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
endTraceSession()
|
endTraceSession()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,15 +220,8 @@ class EmojiRecyclerAdapter(private val dataSource: EmojiDataSource? = null,
|
|||||||
super.onViewRecycled(holder)
|
super.onViewRecycled(holder)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount() = dataSource.rawData.categories
|
||||||
return dataSource?.rawData?.categories?.let {
|
.sumBy { emojiCategory -> 1 /* Section */ + emojiCategory.emojis.size }
|
||||||
var count = /*number of sections*/ it.size
|
|
||||||
for (ad in it) {
|
|
||||||
count += ad.emojis.size
|
|
||||||
}
|
|
||||||
count
|
|
||||||
} ?: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
abstract class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||||
abstract fun bind(s: String?)
|
abstract fun bind(s: String?)
|
||||||
|
@ -24,9 +24,10 @@ import im.vector.riotx.core.resources.StringProvider
|
|||||||
import im.vector.riotx.core.ui.list.genericFooterItem
|
import im.vector.riotx.core.ui.list.genericFooterItem
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class EmojiSearchResultController @Inject constructor(val stringProvider: StringProvider,
|
class EmojiSearchResultController @Inject constructor(
|
||||||
private val fontProvider: EmojiCompatFontProvider)
|
private val stringProvider: StringProvider,
|
||||||
: TypedEpoxyController<EmojiSearchResultViewState>() {
|
private val fontProvider: EmojiCompatFontProvider
|
||||||
|
) : TypedEpoxyController<EmojiSearchResultViewState>() {
|
||||||
|
|
||||||
var emojiTypeface: Typeface? = fontProvider.typeface
|
var emojiTypeface: Typeface? = fontProvider.typeface
|
||||||
|
|
||||||
|
@ -17,43 +17,42 @@ package im.vector.riotx.features.reactions
|
|||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.recyclerview.widget.DividerItemDecoration
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import com.airbnb.mvrx.activityViewModel
|
import com.airbnb.mvrx.activityViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
|
import im.vector.riotx.core.extensions.cleanup
|
||||||
|
import im.vector.riotx.core.extensions.configureWith
|
||||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||||
import im.vector.riotx.core.utils.LiveEvent
|
import im.vector.riotx.core.utils.LiveEvent
|
||||||
import kotlinx.android.synthetic.main.fragment_generic_recycler_epoxy.*
|
import kotlinx.android.synthetic.main.fragment_generic_recycler.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class EmojiSearchResultFragment @Inject constructor(
|
class EmojiSearchResultFragment @Inject constructor(
|
||||||
private val epoxyController: EmojiSearchResultController
|
private val epoxyController: EmojiSearchResultController
|
||||||
) : VectorBaseFragment() {
|
) : VectorBaseFragment(), ReactionClickListener {
|
||||||
|
|
||||||
override fun getLayoutResId(): Int = R.layout.fragment_generic_recycler_epoxy
|
override fun getLayoutResId() = R.layout.fragment_generic_recycler
|
||||||
|
|
||||||
val viewModel: EmojiSearchResultViewModel by activityViewModel()
|
private val viewModel: EmojiSearchResultViewModel by activityViewModel()
|
||||||
|
|
||||||
var sharedViewModel: EmojiChooserViewModel? = null
|
private lateinit var sharedViewModel: EmojiChooserViewModel
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
sharedViewModel = activityViewModelProvider.get(EmojiChooserViewModel::class.java)
|
sharedViewModel = activityViewModelProvider.get(EmojiChooserViewModel::class.java)
|
||||||
|
epoxyController.listener = this
|
||||||
|
recyclerView.configureWith(epoxyController, showDivider = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
epoxyController.listener = null
|
||||||
|
recyclerView.cleanup()
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
epoxyController.listener = object : ReactionClickListener {
|
|
||||||
override fun onReactionSelected(reaction: String) {
|
override fun onReactionSelected(reaction: String) {
|
||||||
sharedViewModel?.selectedReaction = reaction
|
sharedViewModel.selectedReaction = reaction
|
||||||
sharedViewModel?.navigateEvent?.value = LiveEvent(EmojiChooserViewModel.NAVIGATE_FINISH)
|
sharedViewModel.navigateEvent.value = LiveEvent(EmojiChooserViewModel.NAVIGATE_FINISH)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val lmgr = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
|
|
||||||
epoxyRecyclerView.layoutManager = lmgr
|
|
||||||
val dividerItemDecoration = DividerItemDecoration(epoxyRecyclerView.context, lmgr.orientation)
|
|
||||||
epoxyRecyclerView.addItemDecoration(dividerItemDecoration)
|
|
||||||
epoxyRecyclerView.setController(epoxyController)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun invalidate() = withState(viewModel) { state ->
|
override fun invalidate() = withState(viewModel) { state ->
|
||||||
|
@ -22,12 +22,14 @@ import com.airbnb.epoxy.EpoxyModelClass
|
|||||||
import com.airbnb.epoxy.EpoxyModelWithHolder
|
import com.airbnb.epoxy.EpoxyModelWithHolder
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
|
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
|
||||||
|
import im.vector.riotx.core.extensions.setTextOrHide
|
||||||
|
import im.vector.riotx.features.reactions.data.EmojiItem
|
||||||
|
|
||||||
@EpoxyModelClass(layout = R.layout.item_emoji_result)
|
@EpoxyModelClass(layout = R.layout.item_emoji_result)
|
||||||
abstract class EmojiSearchResultItem : EpoxyModelWithHolder<EmojiSearchResultItem.Holder>() {
|
abstract class EmojiSearchResultItem : EpoxyModelWithHolder<EmojiSearchResultItem.Holder>() {
|
||||||
|
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute
|
||||||
lateinit var emojiItem: EmojiDataSource.EmojiItem
|
lateinit var emojiItem: EmojiItem
|
||||||
|
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute
|
||||||
var currentQuery: String? = null
|
var currentQuery: String? = null
|
||||||
@ -41,12 +43,12 @@ abstract class EmojiSearchResultItem : EpoxyModelWithHolder<EmojiSearchResultIte
|
|||||||
override fun bind(holder: Holder) {
|
override fun bind(holder: Holder) {
|
||||||
super.bind(holder)
|
super.bind(holder)
|
||||||
// TODO use query string to highlight the matched query in name and keywords?
|
// TODO use query string to highlight the matched query in name and keywords?
|
||||||
holder.emojiText.text = emojiItem.emojiString()
|
holder.emojiText.text = emojiItem.emoji
|
||||||
holder.emojiText.typeface = emojiTypeFace ?: Typeface.DEFAULT
|
holder.emojiText.typeface = emojiTypeFace ?: Typeface.DEFAULT
|
||||||
holder.emojiNameText.text = emojiItem.name
|
holder.emojiNameText.text = emojiItem.name
|
||||||
holder.emojiKeywordText.text = emojiItem.keywords?.joinToString(", ")
|
holder.emojiKeywordText.setTextOrHide(emojiItem.keywords.joinToString())
|
||||||
holder.view.setOnClickListener {
|
holder.view.setOnClickListener {
|
||||||
onClickListener?.onReactionSelected(emojiItem.emojiString())
|
onClickListener?.onReactionSelected(emojiItem.emoji)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,19 +15,39 @@
|
|||||||
*/
|
*/
|
||||||
package im.vector.riotx.features.reactions
|
package im.vector.riotx.features.reactions
|
||||||
|
|
||||||
|
import com.airbnb.mvrx.ActivityViewModelContext
|
||||||
import com.airbnb.mvrx.MvRxState
|
import com.airbnb.mvrx.MvRxState
|
||||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||||
import com.airbnb.mvrx.ViewModelContext
|
import com.airbnb.mvrx.ViewModelContext
|
||||||
|
import com.squareup.inject.assisted.Assisted
|
||||||
|
import com.squareup.inject.assisted.AssistedInject
|
||||||
import im.vector.riotx.core.platform.VectorViewModel
|
import im.vector.riotx.core.platform.VectorViewModel
|
||||||
|
import im.vector.riotx.features.reactions.data.EmojiDataSource
|
||||||
|
import im.vector.riotx.features.reactions.data.EmojiItem
|
||||||
|
|
||||||
data class EmojiSearchResultViewState(
|
data class EmojiSearchResultViewState(
|
||||||
val query: String = "",
|
val query: String = "",
|
||||||
val results: List<EmojiDataSource.EmojiItem> = emptyList()
|
val results: List<EmojiItem> = emptyList()
|
||||||
) : MvRxState
|
) : MvRxState
|
||||||
|
|
||||||
class EmojiSearchResultViewModel(val dataSource: EmojiDataSource, initialState: EmojiSearchResultViewState)
|
class EmojiSearchResultViewModel @AssistedInject constructor(
|
||||||
|
@Assisted initialState: EmojiSearchResultViewState,
|
||||||
|
private val dataSource: EmojiDataSource)
|
||||||
: VectorViewModel<EmojiSearchResultViewState, EmojiSearchAction>(initialState) {
|
: VectorViewModel<EmojiSearchResultViewState, EmojiSearchAction>(initialState) {
|
||||||
|
|
||||||
|
@AssistedInject.Factory
|
||||||
|
interface Factory {
|
||||||
|
fun create(initialState: EmojiSearchResultViewState): EmojiSearchResultViewModel
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object : MvRxViewModelFactory<EmojiSearchResultViewModel, EmojiSearchResultViewState> {
|
||||||
|
|
||||||
|
override fun create(viewModelContext: ViewModelContext, state: EmojiSearchResultViewState): EmojiSearchResultViewModel? {
|
||||||
|
val activity: EmojiReactionPickerActivity = (viewModelContext as ActivityViewModelContext).activity()
|
||||||
|
return activity.emojiSearchResultViewModelFactory.create(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun handle(action: EmojiSearchAction) {
|
override fun handle(action: EmojiSearchAction) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is EmojiSearchAction.UpdateQuery -> updateQuery(action)
|
is EmojiSearchAction.UpdateQuery -> updateQuery(action)
|
||||||
@ -35,26 +55,27 @@ class EmojiSearchResultViewModel(val dataSource: EmojiDataSource, initialState:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateQuery(action: EmojiSearchAction.UpdateQuery) {
|
private fun updateQuery(action: EmojiSearchAction.UpdateQuery) {
|
||||||
|
val words = action.queryString.split("\\s".toRegex())
|
||||||
setState {
|
setState {
|
||||||
copy(
|
copy(
|
||||||
query = action.queryString,
|
query = action.queryString,
|
||||||
results = dataSource.rawData?.emojis?.toList()
|
// First add emojis with name matching query, sorted by name
|
||||||
?.map { it.second }
|
// Then emojis with keyword matching any of the word in the query, sorted by name
|
||||||
?.filter {
|
results = dataSource.rawData.emojis
|
||||||
it.name.contains(action.queryString, true)
|
.values
|
||||||
|| action.queryString.split("\\s".toRegex()).fold(true, { prev, q ->
|
.filter { emojiItem ->
|
||||||
prev && (it.keywords?.any { it.contains(q, true) } ?: false)
|
emojiItem.name.contains(action.queryString, true)
|
||||||
|
}
|
||||||
|
.sortedBy { it.name }
|
||||||
|
+ dataSource.rawData.emojis
|
||||||
|
.values
|
||||||
|
.filter { emojiItem ->
|
||||||
|
words.fold(true, { prev, word ->
|
||||||
|
prev && emojiItem.keywords.any { keyword -> keyword.contains(word, true) }
|
||||||
})
|
})
|
||||||
} ?: emptyList()
|
}
|
||||||
|
.sortedBy { it.name }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object : MvRxViewModelFactory<EmojiSearchResultViewModel, EmojiSearchResultViewState> {
|
|
||||||
|
|
||||||
override fun create(viewModelContext: ViewModelContext, state: EmojiSearchResultViewState): EmojiSearchResultViewModel? {
|
|
||||||
// TODO get the data source from activity? share it with other fragment
|
|
||||||
return EmojiSearchResultViewModel(EmojiDataSource(viewModelContext.activity), state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* 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.reactions.data
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class EmojiCategory(
|
||||||
|
@Json(name = "id") val id: String,
|
||||||
|
@Json(name = "name") val name: String,
|
||||||
|
@Json(name = "emojis") val emojis: List<String>
|
||||||
|
)
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* 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.reactions.data
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class EmojiData(
|
||||||
|
@Json(name = "categories") val categories: List<EmojiCategory>,
|
||||||
|
@Json(name = "emojis") val emojis: Map<String, EmojiItem>,
|
||||||
|
@Json(name = "aliases") val aliases: Map<String, String>
|
||||||
|
)
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* 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.reactions.data
|
||||||
|
|
||||||
|
import android.content.res.Resources
|
||||||
|
import com.squareup.moshi.Moshi
|
||||||
|
import im.vector.riotx.R
|
||||||
|
import im.vector.riotx.core.di.ScreenScope
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ScreenScope
|
||||||
|
class EmojiDataSource @Inject constructor(
|
||||||
|
resources: Resources
|
||||||
|
) {
|
||||||
|
val rawData = resources.openRawResource(R.raw.emoji_picker_datasource)
|
||||||
|
.use { input ->
|
||||||
|
Moshi.Builder()
|
||||||
|
.build()
|
||||||
|
.adapter(EmojiData::class.java)
|
||||||
|
.fromJson(input.bufferedReader().use { it.readText() })
|
||||||
|
}
|
||||||
|
?: EmojiData(emptyList(), emptyMap(), emptyMap())
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* 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.reactions.data
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
/**
|
||||||
|
* name: 'a',
|
||||||
|
* unified: 'b',
|
||||||
|
* non_qualified: 'c',
|
||||||
|
* has_img_apple: 'd',
|
||||||
|
* has_img_google: 'e',
|
||||||
|
* has_img_twitter: 'f',
|
||||||
|
* has_img_emojione: 'g',
|
||||||
|
* has_img_facebook: 'h',
|
||||||
|
* has_img_messenger: 'i',
|
||||||
|
* keywords: 'j',
|
||||||
|
* sheet: 'k',
|
||||||
|
* emoticons: 'l',
|
||||||
|
* text: 'm',
|
||||||
|
* short_names: 'n',
|
||||||
|
* added_in: 'o'
|
||||||
|
*/
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class EmojiItem(
|
||||||
|
@Json(name = "a") val name: String,
|
||||||
|
@Json(name = "b") val unicode: String,
|
||||||
|
@Json(name = "j") val keywords: List<String> = emptyList()
|
||||||
|
) {
|
||||||
|
// Cannot be private...
|
||||||
|
var cache: String? = null
|
||||||
|
|
||||||
|
val emoji: String
|
||||||
|
get() {
|
||||||
|
cache?.let { return it }
|
||||||
|
|
||||||
|
// "\u0048\u0065\u006C\u006C\u006F World"
|
||||||
|
val utf8Text = unicode
|
||||||
|
.split("-")
|
||||||
|
.joinToString("") { "\\u$it" }
|
||||||
|
return fromUnicode(utf8Text)
|
||||||
|
.also { cache = it }
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private fun fromUnicode(unicode: String): String {
|
||||||
|
val arr = unicode
|
||||||
|
.replace("\\", "")
|
||||||
|
.split("u".toRegex())
|
||||||
|
.dropLastWhile { it.isEmpty() }
|
||||||
|
return buildString {
|
||||||
|
for (i in 1 until arr.size) {
|
||||||
|
val hexVal = Integer.parseInt(arr[i], 16)
|
||||||
|
append(Character.toChars(hexVal))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -19,7 +19,6 @@ package im.vector.riotx.features.roomdirectory
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import com.airbnb.epoxy.EpoxyVisibilityTracker
|
import com.airbnb.epoxy.EpoxyVisibilityTracker
|
||||||
import com.airbnb.mvrx.activityViewModel
|
import com.airbnb.mvrx.activityViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
@ -28,6 +27,8 @@ import com.jakewharton.rxbinding3.appcompat.queryTextChanges
|
|||||||
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
|
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.error.ErrorFormatter
|
import im.vector.riotx.core.error.ErrorFormatter
|
||||||
|
import im.vector.riotx.core.extensions.cleanup
|
||||||
|
import im.vector.riotx.core.extensions.configureWith
|
||||||
import im.vector.riotx.core.extensions.observeEvent
|
import im.vector.riotx.core.extensions.observeEvent
|
||||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||||
import io.reactivex.rxkotlin.subscribeBy
|
import io.reactivex.rxkotlin.subscribeBy
|
||||||
@ -62,6 +63,9 @@ class PublicRoomsFragment @Inject constructor(
|
|||||||
it.setDisplayHomeAsUpEnabled(true)
|
it.setDisplayHomeAsUpEnabled(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sharedActionViewModel = activityViewModelProvider.get(RoomDirectorySharedActionViewModel::class.java)
|
||||||
|
setupRecyclerView()
|
||||||
|
|
||||||
publicRoomsFilter.queryTextChanges()
|
publicRoomsFilter.queryTextChanges()
|
||||||
.debounce(500, TimeUnit.MILLISECONDS)
|
.debounce(500, TimeUnit.MILLISECONDS)
|
||||||
.subscribeBy {
|
.subscribeBy {
|
||||||
@ -79,6 +83,12 @@ class PublicRoomsFragment @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
publicRoomsController.callback = null
|
||||||
|
publicRoomsList.cleanup()
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
return when (item.itemId) {
|
return when (item.itemId) {
|
||||||
R.id.menu_room_directory_change_protocol -> {
|
R.id.menu_room_directory_change_protocol -> {
|
||||||
@ -90,22 +100,11 @@ class PublicRoomsFragment @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
|
||||||
super.onActivityCreated(savedInstanceState)
|
|
||||||
sharedActionViewModel = activityViewModelProvider.get(RoomDirectorySharedActionViewModel::class.java)
|
|
||||||
setupRecyclerView()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupRecyclerView() {
|
private fun setupRecyclerView() {
|
||||||
val epoxyVisibilityTracker = EpoxyVisibilityTracker()
|
val epoxyVisibilityTracker = EpoxyVisibilityTracker()
|
||||||
epoxyVisibilityTracker.attach(publicRoomsList)
|
epoxyVisibilityTracker.attach(publicRoomsList)
|
||||||
|
publicRoomsList.configureWith(publicRoomsController)
|
||||||
val layoutManager = LinearLayoutManager(context)
|
|
||||||
|
|
||||||
publicRoomsList.layoutManager = layoutManager
|
|
||||||
publicRoomsController.callback = this
|
publicRoomsController.callback = this
|
||||||
|
|
||||||
publicRoomsList.setController(publicRoomsController)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPublicRoomClicked(publicRoom: PublicRoom, joinState: JoinState) {
|
override fun onPublicRoomClicked(publicRoom: PublicRoom, joinState: JoinState) {
|
||||||
|
@ -19,11 +19,12 @@ package im.vector.riotx.features.roomdirectory.createroom
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import com.airbnb.mvrx.Success
|
import com.airbnb.mvrx.Success
|
||||||
import com.airbnb.mvrx.activityViewModel
|
import com.airbnb.mvrx.activityViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
|
import im.vector.riotx.core.extensions.cleanup
|
||||||
|
import im.vector.riotx.core.extensions.configureWith
|
||||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||||
import im.vector.riotx.features.roomdirectory.RoomDirectorySharedAction
|
import im.vector.riotx.features.roomdirectory.RoomDirectorySharedAction
|
||||||
import im.vector.riotx.features.roomdirectory.RoomDirectorySharedActionViewModel
|
import im.vector.riotx.features.roomdirectory.RoomDirectorySharedActionViewModel
|
||||||
@ -50,6 +51,12 @@ class CreateRoomFragment @Inject constructor(private val createRoomController: C
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
createRoomForm.cleanup()
|
||||||
|
createRoomController.listener = null
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
return when (item.itemId) {
|
return when (item.itemId) {
|
||||||
R.id.action_create_room -> {
|
R.id.action_create_room -> {
|
||||||
@ -62,12 +69,8 @@ class CreateRoomFragment @Inject constructor(private val createRoomController: C
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setupRecyclerView() {
|
private fun setupRecyclerView() {
|
||||||
val layoutManager = LinearLayoutManager(context)
|
createRoomForm.configureWith(createRoomController)
|
||||||
|
|
||||||
createRoomForm.layoutManager = layoutManager
|
|
||||||
createRoomController.listener = this
|
createRoomController.listener = this
|
||||||
|
|
||||||
createRoomForm.setController(createRoomController)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNameChange(newName: String) {
|
override fun onNameChange(newName: String) {
|
||||||
|
@ -19,12 +19,13 @@ package im.vector.riotx.features.roomdirectory.picker
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import com.airbnb.mvrx.activityViewModel
|
import com.airbnb.mvrx.activityViewModel
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
import im.vector.matrix.android.api.session.room.model.thirdparty.RoomDirectoryData
|
import im.vector.matrix.android.api.session.room.model.thirdparty.RoomDirectoryData
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
|
import im.vector.riotx.core.extensions.cleanup
|
||||||
|
import im.vector.riotx.core.extensions.configureWith
|
||||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||||
import im.vector.riotx.features.roomdirectory.RoomDirectoryAction
|
import im.vector.riotx.features.roomdirectory.RoomDirectoryAction
|
||||||
import im.vector.riotx.features.roomdirectory.RoomDirectorySharedAction
|
import im.vector.riotx.features.roomdirectory.RoomDirectorySharedAction
|
||||||
@ -60,6 +61,12 @@ class RoomDirectoryPickerFragment @Inject constructor(val roomDirectoryPickerVie
|
|||||||
setupRecyclerView()
|
setupRecyclerView()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
roomDirectoryPickerList.cleanup()
|
||||||
|
roomDirectoryPickerController.callback = null
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
override fun getMenuRes() = R.menu.menu_directory_server_picker
|
override fun getMenuRes() = R.menu.menu_directory_server_picker
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
@ -73,12 +80,8 @@ class RoomDirectoryPickerFragment @Inject constructor(val roomDirectoryPickerVie
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setupRecyclerView() {
|
private fun setupRecyclerView() {
|
||||||
val layoutManager = LinearLayoutManager(context)
|
roomDirectoryPickerList.configureWith(roomDirectoryPickerController)
|
||||||
|
|
||||||
roomDirectoryPickerList.layoutManager = layoutManager
|
|
||||||
roomDirectoryPickerController.callback = this
|
roomDirectoryPickerController.callback = this
|
||||||
|
|
||||||
roomDirectoryPickerList.setController(roomDirectoryPickerController)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRoomDirectoryClicked(roomDirectoryData: RoomDirectoryData) {
|
override fun onRoomDirectoryClicked(roomDirectoryData: RoomDirectoryData) {
|
||||||
|
@ -29,6 +29,7 @@ import androidx.recyclerview.widget.RecyclerView
|
|||||||
import androidx.transition.TransitionManager
|
import androidx.transition.TransitionManager
|
||||||
import butterknife.BindView
|
import butterknife.BindView
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
|
import im.vector.riotx.core.extensions.cleanup
|
||||||
import im.vector.riotx.core.platform.VectorBaseActivity
|
import im.vector.riotx.core.platform.VectorBaseActivity
|
||||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||||
import im.vector.riotx.features.rageshake.BugReporter
|
import im.vector.riotx.features.rageshake.BugReporter
|
||||||
@ -136,6 +137,11 @@ class VectorSettingsNotificationsTroubleshootFragment @Inject constructor(
|
|||||||
testManager?.runDiagnostic()
|
testManager?.runDiagnostic()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
mRecyclerView.cleanup()
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
if (resultCode == Activity.RESULT_OK && requestCode == NotificationTroubleshootTestManager.REQ_CODE_FIX) {
|
if (resultCode == Activity.RESULT_OK && requestCode == NotificationTroubleshootTestManager.REQ_CODE_FIX) {
|
||||||
testManager?.retry()
|
testManager?.retry()
|
||||||
|
@ -26,10 +26,12 @@ import com.airbnb.mvrx.fragmentViewModel
|
|||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
import im.vector.riotx.core.error.ErrorFormatter
|
import im.vector.riotx.core.error.ErrorFormatter
|
||||||
|
import im.vector.riotx.core.extensions.cleanup
|
||||||
|
import im.vector.riotx.core.extensions.configureWith
|
||||||
import im.vector.riotx.core.extensions.observeEvent
|
import im.vector.riotx.core.extensions.observeEvent
|
||||||
import im.vector.riotx.core.platform.VectorBaseActivity
|
import im.vector.riotx.core.platform.VectorBaseActivity
|
||||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||||
import kotlinx.android.synthetic.main.fragment_generic_recycler_epoxy.*
|
import kotlinx.android.synthetic.main.fragment_generic_recycler.*
|
||||||
import kotlinx.android.synthetic.main.merge_overlay_waiting_view.*
|
import kotlinx.android.synthetic.main.merge_overlay_waiting_view.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -39,7 +41,7 @@ class VectorSettingsIgnoredUsersFragment @Inject constructor(
|
|||||||
private val errorFormatter: ErrorFormatter
|
private val errorFormatter: ErrorFormatter
|
||||||
) : VectorBaseFragment(), IgnoredUsersController.Callback {
|
) : VectorBaseFragment(), IgnoredUsersController.Callback {
|
||||||
|
|
||||||
override fun getLayoutResId() = R.layout.fragment_generic_recycler_epoxy
|
override fun getLayoutResId() = R.layout.fragment_generic_recycler
|
||||||
|
|
||||||
private val ignoredUsersViewModel: IgnoredUsersViewModel by fragmentViewModel()
|
private val ignoredUsersViewModel: IgnoredUsersViewModel by fragmentViewModel()
|
||||||
|
|
||||||
@ -49,12 +51,18 @@ class VectorSettingsIgnoredUsersFragment @Inject constructor(
|
|||||||
waiting_view_status_text.setText(R.string.please_wait)
|
waiting_view_status_text.setText(R.string.please_wait)
|
||||||
waiting_view_status_text.isVisible = true
|
waiting_view_status_text.isVisible = true
|
||||||
ignoredUsersController.callback = this
|
ignoredUsersController.callback = this
|
||||||
epoxyRecyclerView.setController(ignoredUsersController)
|
recyclerView.configureWith(ignoredUsersController)
|
||||||
ignoredUsersViewModel.requestErrorLiveData.observeEvent(this) {
|
ignoredUsersViewModel.requestErrorLiveData.observeEvent(this) {
|
||||||
displayErrorDialog(it)
|
displayErrorDialog(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
ignoredUsersController.callback = null
|
||||||
|
recyclerView.cleanup()
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
|
||||||
|
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* 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.settings.push
|
||||||
|
|
||||||
|
import com.airbnb.epoxy.TypedEpoxyController
|
||||||
|
import im.vector.riotx.R
|
||||||
|
import im.vector.riotx.core.resources.StringProvider
|
||||||
|
import im.vector.riotx.core.ui.list.genericFooterItem
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class PushGateWayController @Inject constructor(
|
||||||
|
private val stringProvider: StringProvider
|
||||||
|
) : TypedEpoxyController<PushGatewayViewState>() {
|
||||||
|
|
||||||
|
override fun buildModels(data: PushGatewayViewState?) {
|
||||||
|
data?.pushGateways?.invoke()?.let { pushers ->
|
||||||
|
if (pushers.isEmpty()) {
|
||||||
|
genericFooterItem {
|
||||||
|
id("footer")
|
||||||
|
text(stringProvider.getString(R.string.settings_push_gateway_no_pushers))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pushers.forEach {
|
||||||
|
pushGatewayItem {
|
||||||
|
id("${it.pushKey}_${it.appId}")
|
||||||
|
pusher(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} ?: run {
|
||||||
|
genericFooterItem {
|
||||||
|
id("loading")
|
||||||
|
text(stringProvider.getString(R.string.loading))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -17,71 +17,43 @@
|
|||||||
package im.vector.riotx.features.settings.push
|
package im.vector.riotx.features.settings.push
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.recyclerview.widget.DividerItemDecoration
|
import android.view.View
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import com.airbnb.epoxy.TypedEpoxyController
|
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
|
import im.vector.riotx.core.extensions.cleanup
|
||||||
|
import im.vector.riotx.core.extensions.configureWith
|
||||||
import im.vector.riotx.core.platform.VectorBaseActivity
|
import im.vector.riotx.core.platform.VectorBaseActivity
|
||||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||||
import im.vector.riotx.core.resources.StringProvider
|
import kotlinx.android.synthetic.main.fragment_generic_recycler.*
|
||||||
import im.vector.riotx.core.ui.list.genericFooterItem
|
|
||||||
import kotlinx.android.synthetic.main.fragment_generic_recycler_epoxy.*
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
// Referenced in vector_settings_notifications.xml
|
// Referenced in vector_settings_notifications.xml
|
||||||
class PushGatewaysFragment @Inject constructor(
|
class PushGatewaysFragment @Inject constructor(
|
||||||
val pushGatewaysViewModelFactory: PushGatewaysViewModel.Factory
|
val pushGatewaysViewModelFactory: PushGatewaysViewModel.Factory,
|
||||||
|
private val epoxyController: PushGateWayController
|
||||||
) : VectorBaseFragment() {
|
) : VectorBaseFragment() {
|
||||||
|
|
||||||
override fun getLayoutResId(): Int = R.layout.fragment_generic_recycler_epoxy
|
override fun getLayoutResId() = R.layout.fragment_generic_recycler
|
||||||
|
|
||||||
private val viewModel: PushGatewaysViewModel by fragmentViewModel(PushGatewaysViewModel::class)
|
private val viewModel: PushGatewaysViewModel by fragmentViewModel(PushGatewaysViewModel::class)
|
||||||
private val epoxyController by lazy { PushGateWayController(StringProvider(requireContext().resources)) }
|
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
(activity as? VectorBaseActivity)?.supportActionBar?.setTitle(R.string.settings_notifications_targets)
|
(activity as? VectorBaseActivity)?.supportActionBar?.setTitle(R.string.settings_notifications_targets)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
val lmgr = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
|
recyclerView.configureWith(epoxyController, showDivider = true)
|
||||||
epoxyRecyclerView.layoutManager = lmgr
|
}
|
||||||
val dividerItemDecoration = DividerItemDecoration(epoxyRecyclerView.context,
|
|
||||||
lmgr.orientation)
|
override fun onDestroyView() {
|
||||||
epoxyRecyclerView.addItemDecoration(dividerItemDecoration)
|
recyclerView.cleanup()
|
||||||
epoxyRecyclerView.setController(epoxyController)
|
super.onDestroyView()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun invalidate() = withState(viewModel) { state ->
|
override fun invalidate() = withState(viewModel) { state ->
|
||||||
epoxyController.setData(state)
|
epoxyController.setData(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
class PushGateWayController(private val stringProvider: StringProvider) : TypedEpoxyController<PushGatewayViewState>() {
|
|
||||||
override fun buildModels(data: PushGatewayViewState?) {
|
|
||||||
data?.pushGateways?.invoke()?.let { pushers ->
|
|
||||||
if (pushers.isEmpty()) {
|
|
||||||
genericFooterItem {
|
|
||||||
id("footer")
|
|
||||||
text(stringProvider.getString(R.string.settings_push_gateway_no_pushers))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pushers.forEach {
|
|
||||||
pushGatewayItem {
|
|
||||||
id("${it.pushKey}_${it.appId}")
|
|
||||||
pusher(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} ?: run {
|
|
||||||
genericFooterItem {
|
|
||||||
id("footer")
|
|
||||||
text(stringProvider.getString(R.string.loading))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -16,23 +16,23 @@
|
|||||||
package im.vector.riotx.features.settings.push
|
package im.vector.riotx.features.settings.push
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.recyclerview.widget.DividerItemDecoration
|
import android.view.View
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import com.airbnb.epoxy.TypedEpoxyController
|
import com.airbnb.epoxy.TypedEpoxyController
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
import im.vector.riotx.R
|
import im.vector.riotx.R
|
||||||
|
import im.vector.riotx.core.extensions.cleanup
|
||||||
|
import im.vector.riotx.core.extensions.configureWith
|
||||||
import im.vector.riotx.core.platform.VectorBaseActivity
|
import im.vector.riotx.core.platform.VectorBaseActivity
|
||||||
import im.vector.riotx.core.platform.VectorBaseFragment
|
import im.vector.riotx.core.platform.VectorBaseFragment
|
||||||
import im.vector.riotx.core.resources.StringProvider
|
import im.vector.riotx.core.resources.StringProvider
|
||||||
import im.vector.riotx.core.ui.list.genericFooterItem
|
import im.vector.riotx.core.ui.list.genericFooterItem
|
||||||
import kotlinx.android.synthetic.main.fragment_generic_recycler_epoxy.*
|
import kotlinx.android.synthetic.main.fragment_generic_recycler.*
|
||||||
|
|
||||||
// Referenced in vector_settings_notifications.xml
|
// Referenced in vector_settings_notifications.xml
|
||||||
class PushRulesFragment : VectorBaseFragment() {
|
class PushRulesFragment : VectorBaseFragment() {
|
||||||
|
|
||||||
override fun getLayoutResId(): Int = R.layout.fragment_generic_recycler_epoxy
|
override fun getLayoutResId() = R.layout.fragment_generic_recycler
|
||||||
|
|
||||||
private val viewModel: PushRulesViewModel by fragmentViewModel(PushRulesViewModel::class)
|
private val viewModel: PushRulesViewModel by fragmentViewModel(PushRulesViewModel::class)
|
||||||
|
|
||||||
@ -43,14 +43,14 @@ class PushRulesFragment : VectorBaseFragment() {
|
|||||||
(activity as? VectorBaseActivity)?.supportActionBar?.setTitle(R.string.settings_push_rules)
|
(activity as? VectorBaseActivity)?.supportActionBar?.setTitle(R.string.settings_push_rules)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
val lmgr = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
|
recyclerView.configureWith(epoxyController, showDivider = true)
|
||||||
epoxyRecyclerView.layoutManager = lmgr
|
}
|
||||||
val dividerItemDecoration = DividerItemDecoration(epoxyRecyclerView.context,
|
|
||||||
lmgr.orientation)
|
override fun onDestroyView() {
|
||||||
epoxyRecyclerView.addItemDecoration(dividerItemDecoration)
|
recyclerView.cleanup()
|
||||||
epoxyRecyclerView.setController(epoxyController)
|
super.onDestroyView()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun invalidate() = withState(viewModel) { state ->
|
override fun invalidate() = withState(viewModel) { state ->
|
||||||
|
@ -50,9 +50,7 @@ class IncomingShareActivity :
|
|||||||
return supportFragmentManager.findFragmentById(R.id.shareRoomListFragmentContainer) as? RoomListFragment
|
return supportFragmentManager.findFragmentById(R.id.shareRoomListFragmentContainer) as? RoomListFragment
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getLayoutRes(): Int {
|
override fun getLayoutRes() = R.layout.activity_incoming_share
|
||||||
return R.layout.activity_incoming_share
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun injectWith(injector: ScreenComponent) {
|
override fun injectWith(injector: ScreenComponent) {
|
||||||
injector.inject(this)
|
injector.inject(this)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/emoji_recycler_view"
|
android:id="@+id/emojiRecyclerView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:scrollbars="vertical"
|
android:scrollbars="vertical"
|
||||||
@ -9,6 +9,4 @@
|
|||||||
tools:itemCount="100"
|
tools:itemCount="100"
|
||||||
tools:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
tools:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||||
tools:listitem="@layout/grid_item_emoji"
|
tools:listitem="@layout/grid_item_emoji"
|
||||||
tools:spanCount="10">
|
tools:spanCount="10" />
|
||||||
|
|
||||||
</androidx.recyclerview.widget.RecyclerView>
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<com.airbnb.epoxy.EpoxyRecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/breadcrumbsRecyclerView"
|
android:id="@+id/breadcrumbsRecyclerView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
@ -122,7 +122,7 @@
|
|||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/createDirectRoomFilterDivider" />
|
app:layout_constraintTop_toBottomOf="@id/createDirectRoomFilterDivider" />
|
||||||
|
|
||||||
<com.airbnb.epoxy.EpoxyRecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/recyclerView"
|
android:id="@+id/recyclerView"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
|
@ -89,7 +89,7 @@
|
|||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/createDirectRoomSearchByIdContainer" />
|
app:layout_constraintTop_toBottomOf="@+id/createDirectRoomSearchByIdContainer" />
|
||||||
|
|
||||||
<com.airbnb.epoxy.EpoxyRecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/recyclerView"
|
android:id="@+id/recyclerView"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
|
@ -55,7 +55,7 @@
|
|||||||
|
|
||||||
</androidx.appcompat.widget.Toolbar>
|
</androidx.appcompat.widget.Toolbar>
|
||||||
|
|
||||||
<com.airbnb.epoxy.EpoxyRecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/createRoomForm"
|
android:id="@+id/createRoomForm"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<com.airbnb.epoxy.EpoxyRecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/epoxyRecyclerView"
|
android:id="@+id/recyclerView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:itemSpacing="1dp"
|
app:itemSpacing="1dp"
|
@ -5,8 +5,8 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<com.airbnb.epoxy.EpoxyRecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/groupListEpoxyRecyclerView"
|
android:id="@+id/groupListView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:overScrollMode="always"
|
android:overScrollMode="always"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<com.airbnb.epoxy.EpoxyRecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/keysBackupSettingsRecyclerView"
|
android:id="@+id/keysBackupSettingsRecyclerView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
android:text="@string/auth_accept_policies"
|
android:text="@string/auth_accept_policies"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/loginTermsTitle" />
|
app:layout_constraintTop_toBottomOf="@+id/loginTermsTitle" />
|
||||||
|
|
||||||
<com.airbnb.epoxy.EpoxyRecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/loginTermsPolicyList"
|
android:id="@+id/loginTermsPolicyList"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<com.airbnb.epoxy.EpoxyRecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/publicRoomsList"
|
android:id="@+id/publicRoomsList"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<com.airbnb.epoxy.EpoxyRecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/roomDirectoryPickerList"
|
android:id="@+id/roomDirectoryPickerList"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
|
@ -3,49 +3,50 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?riotx_background"
|
||||||
|
android:foreground="?attr/selectableItemBackground"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:minHeight="48dp"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:paddingEnd="8dp"
|
android:paddingStart="@dimen/layout_horizontal_margin"
|
||||||
android:paddingStart="8dp"
|
android:paddingEnd="@dimen/layout_horizontal_margin">
|
||||||
android:minHeight="44dp">
|
|
||||||
|
|
||||||
<!-- size in dp, because we do not want the display to be impacted by font size setting -->
|
<!-- size in dp, because we do not want the display to be impacted by font size setting -->
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/item_emoji_tv"
|
android:id="@+id/item_emoji_tv"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:textSize="25sp"
|
android:textColor="@color/black"
|
||||||
|
android:textSize="25dp"
|
||||||
tools:ignore="SpUsage"
|
tools:ignore="SpUsage"
|
||||||
android:textColor="?android:textColorPrimary"
|
|
||||||
tools:text="@sample/reactions.json/data/reaction" />
|
tools:text="@sample/reactions.json/data/reaction" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:layout_weight="1"
|
||||||
android:layout_weight="1">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/item_emoji_name"
|
android:id="@+id/item_emoji_name"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:textColor="?riotx_text_primary"
|
||||||
android:textStyle="bold"
|
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
android:textColor="?android:textColorPrimary"
|
android:textStyle="bold"
|
||||||
tools:text="Smiley Face" />
|
tools:text="Smiley Face" />
|
||||||
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/item_emoji_keyword"
|
android:id="@+id/item_emoji_keyword"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
|
||||||
android:textSize="14sp"
|
|
||||||
android:maxLines="2"
|
android:maxLines="2"
|
||||||
android:textColor="?android:textColorPrimary"
|
android:textColor="?riotx_text_secondary"
|
||||||
tools:text="Smile, foo, bar" />
|
android:textSize="14sp"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:text="Smile, foo, bar"
|
||||||
|
tools:visibility="visible" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user