forked from GitHub-Mirror/riotX-android
Dagger: start handling app dependencies [WIP]
This commit is contained in:
parent
c2c2d0b21e
commit
9c1f870694
@ -54,7 +54,7 @@ internal class RoomMembers(private val realm: Realm,
|
||||
}
|
||||
return EventEntity
|
||||
.where(realm, roomId, EventType.STATE_ROOM_MEMBER)
|
||||
.contains(EventEntityFields.CONTENT, displayName)
|
||||
.equalTo(EventEntityFields.CONTENT, displayName)
|
||||
.distinct(EventEntityFields.STATE_KEY)
|
||||
.findAll()
|
||||
.size == 1
|
||||
|
@ -138,6 +138,7 @@ dependencies {
|
||||
def big_image_viewer_version = '1.5.6'
|
||||
def glide_version = '4.9.0'
|
||||
def moshi_version = '1.8.0'
|
||||
def daggerVersion = '2.23.1'
|
||||
|
||||
implementation project(":matrix-sdk-android")
|
||||
implementation project(":matrix-sdk-android-rx")
|
||||
@ -219,8 +220,10 @@ dependencies {
|
||||
implementation 'com.github.jaiselrahman:FilePicker:1.2.2'
|
||||
|
||||
// DI
|
||||
implementation "org.koin:koin-android:$koin_version"
|
||||
implementation "org.koin:koin-android-scope:$koin_version"
|
||||
implementation "com.google.dagger:dagger:$daggerVersion"
|
||||
kapt "com.google.dagger:dagger-compiler:$daggerVersion"
|
||||
compileOnly 'com.squareup.inject:assisted-inject-annotations-dagger2:0.4.0'
|
||||
kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.4.0'
|
||||
|
||||
// gplay flavor only
|
||||
gplayImplementation 'com.google.firebase:firebase-core:16.0.8'
|
||||
|
@ -3,9 +3,9 @@ package im.vector.riotredesign
|
||||
import android.graphics.Typeface
|
||||
import androidx.core.provider.FontsContractCompat
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
class EmojiCompatFontProvider : FontsContractCompat.FontRequestCallback() {
|
||||
class EmojiCompatFontProvider @Inject constructor(): FontsContractCompat.FontRequestCallback() {
|
||||
|
||||
var typeface: Typeface? = null
|
||||
set(value) {
|
||||
|
@ -31,67 +31,56 @@ import com.github.piasy.biv.BigImageViewer
|
||||
import com.github.piasy.biv.loader.glide.GlideImageLoader
|
||||
import com.jakewharton.threetenabp.AndroidThreeTen
|
||||
import im.vector.matrix.android.api.Matrix
|
||||
import im.vector.riotredesign.core.di.AppModule
|
||||
import im.vector.riotredesign.core.di.HasInjector
|
||||
import im.vector.riotredesign.core.di.VectorComponent
|
||||
import im.vector.riotredesign.features.configuration.VectorConfiguration
|
||||
import im.vector.riotredesign.features.crypto.keysbackup.KeysBackupModule
|
||||
import im.vector.riotredesign.features.home.HomeModule
|
||||
import im.vector.riotredesign.features.lifecycle.VectorActivityLifecycleCallbacks
|
||||
import im.vector.riotredesign.features.rageshake.VectorFileLogger
|
||||
import im.vector.riotredesign.features.rageshake.VectorUncaughtExceptionHandler
|
||||
import im.vector.riotredesign.features.roomdirectory.RoomDirectoryModule
|
||||
import org.koin.android.ext.android.inject
|
||||
import org.koin.log.EmptyLogger
|
||||
import org.koin.standalone.StandAloneContext.startKoin
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
class VectorApplication : Application() {
|
||||
class VectorApplication : Application(), HasInjector<VectorComponent> {
|
||||
|
||||
lateinit var appContext: Context
|
||||
//font thread handler
|
||||
private var mFontThreadHandler: Handler? = null
|
||||
|
||||
val vectorConfiguration: VectorConfiguration by inject()
|
||||
@Inject lateinit var vectorConfiguration: VectorConfiguration
|
||||
@Inject lateinit var emojiCompatFontProvider: EmojiCompatFontProvider
|
||||
lateinit var vectorComponent: VectorComponent
|
||||
private var fontThreadHandler: Handler? = null
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
appContext = this
|
||||
|
||||
VectorUncaughtExceptionHandler.activate(this)
|
||||
|
||||
// Log
|
||||
VectorFileLogger.init(this)
|
||||
Timber.plant(Timber.DebugTree(), VectorFileLogger)
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
Stetho.initializeWithDefaults(this)
|
||||
}
|
||||
|
||||
AndroidThreeTen.init(this)
|
||||
BigImageViewer.initialize(GlideImageLoader.with(applicationContext))
|
||||
EpoxyController.defaultDiffingHandler = EpoxyAsyncUtil.getAsyncBackgroundHandler()
|
||||
EpoxyController.defaultModelBuildingHandler = EpoxyAsyncUtil.getAsyncBackgroundHandler()
|
||||
val appModule = AppModule(applicationContext).definition
|
||||
val homeModule = HomeModule().definition
|
||||
val roomDirectoryModule = RoomDirectoryModule().definition
|
||||
val keysBackupModule = KeysBackupModule().definition
|
||||
val koin = startKoin(listOf(appModule, homeModule, roomDirectoryModule, keysBackupModule), logger = EmptyLogger())
|
||||
|
||||
Matrix.getInstance().setApplicationFlavor(BuildConfig.FLAVOR_DESCRIPTION)
|
||||
registerActivityLifecycleCallbacks(VectorActivityLifecycleCallbacks())
|
||||
|
||||
val fontRequest = FontRequest(
|
||||
"com.google.android.gms.fonts",
|
||||
"com.google.android.gms",
|
||||
"Noto Color Emoji Compat",
|
||||
R.array.com_google_android_gms_fonts_certs
|
||||
)
|
||||
|
||||
// val efp = koin.koinContext.get<EmojiCompatFontProvider>()
|
||||
FontsContractCompat.requestFont(this, fontRequest, koin.koinContext.get<EmojiCompatFontProvider>(), getFontThreadHandler())
|
||||
|
||||
FontsContractCompat.requestFont(this, fontRequest, emojiCompatFontProvider, getFontThreadHandler())
|
||||
vectorConfiguration.initConfiguration()
|
||||
}
|
||||
|
||||
override fun injector(): VectorComponent {
|
||||
return vectorComponent
|
||||
}
|
||||
|
||||
override fun attachBaseContext(base: Context) {
|
||||
super.attachBaseContext(base)
|
||||
MultiDex.install(this)
|
||||
@ -103,12 +92,15 @@ class VectorApplication : Application() {
|
||||
}
|
||||
|
||||
private fun getFontThreadHandler(): Handler {
|
||||
if (mFontThreadHandler == null) {
|
||||
val handlerThread = HandlerThread("fonts")
|
||||
handlerThread.start()
|
||||
mFontThreadHandler = Handler(handlerThread.looper)
|
||||
return fontThreadHandler ?: createFontThreadHandler().also {
|
||||
fontThreadHandler = it
|
||||
}
|
||||
return mFontThreadHandler!!
|
||||
}
|
||||
|
||||
private fun createFontThreadHandler(): Handler {
|
||||
val handlerThread = HandlerThread("fonts")
|
||||
handlerThread.start()
|
||||
return Handler(handlerThread.looper)
|
||||
}
|
||||
|
||||
}
|
@ -1,107 +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.riotredesign.core.di
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Context.MODE_PRIVATE
|
||||
import im.vector.matrix.android.api.Matrix
|
||||
import im.vector.riotredesign.EmojiCompatFontProvider
|
||||
import im.vector.riotredesign.core.error.ErrorFormatter
|
||||
import im.vector.riotredesign.core.resources.LocaleProvider
|
||||
import im.vector.riotredesign.core.resources.StringArrayProvider
|
||||
import im.vector.riotredesign.core.resources.StringProvider
|
||||
import im.vector.riotredesign.features.configuration.VectorConfiguration
|
||||
import im.vector.riotredesign.features.crypto.keysrequest.KeyRequestHandler
|
||||
import im.vector.riotredesign.features.crypto.verification.IncomingVerificationRequestHandler
|
||||
import im.vector.riotredesign.features.home.HomeRoomListObservableStore
|
||||
import im.vector.riotredesign.features.home.group.SelectedGroupStore
|
||||
import im.vector.riotredesign.features.home.room.list.AlphabeticalRoomComparator
|
||||
import im.vector.riotredesign.features.home.room.list.ChronologicalRoomComparator
|
||||
import im.vector.riotredesign.features.navigation.DefaultNavigator
|
||||
import im.vector.riotredesign.features.navigation.Navigator
|
||||
import im.vector.riotredesign.features.notifications.NotificationDrawerManager
|
||||
import org.koin.dsl.module.module
|
||||
|
||||
class AppModule(private val context: Context) {
|
||||
|
||||
val definition = module {
|
||||
|
||||
single {
|
||||
VectorConfiguration(context)
|
||||
}
|
||||
|
||||
single {
|
||||
LocaleProvider(context.resources)
|
||||
}
|
||||
|
||||
single {
|
||||
StringProvider(context.resources)
|
||||
}
|
||||
|
||||
single {
|
||||
StringArrayProvider(context.resources)
|
||||
}
|
||||
|
||||
single {
|
||||
context.getSharedPreferences("im.vector.riot", MODE_PRIVATE)
|
||||
}
|
||||
|
||||
single {
|
||||
SelectedGroupStore()
|
||||
}
|
||||
|
||||
single {
|
||||
HomeRoomListObservableStore()
|
||||
}
|
||||
|
||||
single {
|
||||
ChronologicalRoomComparator()
|
||||
}
|
||||
|
||||
single {
|
||||
AlphabeticalRoomComparator()
|
||||
}
|
||||
|
||||
single {
|
||||
ErrorFormatter(get())
|
||||
}
|
||||
|
||||
single {
|
||||
NotificationDrawerManager(context)
|
||||
}
|
||||
|
||||
factory {
|
||||
Matrix.getInstance().currentSession!!
|
||||
}
|
||||
|
||||
single {
|
||||
KeyRequestHandler(context, get())
|
||||
}
|
||||
|
||||
single {
|
||||
IncomingVerificationRequestHandler(context, get())
|
||||
}
|
||||
|
||||
factory {
|
||||
DefaultNavigator() as Navigator
|
||||
}
|
||||
|
||||
single {
|
||||
EmojiCompatFontProvider()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.riotredesign.core.di
|
||||
|
||||
interface HasInjector<C> {
|
||||
|
||||
fun injector(): C
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.riotredesign.core.di
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import dagger.BindsInstance
|
||||
import dagger.Component
|
||||
import im.vector.riotredesign.core.platform.SimpleFragmentActivity
|
||||
import im.vector.riotredesign.features.crypto.keysbackup.settings.KeysBackupSettingsFragment
|
||||
import im.vector.riotredesign.features.home.HomeActivity
|
||||
import im.vector.riotredesign.features.home.HomeModule
|
||||
import im.vector.riotredesign.features.home.room.detail.RoomDetailFragment
|
||||
import im.vector.riotredesign.features.roomdirectory.picker.RoomDirectoryPickerFragment
|
||||
import im.vector.riotredesign.features.roomdirectory.roompreview.RoomPreviewNoPreviewFragment
|
||||
|
||||
@Component(dependencies = [VectorComponent::class], modules = [ViewModelModule::class, HomeModule::class])
|
||||
@ScreenScope
|
||||
interface ScreenComponent {
|
||||
|
||||
fun viewModelFactory(): ViewModelProvider.Factory
|
||||
|
||||
fun inject(activity: SimpleFragmentActivity)
|
||||
|
||||
fun inject(activity: HomeActivity)
|
||||
|
||||
fun inject(roomDetailFragment: RoomDetailFragment)
|
||||
|
||||
fun inject(roomDirectoryPickerFragment: RoomDirectoryPickerFragment)
|
||||
|
||||
fun inject(roomPreviewNoPreviewFragment: RoomPreviewNoPreviewFragment)
|
||||
|
||||
fun inject(keysBackupSettingsFragment: KeysBackupSettingsFragment)
|
||||
|
||||
@Component.Factory
|
||||
interface Factory {
|
||||
fun create(vectorComponent: VectorComponent,
|
||||
@BindsInstance context: AppCompatActivity
|
||||
): ScreenComponent
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
* 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
|
||||
* 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,
|
||||
@ -14,22 +14,18 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.riotredesign.features.crypto.keysbackup
|
||||
package im.vector.riotredesign.core.di
|
||||
|
||||
import im.vector.riotredesign.features.crypto.keysbackup.settings.KeysBackupSettingsRecyclerViewController
|
||||
import org.koin.dsl.module.module
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import im.vector.riotredesign.core.glide.GlideApp
|
||||
|
||||
class KeysBackupModule {
|
||||
@Module
|
||||
object ScreenModule {
|
||||
|
||||
companion object {
|
||||
const val KEYS_BACKUP_SCOPE = "KEYS_BACKUP_SCOPE"
|
||||
}
|
||||
@Provides
|
||||
@JvmStatic
|
||||
fun providesGlideRequests(context: AppCompatActivity) = GlideApp.with(context)
|
||||
|
||||
val definition = module(override = true) {
|
||||
|
||||
scope(KEYS_BACKUP_SCOPE) {
|
||||
KeysBackupSettingsRecyclerViewController(get(), get())
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.riotredesign.core.di;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
|
||||
import javax.inject.Scope;
|
||||
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
@Scope
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
public @interface ScreenScope {}
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.riotredesign.core.di
|
||||
|
||||
import com.squareup.inject.assisted.dagger2.AssistedModule
|
||||
import dagger.Module
|
||||
|
||||
/*
|
||||
@Module(includes = [AssistedInject_VectorAssistedModule::class])
|
||||
@AssistedModule
|
||||
class VectorAssistedModule*/
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.riotredesign.core.di
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import dagger.BindsInstance
|
||||
import dagger.Component
|
||||
import im.vector.matrix.android.api.Matrix
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.riotredesign.features.configuration.VectorConfiguration
|
||||
import im.vector.riotredesign.features.crypto.keysrequest.KeyRequestHandler
|
||||
import im.vector.riotredesign.features.crypto.verification.IncomingVerificationRequestHandler
|
||||
import im.vector.riotredesign.features.home.HomeRoomListObservableStore
|
||||
import im.vector.riotredesign.features.home.group.SelectedGroupStore
|
||||
import im.vector.riotredesign.features.navigation.Navigator
|
||||
import im.vector.riotredesign.features.notifications.NotificationDrawerManager
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Component(modules = [VectorModule::class])
|
||||
@Singleton
|
||||
interface VectorComponent {
|
||||
|
||||
fun matrix(): Matrix
|
||||
|
||||
fun currentSession(): Session
|
||||
|
||||
fun notificationDrawerManager(): NotificationDrawerManager
|
||||
|
||||
fun appContext(): Context
|
||||
|
||||
fun resources(): Resources
|
||||
|
||||
fun vectorConfiguration(): VectorConfiguration
|
||||
|
||||
fun navigator(): Navigator
|
||||
|
||||
fun homeRoomListObservableStore(): HomeRoomListObservableStore
|
||||
|
||||
fun selectedGroupStore(): SelectedGroupStore
|
||||
|
||||
fun incomingVerificationRequestHandler(): IncomingVerificationRequestHandler
|
||||
|
||||
fun incomingKeyRequestHandler(): KeyRequestHandler
|
||||
|
||||
@Component.Factory
|
||||
interface Factory {
|
||||
fun create(@BindsInstance context: Context): VectorComponent
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.riotredesign.core.di
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Context.MODE_PRIVATE
|
||||
import android.content.SharedPreferences
|
||||
import android.content.res.Resources
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import im.vector.matrix.android.api.Matrix
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.riotredesign.features.navigation.DefaultNavigator
|
||||
import im.vector.riotredesign.features.navigation.Navigator
|
||||
|
||||
@Module
|
||||
abstract class VectorModule {
|
||||
|
||||
@Module
|
||||
companion object {
|
||||
|
||||
@Provides
|
||||
@JvmStatic
|
||||
fun providesResources(context: Context): Resources {
|
||||
return context.resources
|
||||
}
|
||||
|
||||
@Provides
|
||||
@JvmStatic
|
||||
fun providesSharedPreferences(context: Context): SharedPreferences {
|
||||
return context.getSharedPreferences("im.vector.riot", MODE_PRIVATE)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@JvmStatic
|
||||
fun providesMatrix(): Matrix {
|
||||
return Matrix.getInstance()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@JvmStatic
|
||||
fun providesCurrentSession(matrix: Matrix): Session {
|
||||
//TODO: handle session injection better
|
||||
return matrix.currentSession!!
|
||||
}
|
||||
}
|
||||
|
||||
@Binds
|
||||
abstract fun bindNavigator(navigator: DefaultNavigator): Navigator
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.riotredesign.core.di
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
|
||||
/**
|
||||
* ViewModelFactory which uses Dagger to create the instances.
|
||||
*/
|
||||
class VectorViewModelFactory @Inject constructor(
|
||||
private val creators: @JvmSuppressWildcards Map<Class<out ViewModel>, Provider<ViewModel>>
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
var creator: Provider<out ViewModel>? = creators[modelClass]
|
||||
if (creator == null) {
|
||||
for ((key, value) in creators) {
|
||||
if (modelClass.isAssignableFrom(key)) {
|
||||
creator = value
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (creator == null) {
|
||||
throw IllegalArgumentException("Unknown model class: $modelClass")
|
||||
}
|
||||
try {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return creator.get() as T
|
||||
} catch (e: Exception) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.riotredesign.core.di
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import dagger.MapKey
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@MapKey
|
||||
annotation class ViewModelKey(val value: KClass<out ViewModel>)
|
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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.riotredesign.core.di
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.multibindings.IntoMap
|
||||
import im.vector.riotredesign.features.crypto.keysbackup.restore.KeysBackupRestoreFromKeyViewModel
|
||||
import im.vector.riotredesign.features.crypto.keysbackup.restore.KeysBackupRestoreFromPassphraseViewModel
|
||||
import im.vector.riotredesign.features.crypto.keysbackup.restore.KeysBackupRestoreSharedViewModel
|
||||
import im.vector.riotredesign.features.crypto.keysbackup.settings.KeysBackupSettingsViewModel
|
||||
import im.vector.riotredesign.features.crypto.keysbackup.settings.KeysBackupSettingsViewModel_AssistedFactory
|
||||
import im.vector.riotredesign.features.crypto.keysbackup.setup.KeysBackupSetupSharedViewModel
|
||||
import im.vector.riotredesign.features.crypto.verification.SasVerificationViewModel
|
||||
import im.vector.riotredesign.features.home.HomeActivityViewModel
|
||||
import im.vector.riotredesign.features.home.HomeActivityViewModel_AssistedFactory
|
||||
import im.vector.riotredesign.features.home.HomeDetailViewModel
|
||||
import im.vector.riotredesign.features.home.HomeDetailViewModel_AssistedFactory
|
||||
import im.vector.riotredesign.features.home.HomeNavigationViewModel
|
||||
import im.vector.riotredesign.features.home.group.GroupListViewModel
|
||||
import im.vector.riotredesign.features.home.group.GroupListViewModel_AssistedFactory
|
||||
import im.vector.riotredesign.features.home.room.detail.RoomDetailViewModel
|
||||
import im.vector.riotredesign.features.home.room.detail.RoomDetailViewModel_AssistedFactory
|
||||
import im.vector.riotredesign.features.home.room.detail.composer.TextComposerViewModel
|
||||
import im.vector.riotredesign.features.home.room.detail.composer.TextComposerViewModel_AssistedFactory
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.action.MessageActionsViewModel
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.action.MessageActionsViewModel_AssistedFactory
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.action.MessageMenuViewModel
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.action.MessageMenuViewModel_AssistedFactory
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.action.QuickReactionViewModel
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.action.QuickReactionViewModel_AssistedFactory
|
||||
import im.vector.riotredesign.features.home.room.list.RoomListViewModel
|
||||
import im.vector.riotredesign.features.home.room.list.RoomListViewModel_AssistedFactory
|
||||
import im.vector.riotredesign.features.reactions.EmojiChooserViewModel
|
||||
import im.vector.riotredesign.features.roomdirectory.RoomDirectoryNavigationViewModel
|
||||
import im.vector.riotredesign.features.roomdirectory.RoomDirectoryViewModel
|
||||
import im.vector.riotredesign.features.roomdirectory.RoomDirectoryViewModel_AssistedFactory
|
||||
import im.vector.riotredesign.features.roomdirectory.picker.RoomDirectoryPickerViewModel
|
||||
import im.vector.riotredesign.features.roomdirectory.picker.RoomDirectoryPickerViewModel_AssistedFactory
|
||||
import im.vector.riotredesign.features.roomdirectory.roompreview.RoomPreviewViewModel
|
||||
import im.vector.riotredesign.features.roomdirectory.roompreview.RoomPreviewViewModel_AssistedFactory
|
||||
import im.vector.riotredesign.features.workers.signout.SignOutViewModel
|
||||
|
||||
@Module
|
||||
interface ViewModelModule {
|
||||
|
||||
@Binds
|
||||
fun bindViewModelFactory(factory: VectorViewModelFactory): ViewModelProvider.Factory
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(SignOutViewModel::class)
|
||||
fun bindSignOutViewModel(viewModel: SignOutViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(EmojiChooserViewModel::class)
|
||||
fun bindEmojiChooserViewModel(viewModel: EmojiChooserViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(SasVerificationViewModel::class)
|
||||
fun bindSasVerificationViewModel(viewModel: SasVerificationViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(KeysBackupRestoreFromKeyViewModel::class)
|
||||
fun bindKeysBackupRestoreFromKeyViewModel(viewModel: KeysBackupRestoreFromKeyViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(KeysBackupRestoreSharedViewModel::class)
|
||||
fun bindKeysBackupRestoreSharedViewModel(viewModel: KeysBackupRestoreSharedViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(KeysBackupRestoreFromPassphraseViewModel::class)
|
||||
fun bindKeysBackupRestoreFromPassphraseViewModel(viewModel: KeysBackupRestoreFromPassphraseViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(RoomDirectoryNavigationViewModel::class)
|
||||
fun bindRoomDirectoryNavigationViewModel(viewModel: RoomDirectoryNavigationViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(HomeNavigationViewModel::class)
|
||||
fun bindHomeNavigationViewModel(viewModel: HomeNavigationViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(KeysBackupSetupSharedViewModel::class)
|
||||
fun bindKeysBackupSetupSharedViewModel(viewModel: KeysBackupSetupSharedViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
fun bind_im_vector_riotredesign_features_home_HomeActivityViewModel(factory: HomeActivityViewModel_AssistedFactory): HomeActivityViewModel.Factory
|
||||
|
||||
@Binds
|
||||
fun bind_im_vector_riotredesign_features_home_room_detail_composer_TextComposerViewModel(factory: TextComposerViewModel_AssistedFactory): TextComposerViewModel.Factory
|
||||
|
||||
@Binds
|
||||
fun bind_im_vector_riotredesign_features_home_room_detail_RoomDetailViewModel(factory: RoomDetailViewModel_AssistedFactory): RoomDetailViewModel.Factory
|
||||
|
||||
@Binds
|
||||
fun bind_im_vector_riotredesign_features_home_room_detail_timeline_action_QuickReactionViewModel(factory: QuickReactionViewModel_AssistedFactory): QuickReactionViewModel.Factory
|
||||
|
||||
@Binds
|
||||
fun bind_im_vector_riotredesign_features_home_room_detail_timeline_action_MessageActionsViewModel(factory: MessageActionsViewModel_AssistedFactory): MessageActionsViewModel.Factory
|
||||
|
||||
@Binds
|
||||
fun bind_im_vector_riotredesign_features_home_room_detail_timeline_action_MessageMenuViewModel(factory: MessageMenuViewModel_AssistedFactory): MessageMenuViewModel.Factory
|
||||
|
||||
@Binds
|
||||
fun bind_im_vector_riotredesign_features_home_room_list_RoomListViewModel(factory: RoomListViewModel_AssistedFactory): RoomListViewModel.Factory
|
||||
|
||||
@Binds
|
||||
fun bind_im_vector_riotredesign_features_home_group_GroupListViewModel(factory: GroupListViewModel_AssistedFactory): GroupListViewModel.Factory
|
||||
|
||||
@Binds
|
||||
fun bind_im_vector_riotredesign_features_home_HomeDetailViewModel(factory: HomeDetailViewModel_AssistedFactory): HomeDetailViewModel.Factory
|
||||
|
||||
@Binds
|
||||
fun bind_im_vector_riotredesign_features_crypto_keysbackup_settings_KeysBackupSettingsViewModel(factory: KeysBackupSettingsViewModel_AssistedFactory): KeysBackupSettingsViewModel.Factory
|
||||
|
||||
@Binds
|
||||
fun bind_im_vector_riotredesign_features_roomdirectory_picker_RoomDirectoryPickerViewModel(factory: RoomDirectoryPickerViewModel_AssistedFactory): RoomDirectoryPickerViewModel.Factory
|
||||
|
||||
@Binds
|
||||
fun bind_im_vector_riotredesign_features_roomdirectory_RoomDirectoryViewModel(factory: RoomDirectoryViewModel_AssistedFactory): RoomDirectoryViewModel.Factory
|
||||
|
||||
@Binds
|
||||
fun bind_im_vector_riotredesign_features_roomdirectory_roompreview_RoomPreviewViewModel(factory: RoomPreviewViewModel_AssistedFactory): RoomPreviewViewModel.Factory
|
||||
|
||||
}
|
@ -19,8 +19,9 @@ package im.vector.riotredesign.core.error
|
||||
import im.vector.matrix.android.api.failure.Failure
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.resources.StringProvider
|
||||
import javax.inject.Inject
|
||||
|
||||
class ErrorFormatter(val stringProvider: StringProvider) {
|
||||
class ErrorFormatter @Inject constructor(val stringProvider: StringProvider) {
|
||||
|
||||
|
||||
fun toHumanReadable(failure: Failure): String {
|
||||
|
@ -33,4 +33,4 @@ fun AppCompatActivity.addFragmentToBackstack(fragment: Fragment, frameId: Int, t
|
||||
|
||||
fun AppCompatActivity.hideKeyboard() {
|
||||
currentFocus?.hideKeyboard()
|
||||
}
|
||||
}
|
||||
|
@ -40,4 +40,4 @@ fun Fragment.replaceChildFragment(fragment: Fragment, frameId: Int) {
|
||||
|
||||
fun Fragment.addChildFragmentToBackstack(fragment: Fragment, frameId: Int, tag: String? = null) {
|
||||
childFragmentManager.inTransaction { replace(frameId, fragment).addToBackStack(tag) }
|
||||
}
|
||||
}
|
||||
|
@ -21,13 +21,12 @@ import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import im.vector.riotredesign.core.utils.LiveEvent
|
||||
import im.vector.riotredesign.features.configuration.VectorConfiguration
|
||||
import org.koin.standalone.KoinComponent
|
||||
import org.koin.standalone.inject
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class ConfigurationViewModel : ViewModel(), KoinComponent {
|
||||
|
||||
private val vectorConfiguration: VectorConfiguration by inject()
|
||||
class ConfigurationViewModel @Inject constructor(
|
||||
private val vectorConfiguration: VectorConfiguration
|
||||
) : ViewModel() {
|
||||
|
||||
private var currentConfigurationValue: String? = null
|
||||
|
||||
@ -47,7 +46,6 @@ class ConfigurationViewModel : ViewModel(), KoinComponent {
|
||||
if (newHash != currentConfigurationValue) {
|
||||
Timber.v("Configuration: recreate the Activity")
|
||||
currentConfigurationValue = newHash
|
||||
|
||||
_activityRestarter.postValue(LiveEvent(Unit))
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package im.vector.riotredesign.core.platform
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
@ -25,7 +26,7 @@ import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.extensions.hideKeyboard
|
||||
import kotlinx.android.synthetic.main.activity.*
|
||||
import org.koin.android.ext.android.get
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Simple activity with a toolbar, a waiting overlay, and a fragment container and a session.
|
||||
@ -43,7 +44,13 @@ abstract class SimpleFragmentActivity : VectorBaseActivity() {
|
||||
@BindView(R.id.waiting_view_status_horizontal_progress)
|
||||
lateinit var waitingHorizontalProgress: ProgressBar
|
||||
|
||||
protected val session = get<Session>()
|
||||
@Inject
|
||||
lateinit var session: Session
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
injector().inject(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
|
||||
override fun initUiAndData() {
|
||||
configureToolbar(toolbar)
|
||||
|
@ -22,35 +22,48 @@ import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.annotation.*
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.annotation.MainThread
|
||||
import androidx.annotation.MenuRes
|
||||
import androidx.annotation.Nullable
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import butterknife.BindView
|
||||
import butterknife.ButterKnife
|
||||
import butterknife.Unbinder
|
||||
import com.airbnb.mvrx.BaseMvRxActivity
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
import com.bumptech.glide.util.Util
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import im.vector.riotredesign.BuildConfig
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.di.HasInjector
|
||||
import im.vector.riotredesign.core.di.ScreenComponent
|
||||
import im.vector.riotredesign.core.utils.toast
|
||||
import im.vector.riotredesign.features.configuration.VectorConfiguration
|
||||
import im.vector.riotredesign.features.rageshake.BugReportActivity
|
||||
import im.vector.riotredesign.features.rageshake.BugReporter
|
||||
import im.vector.riotredesign.features.rageshake.RageShake
|
||||
import im.vector.riotredesign.features.roomdirectory.PublicRoomsViewState
|
||||
import im.vector.riotredesign.features.roomdirectory.RoomDirectoryViewModel
|
||||
import im.vector.riotredesign.features.themes.ActivityOtherThemes
|
||||
import im.vector.riotredesign.features.themes.ThemeUtils
|
||||
import im.vector.riotredesign.receivers.DebugReceiver
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.disposables.Disposable
|
||||
import org.koin.android.ext.android.inject
|
||||
import timber.log.Timber
|
||||
import javax.inject.Provider
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
|
||||
abstract class VectorBaseActivity : BaseMvRxActivity() {
|
||||
abstract class VectorBaseActivity : BaseMvRxActivity(), HasInjector<ScreenComponent> {
|
||||
/* ==========================================================================================
|
||||
* UI
|
||||
* ========================================================================================== */
|
||||
@ -64,8 +77,8 @@ abstract class VectorBaseActivity : BaseMvRxActivity() {
|
||||
* DATA
|
||||
* ========================================================================================== */
|
||||
|
||||
private val vectorConfiguration: VectorConfiguration by inject()
|
||||
|
||||
protected lateinit var viewModelFactory: ViewModelProvider.Factory
|
||||
private lateinit var vectorConfiguration: VectorConfiguration
|
||||
private lateinit var configurationViewModel: ConfigurationViewModel
|
||||
|
||||
private var unBinder: Unbinder? = null
|
||||
@ -79,6 +92,7 @@ abstract class VectorBaseActivity : BaseMvRxActivity() {
|
||||
private val restorables = ArrayList<Restorable>()
|
||||
|
||||
private var rageShake: RageShake? = null
|
||||
private lateinit var screenComponent: ScreenComponent
|
||||
|
||||
override fun attachBaseContext(base: Context) {
|
||||
super.attachBaseContext(vectorConfiguration.getLocalisedContext(base))
|
||||
@ -107,10 +121,9 @@ abstract class VectorBaseActivity : BaseMvRxActivity() {
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
configurationViewModel = ViewModelProviders.of(this).get(ConfigurationViewModel::class.java)
|
||||
|
||||
configurationViewModel = ViewModelProviders.of(this, viewModelFactory).get(ConfigurationViewModel::class.java)
|
||||
configurationViewModel.activityRestarter.observe(this, Observer {
|
||||
if (!it.hasBeenHandled) {
|
||||
// Recreate the Activity because configuration has changed
|
||||
@ -202,6 +215,10 @@ abstract class VectorBaseActivity : BaseMvRxActivity() {
|
||||
}
|
||||
|
||||
|
||||
override fun injector(): ScreenComponent {
|
||||
return screenComponent
|
||||
}
|
||||
|
||||
/* ==========================================================================================
|
||||
* PRIVATE METHODS
|
||||
* ========================================================================================== */
|
||||
|
@ -18,24 +18,29 @@ package im.vector.riotredesign.core.platform
|
||||
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.view.*
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.CallSuper
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.annotation.MainThread
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import butterknife.ButterKnife
|
||||
import butterknife.Unbinder
|
||||
import com.airbnb.mvrx.BaseMvRxFragment
|
||||
import com.airbnb.mvrx.MvRx
|
||||
import com.bumptech.glide.util.Util.assertMainThread
|
||||
import im.vector.riotredesign.core.di.HasInjector
|
||||
import im.vector.riotredesign.core.di.ScreenComponent
|
||||
import im.vector.riotredesign.features.navigation.Navigator
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.disposables.Disposable
|
||||
import org.koin.android.ext.android.inject
|
||||
import org.koin.core.parameter.parametersOf
|
||||
import timber.log.Timber
|
||||
|
||||
abstract class VectorBaseFragment : BaseMvRxFragment(), OnBackPressed {
|
||||
abstract class VectorBaseFragment : BaseMvRxFragment(), OnBackPressed, HasInjector<ScreenComponent> {
|
||||
|
||||
// Butterknife unbinder
|
||||
private var mUnBinder: Unbinder? = null
|
||||
@ -48,7 +53,8 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), OnBackPressed {
|
||||
* Navigator
|
||||
* ========================================================================================== */
|
||||
|
||||
protected val navigator: Navigator by inject { parametersOf(this) }
|
||||
protected lateinit var viewModelFactory: ViewModelProvider.Factory
|
||||
protected lateinit var navigator: Navigator
|
||||
|
||||
/* ==========================================================================================
|
||||
* Life cycle
|
||||
@ -57,7 +63,6 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), OnBackPressed {
|
||||
@CallSuper
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
if (getMenuRes() != -1) {
|
||||
setHasOptionsMenu(true)
|
||||
}
|
||||
@ -92,10 +97,13 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), OnBackPressed {
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
|
||||
uiDisposables.dispose()
|
||||
}
|
||||
|
||||
override fun injector(): ScreenComponent {
|
||||
return vectorBaseActivity.injector()
|
||||
}
|
||||
|
||||
/* ==========================================================================================
|
||||
* Restorable
|
||||
* ========================================================================================== */
|
||||
|
@ -18,14 +18,15 @@
|
||||
|
||||
package im.vector.riotredesign.core.resources
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import im.vector.riotredesign.features.themes.ThemeUtils
|
||||
import javax.inject.Inject
|
||||
|
||||
class ColorProvider(private val context: Context) {
|
||||
class ColorProvider @Inject constructor(private val context: AppCompatActivity) {
|
||||
|
||||
fun getColor(@ColorRes colorRes: Int): Int {
|
||||
return ContextCompat.getColor(context, colorRes)
|
||||
|
@ -19,8 +19,9 @@ package im.vector.riotredesign.core.resources
|
||||
import android.content.res.Resources
|
||||
import androidx.core.os.ConfigurationCompat
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class LocaleProvider(private val resources: Resources) {
|
||||
class LocaleProvider @Inject constructor(private val resources: Resources) {
|
||||
|
||||
fun current(): Locale {
|
||||
return ConfigurationCompat.getLocales(resources.configuration)[0]
|
||||
|
@ -19,8 +19,9 @@ package im.vector.riotredesign.core.resources
|
||||
import android.content.res.Resources
|
||||
import androidx.annotation.ArrayRes
|
||||
import androidx.annotation.NonNull
|
||||
import javax.inject.Inject
|
||||
|
||||
class StringArrayProvider(private val resources: Resources) {
|
||||
class StringArrayProvider @Inject constructor(private val resources: Resources) {
|
||||
|
||||
/**
|
||||
* Returns a localized string array from the application's package's
|
||||
|
@ -20,8 +20,9 @@ import android.content.res.Resources
|
||||
import androidx.annotation.NonNull
|
||||
import androidx.annotation.PluralsRes
|
||||
import androidx.annotation.StringRes
|
||||
import javax.inject.Inject
|
||||
|
||||
class StringProvider(private val resources: Resources) {
|
||||
class StringProvider @Inject constructor(private val resources: Resources) {
|
||||
|
||||
/**
|
||||
* Returns a localized string from the application's package's
|
||||
|
@ -28,7 +28,6 @@ import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.features.notifications.NotifiableEventResolver
|
||||
import im.vector.riotredesign.features.notifications.NotificationUtils
|
||||
import org.koin.android.ext.android.inject
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@ -162,7 +161,7 @@ class EventStreamServiceX : VectorService() {
|
||||
val notification = NotificationUtils.buildForegroundServiceNotification(this, R.string.notification_sync_in_progress)
|
||||
startForeground(NotificationUtils.NOTIFICATION_ID_FOREGROUND_SERVICE, notification)
|
||||
}
|
||||
ACTION_GO_TO_FOREGROUND -> {
|
||||
ACTION_GO_TO_FOREGROUND -> {
|
||||
// Stop foreground notification display
|
||||
Timber.i("stopForeground")
|
||||
stopForeground(true)
|
||||
@ -177,9 +176,9 @@ class EventStreamServiceX : VectorService() {
|
||||
|
||||
when (action) {
|
||||
ACTION_START,
|
||||
ACTION_GO_TO_FOREGROUND ->
|
||||
ACTION_GO_TO_FOREGROUND ->
|
||||
when (serviceState) {
|
||||
ServiceState.INIT ->
|
||||
ServiceState.INIT ->
|
||||
start(false)
|
||||
ServiceState.CATCHUP ->
|
||||
// A push has been received before, just change state, to avoid stopping the service when catchup is over
|
||||
@ -190,12 +189,12 @@ class EventStreamServiceX : VectorService() {
|
||||
}
|
||||
ACTION_STOP,
|
||||
ACTION_GO_TO_BACKGROUND,
|
||||
ACTION_LOGOUT ->
|
||||
ACTION_LOGOUT ->
|
||||
stop()
|
||||
ACTION_PUSH_RECEIVED,
|
||||
ACTION_SIMULATED_PUSH_RECEIVED ->
|
||||
when (serviceState) {
|
||||
ServiceState.INIT ->
|
||||
ServiceState.INIT ->
|
||||
start(true)
|
||||
ServiceState.CATCHUP ->
|
||||
catchup(true)
|
||||
@ -203,17 +202,17 @@ class EventStreamServiceX : VectorService() {
|
||||
// Nothing to do
|
||||
Unit
|
||||
}
|
||||
ACTION_PUSH_UPDATE -> pushStatusUpdate()
|
||||
ACTION_BOOT_COMPLETE -> {
|
||||
ACTION_PUSH_UPDATE -> pushStatusUpdate()
|
||||
ACTION_BOOT_COMPLETE -> {
|
||||
// No FCM only
|
||||
mSimulatePushImmediate = true
|
||||
stop()
|
||||
}
|
||||
ACTION_APPLICATION_UPGRADE -> {
|
||||
ACTION_APPLICATION_UPGRADE -> {
|
||||
// FDroid only
|
||||
catchup(true)
|
||||
}
|
||||
else -> {
|
||||
else -> {
|
||||
// Should not happen
|
||||
}
|
||||
}
|
||||
@ -247,8 +246,8 @@ class EventStreamServiceX : VectorService() {
|
||||
val pushSimulatorRequest = OneTimeWorkRequestBuilder<PushSimulatorWorker>()
|
||||
.setInitialDelay(delay.toLong(), TimeUnit.MILLISECONDS)
|
||||
.setConstraints(Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build())
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build())
|
||||
.addTag(PUSH_SIMULATOR_REQUEST_TAG)
|
||||
.build()
|
||||
|
||||
|
@ -20,8 +20,9 @@ import com.airbnb.epoxy.TypedEpoxyController
|
||||
import im.vector.riotredesign.core.resources.StringProvider
|
||||
import im.vector.riotredesign.features.autocomplete.AutocompleteClickListener
|
||||
import im.vector.riotredesign.features.command.Command
|
||||
import javax.inject.Inject
|
||||
|
||||
class AutocompleteCommandController(private val stringProvider: StringProvider) : TypedEpoxyController<List<Command>>() {
|
||||
class AutocompleteCommandController @Inject constructor(private val stringProvider: StringProvider) : TypedEpoxyController<List<Command>>() {
|
||||
|
||||
var listener: AutocompleteClickListener<Command>? = null
|
||||
|
||||
|
@ -20,9 +20,10 @@ import android.content.Context
|
||||
import com.airbnb.epoxy.EpoxyController
|
||||
import im.vector.riotredesign.features.autocomplete.EpoxyAutocompletePresenter
|
||||
import im.vector.riotredesign.features.command.Command
|
||||
import javax.inject.Inject
|
||||
|
||||
class AutocompleteCommandPresenter(context: Context,
|
||||
private val controller: AutocompleteCommandController) :
|
||||
class AutocompleteCommandPresenter @Inject constructor(context: Context,
|
||||
private val controller: AutocompleteCommandController) :
|
||||
EpoxyAutocompletePresenter<Command>(context) {
|
||||
|
||||
init {
|
||||
|
@ -18,8 +18,9 @@ package im.vector.riotredesign.features.autocomplete.command
|
||||
|
||||
import android.text.Spannable
|
||||
import com.otaliastudios.autocomplete.AutocompletePolicy
|
||||
import javax.inject.Inject
|
||||
|
||||
class CommandAutocompletePolicy : AutocompletePolicy {
|
||||
class CommandAutocompletePolicy @Inject constructor() : AutocompletePolicy {
|
||||
|
||||
var enabled: Boolean = true
|
||||
|
||||
@ -37,7 +38,7 @@ class CommandAutocompletePolicy : AutocompletePolicy {
|
||||
// Only if text which starts with '/' and without space
|
||||
override fun shouldShowPopup(text: Spannable?, cursorPos: Int): Boolean {
|
||||
return enabled && text?.startsWith("/") == true
|
||||
&& !text.contains(" ")
|
||||
&& !text.contains(" ")
|
||||
}
|
||||
|
||||
override fun shouldDismissPopup(text: Spannable?, cursorPos: Int): Boolean {
|
||||
|
@ -19,8 +19,9 @@ package im.vector.riotredesign.features.autocomplete.user
|
||||
import com.airbnb.epoxy.TypedEpoxyController
|
||||
import im.vector.matrix.android.api.session.user.model.User
|
||||
import im.vector.riotredesign.features.autocomplete.AutocompleteClickListener
|
||||
import javax.inject.Inject
|
||||
|
||||
class AutocompleteUserController : TypedEpoxyController<List<User>>() {
|
||||
class AutocompleteUserController @Inject constructor(): TypedEpoxyController<List<User>>() {
|
||||
|
||||
var listener: AutocompleteClickListener<User>? = null
|
||||
|
||||
|
@ -22,9 +22,10 @@ import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.Success
|
||||
import im.vector.matrix.android.api.session.user.model.User
|
||||
import im.vector.riotredesign.features.autocomplete.EpoxyAutocompletePresenter
|
||||
import javax.inject.Inject
|
||||
|
||||
class AutocompleteUserPresenter(context: Context,
|
||||
private val controller: AutocompleteUserController
|
||||
class AutocompleteUserPresenter @Inject constructor(context: Context,
|
||||
private val controller: AutocompleteUserController
|
||||
) : EpoxyAutocompletePresenter<User>(context) {
|
||||
|
||||
var callback: Callback? = null
|
||||
|
@ -25,20 +25,22 @@ import im.vector.riotredesign.features.settings.VectorLocale
|
||||
import im.vector.riotredesign.features.themes.ThemeUtils
|
||||
import timber.log.Timber
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Handle locale configuration change, such as theme, font size and locale chosen by the user
|
||||
*/
|
||||
class VectorConfiguration(private val context: Context) {
|
||||
|
||||
class VectorConfiguration @Inject constructor(private val context: Context) {
|
||||
|
||||
// TODO Import mLanguageReceiver From Riot?
|
||||
fun onConfigurationChanged(newConfig: Configuration?) {
|
||||
if (Locale.getDefault().toString() != VectorLocale.applicationLocale.toString()) {
|
||||
Timber.v("## onConfigurationChanged() : the locale has been updated to " + Locale.getDefault().toString()
|
||||
+ ", restore the expected value " + VectorLocale.applicationLocale.toString())
|
||||
+ ", restore the expected value " + VectorLocale.applicationLocale.toString())
|
||||
updateApplicationSettings(VectorLocale.applicationLocale,
|
||||
FontScale.getFontScalePrefValue(context),
|
||||
ThemeUtils.getApplicationTheme(context))
|
||||
FontScale.getFontScalePrefValue(context),
|
||||
ThemeUtils.getApplicationTheme(context))
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,8 +67,8 @@ class VectorConfiguration(private val context: Context) {
|
||||
fun updateApplicationTheme(theme: String) {
|
||||
ThemeUtils.setApplicationTheme(context, theme)
|
||||
updateApplicationSettings(VectorLocale.applicationLocale,
|
||||
FontScale.getFontScalePrefValue(context),
|
||||
theme)
|
||||
FontScale.getFontScalePrefValue(context),
|
||||
theme)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -23,7 +23,6 @@ import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import im.vector.fragments.keysbackup.restore.KeysBackupRestoreFromPassphraseFragment
|
||||
import im.vector.fragments.keysbackup.restore.KeysBackupRestoreSharedViewModel
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.extensions.observeEvent
|
||||
import im.vector.riotredesign.core.platform.SimpleFragmentActivity
|
||||
@ -43,9 +42,8 @@ class KeysBackupRestoreActivity : SimpleFragmentActivity() {
|
||||
|
||||
override fun initUiAndData() {
|
||||
super.initUiAndData()
|
||||
viewModel = ViewModelProviders.of(this).get(KeysBackupRestoreSharedViewModel::class.java)
|
||||
viewModel = ViewModelProviders.of(this, viewModelFactory).get(KeysBackupRestoreSharedViewModel::class.java)
|
||||
viewModel.initSession(session)
|
||||
|
||||
viewModel.keyVersionResult.observe(this, Observer { keyVersion ->
|
||||
|
||||
if (keyVersion != null && supportFragmentManager.fragments.isEmpty()) {
|
||||
|
@ -27,7 +27,6 @@ import butterknife.BindView
|
||||
import butterknife.OnClick
|
||||
import butterknife.OnTextChanged
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import im.vector.fragments.keysbackup.restore.KeysBackupRestoreSharedViewModel
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.platform.VectorBaseFragment
|
||||
import im.vector.riotredesign.core.utils.startImportTextFromFileIntent
|
||||
@ -53,9 +52,9 @@ class KeysBackupRestoreFromKeyFragment : VectorBaseFragment() {
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
viewModel = ViewModelProviders.of(this).get(KeysBackupRestoreFromKeyViewModel::class.java)
|
||||
viewModel = ViewModelProviders.of(this, viewModelFactory).get(KeysBackupRestoreFromKeyViewModel::class.java)
|
||||
sharedViewModel = activity?.run {
|
||||
ViewModelProviders.of(this).get(KeysBackupRestoreSharedViewModel::class.java)
|
||||
ViewModelProviders.of(this, viewModelFactory).get(KeysBackupRestoreSharedViewModel::class.java)
|
||||
} ?: throw Exception("Invalid Activity")
|
||||
|
||||
mKeyTextEdit.setText(viewModel.recoveryCode.value)
|
||||
|
@ -18,7 +18,6 @@ package im.vector.riotredesign.features.crypto.keysbackup.restore
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import im.vector.fragments.keysbackup.restore.KeysBackupRestoreSharedViewModel
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.listeners.StepProgressListener
|
||||
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService
|
||||
@ -28,8 +27,9 @@ import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.platform.WaitingViewData
|
||||
import im.vector.riotredesign.core.ui.views.KeysBackupBanner
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class KeysBackupRestoreFromKeyViewModel : ViewModel() {
|
||||
class KeysBackupRestoreFromKeyViewModel @Inject constructor() : ViewModel() {
|
||||
|
||||
var recoveryCode: MutableLiveData<String> = MutableLiveData()
|
||||
var recoveryCodeErrorText: MutableLiveData<String> = MutableLiveData()
|
||||
|
@ -36,6 +36,7 @@ import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.extensions.showPassword
|
||||
import im.vector.riotredesign.core.platform.VectorBaseFragment
|
||||
import im.vector.riotredesign.features.crypto.keysbackup.restore.KeysBackupRestoreFromPassphraseViewModel
|
||||
import im.vector.riotredesign.features.crypto.keysbackup.restore.KeysBackupRestoreSharedViewModel
|
||||
|
||||
class KeysBackupRestoreFromPassphraseFragment : VectorBaseFragment() {
|
||||
|
||||
@ -68,9 +69,9 @@ class KeysBackupRestoreFromPassphraseFragment : VectorBaseFragment() {
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
||||
viewModel = ViewModelProviders.of(this).get(KeysBackupRestoreFromPassphraseViewModel::class.java)
|
||||
viewModel = ViewModelProviders.of(this, viewModelFactory).get(KeysBackupRestoreFromPassphraseViewModel::class.java)
|
||||
sharedViewModel = activity?.run {
|
||||
ViewModelProviders.of(this).get(KeysBackupRestoreSharedViewModel::class.java)
|
||||
ViewModelProviders.of(this, viewModelFactory).get(KeysBackupRestoreSharedViewModel::class.java)
|
||||
} ?: throw Exception("Invalid Activity")
|
||||
|
||||
|
||||
|
@ -18,7 +18,6 @@ package im.vector.riotredesign.features.crypto.keysbackup.restore
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import im.vector.fragments.keysbackup.restore.KeysBackupRestoreSharedViewModel
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.listeners.StepProgressListener
|
||||
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService
|
||||
@ -28,8 +27,9 @@ import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.platform.WaitingViewData
|
||||
import im.vector.riotredesign.core.ui.views.KeysBackupBanner
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class KeysBackupRestoreFromPassphraseViewModel : ViewModel() {
|
||||
class KeysBackupRestoreFromPassphraseViewModel @Inject constructor() : ViewModel() {
|
||||
|
||||
var passphrase: MutableLiveData<String> = MutableLiveData()
|
||||
var passphraseErrorText: MutableLiveData<String> = MutableLiveData()
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package im.vector.fragments.keysbackup.restore
|
||||
package im.vector.riotredesign.features.crypto.keysbackup.restore
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.LiveData
|
||||
@ -21,13 +21,14 @@ import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult
|
||||
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.platform.WaitingViewData
|
||||
import im.vector.riotredesign.core.utils.LiveEvent
|
||||
import javax.inject.Inject
|
||||
|
||||
class KeysBackupRestoreSharedViewModel : ViewModel() {
|
||||
class KeysBackupRestoreSharedViewModel @Inject constructor() : ViewModel() {
|
||||
|
||||
companion object {
|
||||
const val NAVIGATE_TO_RECOVER_WITH_KEY = "NAVIGATE_TO_RECOVER_WITH_KEY"
|
||||
|
@ -20,7 +20,6 @@ import android.widget.TextView
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import butterknife.BindView
|
||||
import butterknife.OnClick
|
||||
import im.vector.fragments.keysbackup.restore.KeysBackupRestoreSharedViewModel
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.platform.VectorBaseFragment
|
||||
import im.vector.riotredesign.core.utils.LiveEvent
|
||||
@ -40,7 +39,7 @@ class KeysBackupRestoreSuccessFragment : VectorBaseFragment() {
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
sharedViewModel = activity?.run {
|
||||
ViewModelProviders.of(this).get(KeysBackupRestoreSharedViewModel::class.java)
|
||||
ViewModelProviders.of(this, viewModelFactory).get(KeysBackupRestoreSharedViewModel::class.java)
|
||||
} ?: throw Exception("Invalid Activity")
|
||||
|
||||
sharedViewModel.importKeyResult?.let {
|
||||
|
@ -17,16 +17,16 @@ package im.vector.riotredesign.features.crypto.keysbackup.settings
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.viewModel
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.extensions.injector
|
||||
import im.vector.riotredesign.core.platform.SimpleFragmentActivity
|
||||
import im.vector.riotredesign.core.platform.WaitingViewData
|
||||
import im.vector.riotredesign.features.crypto.keysbackup.KeysBackupModule
|
||||
import org.koin.android.scope.ext.android.bindScope
|
||||
import org.koin.android.scope.ext.android.getOrCreateScope
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
class KeysBackupManageActivity : SimpleFragmentActivity() {
|
||||
@ -41,12 +41,15 @@ class KeysBackupManageActivity : SimpleFragmentActivity() {
|
||||
override fun getTitleRes() = R.string.encryption_message_recovery
|
||||
|
||||
private val viewModel: KeysBackupSettingsViewModel by viewModel()
|
||||
@Inject lateinit var keysBackupSettingsViewModelFactory: KeysBackupSettingsViewModel.Factory
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
injector.inject(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
|
||||
override fun initUiAndData() {
|
||||
super.initUiAndData()
|
||||
|
||||
bindScope(getOrCreateScope(KeysBackupModule.KEYS_BACKUP_SCOPE))
|
||||
|
||||
if (supportFragmentManager.fragments.isEmpty()) {
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.container, KeysBackupSettingsFragment.newInstance())
|
||||
|
@ -21,17 +21,15 @@ import androidx.appcompat.app.AlertDialog
|
||||
import com.airbnb.mvrx.activityViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.extensions.injector
|
||||
import im.vector.riotredesign.core.platform.VectorBaseFragment
|
||||
import im.vector.riotredesign.features.crypto.keysbackup.KeysBackupModule
|
||||
import im.vector.riotredesign.features.crypto.keysbackup.restore.KeysBackupRestoreActivity
|
||||
import im.vector.riotredesign.features.crypto.keysbackup.setup.KeysBackupSetupActivity
|
||||
import kotlinx.android.synthetic.main.fragment_keys_backup_settings.*
|
||||
import org.koin.android.ext.android.inject
|
||||
import org.koin.android.scope.ext.android.bindScope
|
||||
import org.koin.android.scope.ext.android.getOrCreateScope
|
||||
import javax.inject.Inject
|
||||
|
||||
class KeysBackupSettingsFragment : VectorBaseFragment(),
|
||||
KeysBackupSettingsRecyclerViewController.Listener {
|
||||
KeysBackupSettingsRecyclerViewController.Listener {
|
||||
|
||||
companion object {
|
||||
fun newInstance() = KeysBackupSettingsFragment()
|
||||
@ -39,14 +37,12 @@ class KeysBackupSettingsFragment : VectorBaseFragment(),
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_keys_backup_settings
|
||||
|
||||
private val keysBackupSettingsRecyclerViewController: KeysBackupSettingsRecyclerViewController by inject()
|
||||
|
||||
@Inject lateinit var keysBackupSettingsRecyclerViewController: KeysBackupSettingsRecyclerViewController
|
||||
private val viewModel: KeysBackupSettingsViewModel by activityViewModel()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
injector().inject(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
bindScope(getOrCreateScope(KeysBackupModule.KEYS_BACKUP_SCOPE))
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
|
@ -17,11 +17,14 @@ package im.vector.riotredesign.features.crypto.keysbackup.settings
|
||||
|
||||
import android.view.View
|
||||
import com.airbnb.epoxy.TypedEpoxyController
|
||||
import com.airbnb.mvrx.*
|
||||
import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.KeysBackupVersionTrust
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.rest.KeysVersionResult
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.epoxy.errorWithRetryItem
|
||||
import im.vector.riotredesign.core.epoxy.loadingItem
|
||||
@ -29,9 +32,10 @@ import im.vector.riotredesign.core.resources.StringProvider
|
||||
import im.vector.riotredesign.core.ui.list.GenericItem
|
||||
import im.vector.riotredesign.core.ui.list.genericItem
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class KeysBackupSettingsRecyclerViewController(val stringProvider: StringProvider,
|
||||
val session: Session) : TypedEpoxyController<KeysBackupSettingViewState>() {
|
||||
class KeysBackupSettingsRecyclerViewController @Inject constructor(val stringProvider: StringProvider,
|
||||
val session: Session) : TypedEpoxyController<KeysBackupSettingViewState>() {
|
||||
|
||||
var listener: Listener? = null
|
||||
|
||||
|
@ -15,7 +15,15 @@
|
||||
*/
|
||||
package im.vector.riotredesign.features.crypto.keysbackup.settings
|
||||
|
||||
import com.airbnb.mvrx.*
|
||||
import com.airbnb.mvrx.ActivityViewModelContext
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupService
|
||||
@ -23,33 +31,37 @@ import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState
|
||||
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupStateListener
|
||||
import im.vector.matrix.android.internal.crypto.keysbackup.model.KeysBackupVersionTrust
|
||||
import im.vector.riotredesign.core.platform.VectorViewModel
|
||||
import org.koin.android.ext.android.get
|
||||
|
||||
|
||||
class KeysBackupSettingsViewModel(initialState: KeysBackupSettingViewState,
|
||||
session: Session) : VectorViewModel<KeysBackupSettingViewState>(initialState),
|
||||
KeysBackupStateListener {
|
||||
class KeysBackupSettingsViewModel @AssistedInject constructor(@Assisted initialState: KeysBackupSettingViewState,
|
||||
session: Session
|
||||
) : VectorViewModel<KeysBackupSettingViewState>(initialState),
|
||||
KeysBackupStateListener {
|
||||
|
||||
@AssistedInject.Factory
|
||||
interface Factory {
|
||||
fun create(initialState: KeysBackupSettingViewState): KeysBackupSettingsViewModel
|
||||
}
|
||||
|
||||
companion object : MvRxViewModelFactory<KeysBackupSettingsViewModel, KeysBackupSettingViewState> {
|
||||
|
||||
@JvmStatic
|
||||
override fun create(viewModelContext: ViewModelContext, state: KeysBackupSettingViewState): KeysBackupSettingsViewModel? {
|
||||
val session = viewModelContext.activity.get<Session>()
|
||||
|
||||
val firstState = state.copy(
|
||||
keysBackupState = session.getKeysBackupService().state,
|
||||
keysBackupVersion = session.getKeysBackupService().keysBackupVersion
|
||||
)
|
||||
|
||||
return KeysBackupSettingsViewModel(firstState, session)
|
||||
val activity: KeysBackupManageActivity = (viewModelContext as ActivityViewModelContext).activity()
|
||||
return activity.keysBackupSettingsViewModelFactory.create(state)
|
||||
}
|
||||
}
|
||||
|
||||
private var keysBackupService: KeysBackupService = session.getKeysBackupService()
|
||||
|
||||
init {
|
||||
setState {
|
||||
this.copy(
|
||||
keysBackupState = session.getKeysBackupService().state,
|
||||
keysBackupVersion = session.getKeysBackupService().keysBackupVersion
|
||||
)
|
||||
}
|
||||
keysBackupService.addListener(this)
|
||||
|
||||
getKeysBackupTrust()
|
||||
}
|
||||
|
||||
@ -90,8 +102,8 @@ class KeysBackupSettingsViewModel(initialState: KeysBackupSettingViewState,
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
keysBackupService.removeListener(this)
|
||||
super.onCleared()
|
||||
}
|
||||
|
||||
override fun onStateChange(newState: KeysBackupState) {
|
||||
@ -142,6 +154,6 @@ class KeysBackupSettingsViewModel(initialState: KeysBackupSettingViewState,
|
||||
val currentBackupState = keysBackupService.state
|
||||
|
||||
return currentBackupState == KeysBackupState.Unknown
|
||||
|| currentBackupState == KeysBackupState.CheckingBackUpOnHomeserver
|
||||
|| currentBackupState == KeysBackupState.CheckingBackUpOnHomeserver
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,6 @@ import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import im.vector.fragments.keysbackup.setup.KeysBackupSetupSharedViewModel
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.dialogs.ExportKeysDialog
|
||||
import im.vector.riotredesign.core.extensions.observeEvent
|
||||
@ -42,7 +41,7 @@ class KeysBackupSetupActivity : SimpleFragmentActivity() {
|
||||
.commitNow()
|
||||
}
|
||||
|
||||
viewModel = ViewModelProviders.of(this).get(KeysBackupSetupSharedViewModel::class.java)
|
||||
viewModel = ViewModelProviders.of(this, viewModelFactory).get(KeysBackupSetupSharedViewModel::class.java)
|
||||
viewModel.showManualExport.value = intent.getBooleanExtra(EXTRA_SHOW_MANUAL_EXPORT, false)
|
||||
viewModel.initSession(session)
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.fragments.keysbackup.setup
|
||||
package im.vector.riotredesign.features.crypto.keysbackup.setup
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
@ -30,11 +30,12 @@ import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.platform.WaitingViewData
|
||||
import im.vector.riotredesign.core.utils.LiveEvent
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* The shared view model between all fragments.
|
||||
*/
|
||||
class KeysBackupSetupSharedViewModel : ViewModel() {
|
||||
class KeysBackupSetupSharedViewModel @Inject constructor() : ViewModel() {
|
||||
|
||||
companion object {
|
||||
const val NAVIGATE_TO_STEP_2 = "NAVIGATE_TO_STEP_2"
|
||||
|
@ -24,7 +24,6 @@ import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import butterknife.BindView
|
||||
import butterknife.OnClick
|
||||
import im.vector.fragments.keysbackup.setup.KeysBackupSetupSharedViewModel
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.platform.VectorBaseFragment
|
||||
import im.vector.riotredesign.core.utils.LiveEvent
|
||||
@ -51,7 +50,7 @@ class KeysBackupSetupStep1Fragment : VectorBaseFragment() {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
||||
viewModel = activity?.run {
|
||||
ViewModelProviders.of(this).get(KeysBackupSetupSharedViewModel::class.java)
|
||||
ViewModelProviders.of(this, viewModelFactory).get(KeysBackupSetupSharedViewModel::class.java)
|
||||
} ?: throw Exception("Invalid Activity")
|
||||
|
||||
viewModel.showManualExport.observe(this, Observer {
|
||||
|
@ -30,7 +30,6 @@ import butterknife.OnClick
|
||||
import butterknife.OnTextChanged
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import com.nulabinc.zxcvbn.Zxcvbn
|
||||
import im.vector.fragments.keysbackup.setup.KeysBackupSetupSharedViewModel
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.extensions.showPassword
|
||||
import im.vector.riotredesign.core.platform.VectorBaseFragment
|
||||
@ -82,7 +81,7 @@ class KeysBackupSetupStep2Fragment : VectorBaseFragment() {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
||||
viewModel = activity?.run {
|
||||
ViewModelProviders.of(this).get(KeysBackupSetupSharedViewModel::class.java)
|
||||
ViewModelProviders.of(this, viewModelFactory).get(KeysBackupSetupSharedViewModel::class.java)
|
||||
} ?: throw Exception("Invalid Activity")
|
||||
|
||||
viewModel.shouldPromptOnBack = true
|
||||
|
@ -28,7 +28,6 @@ import androidx.lifecycle.ViewModelProviders
|
||||
import butterknife.BindView
|
||||
import butterknife.OnClick
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import im.vector.fragments.keysbackup.setup.KeysBackupSetupSharedViewModel
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.files.addEntryToDownloadManager
|
||||
import im.vector.riotredesign.core.files.saveStringToFile
|
||||
@ -58,7 +57,7 @@ class KeysBackupSetupStep3Fragment : VectorBaseFragment() {
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
viewModel = activity?.run {
|
||||
ViewModelProviders.of(this).get(KeysBackupSetupSharedViewModel::class.java)
|
||||
ViewModelProviders.of(this, viewModelFactory).get(KeysBackupSetupSharedViewModel::class.java)
|
||||
} ?: throw Exception("Invalid Activity")
|
||||
|
||||
|
||||
|
@ -40,6 +40,8 @@ import timber.log.Timber
|
||||
import java.text.DateFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.collections.HashMap
|
||||
|
||||
@ -50,21 +52,20 @@ import kotlin.collections.HashMap
|
||||
* If several requests come from same user/device, a single alert is displayed (this alert will accept/reject all request
|
||||
* depending on user action)
|
||||
*/
|
||||
class KeyRequestHandler(val context: Context,
|
||||
val session: Session)
|
||||
|
||||
@Singleton
|
||||
class KeyRequestHandler @Inject constructor(val context: Context,
|
||||
val session: Session)
|
||||
: RoomKeysRequestListener,
|
||||
SasVerificationService.SasVerificationListener {
|
||||
SasVerificationService.SasVerificationListener {
|
||||
|
||||
private val alertsToRequests = HashMap<String, ArrayList<IncomingRoomKeyRequest>>()
|
||||
|
||||
init {
|
||||
session.getSasVerificationService().addListener(this)
|
||||
|
||||
session.addRoomKeysRequestListener(this)
|
||||
}
|
||||
|
||||
fun ensureStarted() = Unit
|
||||
|
||||
/**
|
||||
* Handle incoming key request.
|
||||
*
|
||||
@ -194,8 +195,8 @@ class KeyRequestHandler(val context: Context,
|
||||
Runnable {
|
||||
alert.weakCurrentActivity?.get()?.let {
|
||||
val intent = SASVerificationActivity.outgoingIntent(it,
|
||||
session.sessionParams.credentials.userId,
|
||||
userId, deviceId)
|
||||
session.sessionParams.credentials.userId,
|
||||
userId, deviceId)
|
||||
it.startActivity(intent)
|
||||
}
|
||||
},
|
||||
@ -246,8 +247,8 @@ class KeyRequestHandler(val context: Context,
|
||||
val alertMgrUniqueKey = alertManagerId(deviceId!!, userId!!)
|
||||
alertsToRequests[alertMgrUniqueKey]?.removeAll {
|
||||
it.deviceId == request.deviceId
|
||||
&& it.userId == request.userId
|
||||
&& it.requestId == request.requestId
|
||||
&& it.userId == request.userId
|
||||
&& it.requestId == request.requestId
|
||||
}
|
||||
if (alertsToRequests[alertMgrUniqueKey]?.isEmpty() == true) {
|
||||
PopupAlertManager.cancelAlert(alertMgrUniqueKey)
|
||||
|
@ -23,19 +23,21 @@ import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTransactio
|
||||
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.features.popup.PopupAlertManager
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Listens to the VerificationManager and add a new notification when an incoming request is detected.
|
||||
*/
|
||||
class IncomingVerificationRequestHandler(val context: Context,
|
||||
private val session: Session) : SasVerificationService.SasVerificationListener {
|
||||
@Singleton
|
||||
class IncomingVerificationRequestHandler @Inject constructor(val context: Context,
|
||||
private val session: Session
|
||||
) : SasVerificationService.SasVerificationListener {
|
||||
|
||||
init {
|
||||
session.getSasVerificationService().addListener(this)
|
||||
}
|
||||
|
||||
fun ensureStarted() = Unit
|
||||
|
||||
override fun transactionCreated(tx: SasVerificationTransaction) {}
|
||||
|
||||
override fun transactionUpdated(tx: SasVerificationTransaction) {
|
||||
@ -44,7 +46,7 @@ class IncomingVerificationRequestHandler(val context: Context,
|
||||
//Add a notification for every incoming request
|
||||
val session = Matrix.getInstance().currentSession!!
|
||||
val name = session.getUser(tx.otherUserId)?.displayName
|
||||
?: tx.otherUserId
|
||||
?: tx.otherUserId
|
||||
|
||||
val alert = PopupAlertManager.VectorAlert(
|
||||
"kvr_${tx.transactionId}",
|
||||
@ -54,9 +56,9 @@ class IncomingVerificationRequestHandler(val context: Context,
|
||||
.apply {
|
||||
contentAction = Runnable {
|
||||
val intent = SASVerificationActivity.incomingIntent(context,
|
||||
session.sessionParams.credentials.userId,
|
||||
tx.otherUserId,
|
||||
tx.transactionId)
|
||||
session.sessionParams.credentials.userId,
|
||||
tx.otherUserId,
|
||||
tx.transactionId)
|
||||
weakCurrentActivity?.get()?.startActivity(intent)
|
||||
}
|
||||
dismissedAction = Runnable {
|
||||
@ -72,9 +74,9 @@ class IncomingVerificationRequestHandler(val context: Context,
|
||||
context.getString(R.string.action_open),
|
||||
Runnable {
|
||||
val intent = SASVerificationActivity.incomingIntent(context,
|
||||
session.sessionParams.credentials.userId,
|
||||
tx.otherUserId,
|
||||
tx.transactionId)
|
||||
session.sessionParams.credentials.userId,
|
||||
tx.otherUserId,
|
||||
tx.transactionId)
|
||||
weakCurrentActivity?.get()?.startActivity(intent)
|
||||
}
|
||||
)
|
||||
|
@ -84,7 +84,7 @@ class SASVerificationActivity : SimpleFragmentActivity() {
|
||||
|
||||
override fun initUiAndData() {
|
||||
super.initUiAndData()
|
||||
viewModel = ViewModelProviders.of(this).get(SasVerificationViewModel::class.java)
|
||||
viewModel = ViewModelProviders.of(this, viewModelFactory).get(SasVerificationViewModel::class.java)
|
||||
val transactionID: String? = intent.getStringExtra(EXTRA_TRANSACTION_ID)
|
||||
|
||||
if (isFirstCreation()) {
|
||||
|
@ -53,7 +53,7 @@ class SASVerificationIncomingFragment : VectorBaseFragment() {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
||||
viewModel = activity?.run {
|
||||
ViewModelProviders.of(this).get(SasVerificationViewModel::class.java)
|
||||
ViewModelProviders.of(this, viewModelFactory).get(SasVerificationViewModel::class.java)
|
||||
} ?: throw Exception("Invalid Activity")
|
||||
|
||||
otherUserDisplayNameTextView.text = viewModel.otherUser?.displayName ?: viewModel.otherUserId
|
||||
|
@ -69,7 +69,7 @@ class SASVerificationShortCodeFragment : VectorBaseFragment() {
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
viewModel = activity?.run {
|
||||
ViewModelProviders.of(this).get(SasVerificationViewModel::class.java)
|
||||
ViewModelProviders.of(this, viewModelFactory).get(SasVerificationViewModel::class.java)
|
||||
} ?: throw Exception("Invalid Activity")
|
||||
|
||||
|
||||
|
@ -58,11 +58,7 @@ class SASVerificationStartFragment : VectorBaseFragment() {
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
||||
viewModel = activity?.run {
|
||||
ViewModelProviders.of(this).get(SasVerificationViewModel::class.java)
|
||||
} ?: throw Exception("Invalid Activity")
|
||||
|
||||
viewModel = ViewModelProviders.of(vectorBaseActivity, viewModelFactory).get(SasVerificationViewModel::class.java)
|
||||
viewModel.transactionState.observe(this, Observer {
|
||||
val uxState = (viewModel.transaction as? OutgoingSasVerificationRequest)?.uxState
|
||||
when (uxState) {
|
||||
@ -75,14 +71,14 @@ class SASVerificationStartFragment : VectorBaseFragment() {
|
||||
this.startButtonLoading.animate()
|
||||
|
||||
}
|
||||
OutgoingSasVerificationRequest.UxState.SHOW_SAS -> {
|
||||
OutgoingSasVerificationRequest.UxState.SHOW_SAS -> {
|
||||
viewModel.shortCodeReady()
|
||||
}
|
||||
OutgoingSasVerificationRequest.UxState.CANCELLED_BY_ME,
|
||||
OutgoingSasVerificationRequest.UxState.CANCELLED_BY_OTHER -> {
|
||||
OutgoingSasVerificationRequest.UxState.CANCELLED_BY_OTHER -> {
|
||||
viewModel.navigateCancel()
|
||||
}
|
||||
else -> {
|
||||
else -> {
|
||||
TransitionManager.beginDelayedTransition(this.rootLayout)
|
||||
this.loadingText.isVisible = false
|
||||
this.startButton.isVisible = true
|
||||
|
@ -35,7 +35,7 @@ class SASVerificationVerifiedFragment : VectorBaseFragment() {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
||||
viewModel = activity?.run {
|
||||
ViewModelProviders.of(this).get(SasVerificationViewModel::class.java)
|
||||
ViewModelProviders.of(this, viewModelFactory).get(SasVerificationViewModel::class.java)
|
||||
} ?: throw Exception("Invalid Activity")
|
||||
|
||||
}
|
||||
|
@ -25,10 +25,11 @@ import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTransactio
|
||||
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState
|
||||
import im.vector.matrix.android.api.session.user.model.User
|
||||
import im.vector.riotredesign.core.utils.LiveEvent
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
class SasVerificationViewModel : ViewModel(),
|
||||
SasVerificationService.SasVerificationListener {
|
||||
class SasVerificationViewModel @Inject constructor() : ViewModel(),
|
||||
SasVerificationService.SasVerificationListener {
|
||||
|
||||
|
||||
companion object {
|
||||
|
@ -43,9 +43,7 @@ import im.vector.riotredesign.features.rageshake.BugReporter
|
||||
import im.vector.riotredesign.features.rageshake.VectorUncaughtExceptionHandler
|
||||
import im.vector.riotredesign.features.workers.signout.SignOutUiWorker
|
||||
import kotlinx.android.synthetic.main.activity_home.*
|
||||
import org.koin.android.ext.android.inject
|
||||
import org.koin.android.scope.ext.android.bindScope
|
||||
import org.koin.android.scope.ext.android.getOrCreateScope
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||
@ -57,12 +55,13 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||
|
||||
private val homeActivityViewModel: HomeActivityViewModel by viewModel()
|
||||
private lateinit var navigationViewModel: HomeNavigationViewModel
|
||||
private val homeNavigator by inject<HomeNavigator>()
|
||||
|
||||
@Inject lateinit var homeActivityViewModelFactory: HomeActivityViewModel.Factory
|
||||
@Inject lateinit var homeNavigator: HomeNavigator
|
||||
// TODO Move this elsewhere
|
||||
private val incomingVerificationRequestHandler by inject<IncomingVerificationRequestHandler>()
|
||||
@Inject lateinit var incomingVerificationRequestHandler: IncomingVerificationRequestHandler
|
||||
// TODO Move this elsewhere
|
||||
private val keyRequestHandler by inject<KeyRequestHandler>()
|
||||
@Inject lateinit var keyRequestHandler: KeyRequestHandler
|
||||
|
||||
private var progress: ProgressDialog? = null
|
||||
|
||||
@ -76,10 +75,8 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
bindScope(getOrCreateScope(HomeModule.HOME_SCOPE))
|
||||
homeNavigator.activity = this
|
||||
|
||||
navigationViewModel = ViewModelProviders.of(this).get(HomeNavigationViewModel::class.java)
|
||||
navigationViewModel = ViewModelProviders.of(this, viewModelFactory).get(HomeNavigationViewModel::class.java)
|
||||
|
||||
drawerLayout.addDrawerListener(drawerListener)
|
||||
if (isFirstCreation()) {
|
||||
|
@ -19,10 +19,12 @@ package im.vector.riotredesign.features.home
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import arrow.core.Option
|
||||
import com.airbnb.mvrx.ActivityViewModelContext
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import im.vector.matrix.android.api.Matrix
|
||||
import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.group.model.GroupSummary
|
||||
@ -34,28 +36,31 @@ import im.vector.riotredesign.features.home.group.ALL_COMMUNITIES_GROUP_ID
|
||||
import im.vector.riotredesign.features.home.group.SelectedGroupStore
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.functions.BiFunction
|
||||
import org.koin.android.ext.android.get
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
data class EmptyState(val isEmpty: Boolean = true) : MvRxState
|
||||
|
||||
class HomeActivityViewModel(state: EmptyState,
|
||||
private val session: Session,
|
||||
private val selectedGroupStore: SelectedGroupStore,
|
||||
private val homeRoomListStore: HomeRoomListObservableStore
|
||||
) : VectorViewModel<EmptyState>(state), Session.Listener {
|
||||
class HomeActivityViewModel @AssistedInject constructor(@Assisted initialState: EmptyState,
|
||||
private val session: Session,
|
||||
private val selectedGroupStore: SelectedGroupStore,
|
||||
private val homeRoomListStore: HomeRoomListObservableStore
|
||||
) : VectorViewModel<EmptyState>(initialState), Session.Listener {
|
||||
|
||||
@AssistedInject.Factory
|
||||
interface Factory {
|
||||
fun create(initialState: EmptyState): HomeActivityViewModel
|
||||
}
|
||||
|
||||
companion object : MvRxViewModelFactory<HomeActivityViewModel, EmptyState> {
|
||||
|
||||
@JvmStatic
|
||||
override fun create(viewModelContext: ViewModelContext, state: EmptyState): HomeActivityViewModel? {
|
||||
val session = Matrix.getInstance().currentSession!!
|
||||
val selectedGroupStore = viewModelContext.activity.get<SelectedGroupStore>()
|
||||
val homeRoomListObservableSource = viewModelContext.activity.get<HomeRoomListObservableStore>()
|
||||
return HomeActivityViewModel(state, session, selectedGroupStore, homeRoomListObservableSource)
|
||||
val homeActivity: HomeActivity = (viewModelContext as ActivityViewModelContext).activity()
|
||||
return homeActivity.homeActivityViewModelFactory.create(state)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val _isLoading = MutableLiveData<Boolean>()
|
||||
val isLoading: LiveData<Boolean>
|
||||
get() = _isLoading
|
||||
|
@ -41,7 +41,7 @@ import im.vector.riotredesign.features.home.room.list.UnreadCounterBadgeView
|
||||
import im.vector.riotredesign.features.workers.signout.SignOutViewModel
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.android.synthetic.main.fragment_home_detail.*
|
||||
import org.koin.android.ext.android.inject
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
@Parcelize
|
||||
@ -67,7 +67,8 @@ class HomeDetailFragment : VectorBaseFragment(), KeysBackupBanner.Delegate {
|
||||
private val viewModel: HomeDetailViewModel by fragmentViewModel()
|
||||
private lateinit var navigationViewModel: HomeNavigationViewModel
|
||||
|
||||
private val session by inject<Session>()
|
||||
@Inject lateinit var session: Session
|
||||
@Inject lateinit var homeDetailViewModelFactory: HomeDetailViewModel.Factory
|
||||
|
||||
override fun getLayoutResId(): Int {
|
||||
return R.layout.fragment_home_detail
|
||||
@ -76,7 +77,7 @@ class HomeDetailFragment : VectorBaseFragment(), KeysBackupBanner.Delegate {
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
currentDisplayMode = savedInstanceState?.getSerializable(CURRENT_DISPLAY_MODE) as? RoomListFragment.DisplayMode
|
||||
?: RoomListFragment.DisplayMode.HOME
|
||||
?: RoomListFragment.DisplayMode.HOME
|
||||
|
||||
navigationViewModel = ViewModelProviders.of(requireActivity()).get(HomeNavigationViewModel::class.java)
|
||||
|
||||
@ -89,7 +90,7 @@ class HomeDetailFragment : VectorBaseFragment(), KeysBackupBanner.Delegate {
|
||||
private fun setupKeysBackupBanner() {
|
||||
// Keys backup banner
|
||||
// Use the SignOutViewModel, it observe the keys backup state and this is what we need here
|
||||
val model = ViewModelProviders.of(this).get(SignOutViewModel::class.java)
|
||||
val model = ViewModelProviders.of(this, viewModelFactory).get(SignOutViewModel::class.java)
|
||||
|
||||
model.init(session)
|
||||
|
||||
|
@ -16,28 +16,34 @@
|
||||
|
||||
package im.vector.riotredesign.features.home
|
||||
|
||||
import com.airbnb.mvrx.FragmentViewModelContext
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.rx.rx
|
||||
import im.vector.riotredesign.core.platform.VectorViewModel
|
||||
import org.koin.android.ext.android.get
|
||||
|
||||
/**
|
||||
* View model used to update the home bottom bar notification counts
|
||||
*/
|
||||
class HomeDetailViewModel(initialState: HomeDetailViewState,
|
||||
private val session: Session,
|
||||
private val homeRoomListStore: HomeRoomListObservableStore)
|
||||
class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: HomeDetailViewState,
|
||||
private val session: Session,
|
||||
private val homeRoomListStore: HomeRoomListObservableStore)
|
||||
: VectorViewModel<HomeDetailViewState>(initialState) {
|
||||
|
||||
@AssistedInject.Factory
|
||||
interface Factory {
|
||||
fun create(initialState: HomeDetailViewState): HomeDetailViewModel
|
||||
}
|
||||
|
||||
companion object : MvRxViewModelFactory<HomeDetailViewModel, HomeDetailViewState> {
|
||||
|
||||
@JvmStatic
|
||||
override fun create(viewModelContext: ViewModelContext, state: HomeDetailViewState): HomeDetailViewModel? {
|
||||
val homeRoomListStore = viewModelContext.activity.get<HomeRoomListObservableStore>()
|
||||
val session = viewModelContext.activity.get<Session>()
|
||||
return HomeDetailViewModel(state, session, homeRoomListStore)
|
||||
val fragment: HomeDetailFragment = (viewModelContext as FragmentViewModelContext).fragment()
|
||||
return fragment.homeDetailViewModelFactory.create(state)
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,21 +71,21 @@ class HomeDetailViewModel(initialState: HomeDetailViewState,
|
||||
.subscribe { list ->
|
||||
list.let { summaries ->
|
||||
val peopleNotifications = summaries
|
||||
.filter { it.isDirect }
|
||||
.map { it.notificationCount }
|
||||
.takeIf { it.isNotEmpty() }
|
||||
?.sumBy { i -> i }
|
||||
?: 0
|
||||
.filter { it.isDirect }
|
||||
.map { it.notificationCount }
|
||||
.takeIf { it.isNotEmpty() }
|
||||
?.sumBy { i -> i }
|
||||
?: 0
|
||||
val peopleHasHighlight = summaries
|
||||
.filter { it.isDirect }
|
||||
.any { it.highlightCount > 0 }
|
||||
|
||||
val roomsNotifications = summaries
|
||||
.filter { !it.isDirect }
|
||||
.map { it.notificationCount }
|
||||
.takeIf { it.isNotEmpty() }
|
||||
?.sumBy { i -> i }
|
||||
?: 0
|
||||
.filter { !it.isDirect }
|
||||
.map { it.notificationCount }
|
||||
.takeIf { it.isNotEmpty() }
|
||||
?.sumBy { i -> i }
|
||||
?: 0
|
||||
val roomsHasHighlight = summaries
|
||||
.filter { !it.isDirect }
|
||||
.any { it.highlightCount > 0 }
|
||||
|
@ -24,7 +24,7 @@ import im.vector.riotredesign.core.extensions.replaceChildFragment
|
||||
import im.vector.riotredesign.core.platform.VectorBaseFragment
|
||||
import im.vector.riotredesign.features.home.group.GroupListFragment
|
||||
import kotlinx.android.synthetic.main.fragment_home_drawer.*
|
||||
import org.koin.android.ext.android.inject
|
||||
import javax.inject.Inject
|
||||
|
||||
class HomeDrawerFragment : VectorBaseFragment() {
|
||||
|
||||
@ -35,7 +35,7 @@ class HomeDrawerFragment : VectorBaseFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
val session by inject<Session>()
|
||||
@Inject lateinit var session: Session
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_home_drawer
|
||||
|
||||
|
@ -16,89 +16,20 @@
|
||||
|
||||
package im.vector.riotredesign.features.home
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import im.vector.riotredesign.core.glide.GlideApp
|
||||
import im.vector.riotredesign.core.resources.ColorProvider
|
||||
import im.vector.riotredesign.features.autocomplete.command.AutocompleteCommandController
|
||||
import im.vector.riotredesign.features.autocomplete.command.AutocompleteCommandPresenter
|
||||
import im.vector.riotredesign.features.autocomplete.user.AutocompleteUserController
|
||||
import im.vector.riotredesign.features.autocomplete.user.AutocompleteUserPresenter
|
||||
import im.vector.riotredesign.features.home.group.GroupSummaryController
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.factory.*
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.format.NoticeEventFormatter
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
|
||||
import im.vector.riotredesign.features.home.room.list.RoomSummaryController
|
||||
import im.vector.riotredesign.features.html.EventHtmlRenderer
|
||||
import org.koin.core.parameter.parametersOf
|
||||
import org.koin.dsl.module.module
|
||||
import android.os.Handler
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventControllerHandler
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineAsyncHelper
|
||||
|
||||
class HomeModule {
|
||||
@Module
|
||||
object HomeModule {
|
||||
|
||||
companion object {
|
||||
const val HOME_SCOPE = "HOME_SCOPE"
|
||||
const val ROOM_DETAIL_SCOPE = "ROOM_DETAIL_SCOPE"
|
||||
@Provides
|
||||
@JvmStatic
|
||||
@TimelineEventControllerHandler
|
||||
fun providesTimelineBackgroundHandler(): Handler {
|
||||
return TimelineAsyncHelper.getBackgroundHandler()
|
||||
}
|
||||
|
||||
val definition = module {
|
||||
|
||||
// Activity scope
|
||||
|
||||
scope(HOME_SCOPE) {
|
||||
HomeNavigator()
|
||||
}
|
||||
|
||||
scope(HOME_SCOPE) {
|
||||
HomePermalinkHandler(get(), get())
|
||||
}
|
||||
|
||||
// Fragment scopes
|
||||
|
||||
factory {
|
||||
TimelineDateFormatter(get())
|
||||
}
|
||||
|
||||
factory {
|
||||
NoticeEventFormatter(get())
|
||||
}
|
||||
|
||||
factory { (fragment: Fragment) ->
|
||||
val colorProvider = ColorProvider(fragment.requireContext())
|
||||
val timelineDateFormatter = get<TimelineDateFormatter>()
|
||||
val eventHtmlRenderer = EventHtmlRenderer(GlideApp.with(fragment), fragment.requireContext(), get())
|
||||
val noticeEventFormatter = get<NoticeEventFormatter>(parameters = { parametersOf(fragment) })
|
||||
val timelineMediaSizeProvider = TimelineMediaSizeProvider()
|
||||
val messageItemFactory = MessageItemFactory(colorProvider, timelineMediaSizeProvider,
|
||||
timelineDateFormatter, eventHtmlRenderer, get(), get())
|
||||
|
||||
val timelineItemFactory = TimelineItemFactory(
|
||||
messageItemFactory = messageItemFactory,
|
||||
noticeItemFactory = NoticeItemFactory(noticeEventFormatter),
|
||||
defaultItemFactory = DefaultItemFactory(),
|
||||
encryptionItemFactory = EncryptionItemFactory(get()),
|
||||
encryptedItemFactory = EncryptedItemFactory(get())
|
||||
)
|
||||
TimelineEventController(timelineDateFormatter, timelineItemFactory, timelineMediaSizeProvider)
|
||||
}
|
||||
|
||||
factory {
|
||||
RoomSummaryController(get(), get(), get())
|
||||
}
|
||||
|
||||
factory {
|
||||
GroupSummaryController()
|
||||
}
|
||||
|
||||
scope(ROOM_DETAIL_SCOPE) { (fragment: Fragment) ->
|
||||
val commandController = AutocompleteCommandController(get())
|
||||
AutocompleteCommandPresenter(fragment.requireContext(), commandController)
|
||||
}
|
||||
|
||||
scope(ROOM_DETAIL_SCOPE) { (fragment: Fragment) ->
|
||||
val userController = AutocompleteUserController()
|
||||
AutocompleteUserPresenter(fragment.requireContext(), userController)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -17,5 +17,6 @@
|
||||
package im.vector.riotredesign.features.home
|
||||
|
||||
import im.vector.riotredesign.core.mvrx.NavigationViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
class HomeNavigationViewModel : NavigationViewModel<HomeActivity.Navigation>()
|
||||
class HomeNavigationViewModel @Inject constructor() : NavigationViewModel<HomeActivity.Navigation>()
|
@ -24,11 +24,11 @@ import im.vector.riotredesign.core.extensions.replaceFragment
|
||||
import im.vector.riotredesign.features.navigation.Navigator
|
||||
import kotlinx.android.synthetic.main.activity_home.*
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class HomeNavigator {
|
||||
class HomeNavigator @Inject constructor() {
|
||||
|
||||
var activity: HomeActivity? = null
|
||||
|
||||
private var rootRoomId: String? = null
|
||||
|
||||
fun openSelectedGroup(groupSummary: GroupSummary) {
|
||||
|
@ -20,9 +20,10 @@ import android.net.Uri
|
||||
import im.vector.matrix.android.api.permalinks.PermalinkData
|
||||
import im.vector.matrix.android.api.permalinks.PermalinkParser
|
||||
import im.vector.riotredesign.features.navigation.Navigator
|
||||
import javax.inject.Inject
|
||||
|
||||
class HomePermalinkHandler(private val homeNavigator: HomeNavigator,
|
||||
private val navigator: Navigator) {
|
||||
class HomePermalinkHandler @Inject constructor(private val homeNavigator: HomeNavigator,
|
||||
private val navigator: Navigator) {
|
||||
|
||||
fun launch(deepLink: String?) {
|
||||
val uri = deepLink?.let { Uri.parse(it) }
|
||||
@ -35,16 +36,16 @@ class HomePermalinkHandler(private val homeNavigator: HomeNavigator,
|
||||
}
|
||||
val permalinkData = PermalinkParser.parse(deepLink)
|
||||
when (permalinkData) {
|
||||
is PermalinkData.EventLink -> {
|
||||
is PermalinkData.EventLink -> {
|
||||
homeNavigator.openRoomDetail(permalinkData.roomIdOrAlias, permalinkData.eventId, navigator)
|
||||
}
|
||||
is PermalinkData.RoomLink -> {
|
||||
is PermalinkData.RoomLink -> {
|
||||
homeNavigator.openRoomDetail(permalinkData.roomIdOrAlias, null, navigator)
|
||||
}
|
||||
is PermalinkData.GroupLink -> {
|
||||
is PermalinkData.GroupLink -> {
|
||||
homeNavigator.openGroupDetail(permalinkData.groupId)
|
||||
}
|
||||
is PermalinkData.UserLink -> {
|
||||
is PermalinkData.UserLink -> {
|
||||
homeNavigator.openUserDetail(permalinkData.userId)
|
||||
}
|
||||
is PermalinkData.FallbackLink -> {
|
||||
|
@ -21,8 +21,11 @@ import im.vector.riotredesign.core.utils.RxStore
|
||||
import im.vector.riotredesign.features.home.room.list.RoomListDisplayModeFilter
|
||||
import im.vector.riotredesign.features.home.room.list.RoomListFragment
|
||||
import io.reactivex.Observable
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
class HomeRoomListObservableStore : RxStore<List<RoomSummary>>() {
|
||||
@Singleton
|
||||
class HomeRoomListObservableStore @Inject constructor() : RxStore<List<RoomSummary>>() {
|
||||
|
||||
fun observeFilteredBy(displayMode: RoomListFragment.DisplayMode): Observable<List<RoomSummary>> {
|
||||
return observe()
|
||||
|
@ -27,7 +27,7 @@ import im.vector.riotredesign.core.platform.StateView
|
||||
import im.vector.riotredesign.core.platform.VectorBaseFragment
|
||||
import im.vector.riotredesign.features.home.HomeNavigator
|
||||
import kotlinx.android.synthetic.main.fragment_group_list.*
|
||||
import org.koin.android.ext.android.inject
|
||||
import javax.inject.Inject
|
||||
|
||||
class GroupListFragment : VectorBaseFragment(), GroupSummaryController.Callback {
|
||||
|
||||
@ -38,8 +38,10 @@ class GroupListFragment : VectorBaseFragment(), GroupSummaryController.Callback
|
||||
}
|
||||
|
||||
private val viewModel: GroupListViewModel by fragmentViewModel()
|
||||
private val homeNavigator by inject<HomeNavigator>()
|
||||
private val groupController by inject<GroupSummaryController>()
|
||||
|
||||
@Inject lateinit var groupListViewModelFactory: GroupListViewModel.Factory
|
||||
@Inject lateinit var homeNavigator: HomeNavigator
|
||||
@Inject lateinit var groupController: GroupSummaryController
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_group_list
|
||||
|
||||
|
@ -19,8 +19,11 @@ package im.vector.riotredesign.features.home.group
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import arrow.core.Option
|
||||
import com.airbnb.mvrx.FragmentViewModelContext
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.group.model.GroupSummary
|
||||
import im.vector.matrix.rx.rx
|
||||
@ -28,24 +31,27 @@ import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.platform.VectorViewModel
|
||||
import im.vector.riotredesign.core.resources.StringProvider
|
||||
import im.vector.riotredesign.core.utils.LiveEvent
|
||||
import org.koin.android.ext.android.get
|
||||
|
||||
const val ALL_COMMUNITIES_GROUP_ID = "ALL_COMMUNITIES_GROUP_ID"
|
||||
|
||||
class GroupListViewModel(initialState: GroupListViewState,
|
||||
private val selectedGroupHolder: SelectedGroupStore,
|
||||
private val session: Session,
|
||||
private val stringProvider: StringProvider
|
||||
class GroupListViewModel @AssistedInject constructor(@Assisted initialState: GroupListViewState,
|
||||
private val selectedGroupHolder: SelectedGroupStore,
|
||||
private val session: Session,
|
||||
private val stringProvider: StringProvider
|
||||
) : VectorViewModel<GroupListViewState>(initialState) {
|
||||
|
||||
|
||||
@AssistedInject.Factory
|
||||
interface Factory {
|
||||
fun create(initialState: GroupListViewState): GroupListViewModel
|
||||
}
|
||||
|
||||
companion object : MvRxViewModelFactory<GroupListViewModel, GroupListViewState> {
|
||||
|
||||
@JvmStatic
|
||||
override fun create(viewModelContext: ViewModelContext, state: GroupListViewState): GroupListViewModel? {
|
||||
val currentSession = viewModelContext.activity.get<Session>()
|
||||
val selectedGroupHolder = viewModelContext.activity.get<SelectedGroupStore>()
|
||||
val stringProvider = viewModelContext.activity.get<StringProvider>()
|
||||
return GroupListViewModel(state, selectedGroupHolder, currentSession, stringProvider)
|
||||
val groupListFragment: GroupListFragment = (viewModelContext as FragmentViewModelContext).fragment()
|
||||
return groupListFragment.groupListViewModelFactory.create(state)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,8 +18,9 @@ package im.vector.riotredesign.features.home.group
|
||||
|
||||
import com.airbnb.epoxy.TypedEpoxyController
|
||||
import im.vector.matrix.android.api.session.group.model.GroupSummary
|
||||
import javax.inject.Inject
|
||||
|
||||
class GroupSummaryController : TypedEpoxyController<GroupListViewState>() {
|
||||
class GroupSummaryController @Inject constructor(): TypedEpoxyController<GroupListViewState>() {
|
||||
|
||||
var callback: Callback? = null
|
||||
|
||||
|
@ -19,5 +19,8 @@ package im.vector.riotredesign.features.home.group
|
||||
import arrow.core.Option
|
||||
import im.vector.matrix.android.api.session.group.model.GroupSummary
|
||||
import im.vector.riotredesign.core.utils.RxStore
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
class SelectedGroupStore : RxStore<Option<GroupSummary>>(Option.empty())
|
||||
@Singleton
|
||||
class SelectedGroupStore @Inject constructor() : RxStore<Option<GroupSummary>>(Option.empty())
|
||||
|
@ -57,7 +57,13 @@ import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.EditAggregatedSummary
|
||||
import im.vector.matrix.android.api.session.room.model.Membership
|
||||
import im.vector.matrix.android.api.session.room.model.message.*
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageAudioContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageFileContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageImageContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageTextContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageType
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageVideoContent
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.matrix.android.api.session.user.model.User
|
||||
import im.vector.riotredesign.R
|
||||
@ -67,13 +73,19 @@ import im.vector.riotredesign.core.extensions.hideKeyboard
|
||||
import im.vector.riotredesign.core.extensions.observeEvent
|
||||
import im.vector.riotredesign.core.glide.GlideApp
|
||||
import im.vector.riotredesign.core.platform.VectorBaseFragment
|
||||
import im.vector.riotredesign.core.utils.*
|
||||
import im.vector.riotredesign.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
|
||||
import im.vector.riotredesign.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_CAMERA
|
||||
import im.vector.riotredesign.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_NATIVE_CAMERA
|
||||
import im.vector.riotredesign.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_NATIVE_VIDEO_CAMERA
|
||||
import im.vector.riotredesign.core.utils.checkPermissions
|
||||
import im.vector.riotredesign.core.utils.copyToClipboard
|
||||
import im.vector.riotredesign.core.utils.openCamera
|
||||
import im.vector.riotredesign.core.utils.shareMedia
|
||||
import im.vector.riotredesign.features.autocomplete.command.AutocompleteCommandPresenter
|
||||
import im.vector.riotredesign.features.autocomplete.command.CommandAutocompletePolicy
|
||||
import im.vector.riotredesign.features.autocomplete.user.AutocompleteUserPresenter
|
||||
import im.vector.riotredesign.features.command.Command
|
||||
import im.vector.riotredesign.features.home.AvatarRenderer
|
||||
import im.vector.riotredesign.features.home.HomeModule
|
||||
import im.vector.riotredesign.features.home.HomePermalinkHandler
|
||||
import im.vector.riotredesign.features.home.getColorFromUserId
|
||||
import im.vector.riotredesign.features.home.room.detail.composer.TextComposerActions
|
||||
@ -99,14 +111,11 @@ import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.android.synthetic.main.fragment_room_detail.*
|
||||
import kotlinx.android.synthetic.main.merge_composer_layout.view.*
|
||||
import org.commonmark.parser.Parser
|
||||
import org.koin.android.ext.android.inject
|
||||
import org.koin.android.scope.ext.android.bindScope
|
||||
import org.koin.android.scope.ext.android.getOrCreateScope
|
||||
import org.koin.core.parameter.parametersOf
|
||||
import ru.noties.markwon.Markwon
|
||||
import ru.noties.markwon.html.HtmlPlugin
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
@Parcelize
|
||||
@ -156,19 +165,21 @@ class RoomDetailFragment :
|
||||
}
|
||||
|
||||
private val roomDetailArgs: RoomDetailArgs by args()
|
||||
private val session by inject<Session>()
|
||||
private val glideRequests by lazy {
|
||||
GlideApp.with(this)
|
||||
}
|
||||
|
||||
private val roomDetailViewModel: RoomDetailViewModel by fragmentViewModel()
|
||||
private val textComposerViewModel: TextComposerViewModel by fragmentViewModel()
|
||||
private val timelineEventController: TimelineEventController by inject { parametersOf(this) }
|
||||
private val commandAutocompletePolicy = CommandAutocompletePolicy()
|
||||
private val autocompleteCommandPresenter: AutocompleteCommandPresenter by inject { parametersOf(this) }
|
||||
private val autocompleteUserPresenter: AutocompleteUserPresenter by inject { parametersOf(this) }
|
||||
private val homePermalinkHandler: HomePermalinkHandler by inject()
|
||||
|
||||
@Inject lateinit var session: Session
|
||||
@Inject lateinit var timelineEventController: TimelineEventController
|
||||
@Inject lateinit var commandAutocompletePolicy: CommandAutocompletePolicy
|
||||
@Inject lateinit var autocompleteCommandPresenter: AutocompleteCommandPresenter
|
||||
@Inject lateinit var autocompleteUserPresenter: AutocompleteUserPresenter
|
||||
@Inject lateinit var homePermalinkHandler: HomePermalinkHandler
|
||||
@Inject lateinit var roomDetailViewModelFactory: RoomDetailViewModel.Factory
|
||||
@Inject lateinit var textComposerViewModelFactory: TextComposerViewModel.Factory
|
||||
private lateinit var scrollOnNewMessageCallback: ScrollOnNewMessageCallback
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_room_detail
|
||||
@ -179,9 +190,9 @@ class RoomDetailFragment :
|
||||
lateinit var composerLayout: TextComposerView
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
injector().inject(this)
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
actionViewModel = ViewModelProviders.of(requireActivity()).get(ActionsHandler::class.java)
|
||||
bindScope(getOrCreateScope(HomeModule.ROOM_DETAIL_SCOPE))
|
||||
setupToolbar(roomToolbar)
|
||||
setupRecyclerView()
|
||||
setupComposer()
|
||||
@ -229,18 +240,18 @@ class RoomDetailFragment :
|
||||
//TODO this is used at several places, find way to refactor?
|
||||
val messageContent: MessageContent? =
|
||||
event.annotations?.editSummary?.aggregatedContent?.toModel()
|
||||
?: event.root.content.toModel()
|
||||
?: event.root.content.toModel()
|
||||
val nonFormattedBody = messageContent?.body ?: ""
|
||||
var formattedBody: CharSequence? = null
|
||||
if (messageContent is MessageTextContent && messageContent.format == MessageType.FORMAT_MATRIX_HTML) {
|
||||
val parser = Parser.builder().build()
|
||||
val document = parser.parse(messageContent.formattedBody
|
||||
?: messageContent.body)
|
||||
?: messageContent.body)
|
||||
formattedBody = Markwon.builder(requireContext())
|
||||
.usePlugin(HtmlPlugin.create()).build().render(document)
|
||||
}
|
||||
composerLayout.composerRelatedMessageContent.text = formattedBody
|
||||
?: nonFormattedBody
|
||||
?: nonFormattedBody
|
||||
|
||||
|
||||
if (mode == SendMode.EDIT) {
|
||||
@ -256,7 +267,7 @@ class RoomDetailFragment :
|
||||
}
|
||||
|
||||
AvatarRenderer.render(event.senderAvatar, event.root.sender
|
||||
?: "", event.senderName, composerLayout.composerRelatedMessageAvatar)
|
||||
?: "", event.senderName, composerLayout.composerRelatedMessageAvatar)
|
||||
|
||||
composerLayout.composerEditText.setSelection(composerLayout.composerEditText.text.length)
|
||||
composerLayout.expand {
|
||||
@ -279,9 +290,9 @@ class RoomDetailFragment :
|
||||
REQUEST_FILES_REQUEST_CODE, TAKE_IMAGE_REQUEST_CODE -> handleMediaIntent(data)
|
||||
REACTION_SELECT_REQUEST_CODE -> {
|
||||
val eventId = data.getStringExtra(EmojiReactionPickerActivity.EXTRA_EVENT_ID)
|
||||
?: return
|
||||
?: return
|
||||
val reaction = data.getStringExtra(EmojiReactionPickerActivity.EXTRA_REACTION_RESULT)
|
||||
?: return
|
||||
?: return
|
||||
//TODO check if already reacted with that?
|
||||
roomDetailViewModel.process(RoomDetailActions.SendReaction(reaction, eventId))
|
||||
}
|
||||
@ -363,7 +374,7 @@ class RoomDetailFragment :
|
||||
|
||||
// Add the span
|
||||
val user = session.getUser(item.userId)
|
||||
val span = PillImageSpan(glideRequests, context!!, item.userId, user)
|
||||
val span = PillImageSpan(glideRequests, requireContext(), item.userId, user)
|
||||
span.bind(composerLayout.composerEditText)
|
||||
|
||||
editable.setSpan(span, startIndex, startIndex + displayName.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
@ -619,7 +630,7 @@ class RoomDetailFragment :
|
||||
}
|
||||
MessageMenuViewModel.ACTION_VIEW_REACTIONS -> {
|
||||
val messageInformationData = actionData.data as? MessageInformationData
|
||||
?: return
|
||||
?: return
|
||||
ViewReactionBottomSheet.newInstance(roomDetailArgs.roomId, messageInformationData)
|
||||
.show(requireActivity().supportFragmentManager, "DISPLAY_REACTIONS")
|
||||
}
|
||||
|
@ -19,10 +19,13 @@ package im.vector.riotredesign.features.home.room.detail
|
||||
import android.text.TextUtils
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.airbnb.mvrx.FragmentViewModelContext
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import com.jakewharton.rxrelay2.BehaviorRelay
|
||||
import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.content.ContentAttachmentData
|
||||
@ -41,14 +44,13 @@ import im.vector.riotredesign.features.home.room.detail.timeline.helper.Timeline
|
||||
import io.reactivex.rxkotlin.subscribeBy
|
||||
import org.commonmark.parser.Parser
|
||||
import org.commonmark.renderer.html.HtmlRenderer
|
||||
import org.koin.android.ext.android.get
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
|
||||
class RoomDetailViewModel(initialState: RoomDetailViewState,
|
||||
private val session: Session
|
||||
class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: RoomDetailViewState,
|
||||
private val session: Session
|
||||
) : VectorViewModel<RoomDetailViewState>(initialState) {
|
||||
|
||||
private val room = session.getRoom(initialState.roomId)!!
|
||||
@ -62,14 +64,19 @@ class RoomDetailViewModel(initialState: RoomDetailViewState,
|
||||
}
|
||||
private val timeline = room.createTimeline(eventId, allowedTypes)
|
||||
|
||||
@AssistedInject.Factory
|
||||
interface Factory {
|
||||
fun create(initialState: RoomDetailViewState): RoomDetailViewModel
|
||||
}
|
||||
|
||||
companion object : MvRxViewModelFactory<RoomDetailViewModel, RoomDetailViewState> {
|
||||
|
||||
const val PAGINATION_COUNT = 50
|
||||
|
||||
@JvmStatic
|
||||
override fun create(viewModelContext: ViewModelContext, state: RoomDetailViewState): RoomDetailViewModel? {
|
||||
val currentSession = viewModelContext.activity.get<Session>()
|
||||
return RoomDetailViewModel(state, currentSession)
|
||||
val fragment: RoomDetailFragment = (viewModelContext as FragmentViewModelContext).fragment()
|
||||
return fragment.roomDetailViewModelFactory.create(state)
|
||||
}
|
||||
}
|
||||
|
||||
@ -201,7 +208,7 @@ class RoomDetailViewModel(initialState: RoomDetailViewState,
|
||||
}
|
||||
SendMode.EDIT -> {
|
||||
room.editTextMessage(state.selectedEvent?.root?.eventId
|
||||
?: "", action.text, action.autoMarkdown)
|
||||
?: "", action.text, action.autoMarkdown)
|
||||
setState {
|
||||
copy(
|
||||
sendMode = SendMode.REGULAR,
|
||||
@ -213,7 +220,7 @@ class RoomDetailViewModel(initialState: RoomDetailViewState,
|
||||
SendMode.QUOTE -> {
|
||||
val messageContent: MessageContent? =
|
||||
state.selectedEvent?.annotations?.editSummary?.aggregatedContent?.toModel()
|
||||
?: state.selectedEvent?.root?.content.toModel()
|
||||
?: state.selectedEvent?.root?.content.toModel()
|
||||
val textMsg = messageContent?.body
|
||||
|
||||
val finalText = legacyRiotQuoteText(textMsg, action.text)
|
||||
|
@ -17,35 +17,44 @@
|
||||
package im.vector.riotredesign.features.home.room.detail.composer
|
||||
|
||||
import arrow.core.Option
|
||||
import com.airbnb.mvrx.FragmentViewModelContext
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import com.jakewharton.rxrelay2.BehaviorRelay
|
||||
import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.user.model.User
|
||||
import im.vector.matrix.rx.rx
|
||||
import im.vector.riotredesign.core.platform.VectorViewModel
|
||||
import im.vector.riotredesign.features.home.room.detail.RoomDetailFragment
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.functions.BiFunction
|
||||
import org.koin.android.ext.android.get
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
typealias AutocompleteUserQuery = CharSequence
|
||||
|
||||
class TextComposerViewModel(initialState: TextComposerViewState,
|
||||
private val session: Session
|
||||
class TextComposerViewModel @AssistedInject constructor(@Assisted initialState: TextComposerViewState,
|
||||
private val session: Session
|
||||
) : VectorViewModel<TextComposerViewState>(initialState) {
|
||||
|
||||
|
||||
private val room = session.getRoom(initialState.roomId)!!
|
||||
private val roomId = initialState.roomId
|
||||
|
||||
private val usersQueryObservable = BehaviorRelay.create<Option<AutocompleteUserQuery>>()
|
||||
|
||||
@AssistedInject.Factory
|
||||
interface Factory {
|
||||
fun create(initialState: TextComposerViewState): TextComposerViewModel
|
||||
}
|
||||
|
||||
companion object : MvRxViewModelFactory<TextComposerViewModel, TextComposerViewState> {
|
||||
|
||||
@JvmStatic
|
||||
override fun create(viewModelContext: ViewModelContext, state: TextComposerViewState): TextComposerViewModel? {
|
||||
val currentSession = viewModelContext.activity.get<Session>()
|
||||
return TextComposerViewModel(state, currentSession)
|
||||
val fragment : RoomDetailFragment = (viewModelContext as FragmentViewModelContext).fragment()
|
||||
return fragment.textComposerViewModelFactory.create(state)
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,7 +89,7 @@ class TextComposerViewModel(initialState: TextComposerViewState,
|
||||
} else {
|
||||
users.filter {
|
||||
it.displayName?.startsWith(prefix = filter, ignoreCase = true)
|
||||
?: false
|
||||
?: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,13 +25,25 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import com.airbnb.epoxy.EpoxyController
|
||||
import com.airbnb.epoxy.EpoxyModel
|
||||
import im.vector.matrix.android.api.session.room.model.EditAggregatedSummary
|
||||
import im.vector.matrix.android.api.session.room.model.message.*
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageAudioContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageFileContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageImageContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageVideoContent
|
||||
import im.vector.matrix.android.api.session.room.timeline.Timeline
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.riotredesign.core.epoxy.LoadingItem_
|
||||
import im.vector.riotredesign.core.extensions.localDateTime
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.factory.TimelineItemFactory
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.*
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineEventDiffUtilCallback
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineEventVisibilityStateChangedListener
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.canBeMerged
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.nextDisplayableEvent
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.prevSameTypeEvents
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.senderAvatar
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.senderName
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.DaySeparatorItem
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.DaySeparatorItem_
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.MergedHeaderItem
|
||||
@ -39,11 +51,12 @@ import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInf
|
||||
import im.vector.riotredesign.features.media.ImageContentRenderer
|
||||
import im.vector.riotredesign.features.media.VideoContentRenderer
|
||||
import org.threeten.bp.LocalDateTime
|
||||
import javax.inject.Inject
|
||||
|
||||
class TimelineEventController(private val dateFormatter: TimelineDateFormatter,
|
||||
private val timelineItemFactory: TimelineItemFactory,
|
||||
private val timelineMediaSizeProvider: TimelineMediaSizeProvider,
|
||||
private val backgroundHandler: Handler = TimelineAsyncHelper.getBackgroundHandler()
|
||||
class TimelineEventController @Inject constructor(private val dateFormatter: TimelineDateFormatter,
|
||||
private val timelineItemFactory: TimelineItemFactory,
|
||||
private val timelineMediaSizeProvider: TimelineMediaSizeProvider,
|
||||
@TimelineEventControllerHandler private val backgroundHandler: Handler
|
||||
) : EpoxyController(backgroundHandler, backgroundHandler), Timeline.Listener {
|
||||
|
||||
interface Callback : ReactionPillCallback, AvatarCallback, BaseCallback {
|
||||
@ -175,8 +188,8 @@ class TimelineEventController(private val dateFormatter: TimelineDateFormatter,
|
||||
// Should be build if not cached or if cached but contains mergedHeader or formattedDay
|
||||
// We then are sure we always have items up to date.
|
||||
if (modelCache[position] == null
|
||||
|| modelCache[position]?.mergedHeaderModel != null
|
||||
|| modelCache[position]?.formattedDayModel != null) {
|
||||
|| modelCache[position]?.mergedHeaderModel != null
|
||||
|| modelCache[position]?.formattedDayModel != null) {
|
||||
modelCache[position] = buildItemModels(position, currentSnapshot)
|
||||
}
|
||||
}
|
||||
@ -248,7 +261,7 @@ class TimelineEventController(private val dateFormatter: TimelineDateFormatter,
|
||||
// => handle case where paginating from mergeable events and we get more
|
||||
val previousCollapseStateKey = mergedEventIds.intersect(mergeItemCollapseStates.keys).firstOrNull()
|
||||
val initialCollapseState = mergeItemCollapseStates.remove(previousCollapseStateKey)
|
||||
?: true
|
||||
?: true
|
||||
val isCollapsed = mergeItemCollapseStates.getOrPut(event.localId) { initialCollapseState }
|
||||
if (isCollapsed) {
|
||||
collapsedEventIds.addAll(mergedEventIds)
|
||||
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.riotredesign.features.home.room.detail.timeline
|
||||
|
||||
import javax.inject.Qualifier
|
||||
|
||||
@Qualifier
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class TimelineEventControllerHandler
|
@ -18,11 +18,12 @@ package im.vector.riotredesign.features.home.room.detail.timeline.action
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import im.vector.riotredesign.core.utils.LiveEvent
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Activity shared view model to handle message actions
|
||||
*/
|
||||
class ActionsHandler : ViewModel() {
|
||||
class ActionsHandler @Inject constructor() : ViewModel() {
|
||||
|
||||
data class ActionData(
|
||||
val actionId: String,
|
||||
|
@ -36,6 +36,7 @@ import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.features.home.AvatarRenderer
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
|
||||
import kotlinx.android.synthetic.main.bottom_sheet_message_actions.*
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Bottom sheet fragment that shows a message preview with list of contextual actions
|
||||
@ -43,6 +44,7 @@ import kotlinx.android.synthetic.main.bottom_sheet_message_actions.*
|
||||
*/
|
||||
class MessageActionsBottomSheet : BaseMvRxBottomSheetDialog() {
|
||||
|
||||
@Inject lateinit var messageActionViewModelFactory: MessageActionsViewModel.Factory
|
||||
private val viewModel: MessageActionsViewModel by fragmentViewModel(MessageActionsViewModel::class)
|
||||
|
||||
private lateinit var actionHandlerModel: ActionsHandler
|
||||
|
@ -19,6 +19,8 @@ import com.airbnb.mvrx.FragmentViewModelContext
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.events.model.EventType
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
@ -27,17 +29,16 @@ import im.vector.matrix.android.api.session.room.model.message.MessageTextConten
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageType
|
||||
import im.vector.riotredesign.core.platform.VectorViewModel
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.format.NoticeEventFormatter
|
||||
import org.commonmark.parser.Parser
|
||||
import org.koin.android.ext.android.get
|
||||
import org.koin.core.parameter.parametersOf
|
||||
import ru.noties.markwon.Markwon
|
||||
import ru.noties.markwon.html.HtmlPlugin
|
||||
import timber.log.Timber
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
|
||||
import im.vector.riotredesign.features.html.EventHtmlRenderer
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
|
||||
data class MessageActionState(
|
||||
val roomId: String,
|
||||
val eventId: String,
|
||||
val informationData: MessageInformationData,
|
||||
val userId: String = "",
|
||||
val senderName: String = "",
|
||||
val messageBody: CharSequence? = null,
|
||||
@ -45,64 +46,77 @@ data class MessageActionState(
|
||||
val showPreview: Boolean = false,
|
||||
val canReact: Boolean = false,
|
||||
val senderAvatarPath: String? = null)
|
||||
: MvRxState
|
||||
: MvRxState {
|
||||
|
||||
constructor(args: TimelineEventFragmentArgs) : this(roomId = args.roomId, eventId = args.eventId, informationData = args.informationData)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Information related to an event and used to display preview in contextual bottomsheet.
|
||||
*/
|
||||
class MessageActionsViewModel(initialState: MessageActionState) : VectorViewModel<MessageActionState>(initialState) {
|
||||
class MessageActionsViewModel @AssistedInject constructor(@Assisted initialState: MessageActionState,
|
||||
private val eventHtmlRenderer: EventHtmlRenderer,
|
||||
private val session: Session,
|
||||
private val noticeEventFormatter: NoticeEventFormatter
|
||||
) : VectorViewModel<MessageActionState>(initialState) {
|
||||
|
||||
private val roomId = initialState.roomId
|
||||
private val eventId = initialState.eventId
|
||||
private val informationData = initialState.informationData
|
||||
|
||||
@AssistedInject.Factory
|
||||
interface Factory {
|
||||
fun create(initialState: MessageActionState): MessageActionsViewModel
|
||||
}
|
||||
|
||||
companion object : MvRxViewModelFactory<MessageActionsViewModel, MessageActionState> {
|
||||
|
||||
override fun initialState(viewModelContext: ViewModelContext): MessageActionState? {
|
||||
val currentSession = viewModelContext.activity.get<Session>()
|
||||
val fragment = (viewModelContext as? FragmentViewModelContext)?.fragment
|
||||
val noticeFormatter = fragment?.get<NoticeEventFormatter>(parameters = { parametersOf(fragment) })
|
||||
val parcel = viewModelContext.args as TimelineEventFragmentArgs
|
||||
|
||||
val dateFormat = SimpleDateFormat("EEE, d MMM yyyy HH:mm", Locale.getDefault())
|
||||
|
||||
val event = currentSession.getRoom(parcel.roomId)?.getTimeLineEvent(parcel.eventId)
|
||||
var body: CharSequence? = null
|
||||
val originTs = event?.root?.originServerTs
|
||||
return if (event != null) {
|
||||
when (event.root.type) {
|
||||
EventType.MESSAGE -> {
|
||||
val messageContent: MessageContent? = event.annotations?.editSummary?.aggregatedContent?.toModel()
|
||||
?: event.root.content.toModel()
|
||||
body = messageContent?.body
|
||||
if (messageContent is MessageTextContent && messageContent.format == MessageType.FORMAT_MATRIX_HTML) {
|
||||
val parser = Parser.builder().build()
|
||||
val document = parser.parse(messageContent.formattedBody
|
||||
?: messageContent.body)
|
||||
body = Markwon.builder(viewModelContext.activity)
|
||||
.usePlugin(HtmlPlugin.create()).build().render(document)
|
||||
}
|
||||
}
|
||||
EventType.STATE_ROOM_NAME,
|
||||
EventType.STATE_ROOM_TOPIC,
|
||||
EventType.STATE_ROOM_MEMBER,
|
||||
EventType.STATE_HISTORY_VISIBILITY,
|
||||
EventType.CALL_INVITE,
|
||||
EventType.CALL_HANGUP,
|
||||
EventType.CALL_ANSWER -> {
|
||||
body = noticeFormatter?.format(event)
|
||||
}
|
||||
}
|
||||
MessageActionState(
|
||||
userId = event.root.sender ?: "",
|
||||
senderName = parcel.informationData.memberName?.toString() ?: "",
|
||||
messageBody = body,
|
||||
ts = dateFormat.format(Date(originTs ?: 0)),
|
||||
showPreview = body != null,
|
||||
canReact = event.root.type == EventType.MESSAGE && event.sendState.isSent(),
|
||||
senderAvatarPath = parcel.informationData.avatarUrl
|
||||
)
|
||||
} else {
|
||||
//can this happen?
|
||||
Timber.e("Failed to retrieve event")
|
||||
null
|
||||
}
|
||||
override fun create(viewModelContext: ViewModelContext, state: MessageActionState): MessageActionsViewModel? {
|
||||
val fragment: MessageActionsBottomSheet = (viewModelContext as FragmentViewModelContext).fragment()
|
||||
return fragment.messageActionViewModelFactory.create(state)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
init {
|
||||
setState { reduceState(this) }
|
||||
}
|
||||
|
||||
private fun reduceState(state: MessageActionState): MessageActionState {
|
||||
val dateFormat = SimpleDateFormat("EEE, d MMM yyyy HH:mm", Locale.getDefault())
|
||||
val event = session.getRoom(roomId)?.getTimeLineEvent(eventId) ?: return state
|
||||
var body: CharSequence? = null
|
||||
val originTs = event.root.originServerTs
|
||||
when (event.root.type) {
|
||||
EventType.MESSAGE -> {
|
||||
val messageContent: MessageContent? = event.annotations?.editSummary?.aggregatedContent?.toModel()
|
||||
?: event.root.content.toModel()
|
||||
body = messageContent?.body
|
||||
if (messageContent is MessageTextContent && messageContent.format == MessageType.FORMAT_MATRIX_HTML) {
|
||||
body = eventHtmlRenderer.render(messageContent.formattedBody
|
||||
?: messageContent.body)
|
||||
}
|
||||
}
|
||||
EventType.STATE_ROOM_NAME,
|
||||
EventType.STATE_ROOM_TOPIC,
|
||||
EventType.STATE_ROOM_MEMBER,
|
||||
EventType.STATE_HISTORY_VISIBILITY,
|
||||
EventType.CALL_INVITE,
|
||||
EventType.CALL_HANGUP,
|
||||
EventType.CALL_ANSWER -> {
|
||||
body = noticeEventFormatter.format(event)
|
||||
}
|
||||
}
|
||||
return state.copy(
|
||||
userId = event.root.sender ?: "",
|
||||
senderName = informationData.memberName?.toString() ?: "",
|
||||
messageBody = body,
|
||||
ts = dateFormat.format(Date(originTs ?: 0)),
|
||||
showPreview = body != null,
|
||||
canReact = event.root.type == EventType.MESSAGE && event.sendState.isSent(),
|
||||
senderAvatarPath = informationData.avatarUrl
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -29,16 +29,16 @@ import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.features.themes.ThemeUtils
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Fragment showing the list of available contextual action for a given message.
|
||||
*/
|
||||
class MessageMenuFragment : BaseMvRxFragment() {
|
||||
|
||||
@Inject lateinit var messageMenuViewModelFactory: MessageMenuViewModel.Factory
|
||||
private val viewModel: MessageMenuViewModel by fragmentViewModel(MessageMenuViewModel::class)
|
||||
|
||||
private var addSeparators = false
|
||||
|
||||
var interactionListener: InteractionListener? = null
|
||||
|
||||
override fun invalidate() = withState(viewModel) { state ->
|
||||
|
@ -15,9 +15,12 @@
|
||||
*/
|
||||
package im.vector.riotredesign.features.home.room.detail.timeline.action
|
||||
|
||||
import com.airbnb.mvrx.FragmentViewModelContext
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.events.model.EventType
|
||||
import im.vector.matrix.android.api.session.events.model.toContent
|
||||
@ -29,44 +32,76 @@ import im.vector.matrix.android.api.session.room.send.SendState
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.platform.VectorViewModel
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
|
||||
import org.json.JSONObject
|
||||
import org.koin.android.ext.android.get
|
||||
|
||||
data class SimpleAction(val uid: String, val titleRes: Int, val iconResId: Int?, val data: Any? = null)
|
||||
|
||||
data class MessageMenuState(val actions: List<SimpleAction> = emptyList()) : MvRxState
|
||||
data class MessageMenuState(
|
||||
val roomId: String,
|
||||
val eventId: String,
|
||||
val informationData: MessageInformationData,
|
||||
val actions: List<SimpleAction> = emptyList()
|
||||
) : MvRxState {
|
||||
|
||||
constructor(args: TimelineEventFragmentArgs) : this(roomId = args.roomId, eventId = args.eventId, informationData = args.informationData)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages list actions for a given message (copy / paste / forward...)
|
||||
*/
|
||||
class MessageMenuViewModel(initialState: MessageMenuState) : VectorViewModel<MessageMenuState>(initialState) {
|
||||
class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: MessageMenuState,
|
||||
private val session: Session) : VectorViewModel<MessageMenuState>(initialState) {
|
||||
|
||||
@AssistedInject.Factory
|
||||
interface Factory {
|
||||
fun create(initialState: MessageMenuState): MessageMenuViewModel
|
||||
}
|
||||
|
||||
companion object : MvRxViewModelFactory<MessageMenuViewModel, MessageMenuState> {
|
||||
|
||||
override fun initialState(viewModelContext: ViewModelContext): MessageMenuState? {
|
||||
// Args are accessible from the context.
|
||||
val currentSession = viewModelContext.activity.get<Session>()
|
||||
val parcel = viewModelContext.args as TimelineEventFragmentArgs
|
||||
val event = currentSession.getRoom(parcel.roomId)?.getTimeLineEvent(parcel.eventId)
|
||||
?: return null
|
||||
const val ACTION_ADD_REACTION = "add_reaction"
|
||||
const val ACTION_COPY = "copy"
|
||||
const val ACTION_EDIT = "edit"
|
||||
const val ACTION_QUOTE = "quote"
|
||||
const val ACTION_REPLY = "reply"
|
||||
const val ACTION_SHARE = "share"
|
||||
const val ACTION_RESEND = "resend"
|
||||
const val ACTION_DELETE = "delete"
|
||||
const val VIEW_SOURCE = "VIEW_SOURCE"
|
||||
const val VIEW_DECRYPTED_SOURCE = "VIEW_DECRYPTED_SOURCE"
|
||||
const val ACTION_COPY_PERMALINK = "ACTION_COPY_PERMALINK"
|
||||
const val ACTION_FLAG = "ACTION_FLAG"
|
||||
const val ACTION_QUICK_REACT = "ACTION_QUICK_REACT"
|
||||
const val ACTION_VIEW_REACTIONS = "ACTION_VIEW_REACTIONS"
|
||||
|
||||
val messageContent: MessageContent? = event.annotations?.editSummary?.aggregatedContent?.toModel()
|
||||
?: event.root.content.toModel()
|
||||
val type = messageContent?.type
|
||||
override fun create(viewModelContext: ViewModelContext, state: MessageMenuState): MessageMenuViewModel? {
|
||||
val fragment: MessageMenuFragment = (viewModelContext as FragmentViewModelContext).fragment()
|
||||
return fragment.messageMenuViewModelFactory.create(state)
|
||||
}
|
||||
}
|
||||
|
||||
if (!event.sendState.isSent()) {
|
||||
//Resend and Delete
|
||||
return MessageMenuState(
|
||||
//TODO
|
||||
listOf(
|
||||
init {
|
||||
setState { reduceState(this) }
|
||||
}
|
||||
|
||||
private fun reduceState(state: MessageMenuState): MessageMenuState {
|
||||
val event = session.getRoom(state.roomId)?.getTimeLineEvent(state.eventId) ?: return state
|
||||
|
||||
val messageContent: MessageContent? = event.annotations?.editSummary?.aggregatedContent?.toModel()
|
||||
?: event.root.content.toModel()
|
||||
val type = messageContent?.type
|
||||
|
||||
val actions = if (!event.sendState.isSent()) {
|
||||
//Resend and Delete
|
||||
listOf<SimpleAction>(
|
||||
// SimpleAction(ACTION_RESEND, R.string.resend, R.drawable.ic_send, event.root.eventId),
|
||||
// //TODO delete icon
|
||||
// SimpleAction(ACTION_DELETE, R.string.delete, R.drawable.ic_delete, event.root.eventId)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val actions = ArrayList<SimpleAction>().apply {
|
||||
)
|
||||
} else {
|
||||
ArrayList<SimpleAction>().apply {
|
||||
|
||||
if (event.sendState == SendState.SENDING) {
|
||||
//TODO add cancel?
|
||||
@ -86,28 +121,28 @@ class MessageMenuViewModel(initialState: MessageMenuState) : VectorViewModel<Mes
|
||||
this.add(SimpleAction(ACTION_REPLY, R.string.reply, R.drawable.ic_reply, event.root.eventId))
|
||||
}
|
||||
|
||||
if (canEdit(event, currentSession.sessionParams.credentials.userId)) {
|
||||
if (canEdit(event, session.sessionParams.credentials.userId)) {
|
||||
this.add(SimpleAction(ACTION_EDIT, R.string.edit, R.drawable.ic_edit, event.root.eventId))
|
||||
}
|
||||
|
||||
if (canRedact(event, currentSession.sessionParams.credentials.userId)) {
|
||||
if (canRedact(event, session.sessionParams.credentials.userId)) {
|
||||
this.add(SimpleAction(ACTION_DELETE, R.string.delete, R.drawable.ic_delete, event.root.eventId))
|
||||
}
|
||||
|
||||
if (canQuote(event, messageContent)) {
|
||||
this.add(SimpleAction(ACTION_QUOTE, R.string.quote, R.drawable.ic_quote, parcel.eventId))
|
||||
this.add(SimpleAction(ACTION_QUOTE, R.string.quote, R.drawable.ic_quote, state.eventId))
|
||||
}
|
||||
|
||||
if (canViewReactions(event)) {
|
||||
this.add(SimpleAction(ACTION_VIEW_REACTIONS, R.string.message_view_reaction, R.drawable.ic_view_reactions, parcel.informationData))
|
||||
this.add(SimpleAction(ACTION_VIEW_REACTIONS, R.string.message_view_reaction, R.drawable.ic_view_reactions, state.informationData))
|
||||
}
|
||||
|
||||
if (canShare(type)) {
|
||||
if (messageContent is MessageImageContent) {
|
||||
this.add(
|
||||
SimpleAction(ACTION_SHARE,
|
||||
R.string.share, R.drawable.ic_share,
|
||||
currentSession.contentUrlResolver().resolveFullSize(messageContent.url))
|
||||
R.string.share, R.drawable.ic_share,
|
||||
session.contentUrlResolver().resolveFullSize(messageContent.url))
|
||||
)
|
||||
}
|
||||
//TODO
|
||||
@ -123,120 +158,103 @@ class MessageMenuViewModel(initialState: MessageMenuState) : VectorViewModel<Mes
|
||||
|
||||
this.add(SimpleAction(VIEW_SOURCE, R.string.view_source, R.drawable.ic_view_source, JSONObject(event.root.toContent()).toString(4)))
|
||||
if (event.isEncrypted()) {
|
||||
this.add(SimpleAction(VIEW_DECRYPTED_SOURCE, R.string.view_decrypted_source, R.drawable.ic_view_source, parcel.eventId))
|
||||
this.add(SimpleAction(VIEW_DECRYPTED_SOURCE, R.string.view_decrypted_source, R.drawable.ic_view_source, state.eventId))
|
||||
}
|
||||
this.add(SimpleAction(ACTION_COPY_PERMALINK, R.string.permalink, R.drawable.ic_permalink, parcel.eventId))
|
||||
this.add(SimpleAction(ACTION_COPY_PERMALINK, R.string.permalink, R.drawable.ic_permalink, state.eventId))
|
||||
|
||||
if (currentSession.sessionParams.credentials.userId != event.root.sender && event.root.getClearType() == EventType.MESSAGE) {
|
||||
if (session.sessionParams.credentials.userId != event.root.sender && event.root.getClearType() == EventType.MESSAGE) {
|
||||
//not sent by me
|
||||
this.add(SimpleAction(ACTION_FLAG, R.string.report_content, R.drawable.ic_flag, parcel.eventId))
|
||||
this.add(SimpleAction(ACTION_FLAG, R.string.report_content, R.drawable.ic_flag, state.eventId))
|
||||
}
|
||||
}
|
||||
|
||||
return MessageMenuState(actions)
|
||||
}
|
||||
return state.copy(actions = actions)
|
||||
}
|
||||
|
||||
private fun canReply(event: TimelineEvent, messageContent: MessageContent?): Boolean {
|
||||
//Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
|
||||
if (event.root.getClearType() != EventType.MESSAGE) return false
|
||||
return when (messageContent?.type) {
|
||||
MessageType.MSGTYPE_TEXT,
|
||||
MessageType.MSGTYPE_NOTICE,
|
||||
MessageType.MSGTYPE_EMOTE,
|
||||
MessageType.MSGTYPE_IMAGE,
|
||||
MessageType.MSGTYPE_VIDEO,
|
||||
MessageType.MSGTYPE_AUDIO,
|
||||
MessageType.MSGTYPE_FILE -> true
|
||||
else -> false
|
||||
|
||||
private fun canReply(event: TimelineEvent, messageContent: MessageContent?): Boolean {
|
||||
//Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
|
||||
if (event.root.getClearType() != EventType.MESSAGE) return false
|
||||
return when (messageContent?.type) {
|
||||
MessageType.MSGTYPE_TEXT,
|
||||
MessageType.MSGTYPE_NOTICE,
|
||||
MessageType.MSGTYPE_EMOTE,
|
||||
MessageType.MSGTYPE_IMAGE,
|
||||
MessageType.MSGTYPE_VIDEO,
|
||||
MessageType.MSGTYPE_AUDIO,
|
||||
MessageType.MSGTYPE_FILE -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun canReact(event: TimelineEvent, messageContent: MessageContent?): Boolean {
|
||||
//Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
|
||||
return event.root.getClearType() == EventType.MESSAGE
|
||||
}
|
||||
|
||||
private fun canQuote(event: TimelineEvent, messageContent: MessageContent?): Boolean {
|
||||
//Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
|
||||
if (event.root.getClearType() != EventType.MESSAGE) return false
|
||||
return when (messageContent?.type) {
|
||||
MessageType.MSGTYPE_TEXT,
|
||||
MessageType.MSGTYPE_NOTICE,
|
||||
MessageType.MSGTYPE_EMOTE,
|
||||
MessageType.FORMAT_MATRIX_HTML,
|
||||
MessageType.MSGTYPE_LOCATION -> {
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun canReact(event: TimelineEvent, messageContent: MessageContent?): Boolean {
|
||||
//Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
|
||||
return event.root.getClearType() == EventType.MESSAGE
|
||||
}
|
||||
private fun canRedact(event: TimelineEvent, myUserId: String): Boolean {
|
||||
//Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
|
||||
if (event.root.getClearType() != EventType.MESSAGE) return false
|
||||
//TODO if user is admin or moderator
|
||||
return event.root.sender == myUserId
|
||||
}
|
||||
|
||||
private fun canQuote(event: TimelineEvent, messageContent: MessageContent?): Boolean {
|
||||
//Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
|
||||
if (event.root.getClearType() != EventType.MESSAGE) return false
|
||||
return when (messageContent?.type) {
|
||||
MessageType.MSGTYPE_TEXT,
|
||||
MessageType.MSGTYPE_NOTICE,
|
||||
MessageType.MSGTYPE_EMOTE,
|
||||
MessageType.FORMAT_MATRIX_HTML,
|
||||
MessageType.MSGTYPE_LOCATION -> {
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
private fun canViewReactions(event: TimelineEvent): Boolean {
|
||||
//Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
|
||||
if (event.root.getClearType() != EventType.MESSAGE) return false
|
||||
//TODO if user is admin or moderator
|
||||
return event.annotations?.reactionsSummary?.isNotEmpty() ?: false
|
||||
}
|
||||
|
||||
private fun canEdit(event: TimelineEvent, myUserId: String): Boolean {
|
||||
//Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
|
||||
if (event.root.getClearType() != EventType.MESSAGE) return false
|
||||
//TODO if user is admin or moderator
|
||||
val messageContent = event.root.content.toModel<MessageContent>()
|
||||
return event.root.sender == myUserId && (
|
||||
messageContent?.type == MessageType.MSGTYPE_TEXT
|
||||
|| messageContent?.type == MessageType.MSGTYPE_EMOTE
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
private fun canCopy(type: String?): Boolean {
|
||||
return when (type) {
|
||||
MessageType.MSGTYPE_TEXT,
|
||||
MessageType.MSGTYPE_NOTICE,
|
||||
MessageType.MSGTYPE_EMOTE,
|
||||
MessageType.FORMAT_MATRIX_HTML,
|
||||
MessageType.MSGTYPE_LOCATION -> {
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
|
||||
private fun canRedact(event: TimelineEvent, myUserId: String): Boolean {
|
||||
//Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
|
||||
if (event.root.getClearType() != EventType.MESSAGE) return false
|
||||
//TODO if user is admin or moderator
|
||||
return event.root.sender == myUserId
|
||||
}
|
||||
|
||||
private fun canViewReactions(event: TimelineEvent): Boolean {
|
||||
//Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
|
||||
if (event.root.getClearType() != EventType.MESSAGE) return false
|
||||
//TODO if user is admin or moderator
|
||||
return event.annotations?.reactionsSummary?.isNotEmpty() ?: false
|
||||
}
|
||||
|
||||
private fun canEdit(event: TimelineEvent, myUserId: String): Boolean {
|
||||
//Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
|
||||
if (event.root.getClearType() != EventType.MESSAGE) return false
|
||||
//TODO if user is admin or moderator
|
||||
val messageContent = event.root.content.toModel<MessageContent>()
|
||||
return event.root.sender == myUserId && (
|
||||
messageContent?.type == MessageType.MSGTYPE_TEXT
|
||||
|| messageContent?.type == MessageType.MSGTYPE_EMOTE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun canCopy(type: String?): Boolean {
|
||||
return when (type) {
|
||||
MessageType.MSGTYPE_TEXT,
|
||||
MessageType.MSGTYPE_NOTICE,
|
||||
MessageType.MSGTYPE_EMOTE,
|
||||
MessageType.FORMAT_MATRIX_HTML,
|
||||
MessageType.MSGTYPE_LOCATION -> {
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
private fun canShare(type: String?): Boolean {
|
||||
return when (type) {
|
||||
MessageType.MSGTYPE_IMAGE,
|
||||
MessageType.MSGTYPE_AUDIO,
|
||||
MessageType.MSGTYPE_VIDEO -> {
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
|
||||
|
||||
private fun canShare(type: String?): Boolean {
|
||||
return when (type) {
|
||||
MessageType.MSGTYPE_IMAGE,
|
||||
MessageType.MSGTYPE_AUDIO,
|
||||
MessageType.MSGTYPE_VIDEO -> {
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
const val ACTION_ADD_REACTION = "add_reaction"
|
||||
const val ACTION_COPY = "copy"
|
||||
const val ACTION_EDIT = "edit"
|
||||
const val ACTION_QUOTE = "quote"
|
||||
const val ACTION_REPLY = "reply"
|
||||
const val ACTION_SHARE = "share"
|
||||
const val ACTION_RESEND = "resend"
|
||||
const val ACTION_DELETE = "delete"
|
||||
const val VIEW_SOURCE = "VIEW_SOURCE"
|
||||
const val VIEW_DECRYPTED_SOURCE = "VIEW_DECRYPTED_SOURCE"
|
||||
const val ACTION_COPY_PERMALINK = "ACTION_COPY_PERMALINK"
|
||||
const val ACTION_FLAG = "ACTION_FLAG"
|
||||
const val ACTION_QUICK_REACT = "ACTION_QUICK_REACT"
|
||||
const val ACTION_VIEW_REACTIONS = "ACTION_VIEW_REACTIONS"
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.riotredesign.EmojiCompatFontProvider
|
||||
import im.vector.riotredesign.R
|
||||
import org.koin.android.ext.android.inject
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Quick Reaction Fragment (agree / like reactions)
|
||||
@ -57,7 +57,8 @@ class QuickReactionFragment : BaseMvRxFragment() {
|
||||
|
||||
var interactionListener: InteractionListener? = null
|
||||
|
||||
val fontProvider by inject<EmojiCompatFontProvider>()
|
||||
@Inject lateinit var fontProvider: EmojiCompatFontProvider
|
||||
@Inject lateinit var quickReactionViewModelFactory: QuickReactionViewModel.Factory
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
val view = inflater.inflate(R.layout.adapter_item_action_quick_reaction, container, false)
|
||||
@ -68,10 +69,10 @@ class QuickReactionFragment : BaseMvRxFragment() {
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
quickReact1Text.text = QuickReactionViewModel.agreePositive
|
||||
quickReact2Text.text = QuickReactionViewModel.agreeNegative
|
||||
quickReact3Text.text = QuickReactionViewModel.likePositive
|
||||
quickReact4Text.text = QuickReactionViewModel.likeNegative
|
||||
quickReact1Text.text = QuickReactionViewModel.AGREE_POSITIVE
|
||||
quickReact2Text.text = QuickReactionViewModel.AGREE_NEGATIVE
|
||||
quickReact3Text.text = QuickReactionViewModel.LIKE_POSITIVE
|
||||
quickReact4Text.text = QuickReactionViewModel.LIKE_NEGATIVE
|
||||
|
||||
listOf(quickReact1Text, quickReact2Text, quickReact3Text, quickReact4Text).forEach {
|
||||
it.typeface = fontProvider.typeface ?: Typeface.DEFAULT
|
||||
@ -96,7 +97,7 @@ class QuickReactionFragment : BaseMvRxFragment() {
|
||||
override fun invalidate() = withState(viewModel) {
|
||||
|
||||
TransitionManager.beginDelayedTransition(rootLayout)
|
||||
when (it.agreeTrigleState) {
|
||||
when (it.agreeTriggleState) {
|
||||
TriggleState.NONE -> {
|
||||
quickReact1Text.alpha = 1f
|
||||
quickReact2Text.alpha = 1f
|
||||
@ -130,7 +131,7 @@ class QuickReactionFragment : BaseMvRxFragment() {
|
||||
if (it.selectionResult != null) {
|
||||
val clikedOn = it.selectionResult.first
|
||||
interactionListener?.didQuickReactWith(clikedOn, QuickReactionViewModel.getOpposite(clikedOn)
|
||||
?: "", it.selectionResult.second, it.eventId)
|
||||
?: "", it.selectionResult.second, it.eventId)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,12 +15,15 @@
|
||||
*/
|
||||
package im.vector.riotredesign.features.home.room.detail.timeline.action
|
||||
|
||||
import com.airbnb.mvrx.FragmentViewModelContext
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.riotredesign.core.platform.VectorViewModel
|
||||
import org.koin.android.ext.android.get
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
|
||||
|
||||
/**
|
||||
* Quick reactions state, it's a toggle with 3rd state
|
||||
@ -32,33 +35,99 @@ enum class TriggleState {
|
||||
}
|
||||
|
||||
data class QuickReactionState(
|
||||
val agreeTrigleState: TriggleState = TriggleState.NONE,
|
||||
val roomId: String,
|
||||
val eventId: String,
|
||||
val informationData: MessageInformationData,
|
||||
val agreeTriggleState: TriggleState = TriggleState.NONE,
|
||||
val likeTriggleState: TriggleState = TriggleState.NONE,
|
||||
/** Pair of 'clickedOn' and current toggles state*/
|
||||
val selectionResult: Pair<String, List<String>>? = null,
|
||||
val eventId: String = "") : MvRxState
|
||||
val selectionResult: Pair<String, List<String>>? = null
|
||||
) : MvRxState {
|
||||
|
||||
constructor(args: TimelineEventFragmentArgs) : this(roomId = args.roomId, eventId = args.eventId, informationData = args.informationData)
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Quick reaction view model
|
||||
*/
|
||||
class QuickReactionViewModel(initialState: QuickReactionState) : VectorViewModel<QuickReactionState>(initialState) {
|
||||
class QuickReactionViewModel @AssistedInject constructor(@Assisted initialState: QuickReactionState,
|
||||
private val session: Session) : VectorViewModel<QuickReactionState>(initialState) {
|
||||
|
||||
@AssistedInject.Factory
|
||||
interface Factory {
|
||||
fun create(initialState: QuickReactionState): QuickReactionViewModel
|
||||
}
|
||||
|
||||
companion object : MvRxViewModelFactory<QuickReactionViewModel, QuickReactionState> {
|
||||
|
||||
const val AGREE_POSITIVE = "👍"
|
||||
const val AGREE_NEGATIVE = "👎"
|
||||
const val LIKE_POSITIVE = "🙂"
|
||||
const val LIKE_NEGATIVE = "😔"
|
||||
|
||||
fun getOpposite(reaction: String): String? {
|
||||
return when (reaction) {
|
||||
AGREE_POSITIVE -> AGREE_NEGATIVE
|
||||
AGREE_NEGATIVE -> AGREE_POSITIVE
|
||||
LIKE_POSITIVE -> LIKE_NEGATIVE
|
||||
LIKE_NEGATIVE -> LIKE_POSITIVE
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
override fun create(viewModelContext: ViewModelContext, state: QuickReactionState): QuickReactionViewModel? {
|
||||
val fragment: QuickReactionFragment = (viewModelContext as FragmentViewModelContext).fragment()
|
||||
return fragment.quickReactionViewModelFactory.create(state)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
setState { reduceState(this) }
|
||||
}
|
||||
|
||||
private fun reduceState(state: QuickReactionState): QuickReactionState {
|
||||
val event = session.getRoom(state.roomId)?.getTimeLineEvent(state.eventId) ?: return state
|
||||
var agreeTriggle: TriggleState = TriggleState.NONE
|
||||
var likeTriggle: TriggleState = TriggleState.NONE
|
||||
event.annotations?.reactionsSummary?.forEach {
|
||||
//it.addedByMe
|
||||
if (it.addedByMe) {
|
||||
if (AGREE_POSITIVE == it.key) {
|
||||
agreeTriggle = TriggleState.FIRST
|
||||
} else if (AGREE_NEGATIVE == it.key) {
|
||||
agreeTriggle = TriggleState.SECOND
|
||||
}
|
||||
|
||||
if (LIKE_POSITIVE == it.key) {
|
||||
likeTriggle = TriggleState.FIRST
|
||||
} else if (LIKE_NEGATIVE == it.key) {
|
||||
likeTriggle = TriggleState.SECOND
|
||||
}
|
||||
}
|
||||
}
|
||||
return state.copy(
|
||||
agreeTriggleState = agreeTriggle,
|
||||
likeTriggleState = likeTriggle
|
||||
)
|
||||
}
|
||||
|
||||
fun toggleAgree(isFirst: Boolean) = withState {
|
||||
if (isFirst) {
|
||||
setState {
|
||||
val newTriggle = if (it.agreeTrigleState == TriggleState.FIRST) TriggleState.NONE else TriggleState.FIRST
|
||||
val newTriggle = if (it.agreeTriggleState == TriggleState.FIRST) TriggleState.NONE else TriggleState.FIRST
|
||||
copy(
|
||||
agreeTrigleState = newTriggle,
|
||||
selectionResult = Pair(agreePositive, getReactions(this, newTriggle, null))
|
||||
agreeTriggleState = newTriggle,
|
||||
selectionResult = Pair(AGREE_POSITIVE, getReactions(this, newTriggle, null))
|
||||
)
|
||||
}
|
||||
} else {
|
||||
setState {
|
||||
val newTriggle = if (it.agreeTrigleState == TriggleState.SECOND) TriggleState.NONE else TriggleState.SECOND
|
||||
val newTriggle = if (it.agreeTriggleState == TriggleState.SECOND) TriggleState.NONE else TriggleState.SECOND
|
||||
copy(
|
||||
agreeTrigleState = agreeTrigleState,
|
||||
selectionResult = Pair(agreeNegative, getReactions(this, newTriggle, null))
|
||||
agreeTriggleState = agreeTriggleState,
|
||||
selectionResult = Pair(AGREE_NEGATIVE, getReactions(this, newTriggle, null))
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -70,7 +139,7 @@ class QuickReactionViewModel(initialState: QuickReactionState) : VectorViewModel
|
||||
val newTriggle = if (it.likeTriggleState == TriggleState.FIRST) TriggleState.NONE else TriggleState.FIRST
|
||||
copy(
|
||||
likeTriggleState = newTriggle,
|
||||
selectionResult = Pair(likePositive, getReactions(this, null, newTriggle))
|
||||
selectionResult = Pair(LIKE_POSITIVE, getReactions(this, null, newTriggle))
|
||||
)
|
||||
}
|
||||
} else {
|
||||
@ -78,7 +147,7 @@ class QuickReactionViewModel(initialState: QuickReactionState) : VectorViewModel
|
||||
val newTriggle = if (it.likeTriggleState == TriggleState.SECOND) TriggleState.NONE else TriggleState.SECOND
|
||||
copy(
|
||||
likeTriggleState = newTriggle,
|
||||
selectionResult = Pair(likeNegative, getReactions(this, null, newTriggle))
|
||||
selectionResult = Pair(LIKE_NEGATIVE, getReactions(this, null, newTriggle))
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -87,64 +156,17 @@ class QuickReactionViewModel(initialState: QuickReactionState) : VectorViewModel
|
||||
private fun getReactions(state: QuickReactionState, newState1: TriggleState?, newState2: TriggleState?): List<String> {
|
||||
return ArrayList<String>(4).apply {
|
||||
when (newState2 ?: state.likeTriggleState) {
|
||||
TriggleState.FIRST -> add(likePositive)
|
||||
TriggleState.SECOND -> add(likeNegative)
|
||||
TriggleState.FIRST -> add(LIKE_POSITIVE)
|
||||
TriggleState.SECOND -> add(LIKE_NEGATIVE)
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
when (newState1 ?: state.agreeTrigleState) {
|
||||
TriggleState.FIRST -> add(agreePositive)
|
||||
TriggleState.SECOND -> add(agreeNegative)
|
||||
when (newState1 ?: state.agreeTriggleState) {
|
||||
TriggleState.FIRST -> add(AGREE_POSITIVE)
|
||||
TriggleState.SECOND -> add(AGREE_NEGATIVE)
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
companion object : MvRxViewModelFactory<QuickReactionViewModel, QuickReactionState> {
|
||||
|
||||
val agreePositive = "👍"
|
||||
val agreeNegative = "👎"
|
||||
val likePositive = "🙂"
|
||||
val likeNegative = "😔"
|
||||
|
||||
fun getOpposite(reaction: String): String? {
|
||||
return when (reaction) {
|
||||
agreePositive -> agreeNegative
|
||||
agreeNegative -> agreePositive
|
||||
likePositive -> likeNegative
|
||||
likeNegative -> likePositive
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
override fun initialState(viewModelContext: ViewModelContext): QuickReactionState? {
|
||||
// Args are accessible from the context.
|
||||
// val foo = vieWModelContext.args<MyArgs>.foo
|
||||
val currentSession = viewModelContext.activity.get<Session>()
|
||||
val parcel = viewModelContext.args as TimelineEventFragmentArgs
|
||||
val event = currentSession.getRoom(parcel.roomId)?.getTimeLineEvent(parcel.eventId)
|
||||
?: return null
|
||||
var agreeTriggle: TriggleState = TriggleState.NONE
|
||||
var likeTriggle: TriggleState = TriggleState.NONE
|
||||
event.annotations?.reactionsSummary?.forEach {
|
||||
//it.addedByMe
|
||||
if (it.addedByMe) {
|
||||
if (agreePositive == it.key) {
|
||||
agreeTriggle = TriggleState.FIRST
|
||||
} else if (agreeNegative == it.key) {
|
||||
agreeTriggle = TriggleState.SECOND
|
||||
}
|
||||
|
||||
if (likePositive == it.key) {
|
||||
likeTriggle = TriggleState.FIRST
|
||||
} else if (likeNegative == it.key) {
|
||||
likeTriggle = TriggleState.SECOND
|
||||
}
|
||||
}
|
||||
}
|
||||
return QuickReactionState(agreeTriggle, likeTriggle, null, event.root.eventId ?: "")
|
||||
}
|
||||
}
|
||||
}
|
@ -19,8 +19,9 @@ package im.vector.riotredesign.features.home.room.detail.timeline.factory
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.DefaultItem
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.DefaultItem_
|
||||
import javax.inject.Inject
|
||||
|
||||
class DefaultItemFactory {
|
||||
class DefaultItemFactory @Inject constructor(){
|
||||
|
||||
fun create(event: TimelineEvent, exception: Exception? = null): DefaultItem? {
|
||||
val text = if (exception == null) {
|
||||
|
@ -30,9 +30,10 @@ import im.vector.riotredesign.features.home.room.detail.timeline.helper.senderAv
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.senderName
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem_
|
||||
import javax.inject.Inject
|
||||
|
||||
// This class handles timeline event who haven't been successfully decrypted
|
||||
class EncryptedItemFactory(private val stringProvider: StringProvider) {
|
||||
class EncryptedItemFactory @Inject constructor(private val stringProvider: StringProvider) {
|
||||
|
||||
fun create(timelineEvent: TimelineEvent): VectorEpoxyModel<*>? {
|
||||
return when {
|
||||
|
@ -28,8 +28,9 @@ import im.vector.riotredesign.features.home.room.detail.timeline.helper.senderNa
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem_
|
||||
import javax.inject.Inject
|
||||
|
||||
class EncryptionItemFactory(private val stringProvider: StringProvider) {
|
||||
class EncryptionItemFactory @Inject constructor(private val stringProvider: StringProvider) {
|
||||
|
||||
fun create(event: TimelineEvent): NoticeItem? {
|
||||
val text = buildNoticeText(event.root, event.senderName) ?: return null
|
||||
@ -52,7 +53,7 @@ class EncryptionItemFactory(private val stringProvider: StringProvider) {
|
||||
val content = event.content.toModel<EncryptionEventContent>() ?: return null
|
||||
stringProvider.getString(R.string.notice_end_to_end, senderName, content.algorithm)
|
||||
}
|
||||
else -> null
|
||||
else -> null
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -29,7 +29,14 @@ import im.vector.matrix.android.api.session.events.model.EventType
|
||||
import im.vector.matrix.android.api.session.events.model.RelationType
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.EditAggregatedSummary
|
||||
import im.vector.matrix.android.api.session.room.model.message.*
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageAudioContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageEmoteContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageFileContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageImageContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageNoticeContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageTextContent
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageVideoContent
|
||||
import im.vector.matrix.android.api.session.room.send.SendState
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.riotredesign.EmojiCompatFontProvider
|
||||
@ -44,18 +51,32 @@ import im.vector.riotredesign.features.home.getColorFromUserId
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.*
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.BlankItem_
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.DefaultItem
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.DefaultItem_
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageFileItem
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageFileItem_
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageImageVideoItem
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageImageVideoItem_
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageTextItem
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageTextItem_
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.ReactionInfoData
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.RedactedMessageItem
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.RedactedMessageItem_
|
||||
import im.vector.riotredesign.features.html.EventHtmlRenderer
|
||||
import im.vector.riotredesign.features.media.ImageContentRenderer
|
||||
import im.vector.riotredesign.features.media.VideoContentRenderer
|
||||
import me.gujun.android.span.span
|
||||
import javax.inject.Inject
|
||||
|
||||
class MessageItemFactory(private val colorProvider: ColorProvider,
|
||||
private val timelineMediaSizeProvider: TimelineMediaSizeProvider,
|
||||
private val timelineDateFormatter: TimelineDateFormatter,
|
||||
private val htmlRenderer: EventHtmlRenderer,
|
||||
private val stringProvider: StringProvider,
|
||||
private val emojiCompatFontProvider: EmojiCompatFontProvider) {
|
||||
class MessageItemFactory @Inject constructor(
|
||||
private val colorProvider: ColorProvider,
|
||||
private val timelineMediaSizeProvider: TimelineMediaSizeProvider,
|
||||
private val timelineDateFormatter: TimelineDateFormatter,
|
||||
private val htmlRenderer: EventHtmlRenderer,
|
||||
private val stringProvider: StringProvider,
|
||||
private val emojiCompatFontProvider: EmojiCompatFontProvider) {
|
||||
|
||||
fun create(event: TimelineEvent,
|
||||
nextEvent: TimelineEvent?,
|
||||
@ -68,33 +89,33 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
|
||||
val nextDate = nextEvent?.root?.localDateTime()
|
||||
val addDaySeparator = date.toLocalDate() != nextDate?.toLocalDate()
|
||||
val isNextMessageReceivedMoreThanOneHourAgo = nextDate?.isBefore(date.minusMinutes(60))
|
||||
?: false
|
||||
?: false
|
||||
|
||||
val showInformation = addDaySeparator
|
||||
|| event.senderAvatar != nextEvent?.senderAvatar
|
||||
|| event.senderName != nextEvent?.senderName
|
||||
|| nextEvent?.root?.getClearType() != EventType.MESSAGE
|
||||
|| isNextMessageReceivedMoreThanOneHourAgo
|
||||
|| event.senderAvatar != nextEvent?.senderAvatar
|
||||
|| event.senderName != nextEvent?.senderName
|
||||
|| nextEvent?.root?.getClearType() != EventType.MESSAGE
|
||||
|| isNextMessageReceivedMoreThanOneHourAgo
|
||||
|
||||
val time = timelineDateFormatter.formatMessageHour(date)
|
||||
val avatarUrl = event.senderAvatar
|
||||
val memberName = event.senderName ?: event.root.sender ?: ""
|
||||
val formattedMemberName = span(memberName) {
|
||||
textColor = colorProvider.getColor(getColorFromUserId(event.root.sender
|
||||
?: ""))
|
||||
?: ""))
|
||||
}
|
||||
val hasBeenEdited = event.annotations?.editSummary != null
|
||||
val informationData = MessageInformationData(eventId = eventId,
|
||||
senderId = event.root.sender ?: "",
|
||||
sendState = event.sendState,
|
||||
time = time,
|
||||
avatarUrl = avatarUrl,
|
||||
memberName = formattedMemberName,
|
||||
showInformation = showInformation,
|
||||
orderedReactionList = event.annotations?.reactionsSummary?.map {
|
||||
ReactionInfoData(it.key, it.count, it.addedByMe, it.localEchoEvents.isEmpty())
|
||||
},
|
||||
hasBeenEdited = hasBeenEdited
|
||||
senderId = event.root.sender ?: "",
|
||||
sendState = event.sendState,
|
||||
time = time,
|
||||
avatarUrl = avatarUrl,
|
||||
memberName = formattedMemberName,
|
||||
showInformation = showInformation,
|
||||
orderedReactionList = event.annotations?.reactionsSummary?.map {
|
||||
ReactionInfoData(it.key, it.count, it.addedByMe, it.localEchoEvents.isEmpty())
|
||||
},
|
||||
hasBeenEdited = hasBeenEdited
|
||||
)
|
||||
|
||||
if (event.root.unsignedData?.redactedEvent != null) {
|
||||
@ -104,9 +125,9 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
|
||||
|
||||
val messageContent: MessageContent =
|
||||
event.annotations?.editSummary?.aggregatedContent?.toModel()
|
||||
?: event.root.getClearContent().toModel()
|
||||
?: //Malformed content, we should echo something on screen
|
||||
return DefaultItem_().text(stringProvider.getString(R.string.malformed_message))
|
||||
?: event.root.getClearContent().toModel()
|
||||
?: //Malformed content, we should echo something on screen
|
||||
return DefaultItem_().text(stringProvider.getString(R.string.malformed_message))
|
||||
|
||||
if (messageContent.relatesTo?.type == RelationType.REPLACE) {
|
||||
// ignore replace event, the targeted id is already edited
|
||||
@ -116,16 +137,16 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
|
||||
// val ev = all.toModel<Event>()
|
||||
return when (messageContent) {
|
||||
is MessageEmoteContent -> buildEmoteMessageItem(messageContent,
|
||||
informationData,
|
||||
hasBeenEdited,
|
||||
event.annotations?.editSummary,
|
||||
callback)
|
||||
informationData,
|
||||
hasBeenEdited,
|
||||
event.annotations?.editSummary,
|
||||
callback)
|
||||
is MessageTextContent -> buildTextMessageItem(event.sendState,
|
||||
messageContent,
|
||||
informationData,
|
||||
hasBeenEdited,
|
||||
event.annotations?.editSummary,
|
||||
callback
|
||||
messageContent,
|
||||
informationData,
|
||||
hasBeenEdited,
|
||||
event.annotations?.editSummary,
|
||||
callback
|
||||
)
|
||||
is MessageImageContent -> buildImageMessageItem(messageContent, informationData, callback)
|
||||
is MessageNoticeContent -> buildNoticeMessageItem(messageContent, informationData, callback)
|
||||
@ -156,7 +177,7 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
|
||||
}))
|
||||
.longClickListener { view ->
|
||||
return@longClickListener callback?.onEventLongClicked(informationData, messageContent, view)
|
||||
?: false
|
||||
?: false
|
||||
}
|
||||
}
|
||||
|
||||
@ -176,7 +197,7 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
|
||||
}))
|
||||
.longClickListener { view ->
|
||||
return@longClickListener callback?.onEventLongClicked(informationData, messageContent, view)
|
||||
?: false
|
||||
?: false
|
||||
}
|
||||
.clickListener(
|
||||
DebouncedClickListener(View.OnClickListener { _ ->
|
||||
@ -221,7 +242,7 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
|
||||
}))
|
||||
.longClickListener { view ->
|
||||
return@longClickListener callback?.onEventLongClicked(informationData, messageContent, view)
|
||||
?: false
|
||||
?: false
|
||||
}
|
||||
}
|
||||
|
||||
@ -259,7 +280,7 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
|
||||
.clickListener { view -> callback?.onVideoMessageClicked(messageContent, videoData, view) }
|
||||
.longClickListener { view ->
|
||||
return@longClickListener callback?.onEventLongClicked(informationData, messageContent, view)
|
||||
?: false
|
||||
?: false
|
||||
}
|
||||
}
|
||||
|
||||
@ -296,7 +317,7 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
|
||||
}))
|
||||
.longClickListener { view ->
|
||||
return@longClickListener callback?.onEventLongClicked(informationData, messageContent, view)
|
||||
?: false
|
||||
?: false
|
||||
}
|
||||
}
|
||||
|
||||
@ -328,9 +349,9 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
|
||||
//nop
|
||||
}
|
||||
},
|
||||
editStart,
|
||||
editEnd,
|
||||
Spanned.SPAN_INCLUSIVE_EXCLUSIVE)
|
||||
editStart,
|
||||
editEnd,
|
||||
Spanned.SPAN_INCLUSIVE_EXCLUSIVE)
|
||||
return spannable
|
||||
}
|
||||
|
||||
@ -362,7 +383,7 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
|
||||
}))
|
||||
.longClickListener { view ->
|
||||
return@longClickListener callback?.onEventLongClicked(informationData, messageContent, view)
|
||||
?: false
|
||||
?: false
|
||||
}
|
||||
}
|
||||
|
||||
@ -395,7 +416,7 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
|
||||
}))
|
||||
.longClickListener { view ->
|
||||
return@longClickListener callback?.onEventLongClicked(informationData, messageContent, view)
|
||||
?: false
|
||||
?: false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,8 +24,9 @@ import im.vector.riotredesign.features.home.room.detail.timeline.helper.senderNa
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem_
|
||||
import javax.inject.Inject
|
||||
|
||||
class NoticeItemFactory(private val eventFormatter: NoticeEventFormatter) {
|
||||
class NoticeItemFactory @Inject constructor(private val eventFormatter: NoticeEventFormatter) {
|
||||
|
||||
fun create(event: TimelineEvent,
|
||||
callback: TimelineEventController.Callback?): NoticeItem? {
|
||||
|
@ -28,12 +28,13 @@ import im.vector.riotredesign.features.home.room.detail.timeline.helper.Timeline
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageTextItem_
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class TimelineItemFactory(private val messageItemFactory: MessageItemFactory,
|
||||
private val encryptionItemFactory: EncryptionItemFactory,
|
||||
private val encryptedItemFactory: EncryptedItemFactory,
|
||||
private val noticeItemFactory: NoticeItemFactory,
|
||||
private val defaultItemFactory: DefaultItemFactory) {
|
||||
class TimelineItemFactory @Inject constructor(private val messageItemFactory: MessageItemFactory,
|
||||
private val encryptionItemFactory: EncryptionItemFactory,
|
||||
private val encryptedItemFactory: EncryptedItemFactory,
|
||||
private val noticeItemFactory: NoticeItemFactory,
|
||||
private val defaultItemFactory: DefaultItemFactory) {
|
||||
|
||||
fun create(event: TimelineEvent,
|
||||
nextEvent: TimelineEvent?,
|
||||
@ -64,22 +65,23 @@ class TimelineItemFactory(private val messageItemFactory: MessageItemFactory,
|
||||
//These are just for debug to display hidden event, they should be filtered out in normal mode
|
||||
if (TimelineDisplayableEvents.DEBUG_HIDDEN_EVENT) {
|
||||
val informationData = MessageInformationData(eventId = event.root.eventId
|
||||
?: "?",
|
||||
senderId = event.root.sender ?: "",
|
||||
sendState = event.sendState,
|
||||
time = "",
|
||||
avatarUrl = null,
|
||||
memberName = "",
|
||||
showInformation = false
|
||||
?: "?",
|
||||
senderId = event.root.sender
|
||||
?: "",
|
||||
sendState = event.sendState,
|
||||
time = "",
|
||||
avatarUrl = null,
|
||||
memberName = "",
|
||||
showInformation = false
|
||||
)
|
||||
val messageContent = event.root.content.toModel<MessageContent>()
|
||||
?: MessageDefaultContent("", "", null, null)
|
||||
?: MessageDefaultContent("", "", null, null)
|
||||
MessageTextItem_()
|
||||
.informationData(informationData)
|
||||
.message("{ \"type\": ${event.root.type} }")
|
||||
.longClickListener { view ->
|
||||
return@longClickListener callback?.onEventLongClicked(informationData, messageContent, view)
|
||||
?: false
|
||||
?: false
|
||||
}
|
||||
} else {
|
||||
Timber.w("Ignored event (type: ${event.root.type}")
|
||||
|
@ -20,15 +20,21 @@ import android.text.TextUtils
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.events.model.EventType
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.*
|
||||
import im.vector.matrix.android.api.session.room.model.Membership
|
||||
import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibility
|
||||
import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibilityContent
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMember
|
||||
import im.vector.matrix.android.api.session.room.model.RoomNameContent
|
||||
import im.vector.matrix.android.api.session.room.model.RoomTopicContent
|
||||
import im.vector.matrix.android.api.session.room.model.call.CallInviteContent
|
||||
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
|
||||
import im.vector.riotredesign.R
|
||||
import im.vector.riotredesign.core.resources.StringProvider
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.senderName
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class NoticeEventFormatter(private val stringProvider: StringProvider) {
|
||||
class NoticeEventFormatter @Inject constructor(private val stringProvider: StringProvider) {
|
||||
|
||||
fun format(timelineEvent: TimelineEvent): CharSequence? {
|
||||
return when (val type = timelineEvent.root.getClearType()) {
|
||||
@ -66,7 +72,7 @@ class NoticeEventFormatter(private val stringProvider: StringProvider) {
|
||||
|
||||
private fun formatRoomHistoryVisibilityEvent(event: Event, senderName: String?): CharSequence? {
|
||||
val historyVisibility = event.getClearContent().toModel<RoomHistoryVisibilityContent>()?.historyVisibility
|
||||
?: return null
|
||||
?: return null
|
||||
|
||||
val formattedVisibility = when (historyVisibility) {
|
||||
RoomHistoryVisibility.SHARED -> stringProvider.getString(R.string.notice_room_visibility_shared)
|
||||
@ -116,7 +122,7 @@ class NoticeEventFormatter(private val stringProvider: StringProvider) {
|
||||
stringProvider.getString(R.string.notice_display_name_removed, event.sender, prevEventContent?.displayName)
|
||||
else ->
|
||||
stringProvider.getString(R.string.notice_display_name_changed_from,
|
||||
event.sender, prevEventContent?.displayName, eventContent?.displayName)
|
||||
event.sender, prevEventContent?.displayName, eventContent?.displayName)
|
||||
}
|
||||
displayText.append(displayNameText)
|
||||
}
|
||||
@ -143,7 +149,7 @@ class NoticeEventFormatter(private val stringProvider: StringProvider) {
|
||||
when {
|
||||
eventContent.thirdPartyInvite != null ->
|
||||
stringProvider.getString(R.string.notice_room_third_party_registered_invite,
|
||||
targetDisplayName, eventContent.thirdPartyInvite?.displayName)
|
||||
targetDisplayName, eventContent.thirdPartyInvite?.displayName)
|
||||
TextUtils.equals(event.stateKey, selfUserId) ->
|
||||
stringProvider.getString(R.string.notice_room_invite_you, senderDisplayName)
|
||||
event.stateKey.isNullOrEmpty() ->
|
||||
|
@ -19,8 +19,10 @@ package im.vector.riotredesign.features.home.room.detail.timeline.helper
|
||||
import im.vector.riotredesign.core.resources.LocaleProvider
|
||||
import org.threeten.bp.LocalDateTime
|
||||
import org.threeten.bp.format.DateTimeFormatter
|
||||
import javax.inject.Inject
|
||||
|
||||
class TimelineDateFormatter(private val localeProvider: LocaleProvider) {
|
||||
|
||||
class TimelineDateFormatter @Inject constructor (private val localeProvider: LocaleProvider) {
|
||||
|
||||
private val messageHourFormatter by lazy {
|
||||
DateTimeFormatter.ofPattern("H:mm", localeProvider.current())
|
||||
|
@ -17,8 +17,9 @@
|
||||
package im.vector.riotredesign.features.home.room.detail.timeline.helper
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import javax.inject.Inject
|
||||
|
||||
class TimelineMediaSizeProvider {
|
||||
class TimelineMediaSizeProvider @Inject constructor() {
|
||||
|
||||
lateinit var recyclerView: RecyclerView
|
||||
private var cachedSize: Pair<Int, Int>? = null
|
||||
|
@ -17,9 +17,9 @@
|
||||
package im.vector.riotredesign.features.home.room.list
|
||||
|
||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||
import javax.inject.Inject
|
||||
|
||||
class AlphabeticalRoomComparator
|
||||
: Comparator<RoomSummary> {
|
||||
class AlphabeticalRoomComparator @Inject constructor() : Comparator<RoomSummary> {
|
||||
|
||||
override fun compare(leftRoomSummary: RoomSummary?, rightRoomSummary: RoomSummary?): Int {
|
||||
return when {
|
||||
|
@ -17,8 +17,9 @@
|
||||
package im.vector.riotredesign.features.home.room.list
|
||||
|
||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||
import javax.inject.Inject
|
||||
|
||||
class ChronologicalRoomComparator : Comparator<RoomSummary> {
|
||||
class ChronologicalRoomComparator @Inject constructor() : Comparator<RoomSummary> {
|
||||
|
||||
override fun compare(leftRoomSummary: RoomSummary?, rightRoomSummary: RoomSummary?): Int {
|
||||
var rightTimestamp = 0L
|
||||
|
@ -23,7 +23,11 @@ import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.airbnb.mvrx.*
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.Incomplete
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.args
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import im.vector.matrix.android.api.failure.Failure
|
||||
import im.vector.matrix.android.api.session.room.model.Membership
|
||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||
@ -36,7 +40,7 @@ import im.vector.riotredesign.core.platform.VectorBaseFragment
|
||||
import im.vector.riotredesign.features.home.room.list.widget.FabMenuView
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.android.synthetic.main.fragment_room_list.*
|
||||
import org.koin.android.ext.android.inject
|
||||
import javax.inject.Inject
|
||||
|
||||
@Parcelize
|
||||
data class RoomListParams(
|
||||
@ -61,7 +65,8 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Callback, O
|
||||
}
|
||||
|
||||
private val roomListParams: RoomListParams by args()
|
||||
private val roomController by inject<RoomSummaryController>()
|
||||
@Inject lateinit var roomController: RoomSummaryController
|
||||
@Inject lateinit var roomListViewModelFactory: RoomListViewModel.Factory
|
||||
private val roomListViewModel: RoomListViewModel by fragmentViewModel()
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_room_list
|
||||
|
@ -19,9 +19,12 @@ package im.vector.riotredesign.features.home.room.list
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import arrow.core.Option
|
||||
import com.airbnb.mvrx.FragmentViewModelContext
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import com.jakewharton.rxrelay2.BehaviorRelay
|
||||
import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.session.room.model.Membership
|
||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||
@ -29,26 +32,27 @@ import im.vector.matrix.android.api.session.room.model.tag.RoomTag
|
||||
import im.vector.riotredesign.core.platform.VectorViewModel
|
||||
import im.vector.riotredesign.core.utils.LiveEvent
|
||||
import im.vector.riotredesign.features.home.HomeRoomListObservableStore
|
||||
import org.koin.android.ext.android.get
|
||||
|
||||
typealias RoomListFilterName = CharSequence
|
||||
|
||||
class RoomListViewModel(initialState: RoomListViewState,
|
||||
private val session: Session,
|
||||
private val homeRoomListObservableSource: HomeRoomListObservableStore,
|
||||
private val alphabeticalRoomComparator: AlphabeticalRoomComparator,
|
||||
private val chronologicalRoomComparator: ChronologicalRoomComparator)
|
||||
class RoomListViewModel @AssistedInject constructor(@Assisted initialState: RoomListViewState,
|
||||
private val session: Session,
|
||||
private val homeRoomListObservableSource: HomeRoomListObservableStore,
|
||||
private val alphabeticalRoomComparator: AlphabeticalRoomComparator,
|
||||
private val chronologicalRoomComparator: ChronologicalRoomComparator)
|
||||
: VectorViewModel<RoomListViewState>(initialState) {
|
||||
|
||||
@AssistedInject.Factory
|
||||
interface Factory {
|
||||
fun create(initialState: RoomListViewState): RoomListViewModel
|
||||
}
|
||||
|
||||
companion object : MvRxViewModelFactory<RoomListViewModel, RoomListViewState> {
|
||||
|
||||
@JvmStatic
|
||||
override fun create(viewModelContext: ViewModelContext, state: RoomListViewState): RoomListViewModel? {
|
||||
val currentSession = viewModelContext.activity.get<Session>()
|
||||
val homeRoomListObservableSource = viewModelContext.activity.get<HomeRoomListObservableStore>()
|
||||
val chronologicalRoomComparator = viewModelContext.activity.get<ChronologicalRoomComparator>()
|
||||
val alphabeticalRoomComparator = viewModelContext.activity.get<AlphabeticalRoomComparator>()
|
||||
return RoomListViewModel(state, currentSession, homeRoomListObservableSource, alphabeticalRoomComparator, chronologicalRoomComparator)
|
||||
val fragment: RoomListFragment = (viewModelContext as FragmentViewModelContext).fragment()
|
||||
return fragment.roomListViewModelFactory.create(state)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,10 +27,11 @@ import im.vector.riotredesign.core.resources.DateProvider
|
||||
import im.vector.riotredesign.core.resources.StringProvider
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.format.NoticeEventFormatter
|
||||
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter
|
||||
import javax.inject.Inject
|
||||
|
||||
class RoomSummaryController(private val stringProvider: StringProvider,
|
||||
private val eventFormatter: NoticeEventFormatter,
|
||||
private val timelineDateFormatter: TimelineDateFormatter
|
||||
class RoomSummaryController @Inject constructor(private val stringProvider: StringProvider,
|
||||
private val eventFormatter: NoticeEventFormatter,
|
||||
private val timelineDateFormatter: TimelineDateFormatter
|
||||
) : TypedEpoxyController<RoomListViewState>() {
|
||||
|
||||
var callback: Callback? = null
|
||||
|
@ -19,11 +19,12 @@
|
||||
package im.vector.riotredesign.features.html
|
||||
|
||||
import android.content.Context
|
||||
import android.text.style.ClickableSpan
|
||||
import android.text.style.URLSpan
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import im.vector.matrix.android.api.permalinks.PermalinkData
|
||||
import im.vector.matrix.android.api.permalinks.PermalinkParser
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.riotredesign.core.glide.GlideApp
|
||||
import im.vector.riotredesign.core.glide.GlideRequests
|
||||
import org.commonmark.node.BlockQuote
|
||||
import org.commonmark.node.HtmlBlock
|
||||
@ -50,13 +51,12 @@ import ru.noties.markwon.html.tag.SubScriptHandler
|
||||
import ru.noties.markwon.html.tag.SuperScriptHandler
|
||||
import ru.noties.markwon.html.tag.UnderlineHandler
|
||||
import java.util.Arrays.asList
|
||||
import javax.inject.Inject
|
||||
|
||||
class EventHtmlRenderer(glideRequests: GlideRequests,
|
||||
context: Context,
|
||||
session: Session) {
|
||||
|
||||
class EventHtmlRenderer @Inject constructor(context: AppCompatActivity,
|
||||
session: Session) {
|
||||
private val markwon = Markwon.builder(context)
|
||||
.usePlugin(MatrixPlugin.create(glideRequests, context, session))
|
||||
.usePlugin(MatrixPlugin.create(GlideApp.with(context), context, session))
|
||||
.build()
|
||||
|
||||
fun render(text: String): CharSequence {
|
||||
|
@ -20,8 +20,9 @@ import android.app.Activity
|
||||
import android.app.Application
|
||||
import android.os.Bundle
|
||||
import im.vector.riotredesign.features.popup.PopupAlertManager
|
||||
import javax.inject.Inject
|
||||
|
||||
class VectorActivityLifecycleCallbacks : Application.ActivityLifecycleCallbacks {
|
||||
class VectorActivityLifecycleCallbacks @Inject constructor() : Application.ActivityLifecycleCallbacks {
|
||||
override fun onActivityPaused(activity: Activity) {
|
||||
}
|
||||
|
||||
|
@ -27,9 +27,9 @@ import im.vector.riotredesign.features.home.room.detail.RoomDetailArgs
|
||||
import im.vector.riotredesign.features.roomdirectory.RoomDirectoryActivity
|
||||
import im.vector.riotredesign.features.roomdirectory.roompreview.RoomPreviewActivity
|
||||
import im.vector.riotredesign.features.settings.VectorSettingsActivity
|
||||
import javax.inject.Inject
|
||||
|
||||
class DefaultNavigator : Navigator {
|
||||
|
||||
class DefaultNavigator @Inject constructor() : Navigator {
|
||||
|
||||
override fun openRoom(roomId: String, context: Context) {
|
||||
val args = RoomDetailArgs(roomId)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user