Commit 60009b87 authored by Filipe de Lima Brito's avatar Filipe de Lima Brito

Merge branch 'develop' into feature/create-channel

parents 2921363b 38fc3787
...@@ -93,7 +93,7 @@ jobs: ...@@ -93,7 +93,7 @@ jobs:
- run: - run:
name: Build APK name: Build APK
command: | command: |
./gradlew assembleRelease --quiet --console=plain --stacktrace ./gradlew assembleRelease --info --console=plain --stacktrace
- store_artifacts: - store_artifacts:
path: app/build/outputs/apk path: app/build/outputs/apk
destination: apks destination: apks
......
...@@ -3,7 +3,6 @@ apply plugin: 'io.fabric' ...@@ -3,7 +3,6 @@ apply plugin: 'io.fabric'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-kapt'
apply plugin: 'realm-android'
android { android {
compileSdkVersion versions.compileSdk compileSdkVersion versions.compileSdk
...@@ -13,9 +12,9 @@ android { ...@@ -13,9 +12,9 @@ android {
applicationId "chat.rocket.android" applicationId "chat.rocket.android"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion versions.targetSdk targetSdkVersion versions.targetSdk
versionCode 2026 versionCode 2030
versionName "2.3.2" versionName "2.4.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true multiDexEnabled true
} }
...@@ -68,12 +67,11 @@ dependencies { ...@@ -68,12 +67,11 @@ dependencies {
implementation libraries.appCompat implementation libraries.appCompat
implementation libraries.recyclerview implementation libraries.recyclerview
implementation libraries.design implementation libraries.material
implementation libraries.constraintLayout implementation libraries.constraintlayout
implementation libraries.cardView implementation libraries.cardview
implementation libraries.flexbox implementation libraries.flexbox
implementation libraries.customTabs implementation libraries.browser
implementation libraries.supportv4
implementation libraries.androidKtx implementation libraries.androidKtx
...@@ -87,7 +85,6 @@ dependencies { ...@@ -87,7 +85,6 @@ dependencies {
implementation libraries.room implementation libraries.room
kapt libraries.roomProcessor kapt libraries.roomProcessor
implementation libraries.roomRxjava
implementation libraries.lifecycleExtensions implementation libraries.lifecycleExtensions
kapt libraries.lifecycleCompiler kapt libraries.lifecycleCompiler
...@@ -119,7 +116,9 @@ dependencies { ...@@ -119,7 +116,9 @@ dependencies {
implementation libraries.aVLoadingIndicatorView implementation libraries.aVLoadingIndicatorView
implementation('com.crashlytics.sdk.android:crashlytics:2.6.8@aar') { implementation "com.github.luciofm:livedata-ktx:b1e8bbc25a"
implementation('com.crashlytics.sdk.android:crashlytics:2.9.2@aar') {
transitive = true transitive = true
} }
......
package chat.rocket.android.chatroom.ui package chat.rocket.android.chatroom.ui
import android.content.Intent import android.content.Intent
import android.support.test.espresso.intent.rule.IntentsTestRule import androidx.test.espresso.intent.rule.IntentsTestRule
import android.support.test.filters.LargeTest import androidx.test.filters.LargeTest
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import android.app.Activity import android.app.Activity
import android.app.Instrumentation.ActivityResult import android.app.Instrumentation.ActivityResult
import android.support.test.InstrumentationRegistry import androidx.test.InstrumentationRegistry
import android.support.test.espresso.intent.Intents.intended import androidx.test.espresso.intent.Intents.intended
import android.support.test.espresso.intent.Intents.intending import androidx.test.espresso.intent.Intents.intending
import android.support.test.espresso.intent.matcher.IntentMatchers.* import androidx.test.espresso.intent.matcher.IntentMatchers.*
import org.hamcrest.Matchers.allOf import org.hamcrest.Matchers.allOf
import org.hamcrest.Matchers.not import org.hamcrest.Matchers.not
import org.junit.Before import org.junit.Before
......
package chat.rocket.android.app package chat.rocket.android.app
import android.arch.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import android.arch.lifecycle.LifecycleObserver import androidx.lifecycle.LifecycleObserver
import android.arch.lifecycle.OnLifecycleEvent import androidx.lifecycle.OnLifecycleEvent
import chat.rocket.android.server.domain.GetAccountInteractor import chat.rocket.android.server.domain.GetAccountInteractor
import chat.rocket.android.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
......
import android.content.Context import android.content.Context
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.support.v4.content.ContextCompat import androidx.core.content.ContextCompat
import android.support.v4.graphics.drawable.DrawableCompat import androidx.core.graphics.drawable.DrawableCompat
import android.widget.TextView import android.widget.TextView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.common.model.UserStatus import chat.rocket.common.model.UserStatus
......
...@@ -3,19 +3,12 @@ package chat.rocket.android.app ...@@ -3,19 +3,12 @@ package chat.rocket.android.app
import android.app.Activity import android.app.Activity
import android.app.Application import android.app.Application
import android.app.Service import android.app.Service
import android.arch.lifecycle.ProcessLifecycleOwner
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import androidx.core.content.edit import androidx.core.content.edit
import androidx.lifecycle.ProcessLifecycleOwner
import chat.rocket.android.BuildConfig import chat.rocket.android.BuildConfig
import chat.rocket.android.app.migration.RealmMigration
import chat.rocket.android.app.migration.RocketChatLibraryModule
import chat.rocket.android.app.migration.RocketChatServerModule
import chat.rocket.android.app.migration.model.RealmBasedServerInfo
import chat.rocket.android.app.migration.model.RealmPublicSetting
import chat.rocket.android.app.migration.model.RealmSession
import chat.rocket.android.app.migration.model.RealmUser
import chat.rocket.android.dagger.DaggerAppComponent import chat.rocket.android.dagger.DaggerAppComponent
import chat.rocket.android.dagger.qualifier.ForMessages import chat.rocket.android.dagger.qualifier.ForMessages
import chat.rocket.android.helper.CrashlyticsTree import chat.rocket.android.helper.CrashlyticsTree
...@@ -24,19 +17,9 @@ import chat.rocket.android.infrastructure.installCrashlyticsWrapper ...@@ -24,19 +17,9 @@ import chat.rocket.android.infrastructure.installCrashlyticsWrapper
import chat.rocket.android.server.domain.AccountsRepository import chat.rocket.android.server.domain.AccountsRepository
import chat.rocket.android.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.SITE_URL import chat.rocket.android.server.domain.SITE_URL
import chat.rocket.android.server.domain.SaveCurrentServerInteractor
import chat.rocket.android.server.domain.SettingsRepository
import chat.rocket.android.server.domain.TokenRepository import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.server.domain.favicon
import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.domain.wideTile
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.serverLogoUrl
import chat.rocket.android.widget.emoji.EmojiRepository import chat.rocket.android.widget.emoji.EmojiRepository
import chat.rocket.common.model.Token
import chat.rocket.core.model.Value
import com.crashlytics.android.Crashlytics import com.crashlytics.android.Crashlytics
import com.crashlytics.android.core.CrashlyticsCore import com.crashlytics.android.core.CrashlyticsCore
import com.facebook.drawee.backends.pipeline.DraweeConfig import com.facebook.drawee.backends.pipeline.DraweeConfig
...@@ -49,10 +32,6 @@ import dagger.android.HasActivityInjector ...@@ -49,10 +32,6 @@ import dagger.android.HasActivityInjector
import dagger.android.HasBroadcastReceiverInjector import dagger.android.HasBroadcastReceiverInjector
import dagger.android.HasServiceInjector import dagger.android.HasServiceInjector
import io.fabric.sdk.android.Fabric import io.fabric.sdk.android.Fabric
import io.realm.Realm
import io.realm.RealmConfiguration
import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.launch
import timber.log.Timber import timber.log.Timber
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import javax.inject.Inject import javax.inject.Inject
...@@ -83,17 +62,11 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -83,17 +62,11 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
@Inject @Inject
lateinit var settingsInteractor: GetSettingsInteractor lateinit var settingsInteractor: GetSettingsInteractor
@Inject @Inject
lateinit var settingsRepository: SettingsRepository
@Inject
lateinit var tokenRepository: TokenRepository lateinit var tokenRepository: TokenRepository
@Inject @Inject
lateinit var accountRepository: AccountsRepository
@Inject
lateinit var saveCurrentServerRepository: SaveCurrentServerInteractor
@Inject
lateinit var prefs: SharedPreferences
@Inject
lateinit var localRepository: LocalRepository lateinit var localRepository: LocalRepository
@Inject
lateinit var accountRepository: AccountsRepository
@Inject @Inject
@field:ForMessages @field:ForMessages
...@@ -127,15 +100,7 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -127,15 +100,7 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
localRepository.setOldMessagesCleanedUp() localRepository.setOldMessagesCleanedUp()
} }
// TODO - remove this and all realm stuff when we got to 80% in 2.0 // TODO - remove REALM files.
try {
if (!localRepository.hasMigrated()) {
migrateFromLegacy()
}
} catch (ex: Exception) {
Timber.d(ex, "Error migrating old accounts")
}
// TODO - remove this // TODO - remove this
checkCurrentServer() checkCurrentServer()
} }
...@@ -160,110 +125,6 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -160,110 +125,6 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
} }
} }
private fun migrateFromLegacy() {
Realm.init(this)
val serveListConfiguration = RealmConfiguration.Builder()
.name("server.list.realm")
.schemaVersion(6)
.migration(RealmMigration())
.modules(RocketChatServerModule())
.build()
val serverRealm = Realm.getInstance(serveListConfiguration)
val serversInfoList = serverRealm.where(RealmBasedServerInfo::class.java).findAll().toList()
serversInfoList.forEach { server ->
val hostname = server.hostname
val url = if (server.insecure) "http://$hostname" else "https://$hostname"
val config = RealmConfiguration.Builder()
.name("${server.hostname}.realm")
.schemaVersion(6)
.migration(RealmMigration())
.modules(RocketChatLibraryModule())
.build()
val realm = Realm.getInstance(config)
val user = realm.where(RealmUser::class.java)
.isNotEmpty(RealmUser.EMAILS).findFirst()
val session = realm.where(RealmSession::class.java).findFirst()
migratePublicSettings(url, realm)
if (user != null && session != null) {
val authToken = session.token
settingsRepository.get(url)
migrateServerInfo(url, authToken!!, settingsRepository.get(url), user)
}
realm.close()
}
migrateCurrentServer(serversInfoList)
serverRealm.close()
localRepository.setMigrated(true)
}
private fun migrateServerInfo(url: String, authToken: String, settings: PublicSettings, user: RealmUser) {
val userId = user._id
val avatar = url.avatarUrl(user.username!!)
val icon = settings.favicon()?.let {
url.serverLogoUrl(it)
}
val logo = settings.wideTile()?.let {
url.serverLogoUrl(it)
}
val account = Account(url, icon, logo, user.username!!, avatar)
launch(CommonPool) {
tokenRepository.save(url, Token(userId!!, authToken))
accountRepository.save(account)
}
}
private fun migratePublicSettings(url: String, realm: Realm) {
val settings = realm.where(RealmPublicSetting::class.java).findAll()
val serverSettings = hashMapOf<String, Value<Any>>()
settings.toList().forEach { setting ->
val type = setting.type!!
val value = setting.value!!
val convertedSetting = when (type) {
"string" -> Value(value)
"language" -> Value(value)
"boolean" -> Value(value.toBoolean())
"int" -> try {
Value(value.toInt())
} catch (ex: NumberFormatException) {
Value(0)
}
else -> null // ignore
}
if (convertedSetting != null) {
val id = setting._id!!
serverSettings.put(id, convertedSetting)
}
}
settingsRepository.save(url, serverSettings)
}
private fun migrateCurrentServer(serversList: List<RealmBasedServerInfo>) {
if (getCurrentServerInteractor.get() == null) {
var currentServer = getSharedPreferences("cache", Context.MODE_PRIVATE)
.getString("KEY_SELECTED_SERVER_HOSTNAME", null)
currentServer = if (serversList.isNotEmpty()) {
val server = serversList.find { it.hostname == currentServer }
val hostname = server!!.hostname
if (server.insecure) {
"http://$hostname"
} else {
"https://$hostname"
}
} else {
"http://$currentServer"
}
saveCurrentServerRepository.save(currentServer)
}
}
private fun setupCrashlytics() { private fun setupCrashlytics() {
val core = CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build() val core = CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build()
Fabric.with(this, Crashlytics.Builder().core(core).build()) Fabric.with(this, Crashlytics.Builder().core(core).build())
...@@ -305,11 +166,6 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -305,11 +166,6 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
} }
} }
private fun LocalRepository.setMigrated(migrated: Boolean) {
save(LocalRepository.MIGRATION_FINISHED_KEY, migrated)
}
private fun LocalRepository.hasMigrated() = getBoolean(LocalRepository.MIGRATION_FINISHED_KEY)
private fun LocalRepository.needOldMessagesCleanUp() = getBoolean(CLEANUP_OLD_MESSAGES_NEEDED, true) private fun LocalRepository.needOldMessagesCleanUp() = getBoolean(CLEANUP_OLD_MESSAGES_NEEDED, true)
private fun LocalRepository.setOldMessagesCleanedUp() = save(CLEANUP_OLD_MESSAGES_NEEDED, false) private fun LocalRepository.setOldMessagesCleanedUp() = save(CLEANUP_OLD_MESSAGES_NEEDED, false)
......
package chat.rocket.android.app
import android.arch.persistence.room.Database
import android.arch.persistence.room.RoomDatabase
import chat.rocket.android.server.infraestructure.ServerDao
import chat.rocket.android.server.infraestructure.ServerEntity
@Database(entities = arrayOf(ServerEntity::class), version = 1, exportSchema = false)
abstract class RocketChatDatabase : RoomDatabase() {
abstract fun serverDao(): ServerDao
}
package chat.rocket.android.app.migration
import chat.rocket.android.BuildConfig
import chat.rocket.android.app.migration.model.RealmUser
import io.realm.DynamicRealm
import io.realm.RealmMigration
class RealmMigration : RealmMigration {
override fun migrate(dynamicRealm: DynamicRealm, oldVersion: Long, newVersion: Long) {
var oldVersion = oldVersion
val schema = dynamicRealm.schema
if (oldVersion == 0L) {
// NOOP
oldVersion++
}
if (oldVersion == 1L) {
oldVersion++
}
if (oldVersion == 2L) {
oldVersion++
}
if (oldVersion == 3L) {
oldVersion++
}
if (oldVersion == 4L) {
oldVersion++
}
if (oldVersion == 5L) {
val userSchema = schema.get("RealmUser")
try {
userSchema?.addField(RealmUser.NAME, String::class.java)
} catch (e: IllegalArgumentException) {
if (BuildConfig.DEBUG) {
e.printStackTrace()
}
// ignore; it makes here if the schema for this model was already update before without migration
}
}
}
// hack around to avoid "new different configuration cannot access the same file" error
override fun hashCode(): Int {
return 37
}
override fun equals(o: Any?): Boolean {
return o is chat.rocket.android.app.migration.RealmMigration
}
// end hack
}
\ No newline at end of file
package chat.rocket.android.app.migration
import io.realm.annotations.RealmModule
@RealmModule(library = true, allClasses = true)
class RocketChatLibraryModule
\ No newline at end of file
package chat.rocket.android.app.migration
import chat.rocket.android.app.migration.model.RealmBasedServerInfo
import io.realm.annotations.RealmModule
@RealmModule(library = true, classes = arrayOf(RealmBasedServerInfo::class))
class RocketChatServerModule
\ No newline at end of file
package chat.rocket.android.app.migration.model
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
open class RealmBasedServerInfo : RealmObject() {
@PrimaryKey
@JvmField var hostname: String? = null
@JvmField var name: String? = null
@JvmField var session: String? = null
@JvmField var insecure: Boolean = false
}
\ No newline at end of file
package chat.rocket.android.app.migration.model
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
open class RealmEmail : RealmObject() {
@PrimaryKey
@JvmField
var address: String? = null
@JvmField
var verified: Boolean = false
}
\ No newline at end of file
package chat.rocket.android.app.migration.model
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
open class RealmPreferences : RealmObject() {
@PrimaryKey
@JvmField
var id: String? = null
@JvmField
var newRoomNotification: String? = null
@JvmField
var newMessageNotification: String? = null
@JvmField
var useEmojis: Boolean = false
@JvmField
var convertAsciiEmoji: Boolean = false
@JvmField
var saveMobileBandwidth: Boolean = false
@JvmField
var collapseMediaByDefault: Boolean = false
@JvmField
var unreadRoomsMode: Boolean = false
@JvmField
var autoImageLoad: Boolean = false
@JvmField
var emailNotificationMode: String? = null
@JvmField
var unreadAlert: Boolean = false
@JvmField
var desktopNotificationDuration: Int = 0
@JvmField
var viewMode: Int = 0
@JvmField
var hideUsernames: Boolean = false
@JvmField
var hideAvatars: Boolean = false
@JvmField
var hideFlexTab: Boolean = false
}
\ No newline at end of file
package chat.rocket.android.app.migration.model
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
open class RealmPublicSetting : RealmObject() {
@PrimaryKey
@JvmField
var _id: String? = null
@JvmField
var group: String? = null
@JvmField
var type: String? = null
@JvmField
var value: String? = null
@JvmField
var _updatedAt: Long = 0
@JvmField
var meta: String? = null
}
\ No newline at end of file
package chat.rocket.android.app.migration.model
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
open class RealmSession : RealmObject() {
@JvmField
@PrimaryKey
var sessionId: Int = 0 //only 0 is used!
@JvmField
var token: String? = null
@JvmField
var tokenVerified: Boolean = false
@JvmField
var error: String? = null
}
\ No newline at end of file
package chat.rocket.android.app.migration.model
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
open class RealmSettings : RealmObject() {
@PrimaryKey
@JvmField
var id: String? = null
@JvmField
var preferences: RealmPreferences? = null
}
\ No newline at end of file
package chat.rocket.android.app.migration.model
import io.realm.RealmList
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
open class RealmUser : RealmObject() {
companion object {
const val ID = "_id"
const val NAME = "name"
const val USERNAME = "username"
const val STATUS = "status"
const val UTC_OFFSET = "utcOffset"
const val EMAILS = "emails"
const val SETTINGS = "settings"
const val STATUS_ONLINE = "online"
const val STATUS_BUSY = "busy"
const val STATUS_AWAY = "away"
const val STATUS_OFFLINE = "offline"
}
@PrimaryKey
@JvmField
var _id: String? = null
@JvmField
var name: String? = null
@JvmField
var username: String? = null
@JvmField
var status: String? = null
@JvmField
var utcOffset: Double = 0.toDouble()
@JvmField
var emails: RealmList<RealmEmail>? = null
@JvmField
var settings: RealmSettings? = null
}
\ No newline at end of file
package chat.rocket.android.authentication.login.di package chat.rocket.android.authentication.login.di
import android.arch.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.authentication.login.presentation.LoginView import chat.rocket.android.authentication.login.presentation.LoginView
import chat.rocket.android.authentication.login.ui.LoginFragment import chat.rocket.android.authentication.login.ui.LoginFragment
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
...@@ -10,20 +10,26 @@ import dagger.Provides ...@@ -10,20 +10,26 @@ import dagger.Provides
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.experimental.Job
@Module @Module
@PerFragment
class LoginFragmentModule { class LoginFragmentModule {
@Provides @Provides
@PerFragment
fun provideJob() = Job()
@Provides
@PerFragment
fun loginView(frag: LoginFragment): LoginView { fun loginView(frag: LoginFragment): LoginView {
return frag return frag
} }
@Provides @Provides
@PerFragment
fun provideLifecycleOwner(frag: LoginFragment): LifecycleOwner { fun provideLifecycleOwner(frag: LoginFragment): LifecycleOwner {
return frag return frag
} }
@Provides @Provides
@PerFragment
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy { fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs) return CancelStrategy(owner, jobs)
} }
......
package chat.rocket.android.authentication.login.di package chat.rocket.android.authentication.login.di
import chat.rocket.android.authentication.login.ui.LoginFragment import chat.rocket.android.authentication.login.ui.LoginFragment
import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
@Module abstract class LoginFragmentProvider { @Module abstract class LoginFragmentProvider {
@ContributesAndroidInjector(modules = [LoginFragmentModule::class]) @ContributesAndroidInjector(modules = [LoginFragmentModule::class])
@PerFragment
abstract fun provideLoginFragment(): LoginFragment abstract fun provideLoginFragment(): LoginFragment
} }
\ No newline at end of file
...@@ -6,7 +6,7 @@ import android.content.Intent ...@@ -6,7 +6,7 @@ import android.content.Intent
import android.graphics.PorterDuff import android.graphics.PorterDuff
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.Fragment import androidx.fragment.app.Fragment
import android.text.style.ClickableSpan import android.text.style.ClickableSpan
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
......
package chat.rocket.android.authentication.registerusername.di package chat.rocket.android.authentication.registerusername.di
import android.arch.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.authentication.registerusername.presentation.RegisterUsernameView import chat.rocket.android.authentication.registerusername.presentation.RegisterUsernameView
import chat.rocket.android.authentication.registerusername.ui.RegisterUsernameFragment import chat.rocket.android.authentication.registerusername.ui.RegisterUsernameFragment
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
...@@ -10,20 +10,26 @@ import dagger.Provides ...@@ -10,20 +10,26 @@ import dagger.Provides
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.experimental.Job
@Module @Module
@PerFragment
class RegisterUsernameFragmentModule { class RegisterUsernameFragmentModule {
@Provides @Provides
@PerFragment
fun provideJob() = Job()
@Provides
@PerFragment
fun registerUsernameView(frag: RegisterUsernameFragment): RegisterUsernameView { fun registerUsernameView(frag: RegisterUsernameFragment): RegisterUsernameView {
return frag return frag
} }
@Provides @Provides
@PerFragment
fun provideLifecycleOwner(frag: RegisterUsernameFragment): LifecycleOwner { fun provideLifecycleOwner(frag: RegisterUsernameFragment): LifecycleOwner {
return frag return frag
} }
@Provides @Provides
@PerFragment
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy { fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs) return CancelStrategy(owner, jobs)
} }
......
package chat.rocket.android.authentication.registerusername.di package chat.rocket.android.authentication.registerusername.di
import chat.rocket.android.authentication.registerusername.ui.RegisterUsernameFragment import chat.rocket.android.authentication.registerusername.ui.RegisterUsernameFragment
import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
...@@ -8,5 +9,6 @@ import dagger.android.ContributesAndroidInjector ...@@ -8,5 +9,6 @@ import dagger.android.ContributesAndroidInjector
abstract class RegisterUsernameFragmentProvider { abstract class RegisterUsernameFragmentProvider {
@ContributesAndroidInjector(modules = [RegisterUsernameFragmentModule::class]) @ContributesAndroidInjector(modules = [RegisterUsernameFragmentModule::class])
@PerFragment
abstract fun provideRegisterUsernameFragment(): RegisterUsernameFragment abstract fun provideRegisterUsernameFragment(): RegisterUsernameFragment
} }
\ No newline at end of file
...@@ -3,7 +3,7 @@ package chat.rocket.android.authentication.registerusername.ui ...@@ -3,7 +3,7 @@ package chat.rocket.android.authentication.registerusername.ui
import DrawableHelper import DrawableHelper
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.Fragment import androidx.fragment.app.Fragment
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
......
package chat.rocket.android.authentication.resetpassword.di package chat.rocket.android.authentication.resetpassword.di
import android.arch.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.authentication.resetpassword.presentation.ResetPasswordView import chat.rocket.android.authentication.resetpassword.presentation.ResetPasswordView
import chat.rocket.android.authentication.resetpassword.ui.ResetPasswordFragment import chat.rocket.android.authentication.resetpassword.ui.ResetPasswordFragment
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
...@@ -10,20 +10,26 @@ import dagger.Provides ...@@ -10,20 +10,26 @@ import dagger.Provides
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.experimental.Job
@Module @Module
@PerFragment
class ResetPasswordFragmentModule { class ResetPasswordFragmentModule {
@Provides @Provides
@PerFragment
fun provideJob() = Job()
@Provides
@PerFragment
fun resetPasswordView(frag: ResetPasswordFragment): ResetPasswordView { fun resetPasswordView(frag: ResetPasswordFragment): ResetPasswordView {
return frag return frag
} }
@Provides @Provides
@PerFragment
fun provideLifecycleOwner(frag: ResetPasswordFragment): LifecycleOwner { fun provideLifecycleOwner(frag: ResetPasswordFragment): LifecycleOwner {
return frag return frag
} }
@Provides @Provides
@PerFragment
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy { fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs) return CancelStrategy(owner, jobs)
} }
......
package chat.rocket.android.authentication.resetpassword.di package chat.rocket.android.authentication.resetpassword.di
import chat.rocket.android.authentication.resetpassword.ui.ResetPasswordFragment import chat.rocket.android.authentication.resetpassword.ui.ResetPasswordFragment
import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
...@@ -8,5 +9,6 @@ import dagger.android.ContributesAndroidInjector ...@@ -8,5 +9,6 @@ import dagger.android.ContributesAndroidInjector
abstract class ResetPasswordFragmentProvider { abstract class ResetPasswordFragmentProvider {
@ContributesAndroidInjector(modules = [ResetPasswordFragmentModule::class]) @ContributesAndroidInjector(modules = [ResetPasswordFragmentModule::class])
@PerFragment
abstract fun provideResetPasswordFragment(): ResetPasswordFragment abstract fun provideResetPasswordFragment(): ResetPasswordFragment
} }
\ No newline at end of file
...@@ -3,7 +3,7 @@ package chat.rocket.android.authentication.resetpassword.ui ...@@ -3,7 +3,7 @@ package chat.rocket.android.authentication.resetpassword.ui
import DrawableHelper import DrawableHelper
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.Fragment import androidx.fragment.app.Fragment
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
......
package chat.rocket.android.authentication.server.di package chat.rocket.android.authentication.server.di
import android.arch.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.authentication.server.presentation.ServerView import chat.rocket.android.authentication.server.presentation.ServerView
import chat.rocket.android.authentication.server.ui.ServerFragment import chat.rocket.android.authentication.server.ui.ServerFragment
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.experimental.Job
...@@ -12,16 +13,23 @@ import kotlinx.coroutines.experimental.Job ...@@ -12,16 +13,23 @@ import kotlinx.coroutines.experimental.Job
class ServerFragmentModule { class ServerFragmentModule {
@Provides @Provides
@PerFragment
fun provideJob() = Job()
@Provides
@PerFragment
fun serverView(frag: ServerFragment): ServerView { fun serverView(frag: ServerFragment): ServerView {
return frag return frag
} }
@Provides @Provides
@PerFragment
fun provideLifecycleOwner(frag: ServerFragment): LifecycleOwner { fun provideLifecycleOwner(frag: ServerFragment): LifecycleOwner {
return frag return frag
} }
@Provides @Provides
@PerFragment
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy { fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs) return CancelStrategy(owner, jobs)
} }
......
package chat.rocket.android.authentication.server.di package chat.rocket.android.authentication.server.di
import chat.rocket.android.authentication.server.ui.ServerFragment import chat.rocket.android.authentication.server.ui.ServerFragment
import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
...@@ -8,5 +9,6 @@ import dagger.android.ContributesAndroidInjector ...@@ -8,5 +9,6 @@ import dagger.android.ContributesAndroidInjector
abstract class ServerFragmentProvider { abstract class ServerFragmentProvider {
@ContributesAndroidInjector(modules = [ServerFragmentModule::class]) @ContributesAndroidInjector(modules = [ServerFragmentModule::class])
@PerFragment
abstract fun provideServerFragment(): ServerFragment abstract fun provideServerFragment(): ServerFragment
} }
\ No newline at end of file
...@@ -3,7 +3,7 @@ package chat.rocket.android.authentication.server.ui ...@@ -3,7 +3,7 @@ package chat.rocket.android.authentication.server.ui
import android.app.AlertDialog import android.app.AlertDialog
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.Fragment import androidx.fragment.app.Fragment
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
......
package chat.rocket.android.authentication.signup.di package chat.rocket.android.authentication.signup.di
import android.arch.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.authentication.signup.presentation.SignupView import chat.rocket.android.authentication.signup.presentation.SignupView
import chat.rocket.android.authentication.signup.ui.SignupFragment import chat.rocket.android.authentication.signup.ui.SignupFragment
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
...@@ -10,20 +10,26 @@ import dagger.Provides ...@@ -10,20 +10,26 @@ import dagger.Provides
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.experimental.Job
@Module @Module
@PerFragment
class SignupFragmentModule { class SignupFragmentModule {
@Provides @Provides
@PerFragment
fun provideJob() = Job()
@Provides
@PerFragment
fun signupView(frag: SignupFragment): SignupView { fun signupView(frag: SignupFragment): SignupView {
return frag return frag
} }
@Provides @Provides
@PerFragment
fun provideLifecycleOwner(frag: SignupFragment): LifecycleOwner { fun provideLifecycleOwner(frag: SignupFragment): LifecycleOwner {
return frag return frag
} }
@Provides @Provides
@PerFragment
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy { fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs) return CancelStrategy(owner, jobs)
} }
......
package chat.rocket.android.authentication.signup.di package chat.rocket.android.authentication.signup.di
import chat.rocket.android.authentication.signup.ui.SignupFragment import chat.rocket.android.authentication.signup.ui.SignupFragment
import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
...@@ -8,5 +9,6 @@ import dagger.android.ContributesAndroidInjector ...@@ -8,5 +9,6 @@ import dagger.android.ContributesAndroidInjector
abstract class SignupFragmentProvider { abstract class SignupFragmentProvider {
@ContributesAndroidInjector(modules = [SignupFragmentModule::class]) @ContributesAndroidInjector(modules = [SignupFragmentModule::class])
@PerFragment
abstract fun provideSignupFragment(): SignupFragment abstract fun provideSignupFragment(): SignupFragment
} }
\ No newline at end of file
...@@ -5,7 +5,7 @@ import android.app.Activity ...@@ -5,7 +5,7 @@ import android.app.Activity
import android.content.Intent import android.content.Intent
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.Fragment import androidx.fragment.app.Fragment
import android.text.style.ClickableSpan import android.text.style.ClickableSpan
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
......
package chat.rocket.android.authentication.twofactor.di package chat.rocket.android.authentication.twofactor.di
import android.arch.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.authentication.twofactor.presentation.TwoFAView import chat.rocket.android.authentication.twofactor.presentation.TwoFAView
import chat.rocket.android.authentication.twofactor.ui.TwoFAFragment import chat.rocket.android.authentication.twofactor.ui.TwoFAFragment
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
...@@ -10,20 +10,26 @@ import dagger.Provides ...@@ -10,20 +10,26 @@ import dagger.Provides
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.experimental.Job
@Module @Module
@PerFragment
class TwoFAFragmentModule { class TwoFAFragmentModule {
@Provides @Provides
@PerFragment
fun provideJob() = Job()
@Provides
@PerFragment
fun loginView(frag: TwoFAFragment): TwoFAView { fun loginView(frag: TwoFAFragment): TwoFAView {
return frag return frag
} }
@Provides @Provides
@PerFragment
fun provideLifecycleOwner(frag: TwoFAFragment): LifecycleOwner { fun provideLifecycleOwner(frag: TwoFAFragment): LifecycleOwner {
return frag return frag
} }
@Provides @Provides
@PerFragment
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy { fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs) return CancelStrategy(owner, jobs)
} }
......
package chat.rocket.android.authentication.twofactor.di package chat.rocket.android.authentication.twofactor.di
import chat.rocket.android.authentication.twofactor.ui.TwoFAFragment import chat.rocket.android.authentication.twofactor.ui.TwoFAFragment
import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
@Module abstract class TwoFAFragmentProvider { @Module abstract class TwoFAFragmentProvider {
@ContributesAndroidInjector(modules = [TwoFAFragmentModule::class]) @ContributesAndroidInjector(modules = [TwoFAFragmentModule::class])
@PerFragment
abstract fun provideTwoFAFragment(): TwoFAFragment abstract fun provideTwoFAFragment(): TwoFAFragment
} }
...@@ -4,7 +4,7 @@ import DrawableHelper ...@@ -4,7 +4,7 @@ import DrawableHelper
import android.content.Context import android.content.Context
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.Fragment import androidx.fragment.app.Fragment
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
......
...@@ -3,8 +3,8 @@ package chat.rocket.android.authentication.ui ...@@ -3,8 +3,8 @@ package chat.rocket.android.authentication.ui
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.Fragment import androidx.fragment.app.Fragment
import android.support.v7.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.authentication.domain.model.LoginDeepLinkInfo import chat.rocket.android.authentication.domain.model.LoginDeepLinkInfo
import chat.rocket.android.authentication.domain.model.getLoginDeepLinkInfo import chat.rocket.android.authentication.domain.model.getLoginDeepLinkInfo
......
package chat.rocket.android.chatroom.adapter package chat.rocket.android.chatroom.adapter
import android.support.annotation.IntDef import androidx.annotation.IntDef
const val PEOPLE = 0 const val PEOPLE = 0
const val ROOMS = 1 const val ROOMS = 1
......
package chat.rocket.android.chatroom.adapter package chat.rocket.android.chatroom.adapter
import android.support.v7.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
......
package chat.rocket.android.chatroom.adapter package chat.rocket.android.chatroom.adapter
import android.support.v7.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import android.view.MenuItem import android.view.MenuItem
import android.view.ViewGroup import android.view.ViewGroup
import chat.rocket.android.R import chat.rocket.android.R
......
package chat.rocket.android.chatroom.adapter package chat.rocket.android.chatroom.adapter
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.support.v4.content.ContextCompat import androidx.core.content.ContextCompat
import android.text.method.LinkMovementMethod import android.text.method.LinkMovementMethod
import android.view.View import android.view.View
import chat.rocket.android.R import chat.rocket.android.R
......
package chat.rocket.android.chatroom.adapter package chat.rocket.android.chatroom.adapter
import android.support.v7.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
......
package chat.rocket.android.chatroom.di package chat.rocket.android.chatroom.di
import android.arch.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.chatroom.presentation.ChatRoomView import chat.rocket.android.chatroom.presentation.ChatRoomView
import chat.rocket.android.chatroom.ui.ChatRoomFragment import chat.rocket.android.chatroom.ui.ChatRoomFragment
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
...@@ -12,6 +12,10 @@ import kotlinx.coroutines.experimental.Job ...@@ -12,6 +12,10 @@ import kotlinx.coroutines.experimental.Job
@Module @Module
class ChatRoomFragmentModule { class ChatRoomFragmentModule {
@Provides
@PerFragment
fun provideJob() = Job()
@Provides @Provides
@PerFragment @PerFragment
fun chatRoomView(frag: ChatRoomFragment): ChatRoomView { fun chatRoomView(frag: ChatRoomFragment): ChatRoomView {
......
...@@ -7,8 +7,8 @@ import dagger.Module ...@@ -7,8 +7,8 @@ import dagger.Module
import dagger.Provides import dagger.Provides
@Module @Module
@PerActivity
class ChatRoomModule { class ChatRoomModule {
@Provides @Provides
@PerActivity
fun provideChatRoomNavigator(activity: ChatRoomActivity) = ChatRoomNavigator(activity) fun provideChatRoomNavigator(activity: ChatRoomActivity) = ChatRoomNavigator(activity)
} }
\ No newline at end of file
...@@ -445,7 +445,7 @@ class ChatRoomPresenter @Inject constructor( ...@@ -445,7 +445,7 @@ class ChatRoomPresenter @Inject constructor(
is RoomType.DirectMessage -> "direct" is RoomType.DirectMessage -> "direct"
is RoomType.PrivateGroup -> "group" is RoomType.PrivateGroup -> "group"
is RoomType.Channel -> "channel" is RoomType.Channel -> "channel"
is RoomType.Livechat -> "livechat" is RoomType.LiveChat -> "livechat"
else -> "custom" else -> "custom"
} }
view.showReplyingAction( view.showReplyingAction(
...@@ -658,7 +658,7 @@ class ChatRoomPresenter @Inject constructor( ...@@ -658,7 +658,7 @@ class ChatRoomPresenter @Inject constructor(
try { try {
val chatRooms = chatRoomsInteractor.getAll(currentServer) val chatRooms = chatRoomsInteractor.getAll(currentServer)
.filterNot { .filterNot {
it.type is RoomType.DirectMessage || it.type is RoomType.Livechat it.type is RoomType.DirectMessage || it.type is RoomType.LiveChat
} }
.map { chatRoom -> .map { chatRoom ->
val name = chatRoom.name val name = chatRoom.name
......
package chat.rocket.android.chatroom.ui package chat.rocket.android.chatroom.ui
import android.support.design.widget.BaseTransientBottomBar import com.google.android.material.snackbar.BaseTransientBottomBar
import android.support.v4.view.ViewCompat import androidx.core.view.ViewCompat
import android.text.Spannable import android.text.Spannable
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
import android.view.LayoutInflater import android.view.LayoutInflater
......
...@@ -4,8 +4,8 @@ import DrawableHelper ...@@ -4,8 +4,8 @@ import DrawableHelper
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.Fragment import androidx.fragment.app.Fragment
import android.support.v7.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
import androidx.core.view.isVisible import androidx.core.view.isVisible
import chat.rocket.android.R import chat.rocket.android.R
......
...@@ -8,11 +8,11 @@ import android.content.Intent ...@@ -8,11 +8,11 @@ import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.support.annotation.DrawableRes import androidx.annotation.DrawableRes
import android.support.v4.app.Fragment import androidx.fragment.app.Fragment
import android.support.v7.widget.DefaultItemAnimator import androidx.recyclerview.widget.DefaultItemAnimator
import android.support.v7.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
import android.view.* import android.view.*
import androidx.core.text.bold import androidx.core.text.bold
...@@ -526,8 +526,8 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -526,8 +526,8 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
override fun onNonEmojiKeyPressed(keyCode: Int) { override fun onNonEmojiKeyPressed(keyCode: Int) {
when (keyCode) { when (keyCode) {
KeyEvent.KEYCODE_BACK -> { KeyEvent.KEYCODE_BACK -> with(text_message) {
if (text_message.selectionStart > 0) text_message.text!!.delete(text_message.selectionStart - 1, text_message.selectionStart) if (selectionStart > 0) text?.delete(selectionStart - 1, selectionStart)
} }
else -> throw IllegalArgumentException("pressed key not expected") else -> throw IllegalArgumentException("pressed key not expected")
} }
...@@ -616,13 +616,13 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -616,13 +616,13 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
private fun setupRecyclerView() { private fun setupRecyclerView() {
// Initialize the endlessRecyclerViewScrollListener so we don't NPE at onDestroyView // Initialize the endlessRecyclerViewScrollListener so we don't NPE at onDestroyView
val linearLayoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, true) val linearLayoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, true)
linearLayoutManager.stackFromEnd = true linearLayoutManager.stackFromEnd = true
recycler_view.layoutManager = linearLayoutManager recycler_view.layoutManager = linearLayoutManager
recycler_view.itemAnimator = DefaultItemAnimator() recycler_view.itemAnimator = DefaultItemAnimator()
endlessRecyclerViewScrollListener = object : endlessRecyclerViewScrollListener = object :
EndlessRecyclerViewScrollListener(recycler_view.layoutManager as LinearLayoutManager) { EndlessRecyclerViewScrollListener(recycler_view.layoutManager as LinearLayoutManager) {
override fun onLoadMore(page: Int, totalItemsCount: Int, recyclerView: RecyclerView?) { override fun onLoadMore(page: Int, totalItemsCount: Int, recyclerView: RecyclerView) {
presenter.loadMessages(chatRoomId, chatRoomType, page * 30L) presenter.loadMessages(chatRoomId, chatRoomType, page * 30L)
} }
} }
...@@ -641,7 +641,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -641,7 +641,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
if (isChatRoomReadOnly && !canPost) { if (isChatRoomReadOnly && !canPost) {
text_room_is_read_only.setVisible(true) text_room_is_read_only.setVisible(true)
input_container.setVisible(false) input_container.setVisible(false)
} else if (!isSubscribed && roomTypeOf(chatRoomType) != RoomType.DIRECT_MESSAGE) { } else if (!isSubscribed && roomTypeOf(chatRoomType) !is RoomType.DirectMessage) {
input_container.setVisible(false) input_container.setVisible(false)
button_join_chat.setVisible(true) button_join_chat.setVisible(true)
button_join_chat.setOnClickListener { presenter.joinChat(chatRoomId) } button_join_chat.setOnClickListener { presenter.joinChat(chatRoomId) }
......
package chat.rocket.android.chatroom.ui.bottomsheet package chat.rocket.android.chatroom.ui.bottomsheet
import android.support.design.widget.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import android.support.v7.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import android.view.MenuItem import android.view.MenuItem
import ru.whalemare.sheetmenu.SheetMenu import ru.whalemare.sheetmenu.SheetMenu
import ru.whalemare.sheetmenu.adapter.MenuAdapter import ru.whalemare.sheetmenu.adapter.MenuAdapter
...@@ -11,7 +11,7 @@ class BottomSheetMenu(adapter: MenuAdapter) : SheetMenu(adapter = adapter) { ...@@ -11,7 +11,7 @@ class BottomSheetMenu(adapter: MenuAdapter) : SheetMenu(adapter = adapter) {
override fun processRecycler(recycler: RecyclerView, dialog: BottomSheetDialog) { override fun processRecycler(recycler: RecyclerView, dialog: BottomSheetDialog) {
if (layoutManager == null) { if (layoutManager == null) {
layoutManager = LinearLayoutManager(recycler.context, LinearLayoutManager.VERTICAL, false) layoutManager = LinearLayoutManager(recycler.context)
} }
// Superclass SheetMenu adapter property is nullable MenuAdapter? but this class enforces // Superclass SheetMenu adapter property is nullable MenuAdapter? but this class enforces
......
package chat.rocket.android.chatroom.ui.bottomsheet.adapter package chat.rocket.android.chatroom.ui.bottomsheet.adapter
import android.graphics.Color import android.graphics.Color
import android.support.annotation.ColorInt import androidx.annotation.ColorInt
import android.support.annotation.IdRes import androidx.annotation.IdRes
import android.util.SparseIntArray import android.util.SparseIntArray
import android.view.MenuItem import android.view.MenuItem
import chat.rocket.android.R import chat.rocket.android.R
......
...@@ -4,7 +4,7 @@ import DateTimeHelper ...@@ -4,7 +4,7 @@ import DateTimeHelper
import android.content.Context import android.content.Context
import android.graphics.Color import android.graphics.Color
import android.graphics.Typeface import android.graphics.Typeface
import android.support.v4.content.ContextCompat import androidx.core.content.ContextCompat
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
import android.text.style.ForegroundColorSpan import android.text.style.ForegroundColorSpan
import android.text.style.StyleSpan import android.text.style.StyleSpan
......
package chat.rocket.android.chatrooms.adapter
data class HeaderItemHolder(override val data: String) : ItemHolder<String>
\ No newline at end of file
package chat.rocket.android.chatrooms.adapter
import android.view.View
import kotlinx.android.synthetic.main.item_chatroom_header.view.*
class HeaderViewHolder(itemView: View) : ViewHolder<HeaderItemHolder>(itemView) {
override fun bindViews(data: HeaderItemHolder) {
with(itemView) {
text_chatroom_header.text = data.data
}
}
}
\ No newline at end of file
package chat.rocket.android.chatrooms.adapter
interface ItemHolder<T> {
val data: T
}
\ No newline at end of file
package chat.rocket.android.chatrooms.adapter
import chat.rocket.android.chatrooms.adapter.model.Room
data class RoomItemHolder(override val data: Room) : ItemHolder<Room>
\ No newline at end of file
package chat.rocket.android.chatrooms.adapter
import android.app.Application
import android.text.SpannableStringBuilder
import androidx.core.content.ContextCompat
import androidx.core.text.bold
import androidx.core.text.color
import chat.rocket.android.R
import chat.rocket.android.chatrooms.adapter.model.Room
import chat.rocket.android.db.model.ChatRoom
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.infrastructure.checkIfMyself
import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.showLastMessage
import chat.rocket.android.server.domain.useRealName
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.date
import chat.rocket.android.util.extensions.localDateTime
import chat.rocket.common.model.RoomType
import chat.rocket.common.model.roomTypeOf
import chat.rocket.common.model.userStatusOf
class RoomMapper(private val context: Application,
private val settings: PublicSettings,
private val localRepository: LocalRepository,
private val serverUrl: String) {
private val nameUnreadColor = ContextCompat.getColor(context, R.color.colorPrimaryText)
private val nameColor = ContextCompat.getColor(context, R.color.colorSecondaryText)
private val dateUnreadColor = ContextCompat.getColor(context, R.color.colorAccent)
private val dateColor = ContextCompat.getColor(context, R.color.colorSecondaryText)
private val messageUnreadColor = ContextCompat.getColor(context, android.R.color.primary_text_light)
private val messageColor = ContextCompat.getColor(context, R.color.colorSecondaryText)
fun map(rooms: List<ChatRoom>, grouped: Boolean): List<ItemHolder<*>> {
val list = ArrayList<ItemHolder<*>>(rooms.size + 4)
var lastType: String? = null
rooms.forEach { room ->
if (grouped && lastType != room.chatRoom.type) {
list.add(HeaderItemHolder(roomType(room.chatRoom.type)))
}
list.add(RoomItemHolder(map(room)))
lastType = room.chatRoom.type
}
return list
}
fun map(chatRoom: ChatRoom): Room {
return with(chatRoom.chatRoom) {
val isUnread = alert || unread > 0
val type = roomTypeOf(type)
val status = chatRoom.status?.let { userStatusOf(it) }
val roomName = mapName(name, chatRoom.userFullname, isUnread)
val timestamp = mapDate(lastMessageTimestamp ?: updatedAt, isUnread)
val avatar = if (type is RoomType.DirectMessage) {
serverUrl.avatarUrl(name)
} else {
serverUrl.avatarUrl(name, isGroupOrChannel = true)
}
val unread = mapUnread(unread)
val lastMessage = mapLastMessage(chatRoom.lastMessageUserName,
chatRoom.lastMessageUserFullName, lastMessageText, isUnread)
Room(
id = id,
name = roomName,
type = type,
avatar = avatar,
date = timestamp,
unread = unread,
alert = isUnread,
lastMessage = lastMessage,
status = status
)
}
}
private fun roomType(type: String): String {
val resources = context.resources
return when (type) {
RoomType.CHANNEL -> resources.getString(R.string.header_channel)
RoomType.PRIVATE_GROUP -> resources.getString(R.string.header_private_groups)
RoomType.DIRECT_MESSAGE -> resources.getString(R.string.header_direct_messages)
RoomType.LIVECHAT -> resources.getString(R.string.header_live_chats)
else -> resources.getString(R.string.header_unknown)
}
}
private fun mapLastMessage(name: String?, fullName: String?, text: String?, unread: Boolean): CharSequence? {
return if (!settings.showLastMessage()) {
null
} else if (name != null && text != null) {
val user = if (localRepository.checkIfMyself(name)) {
"${context.getString(R.string.msg_you)}: "
} else {
"${mapName(name, fullName, unread)}: "
}
val color = if (unread) messageUnreadColor else messageColor
SpannableStringBuilder()
.color(color) {
bold { append(user) }
append(text)
}
} else {
context.getText(R.string.msg_no_messages_yet)
}
}
private fun mapName(name: String, fullName: String?, unread: Boolean): CharSequence {
val roomName = if (settings.useRealName()) {
fullName ?: name
} else {
name
}
val color = if (unread) nameUnreadColor else nameColor
return SpannableStringBuilder()
.color(color) {
append(roomName)
}
}
private fun mapUnread(unread: Long): String? {
return when(unread) {
0L -> null
in 1..99 -> unread.toString()
else -> context.getString(R.string.msg_more_than_ninety_nine_unread_messages)
}
}
private fun mapDate(date: Long?, unread: Boolean): CharSequence? {
return date?.localDateTime()?.date(context)?.let {
val color = if (unread) dateUnreadColor else dateColor
SpannableStringBuilder().color(color) {
append(it)
}
}
}
}
\ No newline at end of file
package chat.rocket.android.chatrooms.adapter
import android.content.res.Resources
import android.graphics.drawable.Drawable
import android.view.View
import androidx.core.view.isGone
import androidx.core.view.isVisible
import chat.rocket.android.R
import chat.rocket.common.model.RoomType
import chat.rocket.common.model.UserStatus
import kotlinx.android.synthetic.main.item_chat.view.*
import kotlinx.android.synthetic.main.unread_messages_badge.view.*
class RoomViewHolder(itemView: View, private val listener: (String) -> Unit) : ViewHolder<RoomItemHolder>(itemView) {
private val resources: Resources = itemView.resources
private val channelUnread: Drawable = resources.getDrawable(R.drawable.ic_hashtag_black_12dp)
private val channel: Drawable = resources.getDrawable(R.drawable.ic_hashtag_12dp)
private val groupUnread: Drawable = resources.getDrawable(R.drawable.ic_lock_black_12_dp)
private val group: Drawable = resources.getDrawable(R.drawable.ic_lock_12_dp)
private val online: Drawable = resources.getDrawable(R.drawable.ic_status_online_12dp)
private val away: Drawable = resources.getDrawable(R.drawable.ic_status_away_12dp)
private val busy: Drawable = resources.getDrawable(R.drawable.ic_status_busy_12dp)
private val offline: Drawable = resources.getDrawable(R.drawable.ic_status_invisible_12dp)
override fun bindViews(data: RoomItemHolder) {
val room = data.data
with(itemView) {
image_avatar.setImageURI(room.avatar)
text_chat_name.text = room.name
if (room.lastMessage != null) {
text_last_message.isVisible = true
text_last_message.text = room.lastMessage
} else {
text_last_message.isGone = true
}
if (room.date != null) {
text_last_message_date_time.isVisible = true
text_last_message_date_time.text = room.date
} else {
text_last_message_date_time.isGone = true
}
if (room.unread != null) {
text_total_unread_messages.isVisible = true
text_total_unread_messages.text = room.unread
} else {
text_total_unread_messages.isGone = true
}
if (room.status != null && room.type is RoomType.DirectMessage) {
image_chat_icon.setImageDrawable(getStatusDrawable(room.status))
} else {
image_chat_icon.setImageDrawable(getRoomDrawable(room.type, room.alert))
}
setOnClickListener {
listener(room.id)
}
}
}
private fun getRoomDrawable(type: RoomType, alert: Boolean): Drawable? {
return when(type) {
is RoomType.Channel -> if (alert) channelUnread else channel
is RoomType.PrivateGroup -> if (alert) groupUnread else group
else -> null
}
}
private fun getStatusDrawable(status: UserStatus): Drawable {
return when(status) {
is UserStatus.Online -> online
is UserStatus.Away -> away
is UserStatus.Busy -> busy
else -> offline
}
}
}
\ No newline at end of file
package chat.rocket.android.chatrooms.adapter
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R
import chat.rocket.android.util.extensions.inflate
class RoomsAdapter(private val listener: (String) -> Unit) : RecyclerView.Adapter<ViewHolder<*>>() {
init {
setHasStableIds(true)
}
var values: List<ItemHolder<*>> = ArrayList(0)
set(items) {
field = items
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder<*> {
if (viewType == 0) {
val view = parent.inflate(R.layout.item_chat)
return RoomViewHolder(view, listener)
} else if (viewType == 1) {
val view = parent.inflate(R.layout.item_chatroom_header)
return HeaderViewHolder(view)
}
throw IllegalStateException("View type must be either Room or Header")
}
override fun getItemCount() = values.size
override fun getItemId(position: Int): Long {
val item = values[position]
return when(item) {
is HeaderItemHolder -> item.data.hashCode().toLong()
is RoomItemHolder -> item.data.id.hashCode().toLong()
else -> throw IllegalStateException("View type must be either Room or Header")
}
}
override fun getItemViewType(position: Int): Int {
return when(values[position]) {
is RoomItemHolder -> 0
is HeaderItemHolder -> 1
else -> throw IllegalStateException("View type must be either Room or Header")
}
}
override fun onBindViewHolder(holder: ViewHolder<*>, position: Int) {
if (holder is RoomViewHolder) {
holder.bind(values[position] as RoomItemHolder)
} else if (holder is HeaderViewHolder) {
holder.bind(values[position] as HeaderItemHolder)
}
}
}
\ No newline at end of file
package chat.rocket.android.chatrooms.adapter
import android.view.View
import androidx.recyclerview.widget.RecyclerView
abstract class ViewHolder<T : ItemHolder<*>>(
itemView: View
) : RecyclerView.ViewHolder(itemView) {
var data: T? = null
fun bind(data: T) {
this.data = data
bindViews(data)
}
abstract fun bindViews(data: T)
}
\ No newline at end of file
package chat.rocket.android.chatrooms.adapter.model
import chat.rocket.common.model.RoomType
import chat.rocket.common.model.UserStatus
data class Room(
val id: String,
val type: RoomType,
val name: CharSequence,
val avatar: String,
val date: CharSequence?,
val unread: String?,
val alert: Boolean,
val lastMessage: CharSequence?,
val status: UserStatus?
)
\ No newline at end of file
package chat.rocket.android.chatrooms.di package chat.rocket.android.chatrooms.di
import android.arch.lifecycle.LifecycleOwner import android.app.Application
import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.chatrooms.adapter.RoomMapper
import chat.rocket.android.chatrooms.domain.FetchChatRoomsInteractor
import chat.rocket.android.chatrooms.presentation.ChatRoomsView import chat.rocket.android.chatrooms.presentation.ChatRoomsView
import chat.rocket.android.chatrooms.ui.ChatRoomsFragment import chat.rocket.android.chatrooms.ui.ChatRoomsFragment
import chat.rocket.android.dagger.scope.PerFragment import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.db.ChatRoomDao
import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.db.DatabaseManagerFactory
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.SettingsRepository
import chat.rocket.android.server.infraestructure.ConnectionManager
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.core.RocketChatClient
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import javax.inject.Named
@Module @Module
@PerFragment
class ChatRoomsFragmentModule { class ChatRoomsFragmentModule {
@Provides @Provides
@PerFragment
fun chatRoomsView(frag: ChatRoomsFragment): ChatRoomsView { fun chatRoomsView(frag: ChatRoomsFragment): ChatRoomsView {
return frag return frag
} }
@Provides @Provides
@PerFragment
fun provideLifecycleOwner(frag: ChatRoomsFragment): LifecycleOwner { fun provideLifecycleOwner(frag: ChatRoomsFragment): LifecycleOwner {
return frag return frag
} }
@Provides
@PerFragment
@Named("currentServer")
fun provideCurrentServer(currentServerInteractor: GetCurrentServerInteractor): String {
return currentServerInteractor.get()!!
}
@Provides
@PerFragment
fun provideRocketChatClient(factory: RocketChatClientFactory,
@Named("currentServer") currentServer: String): RocketChatClient {
return factory.create(currentServer)
}
@Provides
@PerFragment
fun provideDatabaseManager(factory: DatabaseManagerFactory,
@Named("currentServer") currentServer: String): DatabaseManager {
return factory.create(currentServer)
}
@Provides
@PerFragment
fun provideChatRoomDao(manager: DatabaseManager): ChatRoomDao = manager.chatRoomDao()
@Provides
@PerFragment
fun provideConnectionManager(factory: ConnectionManagerFactory,
@Named("currentServer") currentServer: String): ConnectionManager {
return factory.create(currentServer)
}
@Provides
@PerFragment
fun provideFetchChatRoomsInteractor(client: RocketChatClient, dbManager: DatabaseManager): FetchChatRoomsInteractor {
return FetchChatRoomsInteractor(client, dbManager)
}
@Provides
@PerFragment
fun providePublicSettings(repository: SettingsRepository,
@Named("currentServer") currentServer: String): PublicSettings {
return repository.get(currentServer)
}
@Provides
@PerFragment
fun provideRoomMapper(context: Application,
repository: SettingsRepository,
localRepository: LocalRepository,
@Named("currentServer") serverUrl: String): RoomMapper {
return RoomMapper(context, repository.get(serverUrl), localRepository, serverUrl)
}
} }
\ No newline at end of file
package chat.rocket.android.chatrooms.domain
import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.db.model.ChatRoomEntity
import chat.rocket.android.db.model.UserEntity
import chat.rocket.android.util.retryIO
import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.chatRooms
import chat.rocket.core.model.ChatRoom
import chat.rocket.core.model.userId
import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.launch
import timber.log.Timber
class FetchChatRoomsInteractor(
private val client: RocketChatClient,
private val dbManager: DatabaseManager
) {
suspend fun refreshChatRooms() {
launch(CommonPool) {
try {
val rooms = retryIO("fetch chatRooms", times = 10,
initialDelay = 200, maxDelay = 2000) {
client.chatRooms().update.map { room ->
mapChatRoom(room)
}
}
Timber.d("Refreshing rooms: $rooms")
dbManager.insert(rooms)
} catch (ex: Exception) {
Timber.d(ex, "Error getting chatrooms")
}
}
}
private suspend fun mapChatRoom(room: ChatRoom): ChatRoomEntity {
with(room) {
val userId = userId()
if (userId != null && dbManager.findUser(userId) == null) {
Timber.d("Missing user, inserting: $userId")
dbManager.insert(UserEntity(userId))
}
lastMessage?.sender?.let { user ->
user.id?.let { id ->
if (dbManager.findUser(id) == null) {
Timber.d("Missing last message user, inserting: $id")
dbManager.insert(UserEntity(id, user.username, user.name))
}
}
}
user?.id?.let { id ->
if (dbManager.findUser(id) == null) {
Timber.d("Missing owner user, inserting: $id")
dbManager.insert(UserEntity(id, user?.username, user?.name))
}
}
return ChatRoomEntity(
id = id,
subscriptionId = subscriptionId,
type = type.toString(),
name = name,
fullname = fullName,
userId = userId,
ownerId = user?.id,
readonly = readonly,
isDefault = default,
favorite = favorite,
open = open,
alert = alert,
unread = unread,
userMentions = userMentions,
groupMentions = groupMentions,
updatedAt = updatedAt,
timestamp = timestamp,
lastSeen = lastSeen,
lastMessageText = lastMessage?.message,
lastMessageUserId = lastMessage?.sender?.id,
lastMessageTimestamp = lastMessage?.timestamp
)
}
}
}
\ No newline at end of file
package chat.rocket.android.chatrooms.infrastructure
import androidx.lifecycle.LiveData
import chat.rocket.android.db.ChatRoomDao
import chat.rocket.android.db.model.ChatRoom
import javax.inject.Inject
class ChatRoomsRepository @Inject constructor(val dao: ChatRoomDao){
fun getChatRooms(order: Order): LiveData<List<ChatRoom>> {
return when(order) {
Order.ACTIVITY -> dao.getAll()
Order.GROUPED_ACTIVITY -> dao.getAllGrouped()
Order.NAME -> dao.getAllAlphabetically()
Order.GROUPED_NAME -> dao.getAllAlphabeticallyGrouped()
}
}
enum class Order {
ACTIVITY,
GROUPED_ACTIVITY,
NAME,
GROUPED_NAME,
}
}
fun ChatRoomsRepository.Order.isGrouped(): Boolean = this == ChatRoomsRepository.Order.GROUPED_ACTIVITY
|| this == ChatRoomsRepository.Order.GROUPED_NAME
\ No newline at end of file
...@@ -4,8 +4,8 @@ import DateTimeHelper ...@@ -4,8 +4,8 @@ import DateTimeHelper
import DrawableHelper import DrawableHelper
import android.content.Context import android.content.Context
import android.graphics.Color import android.graphics.Color
import android.support.v4.content.ContextCompat import androidx.core.content.ContextCompat
import android.support.v7.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
import android.text.style.ForegroundColorSpan import android.text.style.ForegroundColorSpan
import android.view.View import android.view.View
......
package chat.rocket.android.chatrooms.ui package chat.rocket.android.chatrooms.ui
import android.content.Context import android.content.Context
import android.support.v7.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import android.util.SparseArray import android.util.SparseArray
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
......
package chat.rocket.android.chatrooms.viewmodel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations
import androidx.lifecycle.ViewModel
import chat.rocket.android.chatrooms.adapter.ItemHolder
import chat.rocket.android.chatrooms.adapter.RoomMapper
import chat.rocket.android.chatrooms.domain.FetchChatRoomsInteractor
import chat.rocket.android.chatrooms.infrastructure.ChatRoomsRepository
import chat.rocket.android.chatrooms.infrastructure.isGrouped
import chat.rocket.android.server.infraestructure.ConnectionManager
import chat.rocket.android.util.livedata.TransformedLiveData
import chat.rocket.android.util.livedata.transform
import chat.rocket.core.internal.realtime.socket.model.State
import kotlinx.coroutines.experimental.launch
import kotlinx.coroutines.experimental.newSingleThreadContext
import me.henrytao.livedataktx.distinct
import me.henrytao.livedataktx.map
import me.henrytao.livedataktx.nonNull
import timber.log.Timber
class ChatRoomsViewModel(
private val connectionManager: ConnectionManager,
private val interactor: FetchChatRoomsInteractor,
private val repository: ChatRoomsRepository,
private val mapper: RoomMapper
) : ViewModel() {
private val ordering: MutableLiveData<ChatRoomsRepository.Order> = MutableLiveData()
private val runContext = newSingleThreadContext("chat-rooms-view-model")
init {
ordering.value = ChatRoomsRepository.Order.ACTIVITY
}
fun getChatRooms(): LiveData<List<ItemHolder<*>>> {
return Transformations.switchMap(ordering) { order ->
Timber.d("Querying rooms for order: $order")
repository.getChatRooms(order)
.nonNull()
.distinct()
.transform(runContext) { rooms ->
rooms?.let {
mapper.map(rooms, order.isGrouped())
}
}
}
}
fun getStatus(): MutableLiveData<State> {
return connectionManager.statusLiveData.nonNull().distinct().map { state ->
if (state is State.Connected) {
// TODO - add a loading status...
fetchRooms()
}
state
}
}
private fun fetchRooms() {
launch {
interactor.refreshChatRooms()
}
}
fun setOrdering(order: ChatRoomsRepository.Order) {
ordering.value = order
}
}
package chat.rocket.android.chatrooms.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import chat.rocket.android.chatrooms.adapter.RoomMapper
import chat.rocket.android.chatrooms.domain.FetchChatRoomsInteractor
import chat.rocket.android.chatrooms.infrastructure.ChatRoomsRepository
import chat.rocket.android.server.infraestructure.ConnectionManager
import javax.inject.Inject
class ChatRoomsViewModelFactory @Inject constructor(
private val connectionManager: ConnectionManager,
private val interactor: FetchChatRoomsInteractor,
private val repository: ChatRoomsRepository,
private val mapper: RoomMapper
) : ViewModelProvider.NewInstanceFactory() {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>) =
ChatRoomsViewModel(connectionManager, interactor, repository, mapper) as T
}
\ No newline at end of file
package chat.rocket.android.core.behaviours package chat.rocket.android.core.behaviours
import android.support.annotation.StringRes import androidx.annotation.StringRes
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
interface MessageView { interface MessageView {
......
package chat.rocket.android.core.lifecycle package chat.rocket.android.core.lifecycle
import android.arch.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import android.arch.lifecycle.LifecycleObserver import androidx.lifecycle.LifecycleObserver
import android.arch.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import android.arch.lifecycle.OnLifecycleEvent import androidx.lifecycle.OnLifecycleEvent
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.experimental.Job
import javax.inject.Inject import javax.inject.Inject
......
package chat.rocket.android.createchannel.di package chat.rocket.android.createchannel.di
import android.arch.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.createchannel.presentation.CreateChannelView import chat.rocket.android.createchannel.presentation.CreateChannelView
import chat.rocket.android.createchannel.ui.CreateChannelFragment import chat.rocket.android.createchannel.ui.CreateChannelFragment
import chat.rocket.android.dagger.scope.PerFragment import chat.rocket.android.dagger.scope.PerFragment
...@@ -8,15 +8,16 @@ import dagger.Module ...@@ -8,15 +8,16 @@ import dagger.Module
import dagger.Provides import dagger.Provides
@Module @Module
@PerFragment
class CreateChannelModule { class CreateChannelModule {
@Provides @Provides
@PerFragment
fun createChannelView(fragment: CreateChannelFragment): CreateChannelView { fun createChannelView(fragment: CreateChannelFragment): CreateChannelView {
return fragment return fragment
} }
@Provides @Provides
@PerFragment
fun provideLifecycleOwner(fragment: CreateChannelFragment): LifecycleOwner { fun provideLifecycleOwner(fragment: CreateChannelFragment): LifecycleOwner {
return fragment return fragment
} }
......
package chat.rocket.android.createchannel.di package chat.rocket.android.createchannel.di
import chat.rocket.android.createchannel.ui.CreateChannelFragment import chat.rocket.android.createchannel.ui.CreateChannelFragment
import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
...@@ -8,5 +9,6 @@ import dagger.android.ContributesAndroidInjector ...@@ -8,5 +9,6 @@ import dagger.android.ContributesAndroidInjector
abstract class CreateChannelProvider { abstract class CreateChannelProvider {
@ContributesAndroidInjector(modules = [CreateChannelModule::class]) @ContributesAndroidInjector(modules = [CreateChannelModule::class])
@PerFragment
abstract fun provideCreateChannelFragment(): CreateChannelFragment abstract fun provideCreateChannelFragment(): CreateChannelFragment
} }
\ No newline at end of file
package chat.rocket.android.createchannel.ui package chat.rocket.android.createchannel.ui
import android.os.Bundle import android.os.Bundle
import android.support.design.chip.Chip
import android.support.v4.app.Fragment
import android.support.v7.app.AppCompatActivity
import android.support.v7.view.ActionMode
import android.support.v7.widget.LinearLayoutManager
import android.view.* import android.view.*
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.postDelayed import androidx.core.view.postDelayed
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.createchannel.presentation.CreateChannelPresenter import chat.rocket.android.createchannel.presentation.CreateChannelPresenter
import chat.rocket.android.createchannel.presentation.CreateChannelView import chat.rocket.android.createchannel.presentation.CreateChannelView
...@@ -21,6 +20,8 @@ import chat.rocket.android.util.extensions.showToast ...@@ -21,6 +20,8 @@ import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.ui import chat.rocket.android.util.extensions.ui
import chat.rocket.android.widget.DividerItemDecoration import chat.rocket.android.widget.DividerItemDecoration
import chat.rocket.common.model.RoomType import chat.rocket.common.model.RoomType
import chat.rocket.common.model.roomTypeOf
import com.google.android.material.chip.Chip
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
...@@ -38,7 +39,7 @@ class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback ...@@ -38,7 +39,7 @@ class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback
} }
} }
private val compositeDisposable = CompositeDisposable() private val compositeDisposable = CompositeDisposable()
private var channelType: RoomType = RoomType.CHANNEL private var channelType: String = RoomType.CHANNEL
private var isChannelReadOnly: Boolean = false private var isChannelReadOnly: Boolean = false
private var memberList = arrayListOf<String>() private var memberList = arrayListOf<String>()
...@@ -145,7 +146,7 @@ class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback ...@@ -145,7 +146,7 @@ class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback
return when (menuItem.itemId) { return when (menuItem.itemId) {
R.id.action_create_channel -> { R.id.action_create_channel -> {
createChannelPresenter.createChannel( createChannelPresenter.createChannel(
channelType, roomTypeOf(channelType),
text_channel_name.text.toString(), text_channel_name.text.toString(),
memberList, memberList,
isChannelReadOnly isChannelReadOnly
...@@ -234,7 +235,7 @@ class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback ...@@ -234,7 +235,7 @@ class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback
private fun startActionMode() { private fun startActionMode() {
if (actionMode == null) { if (actionMode == null) {
actionMode = (activity as MainActivity).startSupportActionMode(this) actionMode = (activity as AppCompatActivity).startSupportActionMode(this)
} }
} }
......
...@@ -4,13 +4,11 @@ import android.app.Application ...@@ -4,13 +4,11 @@ import android.app.Application
import android.app.NotificationManager import android.app.NotificationManager
import android.app.job.JobInfo import android.app.job.JobInfo
import android.app.job.JobScheduler import android.app.job.JobScheduler
import android.arch.persistence.room.Room
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import chat.rocket.android.BuildConfig import chat.rocket.android.BuildConfig
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.app.RocketChatDatabase
import chat.rocket.android.authentication.infraestructure.SharedPreferencesMultiServerTokenRepository import chat.rocket.android.authentication.infraestructure.SharedPreferencesMultiServerTokenRepository
import chat.rocket.android.authentication.infraestructure.SharedPreferencesTokenRepository import chat.rocket.android.authentication.infraestructure.SharedPreferencesTokenRepository
import chat.rocket.android.chatroom.service.MessageService import chat.rocket.android.chatroom.service.MessageService
...@@ -21,8 +19,32 @@ import chat.rocket.android.infrastructure.LocalRepository ...@@ -21,8 +19,32 @@ import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.infrastructure.SharedPreferencesLocalRepository import chat.rocket.android.infrastructure.SharedPreferencesLocalRepository
import chat.rocket.android.push.GroupedPush import chat.rocket.android.push.GroupedPush
import chat.rocket.android.push.PushManager import chat.rocket.android.push.PushManager
import chat.rocket.android.server.domain.* import chat.rocket.android.server.domain.AccountsRepository
import chat.rocket.android.server.infraestructure.* import chat.rocket.android.server.domain.ActiveUsersRepository
import chat.rocket.android.server.domain.ChatRoomsRepository
import chat.rocket.android.server.domain.CurrentServerRepository
import chat.rocket.android.server.domain.GetAccountInteractor
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.JobSchedulerInteractor
import chat.rocket.android.server.domain.MessagesRepository
import chat.rocket.android.server.domain.MultiServerTokenRepository
import chat.rocket.android.server.domain.PermissionsRepository
import chat.rocket.android.server.domain.RoomRepository
import chat.rocket.android.server.domain.SettingsRepository
import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.server.domain.UsersRepository
import chat.rocket.android.server.infraestructure.JobSchedulerInteractorImpl
import chat.rocket.android.server.infraestructure.MemoryActiveUsersRepository
import chat.rocket.android.server.infraestructure.MemoryChatRoomsRepository
import chat.rocket.android.server.infraestructure.MemoryRoomRepository
import chat.rocket.android.server.infraestructure.MemoryUsersRepository
import chat.rocket.android.server.infraestructure.SharedPreferencesAccountsRepository
import chat.rocket.android.server.infraestructure.SharedPreferencesMessagesRepository
import chat.rocket.android.server.infraestructure.SharedPreferencesPermissionsRepository
import chat.rocket.android.server.infraestructure.SharedPreferencesSettingsRepository
import chat.rocket.android.server.infraestructure.SharedPrefsConnectingServerRepository
import chat.rocket.android.server.infraestructure.SharedPrefsCurrentServerRepository
import chat.rocket.android.util.AppJsonAdapterFactory import chat.rocket.android.util.AppJsonAdapterFactory
import chat.rocket.android.util.HttpLoggingInterceptor import chat.rocket.android.util.HttpLoggingInterceptor
import chat.rocket.android.util.TimberLogger import chat.rocket.android.util.TimberLogger
...@@ -32,7 +54,6 @@ import chat.rocket.common.model.TimestampAdapter ...@@ -32,7 +54,6 @@ import chat.rocket.common.model.TimestampAdapter
import chat.rocket.common.util.CalendarISO8601Converter import chat.rocket.common.util.CalendarISO8601Converter
import chat.rocket.common.util.Logger import chat.rocket.common.util.Logger
import chat.rocket.common.util.PlatformLogger import chat.rocket.common.util.PlatformLogger
import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.AttachmentAdapterFactory import chat.rocket.core.internal.AttachmentAdapterFactory
import chat.rocket.core.internal.ReactionsAdapter import chat.rocket.core.internal.ReactionsAdapter
import com.facebook.drawee.backends.pipeline.DraweeConfig import com.facebook.drawee.backends.pipeline.DraweeConfig
...@@ -42,7 +63,6 @@ import com.facebook.imagepipeline.listener.RequestLoggingListener ...@@ -42,7 +63,6 @@ import com.facebook.imagepipeline.listener.RequestLoggingListener
import com.squareup.moshi.Moshi import com.squareup.moshi.Moshi
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import kotlinx.coroutines.experimental.Job
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import ru.noties.markwon.SpannableConfiguration import ru.noties.markwon.SpannableConfiguration
import ru.noties.markwon.spans.SpannableTheme import ru.noties.markwon.spans.SpannableTheme
...@@ -53,45 +73,12 @@ import javax.inject.Singleton ...@@ -53,45 +73,12 @@ import javax.inject.Singleton
@Module @Module
class AppModule { class AppModule {
@Provides
@Singleton
fun provideRocketChatClient(okHttpClient: OkHttpClient,
repository: TokenRepository,
logger: PlatformLogger): RocketChatClient {
return RocketChatClient.create {
httpClient = okHttpClient
tokenRepository = repository
platformLogger = logger
// TODO remove
restUrl = "https://open.rocket.chat"
}
}
@Provides
@Singleton
fun provideRocketChatDatabase(context: Application): RocketChatDatabase {
return Room.databaseBuilder(context.applicationContext, RocketChatDatabase::class.java,
"rocketchat-db").build()
}
@Provides
fun provideJob(): Job {
return Job()
}
@Provides @Provides
@Singleton @Singleton
fun provideContext(application: Application): Context { fun provideContext(application: Application): Context {
return application return application
} }
@Provides
@Singleton
fun provideServerDao(database: RocketChatDatabase): ServerDao {
return database.serverDao()
}
@Provides @Provides
@Singleton @Singleton
fun provideHttpLoggingInterceptor(): HttpLoggingInterceptor { fun provideHttpLoggingInterceptor(): HttpLoggingInterceptor {
...@@ -129,7 +116,6 @@ class AppModule { ...@@ -129,7 +116,6 @@ class AppModule {
return OkHttpImagePipelineConfigFactory.newBuilder(context, okHttpClient) return OkHttpImagePipelineConfigFactory.newBuilder(context, okHttpClient)
.setRequestListeners(listeners) .setRequestListeners(listeners)
.setDownsampleEnabled(true) .setDownsampleEnabled(true)
//.experiment().setBitmapPrepareToDraw(true).experiment()
.experiment().setPartialImageCachingEnabled(true).build() .experiment().setPartialImageCachingEnabled(true).build()
} }
......
package chat.rocket.android.db
import androidx.room.Insert
import androidx.room.OnConflictStrategy
interface BaseDao<T> {
@Insert
fun insert(vararg obj: T)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(list: List<T>)
}
\ No newline at end of file
package chat.rocket.android.db
import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Update
import chat.rocket.android.db.model.ChatRoom
import chat.rocket.android.db.model.ChatRoomEntity
import chat.rocket.common.model.RoomType
@Dao
abstract class ChatRoomDao : BaseDao<ChatRoomEntity> {
@Transaction
@Query("""
$BASE_QUERY
WHERE chatrooms.id = :id
""")
abstract fun get(id: String): ChatRoom?
@Transaction
@Query("""
$BASE_QUERY
ORDER BY
CASE
WHEN lastMessageTimeStamp IS NOT NULL THEN lastMessageTimeStamp
ELSE updatedAt
END DESC
""")
abstract fun getAll(): LiveData<List<ChatRoom>>
@Transaction
@Query("""
$BASE_QUERY
ORDER BY
$TYPE_ORDER,
CASE
WHEN lastMessageTimeStamp IS NOT NULL THEN lastMessageTimeStamp
ELSE updatedAt
END DESC
""")
abstract fun getAllGrouped(): LiveData<List<ChatRoom>>
@Transaction
@Query("""
$BASE_QUERY
ORDER BY name
""")
abstract fun getAllAlphabetically(): LiveData<List<ChatRoom>>
@Transaction
@Query("""
$BASE_QUERY
ORDER BY
$TYPE_ORDER,
name
""")
abstract fun getAllAlphabeticallyGrouped(): LiveData<List<ChatRoom>>
@Query("DELETE FROM chatrooms WHERE ID = :id")
abstract fun delete(id: String)
@Insert(onConflict = OnConflictStrategy.REPLACE)
abstract fun insertOrReplace(chatRooms: List<ChatRoomEntity>)
@Insert(onConflict = OnConflictStrategy.REPLACE)
abstract fun insertOrReplace(chatRoom: ChatRoomEntity)
@Update
abstract fun update(list: List<ChatRoomEntity>)
@Transaction
open fun update(toRemove: List<String>, toInsert: List<ChatRoomEntity>, toUpdate: List<ChatRoomEntity>) {
insertOrReplace(toInsert)
update(toUpdate)
toRemove.forEach { id ->
delete(id)
}
}
companion object {
const val BASE_QUERY = """
SELECT chatrooms.*,
users.username as username,
users.name as userFullname,
users.status,
lmUsers.username as lastMessageUserName,
lmUsers.name as lastMessageUserFullName
FROM chatrooms
LEFT JOIN users ON chatrooms.userId = users.id
LEFT JOIN users AS lmUsers ON chatrooms.lastMessageUserId = lmUsers.id
"""
const val TYPE_ORDER = """
CASE
WHEN type = '${RoomType.CHANNEL}' THEN 1
WHEN type = '${RoomType.PRIVATE_GROUP}' THEN 2
WHEN type = '${RoomType.DIRECT_MESSAGE}' THEN 3
WHEN type = '${RoomType.LIVECHAT}' THEN 4
ELSE 5
END
"""
}
}
\ No newline at end of file
This diff is collapsed.
package chat.rocket.android.db
import android.app.Application
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class DatabaseManagerFactory @Inject constructor(private val context: Application) {
private val cache = HashMap<String, DatabaseManager>()
fun create(serverUrl: String): DatabaseManager {
cache[serverUrl]?.let {
Timber.d("Returning cached database for $serverUrl")
return it
}
Timber.d("Returning FRESH database for $serverUrl")
val db = DatabaseManager(context, serverUrl)
cache[serverUrl] = db
return db
}
}
\ No newline at end of file
package chat.rocket.android.db
import androidx.room.Database
import androidx.room.RoomDatabase
import chat.rocket.android.db.model.ChatRoomEntity
import chat.rocket.android.db.model.UserEntity
@Database(
entities = [UserEntity::class, ChatRoomEntity::class],
version = 3,
exportSchema = true
)
abstract class RCDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
abstract fun chatRoomDao(): ChatRoomDao
}
\ No newline at end of file
package chat.rocket.android.db
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Update
import chat.rocket.android.db.model.BaseUserEntity
import chat.rocket.android.db.model.UserEntity
import chat.rocket.android.db.model.UserStatus
import timber.log.Timber
@Dao
abstract class UserDao : BaseDao<UserEntity> {
@Update(onConflict = OnConflictStrategy.IGNORE)
abstract fun update(user: UserEntity): Int
@Query("UPDATE OR IGNORE users set STATUS = :status where ID = :id")
abstract fun update(id: String, status: String): Int
@Query("SELECT id FROM users WHERE ID = :id")
abstract fun findUser(id: String): String?
@Transaction
open fun upsert(user: BaseUserEntity) {
internalUpsert(user)
}
@Transaction
open fun upsert(users: List<BaseUserEntity>) {
users.forEach { internalUpsert(it) }
}
private inline fun internalUpsert(user: BaseUserEntity) {
val count = if (user is UserStatus) {
update(user.id, user.status)
} else {
update(user as UserEntity)
}
if (count == 0 && user is UserEntity) {
Timber.d("missing user, inserting: ${user.id}")
insert(user)
}
}
}
\ No newline at end of file
package chat.rocket.android.db.model
import androidx.room.Embedded
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Index
import androidx.room.PrimaryKey
@Entity(tableName = "chatrooms",
indices = [
Index(value = ["userId"]),
Index(value = ["ownerId"]),
Index(value = ["subscriptionId"], unique = true),
Index(value = ["updatedAt"])
],
foreignKeys = [
ForeignKey(entity = UserEntity::class, parentColumns = ["id"], childColumns = ["ownerId"]),
ForeignKey(entity = UserEntity::class, parentColumns = ["id"], childColumns = ["userId"]),
ForeignKey(entity = UserEntity::class, parentColumns = ["id"], childColumns = ["lastMessageUserId"])
]
)
data class ChatRoomEntity(
@PrimaryKey var id: String,
var subscriptionId: String,
var type: String,
var name: String,
var fullname: String?,
var userId: String?,
var ownerId: String?,
var readonly: Boolean? = false,
var isDefault: Boolean? = false,
var favorite: Boolean? = false,
var open: Boolean = true,
var alert: Boolean = false,
var unread: Long = 0,
var userMentions: Long? = 0,
var groupMentions: Long? = 0,
var updatedAt: Long? = -1,
var timestamp: Long? = -1,
var lastSeen: Long? = -1,
var lastMessageText: String?,
var lastMessageUserId: String?,
var lastMessageTimestamp: Long?
)
data class ChatRoom(
@Embedded var chatRoom: ChatRoomEntity,
var username: String?,
var userFullname: String?,
var status: String?,
var lastMessageUserName: String?,
var lastMessageUserFullName: String?
)
\ No newline at end of file
package chat.rocket.android.db.model
import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
@Entity(tableName = "users",
indices = [Index(value = ["username"])])
data class UserEntity(
@PrimaryKey override val id: String,
var username: String? = null,
var name: String? = null,
override var status: String = "offline",
var utcOffset: Float? = null
) : BaseUserEntity
data class UserStatus(
override val id: String,
override val status: String
) : BaseUserEntity
interface BaseUserEntity {
val id: String
val status: String
}
\ No newline at end of file
package chat.rocket.android.chatroom.di package chat.rocket.android.chatroom.di
import android.arch.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.favoritemessages.presentation.FavoriteMessagesView import chat.rocket.android.favoritemessages.presentation.FavoriteMessagesView
...@@ -10,20 +10,26 @@ import dagger.Provides ...@@ -10,20 +10,26 @@ import dagger.Provides
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.experimental.Job
@Module @Module
@PerFragment
class FavoriteMessagesFragmentModule { class FavoriteMessagesFragmentModule {
@Provides @Provides
@PerFragment
fun provideJob() = Job()
@Provides
@PerFragment
fun provideLifecycleOwner(frag: FavoriteMessagesFragment): LifecycleOwner { fun provideLifecycleOwner(frag: FavoriteMessagesFragment): LifecycleOwner {
return frag return frag
} }
@Provides @Provides
@PerFragment
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy { fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs) return CancelStrategy(owner, jobs)
} }
@Provides @Provides
@PerFragment
fun provideFavoriteMessagesView(frag: FavoriteMessagesFragment): FavoriteMessagesView { fun provideFavoriteMessagesView(frag: FavoriteMessagesFragment): FavoriteMessagesView {
return frag return frag
} }
......
package chat.rocket.android.favoritemessages.ui package chat.rocket.android.favoritemessages.ui
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.widget.DefaultItemAnimator
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.chatroom.adapter.ChatRoomAdapter import chat.rocket.android.chatroom.adapter.ChatRoomAdapter
import chat.rocket.android.chatroom.ui.ChatRoomActivity import chat.rocket.android.chatroom.ui.ChatRoomActivity
...@@ -68,8 +68,7 @@ class FavoriteMessagesFragment : Fragment(), FavoriteMessagesView { ...@@ -68,8 +68,7 @@ class FavoriteMessagesFragment : Fragment(), FavoriteMessagesView {
if (recycler_view.adapter == null) { if (recycler_view.adapter == null) {
adapter = ChatRoomAdapter(enableActions = false) adapter = ChatRoomAdapter(enableActions = false)
recycler_view.adapter = adapter recycler_view.adapter = adapter
val linearLayoutManager = val linearLayoutManager = LinearLayoutManager(context)
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
recycler_view.layoutManager = linearLayoutManager recycler_view.layoutManager = linearLayoutManager
recycler_view.itemAnimator = DefaultItemAnimator() recycler_view.itemAnimator = DefaultItemAnimator()
if (favoriteMessages.size >= 30) { if (favoriteMessages.size >= 30) {
...@@ -78,7 +77,7 @@ class FavoriteMessagesFragment : Fragment(), FavoriteMessagesView { ...@@ -78,7 +77,7 @@ class FavoriteMessagesFragment : Fragment(), FavoriteMessagesView {
override fun onLoadMore( override fun onLoadMore(
page: Int, page: Int,
totalItemsCount: Int, totalItemsCount: Int,
recyclerView: RecyclerView? recyclerView: RecyclerView
) { ) {
presenter.loadFavoriteMessages(chatRoomId) presenter.loadFavoriteMessages(chatRoomId)
} }
......
package chat.rocket.android.files.adapter package chat.rocket.android.files.adapter
import android.support.v7.widget.RecyclerView
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.files.viewmodel.FileViewModel import chat.rocket.android.files.viewmodel.FileViewModel
import chat.rocket.android.util.extensions.inflate import chat.rocket.android.util.extensions.inflate
......
package chat.rocket.android.files.di package chat.rocket.android.files.di
import android.arch.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.files.presentation.FilesView import chat.rocket.android.files.presentation.FilesView
...@@ -10,20 +10,26 @@ import dagger.Provides ...@@ -10,20 +10,26 @@ import dagger.Provides
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.experimental.Job
@Module @Module
@PerFragment
class FilesFragmentModule { class FilesFragmentModule {
@Provides @Provides
@PerFragment
fun provideJob() = Job()
@Provides
@PerFragment
fun provideLifecycleOwner(frag: FilesFragment): LifecycleOwner { fun provideLifecycleOwner(frag: FilesFragment): LifecycleOwner {
return frag return frag
} }
@Provides @Provides
@PerFragment
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy { fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs) return CancelStrategy(owner, jobs)
} }
@Provides @Provides
@PerFragment
fun provideFilesView(frag: FilesFragment): FilesView { fun provideFilesView(frag: FilesFragment): FilesView {
return frag return frag
} }
......
package chat.rocket.android.files.di package chat.rocket.android.files.di
import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.files.ui.FilesFragment import chat.rocket.android.files.ui.FilesFragment
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
...@@ -8,5 +9,6 @@ import dagger.android.ContributesAndroidInjector ...@@ -8,5 +9,6 @@ import dagger.android.ContributesAndroidInjector
abstract class FilesFragmentProvider { abstract class FilesFragmentProvider {
@ContributesAndroidInjector(modules = [FilesFragmentModule::class]) @ContributesAndroidInjector(modules = [FilesFragmentModule::class])
@PerFragment
abstract fun provideFilesFragment(): FilesFragment abstract fun provideFilesFragment(): FilesFragment
} }
\ No newline at end of file
...@@ -3,13 +3,13 @@ package chat.rocket.android.files.ui ...@@ -3,13 +3,13 @@ package chat.rocket.android.files.ui
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.chatroom.ui.ChatRoomActivity import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.files.adapter.FilesAdapter import chat.rocket.android.files.adapter.FilesAdapter
...@@ -42,8 +42,7 @@ class FilesFragment : Fragment(), FilesView { ...@@ -42,8 +42,7 @@ class FilesFragment : Fragment(), FilesView {
lateinit var presenter: FilesPresenter lateinit var presenter: FilesPresenter
private val adapter: FilesAdapter = private val adapter: FilesAdapter =
FilesAdapter { fileViewModel -> presenter.openFile(fileViewModel) } FilesAdapter { fileViewModel -> presenter.openFile(fileViewModel) }
private val linearLayoutManager = private val linearLayoutManager = LinearLayoutManager(context)
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
private lateinit var chatRoomId: String private lateinit var chatRoomId: String
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
...@@ -80,7 +79,7 @@ class FilesFragment : Fragment(), FilesView { ...@@ -80,7 +79,7 @@ class FilesFragment : Fragment(), FilesView {
override fun onLoadMore( override fun onLoadMore(
page: Int, page: Int,
totalItemsCount: Int, totalItemsCount: Int,
recyclerView: RecyclerView? recyclerView: RecyclerView
) { ) {
presenter.loadFiles(chatRoomId) presenter.loadFiles(chatRoomId)
} }
......
...@@ -3,8 +3,8 @@ package chat.rocket.android.helper ...@@ -3,8 +3,8 @@ package chat.rocket.android.helper
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.support.v4.app.ActivityCompat import androidx.core.app.ActivityCompat
import android.support.v4.content.ContextCompat import androidx.core.content.ContextCompat
object AndroidPermissionsHelper { object AndroidPermissionsHelper {
......
package chat.rocket.android.helper package chat.rocket.android.helper
import android.support.v7.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import android.support.v7.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import android.support.v7.widget.StaggeredGridLayoutManager import androidx.recyclerview.widget.StaggeredGridLayoutManager
/** /**
* Info: https://github.com/codepath/android_guides/wiki/Endless-Scrolling-with-AdapterViews-and-RecyclerView * Info: https://github.com/codepath/android_guides/wiki/Endless-Scrolling-with-AdapterViews-and-RecyclerView
...@@ -106,5 +106,5 @@ abstract class EndlessRecyclerViewScrollListener : RecyclerView.OnScrollListener ...@@ -106,5 +106,5 @@ abstract class EndlessRecyclerViewScrollListener : RecyclerView.OnScrollListener
} }
// Defines the process for actually loading more data based on page // Defines the process for actually loading more data based on page
abstract fun onLoadMore(page: Int, totalItemsCount: Int, recyclerView: RecyclerView?) abstract fun onLoadMore(page: Int, totalItemsCount: Int, recyclerView: RecyclerView)
} }
\ No newline at end of file
...@@ -7,8 +7,6 @@ import android.graphics.Color ...@@ -7,8 +7,6 @@ import android.graphics.Color
import android.graphics.Typeface import android.graphics.Typeface
import android.media.MediaScannerConnection import android.media.MediaScannerConnection
import android.os.Environment import android.os.Environment
import android.support.design.widget.AppBarLayout
import android.support.v7.widget.Toolbar
import android.text.TextUtils import android.text.TextUtils
import android.util.TypedValue import android.util.TypedValue
import android.view.ContextThemeWrapper import android.view.ContextThemeWrapper
...@@ -16,6 +14,7 @@ import android.view.ViewGroup ...@@ -16,6 +14,7 @@ import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.widget.Toolbar
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.core.view.setPadding import androidx.core.view.setPadding
import chat.rocket.android.R import chat.rocket.android.R
...@@ -26,6 +25,7 @@ import com.facebook.imagepipeline.cache.DefaultCacheKeyFactory ...@@ -26,6 +25,7 @@ import com.facebook.imagepipeline.cache.DefaultCacheKeyFactory
import com.facebook.imagepipeline.core.ImagePipelineFactory import com.facebook.imagepipeline.core.ImagePipelineFactory
import com.facebook.imagepipeline.request.ImageRequest import com.facebook.imagepipeline.request.ImageRequest
import com.facebook.imagepipeline.request.ImageRequestBuilder import com.facebook.imagepipeline.request.ImageRequestBuilder
import com.google.android.material.appbar.AppBarLayout
import com.stfalcon.frescoimageviewer.ImageViewer import com.stfalcon.frescoimageviewer.ImageViewer
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
......
...@@ -21,7 +21,7 @@ class MessageHelper @Inject constructor( ...@@ -21,7 +21,7 @@ class MessageHelper @Inject constructor(
is RoomType.PrivateGroup -> "group" is RoomType.PrivateGroup -> "group"
is RoomType.Channel -> "channel" is RoomType.Channel -> "channel"
is RoomType.DirectMessage -> "direct" is RoomType.DirectMessage -> "direct"
is RoomType.Livechat -> "livechat" is RoomType.LiveChat -> "livechat"
else -> "custom" else -> "custom"
} }
val name = if (settings.useRealName()) chatRoom.fullName ?: chatRoom.name else chatRoom.name val name = if (settings.useRealName()) chatRoom.fullName ?: chatRoom.name else chatRoom.name
......
...@@ -6,7 +6,7 @@ import android.graphics.Canvas ...@@ -6,7 +6,7 @@ import android.graphics.Canvas
import android.graphics.Paint import android.graphics.Paint
import android.graphics.RectF import android.graphics.RectF
import android.net.Uri import android.net.Uri
import android.support.v4.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import android.text.Spanned import android.text.Spanned
import android.text.style.ClickableSpan import android.text.style.ClickableSpan
import android.text.style.ReplacementSpan import android.text.style.ReplacementSpan
......
...@@ -2,7 +2,7 @@ package chat.rocket.android.helper ...@@ -2,7 +2,7 @@ package chat.rocket.android.helper
import android.app.Activity import android.app.Activity
import android.content.IntentSender import android.content.IntentSender
import android.support.v4.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import com.google.android.gms.auth.api.credentials.* import com.google.android.gms.auth.api.credentials.*
import com.google.android.gms.common.api.CommonStatusCodes import com.google.android.gms.common.api.CommonStatusCodes
import com.google.android.gms.common.api.ResolvableApiException import com.google.android.gms.common.api.ResolvableApiException
......
...@@ -21,7 +21,6 @@ interface LocalRepository { ...@@ -21,7 +21,6 @@ interface LocalRepository {
companion object { companion object {
const val KEY_PUSH_TOKEN = "KEY_PUSH_TOKEN" const val KEY_PUSH_TOKEN = "KEY_PUSH_TOKEN"
const val MIGRATION_FINISHED_KEY = "MIGRATION_FINISHED_KEY"
const val TOKEN_KEY = "token_" const val TOKEN_KEY = "token_"
const val SETTINGS_KEY = "settings_" const val SETTINGS_KEY = "settings_"
const val PERMISSIONS_KEY = "permissions_" const val PERMISSIONS_KEY = "permissions_"
......
package chat.rocket.android.main.adapter package chat.rocket.android.main.adapter
import android.support.v7.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import android.view.View import android.view.View
import chat.rocket.android.server.domain.model.Account import chat.rocket.android.server.domain.model.Account
import kotlinx.android.synthetic.main.item_account.view.* import kotlinx.android.synthetic.main.item_account.view.*
......
package chat.rocket.android.main.adapter package chat.rocket.android.main.adapter
import android.support.v7.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import android.view.ViewGroup import android.view.ViewGroup
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.server.domain.model.Account import chat.rocket.android.server.domain.model.Account
......
package chat.rocket.android.main.adapter package chat.rocket.android.main.adapter
import android.support.v7.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import android.view.View import android.view.View
class AddAccountViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) class AddAccountViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
\ No newline at end of file
package chat.rocket.android.main.adapter package chat.rocket.android.main.adapter
import android.support.v7.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import android.view.View import android.view.View
import chat.rocket.common.model.UserStatus import chat.rocket.common.model.UserStatus
import kotlinx.android.synthetic.main.item_change_status.view.* import kotlinx.android.synthetic.main.item_change_status.view.*
......
package chat.rocket.android.main.di package chat.rocket.android.main.di
import android.arch.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import android.content.Context import android.content.Context
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerActivity import chat.rocket.android.dagger.scope.PerActivity
import chat.rocket.android.dagger.scope.PerFragment
import chat.rocket.android.main.presentation.MainNavigator import chat.rocket.android.main.presentation.MainNavigator
import chat.rocket.android.main.presentation.MainView import chat.rocket.android.main.presentation.MainView
import chat.rocket.android.main.ui.MainActivity import chat.rocket.android.main.ui.MainActivity
...@@ -14,6 +15,10 @@ import kotlinx.coroutines.experimental.Job ...@@ -14,6 +15,10 @@ import kotlinx.coroutines.experimental.Job
@Module @Module
class MainModule { class MainModule {
@Provides
@PerActivity
fun provideJob() = Job()
@Provides @Provides
@PerActivity @PerActivity
fun provideMainNavigator(activity: MainActivity) = MainNavigator(activity) fun provideMainNavigator(activity: MainActivity) = MainNavigator(activity)
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment