Commit 74031054 authored by divyanshu's avatar divyanshu

Merge branch 'develop-2.x' into android-draw

# Conflicts:
#	app/build.gradle
#	app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt
#	app/src/main/res/values-hi-rIN/strings.xml
#	settings.gradle
parents da51aa91 36598c5d
...@@ -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
......
...@@ -11,7 +11,7 @@ This repository contains all the code related to the Android native application ...@@ -11,7 +11,7 @@ This repository contains all the code related to the Android native application
## How to build ## How to build
- Android Studio 3.0+ comes with built in kotlin support, so install the latest version (3.0+) of Android Studio (recommended). For older versions, you need to manually install kotlin plugin. Go to `File > Settings > Plugins` and search for `kotlin` and install it. You'll need to restart the IDE in order to see the changes. - You need to download the latest [Android Studio Preview](https://developer.android.com/studio/preview/) version since the stable IDE version does not support the [JetPack](https://developer.android.com/jetpack/) that is beeing used on this application.
- Make sure that you have the latest **gradle** and the **android plugin** versions installed. Go to `File > Project Structure > Project` and make sure that you have the latest versions installed. Refer [this](https://developer.android.com/studio/releases/gradle-plugin.html#updating-gradle) to see the compatible versions. - Make sure that you have the latest **gradle** and the **android plugin** versions installed. Go to `File > Project Structure > Project` and make sure that you have the latest versions installed. Refer [this](https://developer.android.com/studio/releases/gradle-plugin.html#updating-gradle) to see the compatible versions.
- Kotlin is already configured in the project. To check, go to `Tools > Kotlin > Configure Kotlin in project`. A message saying kotlin is already configured in the project pops up. You can update kotlin to the latest version by going to `Tools > Kotlin > Configure Kotlin updates` and download the latest version of kotlin. - Kotlin is already configured in the project. To check, go to `Tools > Kotlin > Configure Kotlin in project`. A message saying kotlin is already configured in the project pops up. You can update kotlin to the latest version by going to `Tools > Kotlin > Configure Kotlin updates` and download the latest version of kotlin.
......
...@@ -53,7 +53,6 @@ dependencies { ...@@ -53,7 +53,6 @@ dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation project(':player') implementation project(':player')
implementation project(':draw')
implementation libraries.kotlin implementation libraries.kotlin
implementation libraries.coroutines implementation libraries.coroutines
......
This diff is collapsed.
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
......
...@@ -3,17 +3,9 @@ ...@@ -3,17 +3,9 @@
package="chat.rocket.android"> package="chat.rocket.android">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<permission
android:name="${applicationId}.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="${applicationId}.permission.C2D_MESSAGE" />
<application <application
android:name=".app.RocketChatApplication" android:name=".app.RocketChatApplication"
android:allowBackup="true" android:allowBackup="true"
...@@ -39,6 +31,7 @@ ...@@ -39,6 +31,7 @@
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
...@@ -86,23 +79,6 @@ ...@@ -86,23 +79,6 @@
android:name=".settings.password.ui.PasswordActivity" android:name=".settings.password.ui.PasswordActivity"
android:theme="@style/AppTheme" /> android:theme="@style/AppTheme" />
<!-- TODO: Change to fragment -->
<activity
android:name=".settings.about.ui.AboutActivity"
android:theme="@style/AppTheme" />
<receiver
android:name="com.google.android.gms.gcm.GcmReceiver"
android:exported="true"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="${applicationId}" />
</intent-filter>
</receiver>
<receiver <receiver
android:name=".push.DirectReplyReceiver" android:name=".push.DirectReplyReceiver"
android:enabled="true" android:enabled="true"
...@@ -121,10 +97,11 @@ ...@@ -121,10 +97,11 @@
</service> </service>
<service <service
android:name=".push.GcmListenerService" android:name=".push.FirebaseMessagingService"
android:exported="false"> android:enabled="true"
android:exported="true">
<intent-filter> <intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" /> <action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter> </intent-filter>
</service> </service>
......
package chat.rocket.android.about.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import chat.rocket.android.BuildConfig
import chat.rocket.android.R
import chat.rocket.android.main.ui.MainActivity
import kotlinx.android.synthetic.main.app_bar.*
import kotlinx.android.synthetic.main.fragment_about.*
class AboutFragment : Fragment() {
companion object {
fun newInstance() = AboutFragment()
}
override fun onCreateView(inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = inflater.inflate(R.layout.fragment_about, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupToolbar()
setupViews()
}
private fun setupViews() {
text_version_name.text = getString(R.string.msg_version, BuildConfig.VERSION_NAME)
text_build_number.text = getString(R.string.msg_build, BuildConfig.VERSION_CODE)
}
private fun setupToolbar() {
val toolbar = (activity as MainActivity).toolbar
toolbar.title = getString(R.string.title_about)
toolbar.setNavigationIcon(R.drawable.ic_arrow_back_white_24dp)
toolbar.setNavigationOnClickListener {
this.activity?.onBackPressed()
}
}
override fun onStop() {
super.onStop()
(activity as MainActivity).toolbar.setNavigationIcon(R.drawable.ic_menu_white_24dp)
}
}
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
......
...@@ -14,7 +14,7 @@ object DateTimeHelper { ...@@ -14,7 +14,7 @@ object DateTimeHelper {
/** /**
* Returns a [LocalDateTime] from a [Long]. * Returns a [LocalDateTime] from a [Long].
* *
* @param long The [Long] * @param long The [Long] to gets a [LocalDateTime].
* @return The [LocalDateTime] from a [Long]. * @return The [LocalDateTime] from a [Long].
*/ */
fun getLocalDateTime(long: Long): LocalDateTime { fun getLocalDateTime(long: Long): LocalDateTime {
......
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
......
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
...@@ -2,6 +2,7 @@ package chat.rocket.android.authentication.di ...@@ -2,6 +2,7 @@ package chat.rocket.android.authentication.di
import chat.rocket.android.authentication.presentation.AuthenticationNavigator import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.authentication.ui.AuthenticationActivity import chat.rocket.android.authentication.ui.AuthenticationActivity
import chat.rocket.android.dagger.qualifier.ForAuthentication
import chat.rocket.android.dagger.scope.PerActivity import chat.rocket.android.dagger.scope.PerActivity
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
......
package chat.rocket.android.authentication.infraestructure
import chat.rocket.android.authentication.domain.model.TokenModel
import chat.rocket.android.util.DataToDomain
import chat.rocket.common.model.Token
object TokenMapper : DataToDomain<Token, TokenModel> {
override fun translate(data: Token): TokenModel {
return TokenModel(data.userId, data.authToken)
}
}
\ 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
...@@ -42,12 +42,13 @@ class LoginPresenter @Inject constructor( ...@@ -42,12 +42,13 @@ class LoginPresenter @Inject constructor(
private val localRepository: LocalRepository, private val localRepository: LocalRepository,
private val getAccountsInteractor: GetAccountsInteractor, private val getAccountsInteractor: GetAccountsInteractor,
private val settingsInteractor: GetSettingsInteractor, private val settingsInteractor: GetSettingsInteractor,
serverInteractor: GetCurrentServerInteractor, serverInteractor: GetConnectingServerInteractor,
private val saveCurrentServer: SaveCurrentServerInteractor,
private val saveAccountInteractor: SaveAccountInteractor, private val saveAccountInteractor: SaveAccountInteractor,
private val factory: RocketChatClientFactory private val factory: RocketChatClientFactory
) { ) {
// TODO - we should validate the current server when opening the app, and have a nonnull get() // TODO - we should validate the current server when opening the app, and have a nonnull get()
private val currentServer = serverInteractor.get()!! private var currentServer = serverInteractor.get()!!
private lateinit var client: RocketChatClient private lateinit var client: RocketChatClient
private lateinit var settings: PublicSettings private lateinit var settings: PublicSettings
private lateinit var usernameOrEmail: String private lateinit var usernameOrEmail: String
...@@ -56,7 +57,6 @@ class LoginPresenter @Inject constructor( ...@@ -56,7 +57,6 @@ class LoginPresenter @Inject constructor(
private lateinit var credentialSecret: String private lateinit var credentialSecret: String
private lateinit var deepLinkUserId: String private lateinit var deepLinkUserId: String
private lateinit var deepLinkToken: String private lateinit var deepLinkToken: String
private var loginCredentials: Credential? = null
fun setupView() { fun setupView() {
setupConnectionInfo(currentServer) setupConnectionInfo(currentServer)
...@@ -109,6 +109,7 @@ class LoginPresenter @Inject constructor( ...@@ -109,6 +109,7 @@ class LoginPresenter @Inject constructor(
} }
private fun setupConnectionInfo(serverUrl: String) { private fun setupConnectionInfo(serverUrl: String) {
currentServer = serverUrl
client = factory.create(serverUrl) client = factory.create(serverUrl)
settings = settingsInteractor.get(serverUrl) settings = settingsInteractor.get(serverUrl)
} }
...@@ -353,14 +354,12 @@ class LoginPresenter @Inject constructor( ...@@ -353,14 +354,12 @@ class LoginPresenter @Inject constructor(
val username = retryIO("me()") { client.me().username } val username = retryIO("me()") { client.me().username }
if (username != null) { if (username != null) {
localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, username) localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, username)
saveCurrentServer.save(currentServer)
saveAccount(username) saveAccount(username)
saveToken(token) saveToken(token)
registerPushToken() registerPushToken()
if (loginType == TYPE_LOGIN_USER_EMAIL) { if (loginType == TYPE_LOGIN_USER_EMAIL) {
loginCredentials = Credential.Builder(usernameOrEmail) view.saveSmartLockCredentials(usernameOrEmail, password)
.setPassword(password)
.build()
view.saveSmartLockCredentials(loginCredentials)
} }
navigator.toChatList() navigator.toChatList()
} else if (loginType == TYPE_LOGIN_OAUTH) { } else if (loginType == TYPE_LOGIN_OAUTH) {
......
...@@ -243,7 +243,7 @@ interface LoginView : LoadingView, MessageView { ...@@ -243,7 +243,7 @@ interface LoginView : LoadingView, MessageView {
fun alertWrongPassword() fun alertWrongPassword()
/** /**
* Save credentials via google smart lock * Saves Google Smart Lock credentials.
*/ */
fun saveSmartLockCredentials(loginCredential: Credential?) fun saveSmartLockCredentials(id: String, password: String)
} }
\ No newline at end of file
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
...@@ -27,7 +27,8 @@ class RegisterUsernamePresenter @Inject constructor( ...@@ -27,7 +27,8 @@ class RegisterUsernamePresenter @Inject constructor(
private val factory: RocketChatClientFactory, private val factory: RocketChatClientFactory,
private val saveAccountInteractor: SaveAccountInteractor, private val saveAccountInteractor: SaveAccountInteractor,
private val getAccountsInteractor: GetAccountsInteractor, private val getAccountsInteractor: GetAccountsInteractor,
serverInteractor: GetCurrentServerInteractor, serverInteractor: GetConnectingServerInteractor,
private val saveCurrentServer: SaveCurrentServerInteractor,
settingsInteractor: GetSettingsInteractor settingsInteractor: GetSettingsInteractor
) { ) {
private val currentServer = serverInteractor.get()!! private val currentServer = serverInteractor.get()!!
...@@ -47,6 +48,7 @@ class RegisterUsernamePresenter @Inject constructor( ...@@ -47,6 +48,7 @@ class RegisterUsernamePresenter @Inject constructor(
val registeredUsername = me.username val registeredUsername = me.username
if (registeredUsername != null) { if (registeredUsername != null) {
saveAccount(registeredUsername) saveAccount(registeredUsername)
saveCurrentServer.save(currentServer)
tokenRepository.save(currentServer, Token(userId, authToken)) tokenRepository.save(currentServer, Token(userId, authToken))
registerPushToken() registerPushToken()
navigator.toChatList() navigator.toChatList()
......
...@@ -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
...@@ -2,6 +2,7 @@ package chat.rocket.android.authentication.resetpassword.presentation ...@@ -2,6 +2,7 @@ package chat.rocket.android.authentication.resetpassword.presentation
import chat.rocket.android.authentication.presentation.AuthenticationNavigator import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.server.domain.GetConnectingServerInteractor
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 chat.rocket.android.util.extensions.isEmail import chat.rocket.android.util.extensions.isEmail
...@@ -19,7 +20,7 @@ class ResetPasswordPresenter @Inject constructor( ...@@ -19,7 +20,7 @@ class ResetPasswordPresenter @Inject constructor(
private val strategy: CancelStrategy, private val strategy: CancelStrategy,
private val navigator: AuthenticationNavigator, private val navigator: AuthenticationNavigator,
factory: RocketChatClientFactory, factory: RocketChatClientFactory,
serverInteractor: GetCurrentServerInteractor serverInteractor: GetConnectingServerInteractor
) { ) {
private val currentServer = serverInteractor.get()!! private val currentServer = serverInteractor.get()!!
private val client: RocketChatClient = factory.create(currentServer) private val client: RocketChatClient = factory.create(currentServer)
......
...@@ -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
...@@ -6,7 +6,7 @@ import chat.rocket.android.core.behaviours.showMessage ...@@ -6,7 +6,7 @@ import chat.rocket.android.core.behaviours.showMessage
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.server.domain.GetAccountsInteractor import chat.rocket.android.server.domain.GetAccountsInteractor
import chat.rocket.android.server.domain.RefreshSettingsInteractor import chat.rocket.android.server.domain.RefreshSettingsInteractor
import chat.rocket.android.server.domain.SaveCurrentServerInteractor import chat.rocket.android.server.domain.SaveConnectingServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.presentation.CheckServerPresenter import chat.rocket.android.server.presentation.CheckServerPresenter
import chat.rocket.android.util.extensions.isValidUrl import chat.rocket.android.util.extensions.isValidUrl
...@@ -16,7 +16,7 @@ import javax.inject.Inject ...@@ -16,7 +16,7 @@ import javax.inject.Inject
class ServerPresenter @Inject constructor(private val view: ServerView, class ServerPresenter @Inject constructor(private val view: ServerView,
private val strategy: CancelStrategy, private val strategy: CancelStrategy,
private val navigator: AuthenticationNavigator, private val navigator: AuthenticationNavigator,
private val serverInteractor: SaveCurrentServerInteractor, private val serverInteractor: SaveConnectingServerInteractor,
private val refreshSettingsInteractor: RefreshSettingsInteractor, private val refreshSettingsInteractor: RefreshSettingsInteractor,
private val getAccountsInteractor: GetAccountsInteractor, private val getAccountsInteractor: GetAccountsInteractor,
factory: RocketChatClientFactory factory: RocketChatClientFactory
......
...@@ -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
...@@ -15,7 +15,6 @@ import chat.rocket.core.internal.rest.login ...@@ -15,7 +15,6 @@ import chat.rocket.core.internal.rest.login
import chat.rocket.core.internal.rest.me import chat.rocket.core.internal.rest.me
import chat.rocket.core.internal.rest.signup import chat.rocket.core.internal.rest.signup
import chat.rocket.core.model.Myself import chat.rocket.core.model.Myself
import com.google.android.gms.auth.api.credentials.Credential
import javax.inject.Inject import javax.inject.Inject
class SignupPresenter @Inject constructor( class SignupPresenter @Inject constructor(
...@@ -23,7 +22,8 @@ class SignupPresenter @Inject constructor( ...@@ -23,7 +22,8 @@ class SignupPresenter @Inject constructor(
private val strategy: CancelStrategy, private val strategy: CancelStrategy,
private val navigator: AuthenticationNavigator, private val navigator: AuthenticationNavigator,
private val localRepository: LocalRepository, private val localRepository: LocalRepository,
private val serverInteractor: GetCurrentServerInteractor, private val serverInteractor: GetConnectingServerInteractor,
private val saveCurrentServerInteractor: SaveCurrentServerInteractor,
private val factory: RocketChatClientFactory, private val factory: RocketChatClientFactory,
private val saveAccountInteractor: SaveAccountInteractor, private val saveAccountInteractor: SaveAccountInteractor,
private val getAccountsInteractor: GetAccountsInteractor, private val getAccountsInteractor: GetAccountsInteractor,
...@@ -61,13 +61,11 @@ class SignupPresenter @Inject constructor( ...@@ -61,13 +61,11 @@ class SignupPresenter @Inject constructor(
// TODO This function returns a user token so should we save it? // TODO This function returns a user token so should we save it?
retryIO("login") { client.login(username, password) } retryIO("login") { client.login(username, password) }
val me = retryIO("me") { client.me() } val me = retryIO("me") { client.me() }
saveCurrentServerInteractor.save(currentServer)
localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, me.username) localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, me.username)
saveAccount(me) saveAccount(me)
registerPushToken() registerPushToken()
val loginCredentials = Credential.Builder(email) view.saveSmartLockCredentials(username, password)
.setPassword(password)
.build()
view.saveSmartLockCredentials(loginCredentials)
navigator.toChatList() navigator.toChatList()
} catch (exception: RocketChatException) { } catch (exception: RocketChatException) {
exception.message?.let { exception.message?.let {
......
...@@ -27,7 +27,7 @@ interface SignupView : LoadingView, MessageView { ...@@ -27,7 +27,7 @@ interface SignupView : LoadingView, MessageView {
fun alertBlankEmail() fun alertBlankEmail()
/** /**
* Save credentials via google smart lock * Saves Google Smart Lock credentials.
*/ */
fun saveSmartLockCredentials(loginCredential: Credential) fun saveSmartLockCredentials(id: String, password: String)
} }
\ No newline at end of file
package chat.rocket.android.authentication.signup.ui package chat.rocket.android.authentication.signup.ui
import DrawableHelper import DrawableHelper
import android.app.Activity.RESULT_OK 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
import android.view.ViewGroup import android.view.ViewGroup
import android.view.ViewTreeObserver import android.view.ViewTreeObserver
import android.widget.Toast
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.authentication.login.ui.googleApiClient import chat.rocket.android.R.string.message_credentials_saved_successfully
import chat.rocket.android.authentication.signup.presentation.SignupPresenter import chat.rocket.android.authentication.signup.presentation.SignupPresenter
import chat.rocket.android.authentication.signup.presentation.SignupView import chat.rocket.android.authentication.signup.presentation.SignupView
import chat.rocket.android.helper.KeyboardHelper import chat.rocket.android.helper.KeyboardHelper
import chat.rocket.android.helper.SmartLockHelper
import chat.rocket.android.helper.TextHelper import chat.rocket.android.helper.TextHelper
import chat.rocket.android.util.extensions.* import chat.rocket.android.util.extensions.*
import com.google.android.gms.auth.api.Auth import com.google.android.gms.auth.api.credentials.Credentials
import com.google.android.gms.auth.api.credentials.Credential
import com.google.android.gms.common.api.ResolvingResultCallbacks
import com.google.android.gms.common.api.Status
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_authentication_sign_up.* import kotlinx.android.synthetic.main.fragment_authentication_sign_up.*
import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
internal const val SAVE_CREDENTIALS = 1 internal const val SAVE_CREDENTIALS = 1
class SignupFragment : Fragment(), SignupView { class SignupFragment : Fragment(), SignupView {
@Inject @Inject
lateinit var presenter: SignupPresenter lateinit var presenter: SignupPresenter
private lateinit var credentialsToBeSaved: Credential
private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener { private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener {
if (KeyboardHelper.isSoftKeyboardShown(relative_layout.rootView)) { if (KeyboardHelper.isSoftKeyboardShown(relative_layout.rootView)) {
bottom_container.setVisible(false) bottom_container.setVisible(false)
...@@ -120,44 +114,16 @@ class SignupFragment : Fragment(), SignupView { ...@@ -120,44 +114,16 @@ class SignupFragment : Fragment(), SignupView {
} }
} }
override fun saveSmartLockCredentials(loginCredential: Credential) {
credentialsToBeSaved = loginCredential
googleApiClient.let {
if (it.isConnected) {
saveCredentials()
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == SAVE_CREDENTIALS) { if (resultCode == Activity.RESULT_OK) {
if (resultCode == RESULT_OK) { if (data != null) {
Toast.makeText( if (requestCode == SAVE_CREDENTIALS) {
context, showMessage(getString(message_credentials_saved_successfully))
getString(R.string.message_credentials_saved_successfully), }
Toast.LENGTH_SHORT
).show()
} else {
Timber.e("ERROR: Cancelled by user")
} }
} }
} }
private fun saveCredentials() {
activity?.let {
Auth.CredentialsApi.save(googleApiClient, credentialsToBeSaved).setResultCallback(
object : ResolvingResultCallbacks<Status>(it, SAVE_CREDENTIALS) {
override fun onSuccess(status: Status) {
Timber.d("save:SUCCESS:$status")
}
override fun onUnresolvableFailure(status: Status) {
Timber.e("save:FAILURE:$status")
}
})
}
}
override fun showLoading() { override fun showLoading() {
ui { ui {
enableUserInput(false) enableUserInput(false)
...@@ -188,6 +154,12 @@ class SignupFragment : Fragment(), SignupView { ...@@ -188,6 +154,12 @@ class SignupFragment : Fragment(), SignupView {
showMessage(getString(R.string.msg_generic_error)) showMessage(getString(R.string.msg_generic_error))
} }
override fun saveSmartLockCredentials(id: String, password: String) {
activity?.let {
SmartLockHelper.save(Credentials.getClient(it), it, id, password)
}
}
private fun tintEditTextDrawableStart() { private fun tintEditTextDrawableStart() {
ui { ui {
val personDrawable = val personDrawable =
......
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
} }
...@@ -25,7 +25,8 @@ class TwoFAPresenter @Inject constructor(private val view: TwoFAView, ...@@ -25,7 +25,8 @@ class TwoFAPresenter @Inject constructor(private val view: TwoFAView,
private val navigator: AuthenticationNavigator, private val navigator: AuthenticationNavigator,
private val tokenRepository: TokenRepository, private val tokenRepository: TokenRepository,
private val localRepository: LocalRepository, private val localRepository: LocalRepository,
private val serverInteractor: GetCurrentServerInteractor, private val serverInteractor: GetConnectingServerInteractor,
private val saveCurrentServerInteractor: SaveCurrentServerInteractor,
private val factory: RocketChatClientFactory, private val factory: RocketChatClientFactory,
private val saveAccountInteractor: SaveAccountInteractor, private val saveAccountInteractor: SaveAccountInteractor,
private val getAccountsInteractor: GetAccountsInteractor, private val getAccountsInteractor: GetAccountsInteractor,
...@@ -55,6 +56,7 @@ class TwoFAPresenter @Inject constructor(private val view: TwoFAView, ...@@ -55,6 +56,7 @@ class TwoFAPresenter @Inject constructor(private val view: TwoFAView,
} }
val me = retryIO("me") { client.me() } val me = retryIO("me") { client.me() }
saveAccount(me) saveAccount(me)
saveCurrentServerInteractor.save(currentServer)
tokenRepository.save(server, token) tokenRepository.save(server, token)
registerPushToken() registerPushToken()
navigator.toChatList() navigator.toChatList()
......
...@@ -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
...@@ -32,19 +32,27 @@ class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector { ...@@ -32,19 +32,27 @@ class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector {
setContentView(R.layout.activity_authentication) setContentView(R.layout.activity_authentication)
setTheme(R.style.AuthenticationTheme) setTheme(R.style.AuthenticationTheme)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
}
override fun onStart() {
super.onStart()
val deepLinkInfo = intent.getLoginDeepLinkInfo() val deepLinkInfo = intent.getLoginDeepLinkInfo()
launch(UI + job) { launch(UI + job) {
val newServer = intent.getBooleanExtra(INTENT_ADD_NEW_SERVER, false) val newServer = intent.getBooleanExtra(INTENT_ADD_NEW_SERVER, false)
// if we got authenticateWithDeepLink information, pass true to newServer also // if we got authenticateWithDeepLink information, pass true to newServer also
presenter.loadCredentials(newServer || deepLinkInfo != null) { authenticated -> presenter.loadCredentials(newServer || deepLinkInfo != null) { authenticated ->
if (!authenticated) { if (!authenticated) {
showServerInput(savedInstanceState, deepLinkInfo) showServerInput(deepLinkInfo)
} }
} }
} }
} }
override fun onStop() {
job.cancel()
super.onStop()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
val currentFragment = supportFragmentManager.findFragmentById(R.id.fragment_container) val currentFragment = supportFragmentManager.findFragmentById(R.id.fragment_container)
...@@ -53,17 +61,12 @@ class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector { ...@@ -53,17 +61,12 @@ class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector {
} }
} }
override fun onDestroy() {
job.cancel()
super.onDestroy()
}
override fun supportFragmentInjector(): AndroidInjector<Fragment> { override fun supportFragmentInjector(): AndroidInjector<Fragment> {
return fragmentDispatchingAndroidInjector return fragmentDispatchingAndroidInjector
} }
fun showServerInput(savedInstanceState: Bundle?, deepLinkInfo: LoginDeepLinkInfo?) { fun showServerInput(deepLinkInfo: LoginDeepLinkInfo?) {
addFragment("ServerFragment", R.id.fragment_container) { addFragment("ServerFragment", R.id.fragment_container, allowStateLoss = true) {
ServerFragment.newInstance(deepLinkInfo) ServerFragment.newInstance(deepLinkInfo)
} }
} }
......
package chat.rocket.android.chatroom.adapter package chat.rocket.android.chatroom.adapter
import android.view.View import android.view.View
import chat.rocket.android.chatroom.viewmodel.AudioAttachmentViewModel import androidx.core.view.isVisible
import chat.rocket.android.chatroom.uimodel.AudioAttachmentUiModel
import chat.rocket.android.player.PlayerActivity import chat.rocket.android.player.PlayerActivity
import chat.rocket.android.util.extensions.setVisible import chat.rocket.android.emoji.EmojiReactionListener
import chat.rocket.android.widget.emoji.EmojiReactionListener
import kotlinx.android.synthetic.main.message_attachment.view.* import kotlinx.android.synthetic.main.message_attachment.view.*
class AudioAttachmentViewHolder(itemView: View, class AudioAttachmentViewHolder(itemView: View,
listener: ActionsListener, listener: ActionsListener,
reactionListener: EmojiReactionListener? = null) reactionListener: EmojiReactionListener? = null)
: BaseViewHolder<AudioAttachmentViewModel>(itemView, listener, reactionListener) { : BaseViewHolder<AudioAttachmentUiModel>(itemView, listener, reactionListener) {
init { init {
with(itemView) { with(itemView) {
setupActionMenu(attachment_container) setupActionMenu(attachment_container)
image_attachment.setVisible(false) image_attachment.isVisible = false
audio_video_attachment.setVisible(true) audio_video_attachment.isVisible = true
} }
} }
override fun bindViews(data: AudioAttachmentViewModel) { override fun bindViews(data: AudioAttachmentUiModel) {
with(itemView) { with(itemView) {
file_name.text = data.attachmentTitle file_name.text = data.attachmentTitle
audio_video_attachment.setOnClickListener { view -> audio_video_attachment.setOnClickListener { view ->
......
...@@ -5,16 +5,16 @@ import android.net.Uri ...@@ -5,16 +5,16 @@ import android.net.Uri
import android.view.View import android.view.View
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import chat.rocket.android.chatroom.viewmodel.AuthorAttachmentViewModel import chat.rocket.android.chatroom.uimodel.AuthorAttachmentUiModel
import chat.rocket.android.emoji.EmojiReactionListener
import chat.rocket.android.util.extensions.content import chat.rocket.android.util.extensions.content
import chat.rocket.android.widget.emoji.EmojiReactionListener
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
import kotlinx.android.synthetic.main.item_author_attachment.view.* import kotlinx.android.synthetic.main.item_author_attachment.view.*
class AuthorAttachmentViewHolder(itemView: View, class AuthorAttachmentViewHolder(itemView: View,
listener: ActionsListener, listener: ActionsListener,
reactionListener: EmojiReactionListener? = null) reactionListener: EmojiReactionListener? = null)
: BaseViewHolder<AuthorAttachmentViewModel>(itemView, listener, reactionListener) { : BaseViewHolder<AuthorAttachmentUiModel>(itemView, listener, reactionListener) {
init { init {
with(itemView) { with(itemView) {
...@@ -22,7 +22,7 @@ class AuthorAttachmentViewHolder(itemView: View, ...@@ -22,7 +22,7 @@ class AuthorAttachmentViewHolder(itemView: View,
} }
} }
override fun bindViews(data: AuthorAttachmentViewModel) { override fun bindViews(data: AuthorAttachmentUiModel) {
with(itemView) { with(itemView) {
data.icon?.let { icon -> data.icon?.let { icon ->
author_icon.isVisible = true author_icon.isVisible = true
......
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 android.view.ContextThemeWrapper
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.children import androidx.core.view.children
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.chatroom.ui.bottomsheet.BottomSheetMenu import chat.rocket.android.chatroom.ui.bottomsheet.MessageActionsBottomSheet
import chat.rocket.android.chatroom.ui.bottomsheet.adapter.ActionListAdapter import chat.rocket.android.chatroom.uimodel.BaseUiModel
import chat.rocket.android.chatroom.viewmodel.BaseViewModel import chat.rocket.android.emoji.Emoji
import chat.rocket.android.widget.emoji.Emoji import chat.rocket.android.emoji.EmojiReactionListener
import chat.rocket.android.widget.emoji.EmojiReactionListener import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.toList
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
import chat.rocket.core.model.isSystemMessage import chat.rocket.core.model.isSystemMessage
import com.google.android.flexbox.FlexDirection import com.google.android.flexbox.FlexDirection
import com.google.android.flexbox.FlexboxLayoutManager import com.google.android.flexbox.FlexboxLayoutManager
import ru.whalemare.sheetmenu.extension.inflate
import ru.whalemare.sheetmenu.extension.toList
abstract class BaseViewHolder<T : BaseViewModel<*>>( abstract class BaseViewHolder<T : BaseUiModel<*>>(
itemView: View, itemView: View,
private val listener: ActionsListener, private val listener: ActionsListener,
var reactionListener: EmojiReactionListener? = null var reactionListener: EmojiReactionListener? = null
...@@ -89,8 +90,15 @@ abstract class BaseViewHolder<T : BaseViewModel<*>>( ...@@ -89,8 +90,15 @@ abstract class BaseViewHolder<T : BaseViewModel<*>>(
setTitle(if (isStarred) R.string.action_msg_unstar else R.string.action_msg_star) setTitle(if (isStarred) R.string.action_msg_unstar else R.string.action_msg_star)
isChecked = isStarred isChecked = isStarred
} }
val adapter = ActionListAdapter(menuItems, this@BaseViewHolder) view.context?.let {
BottomSheetMenu(adapter).show(view.context) if (it is ContextThemeWrapper && it.baseContext is AppCompatActivity) {
with(it.baseContext as AppCompatActivity) {
val actionsBottomSheet = MessageActionsBottomSheet()
actionsBottomSheet.addItems(menuItems, this@BaseViewHolder)
actionsBottomSheet.show(supportFragmentManager, null)
}
}
}
} }
} }
} }
......
package chat.rocket.android.chatroom.adapter package chat.rocket.android.chatroom.adapter
import android.support.v7.widget.RecyclerView import android.app.AlertDialog
import android.content.Context
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
import chat.rocket.android.chatroom.presentation.ChatRoomPresenter import chat.rocket.android.chatroom.presentation.ChatRoomPresenter
import chat.rocket.android.chatroom.viewmodel.* import chat.rocket.android.chatroom.uimodel.*
import chat.rocket.android.util.extensions.inflate import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.widget.emoji.EmojiReactionListener import chat.rocket.android.emoji.EmojiReactionListener
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
import chat.rocket.core.model.isSystemMessage import chat.rocket.core.model.isSystemMessage
import timber.log.Timber import timber.log.Timber
...@@ -18,9 +20,10 @@ class ChatRoomAdapter( ...@@ -18,9 +20,10 @@ class ChatRoomAdapter(
private val roomName: String? = null, private val roomName: String? = null,
private val presenter: ChatRoomPresenter? = null, private val presenter: ChatRoomPresenter? = null,
private val enableActions: Boolean = true, private val enableActions: Boolean = true,
private val reactionListener: EmojiReactionListener? = null private val reactionListener: EmojiReactionListener? = null,
private val context: Context? = null
) : RecyclerView.Adapter<BaseViewHolder<*>>() { ) : RecyclerView.Adapter<BaseViewHolder<*>>() {
private val dataSet = ArrayList<BaseViewModel<*>>() private val dataSet = ArrayList<BaseUiModel<*>>()
init { init {
setHasStableIds(true) setHasStableIds(true)
...@@ -28,43 +31,43 @@ class ChatRoomAdapter( ...@@ -28,43 +31,43 @@ class ChatRoomAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<*> { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<*> {
return when (viewType.toViewType()) { return when (viewType.toViewType()) {
BaseViewModel.ViewType.MESSAGE -> { BaseUiModel.ViewType.MESSAGE -> {
val view = parent.inflate(R.layout.item_message) val view = parent.inflate(R.layout.item_message)
MessageViewHolder(view, actionsListener, reactionListener) MessageViewHolder(view, actionsListener, reactionListener)
} }
BaseViewModel.ViewType.IMAGE_ATTACHMENT -> { BaseUiModel.ViewType.IMAGE_ATTACHMENT -> {
val view = parent.inflate(R.layout.message_attachment) val view = parent.inflate(R.layout.message_attachment)
ImageAttachmentViewHolder(view, actionsListener, reactionListener) ImageAttachmentViewHolder(view, actionsListener, reactionListener)
} }
BaseViewModel.ViewType.AUDIO_ATTACHMENT -> { BaseUiModel.ViewType.AUDIO_ATTACHMENT -> {
val view = parent.inflate(R.layout.message_attachment) val view = parent.inflate(R.layout.message_attachment)
AudioAttachmentViewHolder(view, actionsListener, reactionListener) AudioAttachmentViewHolder(view, actionsListener, reactionListener)
} }
BaseViewModel.ViewType.VIDEO_ATTACHMENT -> { BaseUiModel.ViewType.VIDEO_ATTACHMENT -> {
val view = parent.inflate(R.layout.message_attachment) val view = parent.inflate(R.layout.message_attachment)
VideoAttachmentViewHolder(view, actionsListener, reactionListener) VideoAttachmentViewHolder(view, actionsListener, reactionListener)
} }
BaseViewModel.ViewType.URL_PREVIEW -> { BaseUiModel.ViewType.URL_PREVIEW -> {
val view = parent.inflate(R.layout.message_url_preview) val view = parent.inflate(R.layout.message_url_preview)
UrlPreviewViewHolder(view, actionsListener, reactionListener) UrlPreviewViewHolder(view, actionsListener, reactionListener)
} }
BaseViewModel.ViewType.MESSAGE_ATTACHMENT -> { BaseUiModel.ViewType.MESSAGE_ATTACHMENT -> {
val view = parent.inflate(R.layout.item_message_attachment) val view = parent.inflate(R.layout.item_message_attachment)
MessageAttachmentViewHolder(view, actionsListener, reactionListener) MessageAttachmentViewHolder(view, actionsListener, reactionListener)
} }
BaseViewModel.ViewType.AUTHOR_ATTACHMENT -> { BaseUiModel.ViewType.AUTHOR_ATTACHMENT -> {
val view = parent.inflate(R.layout.item_author_attachment) val view = parent.inflate(R.layout.item_author_attachment)
AuthorAttachmentViewHolder(view, actionsListener, reactionListener) AuthorAttachmentViewHolder(view, actionsListener, reactionListener)
} }
BaseViewModel.ViewType.COLOR_ATTACHMENT -> { BaseUiModel.ViewType.COLOR_ATTACHMENT -> {
val view = parent.inflate(R.layout.item_color_attachment) val view = parent.inflate(R.layout.item_color_attachment)
ColorAttachmentViewHolder(view, actionsListener, reactionListener) ColorAttachmentViewHolder(view, actionsListener, reactionListener)
} }
BaseViewModel.ViewType.GENERIC_FILE_ATTACHMENT -> { BaseUiModel.ViewType.GENERIC_FILE_ATTACHMENT -> {
val view = parent.inflate(R.layout.item_file_attachment) val view = parent.inflate(R.layout.item_file_attachment)
GenericFileAttachmentViewHolder(view, actionsListener, reactionListener) GenericFileAttachmentViewHolder(view, actionsListener, reactionListener)
} }
BaseViewModel.ViewType.MESSAGE_REPLY -> { BaseUiModel.ViewType.MESSAGE_REPLY -> {
val view = parent.inflate(R.layout.item_message_reply) val view = parent.inflate(R.layout.item_message_reply)
MessageReplyViewHolder(view, actionsListener, reactionListener) { roomName, permalink -> MessageReplyViewHolder(view, actionsListener, reactionListener) { roomName, permalink ->
presenter?.openDirectMessage(roomName, permalink) presenter?.openDirectMessage(roomName, permalink)
...@@ -104,45 +107,45 @@ class ChatRoomAdapter( ...@@ -104,45 +107,45 @@ class ChatRoomAdapter(
when (holder) { when (holder) {
is MessageViewHolder -> is MessageViewHolder ->
holder.bind(dataSet[position] as MessageViewModel) holder.bind(dataSet[position] as MessageUiModel)
is ImageAttachmentViewHolder -> is ImageAttachmentViewHolder ->
holder.bind(dataSet[position] as ImageAttachmentViewModel) holder.bind(dataSet[position] as ImageAttachmentUiModel)
is AudioAttachmentViewHolder -> is AudioAttachmentViewHolder ->
holder.bind(dataSet[position] as AudioAttachmentViewModel) holder.bind(dataSet[position] as AudioAttachmentUiModel)
is VideoAttachmentViewHolder -> is VideoAttachmentViewHolder ->
holder.bind(dataSet[position] as VideoAttachmentViewModel) holder.bind(dataSet[position] as VideoAttachmentUiModel)
is UrlPreviewViewHolder -> is UrlPreviewViewHolder ->
holder.bind(dataSet[position] as UrlPreviewViewModel) holder.bind(dataSet[position] as UrlPreviewUiModel)
is MessageAttachmentViewHolder -> is MessageAttachmentViewHolder ->
holder.bind(dataSet[position] as MessageAttachmentViewModel) holder.bind(dataSet[position] as MessageAttachmentUiModel)
is AuthorAttachmentViewHolder -> is AuthorAttachmentViewHolder ->
holder.bind(dataSet[position] as AuthorAttachmentViewModel) holder.bind(dataSet[position] as AuthorAttachmentUiModel)
is ColorAttachmentViewHolder -> is ColorAttachmentViewHolder ->
holder.bind(dataSet[position] as ColorAttachmentViewModel) holder.bind(dataSet[position] as ColorAttachmentUiModel)
is GenericFileAttachmentViewHolder -> is GenericFileAttachmentViewHolder ->
holder.bind(dataSet[position] as GenericFileAttachmentViewModel) holder.bind(dataSet[position] as GenericFileAttachmentUiModel)
is MessageReplyViewHolder -> is MessageReplyViewHolder ->
holder.bind(dataSet[position] as MessageReplyViewModel) holder.bind(dataSet[position] as MessageReplyUiModel)
} }
} }
override fun getItemId(position: Int): Long { override fun getItemId(position: Int): Long {
val model = dataSet[position] val model = dataSet[position]
return when (model) { return when (model) {
is MessageViewModel -> model.messageId.hashCode().toLong() is MessageUiModel -> model.messageId.hashCode().toLong()
is BaseFileAttachmentViewModel -> model.id is BaseFileAttachmentUiModel -> model.id
is AuthorAttachmentViewModel -> model.id is AuthorAttachmentUiModel -> model.id
else -> return position.toLong() else -> return position.toLong()
} }
} }
fun appendData(dataSet: List<BaseViewModel<*>>) { fun appendData(dataSet: List<BaseUiModel<*>>) {
val previousDataSetSize = this.dataSet.size val previousDataSetSize = this.dataSet.size
this.dataSet.addAll(dataSet) this.dataSet.addAll(dataSet)
notifyItemChanged(previousDataSetSize, dataSet.size) notifyItemChanged(previousDataSetSize, dataSet.size)
} }
fun prependData(dataSet: List<BaseViewModel<*>>) { fun prependData(dataSet: List<BaseUiModel<*>>) {
val item = dataSet.indexOfFirst { newItem -> val item = dataSet.indexOfFirst { newItem ->
this.dataSet.indexOfFirst { it.messageId == newItem.messageId && it.viewType == newItem.viewType } > -1 this.dataSet.indexOfFirst { it.messageId == newItem.messageId && it.viewType == newItem.viewType } > -1
} }
...@@ -162,7 +165,7 @@ class ChatRoomAdapter( ...@@ -162,7 +165,7 @@ class ChatRoomAdapter(
} }
} }
fun updateItem(message: BaseViewModel<*>) { fun updateItem(message: BaseUiModel<*>) {
val index = dataSet.indexOfLast { it.messageId == message.messageId } val index = dataSet.indexOfLast { it.messageId == message.messageId }
val indexOfNext = dataSet.indexOfFirst { it.messageId == message.messageId } val indexOfNext = dataSet.indexOfFirst { it.messageId == message.messageId }
Timber.d("index: $index") Timber.d("index: $index")
...@@ -233,7 +236,16 @@ class ChatRoomAdapter( ...@@ -233,7 +236,16 @@ class ChatRoomAdapter(
presenter?.unpinMessage(id) presenter?.unpinMessage(id)
} }
} }
R.id.action_message_delete -> presenter?.deleteMessage(roomId, id) R.id.action_message_delete -> {
context?.let {
val builder = AlertDialog.Builder(it)
builder.setTitle(it.getString(R.string.msg_delete_message))
.setMessage(it.getString(R.string.msg_delete_description))
.setPositiveButton(it.getString(R.string.msg_ok)) { _, _ -> presenter?.deleteMessage(roomId, id) }
.setNegativeButton(it.getString(R.string.msg_cancel)) { _, _ -> }
.show()
}
}
R.id.action_menu_msg_react -> presenter?.showReactions(id) R.id.action_menu_msg_react -> presenter?.showReactions(id)
else -> TODO("Not implemented") else -> TODO("Not implemented")
} }
......
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
import chat.rocket.android.chatroom.viewmodel.ColorAttachmentViewModel import chat.rocket.android.chatroom.uimodel.ColorAttachmentUiModel
import chat.rocket.android.widget.emoji.EmojiReactionListener import chat.rocket.android.emoji.EmojiReactionListener
import kotlinx.android.synthetic.main.item_color_attachment.view.* import kotlinx.android.synthetic.main.item_color_attachment.view.*
class ColorAttachmentViewHolder(itemView: View, class ColorAttachmentViewHolder(itemView: View,
listener: BaseViewHolder.ActionsListener, listener: BaseViewHolder.ActionsListener,
reactionListener: EmojiReactionListener? = null) reactionListener: EmojiReactionListener? = null)
: BaseViewHolder<ColorAttachmentViewModel>(itemView, listener, reactionListener) { : BaseViewHolder<ColorAttachmentUiModel>(itemView, listener, reactionListener) {
val drawable: Drawable? = ContextCompat.getDrawable(itemView.context, val drawable: Drawable? = ContextCompat.getDrawable(itemView.context,
R.drawable.quote_vertical_bar) R.drawable.quote_vertical_gray_bar)
init { init {
with(itemView) { with(itemView) {
...@@ -25,7 +25,7 @@ class ColorAttachmentViewHolder(itemView: View, ...@@ -25,7 +25,7 @@ class ColorAttachmentViewHolder(itemView: View,
} }
} }
override fun bindViews(data: ColorAttachmentViewModel) { override fun bindViews(data: ColorAttachmentUiModel) {
with(itemView) { with(itemView) {
drawable?.let { drawable?.let {
quote_bar.background = drawable.mutate().apply { setTint(data.color) } quote_bar.background = drawable.mutate().apply { setTint(data.color) }
......
...@@ -6,7 +6,7 @@ import android.view.ViewGroup ...@@ -6,7 +6,7 @@ import android.view.ViewGroup
import android.widget.TextView import android.widget.TextView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.chatroom.adapter.CommandSuggestionsAdapter.CommandSuggestionsViewHolder import chat.rocket.android.chatroom.adapter.CommandSuggestionsAdapter.CommandSuggestionsViewHolder
import chat.rocket.android.chatroom.viewmodel.suggestion.CommandSuggestionViewModel import chat.rocket.android.chatroom.uimodel.suggestion.CommandSuggestionUiModel
import chat.rocket.android.widget.autocompletion.model.SuggestionModel import chat.rocket.android.widget.autocompletion.model.SuggestionModel
import chat.rocket.android.widget.autocompletion.ui.BaseSuggestionViewHolder import chat.rocket.android.widget.autocompletion.ui.BaseSuggestionViewHolder
import chat.rocket.android.widget.autocompletion.ui.SuggestionsAdapter import chat.rocket.android.widget.autocompletion.ui.SuggestionsAdapter
...@@ -23,7 +23,7 @@ class CommandSuggestionsAdapter : SuggestionsAdapter<CommandSuggestionsViewHolde ...@@ -23,7 +23,7 @@ class CommandSuggestionsAdapter : SuggestionsAdapter<CommandSuggestionsViewHolde
class CommandSuggestionsViewHolder(view: View) : BaseSuggestionViewHolder(view) { class CommandSuggestionsViewHolder(view: View) : BaseSuggestionViewHolder(view) {
override fun bind(item: SuggestionModel, itemClickListener: SuggestionsAdapter.ItemClickListener?) { override fun bind(item: SuggestionModel, itemClickListener: SuggestionsAdapter.ItemClickListener?) {
item as CommandSuggestionViewModel item as CommandSuggestionUiModel
with(itemView) { with(itemView) {
val nameTextView = itemView.findViewById<TextView>(R.id.text_command_name) val nameTextView = itemView.findViewById<TextView>(R.id.text_command_name)
val descriptionTextView = itemView.findViewById<TextView>(R.id.text_command_description) val descriptionTextView = itemView.findViewById<TextView>(R.id.text_command_description)
......
package chat.rocket.android.chatroom.adapter package chat.rocket.android.chatroom.adapter
import android.content.Intent import android.content.Intent
import android.net.Uri
import android.view.View import android.view.View
import androidx.core.net.toUri import androidx.core.net.toUri
import chat.rocket.android.chatroom.viewmodel.GenericFileAttachmentViewModel import chat.rocket.android.chatroom.uimodel.GenericFileAttachmentUiModel
import chat.rocket.android.emoji.EmojiReactionListener
import chat.rocket.android.util.extensions.content import chat.rocket.android.util.extensions.content
import chat.rocket.android.widget.emoji.EmojiReactionListener
import chat.rocket.common.util.ifNull
import kotlinx.android.synthetic.main.item_file_attachment.view.* import kotlinx.android.synthetic.main.item_file_attachment.view.*
class GenericFileAttachmentViewHolder(itemView: View, class GenericFileAttachmentViewHolder(itemView: View,
listener: ActionsListener, listener: ActionsListener,
reactionListener: EmojiReactionListener? = null) reactionListener: EmojiReactionListener? = null)
: BaseViewHolder<GenericFileAttachmentViewModel>(itemView, listener, reactionListener) { : BaseViewHolder<GenericFileAttachmentUiModel>(itemView, listener, reactionListener) {
init { init {
with(itemView) { with(itemView) {
...@@ -21,7 +19,7 @@ class GenericFileAttachmentViewHolder(itemView: View, ...@@ -21,7 +19,7 @@ class GenericFileAttachmentViewHolder(itemView: View,
} }
} }
override fun bindViews(data: GenericFileAttachmentViewModel) { override fun bindViews(data: GenericFileAttachmentUiModel) {
with(itemView) { with(itemView) {
text_file_name.content = data.attachmentTitle text_file_name.content = data.attachmentTitle
......
package chat.rocket.android.chatroom.adapter package chat.rocket.android.chatroom.adapter
import android.view.View import android.view.View
import chat.rocket.android.chatroom.viewmodel.ImageAttachmentViewModel import chat.rocket.android.chatroom.uimodel.ImageAttachmentUiModel
import chat.rocket.android.helper.ImageHelper import chat.rocket.android.helper.ImageHelper
import chat.rocket.android.widget.emoji.EmojiReactionListener import chat.rocket.android.emoji.EmojiReactionListener
import com.facebook.drawee.backends.pipeline.Fresco import com.facebook.drawee.backends.pipeline.Fresco
import kotlinx.android.synthetic.main.message_attachment.view.* import kotlinx.android.synthetic.main.message_attachment.view.*
...@@ -11,7 +11,7 @@ class ImageAttachmentViewHolder( ...@@ -11,7 +11,7 @@ class ImageAttachmentViewHolder(
itemView: View, itemView: View,
listener: ActionsListener, listener: ActionsListener,
reactionListener: EmojiReactionListener? = null reactionListener: EmojiReactionListener? = null
) : BaseViewHolder<ImageAttachmentViewModel>(itemView, listener, reactionListener) { ) : BaseViewHolder<ImageAttachmentUiModel>(itemView, listener, reactionListener) {
init { init {
with(itemView) { with(itemView) {
...@@ -19,7 +19,7 @@ class ImageAttachmentViewHolder( ...@@ -19,7 +19,7 @@ class ImageAttachmentViewHolder(
} }
} }
override fun bindViews(data: ImageAttachmentViewModel) { override fun bindViews(data: ImageAttachmentUiModel) {
with(itemView) { with(itemView) {
val controller = Fresco.newDraweeControllerBuilder().apply { val controller = Fresco.newDraweeControllerBuilder().apply {
setUri(data.attachmentUrl) setUri(data.attachmentUrl)
...@@ -30,7 +30,7 @@ class ImageAttachmentViewHolder( ...@@ -30,7 +30,7 @@ class ImageAttachmentViewHolder(
file_name.text = data.attachmentTitle file_name.text = data.attachmentTitle
image_attachment.setOnClickListener { image_attachment.setOnClickListener {
ImageHelper.openImage( ImageHelper.openImage(
it.context, context,
data.attachmentUrl, data.attachmentUrl,
data.attachmentTitle.toString() data.attachmentTitle.toString()
) )
......
...@@ -2,15 +2,15 @@ package chat.rocket.android.chatroom.adapter ...@@ -2,15 +2,15 @@ package chat.rocket.android.chatroom.adapter
import android.text.method.LinkMovementMethod import android.text.method.LinkMovementMethod
import android.view.View import android.view.View
import chat.rocket.android.chatroom.viewmodel.MessageAttachmentViewModel import chat.rocket.android.chatroom.uimodel.MessageAttachmentUiModel
import chat.rocket.android.widget.emoji.EmojiReactionListener import chat.rocket.android.emoji.EmojiReactionListener
import kotlinx.android.synthetic.main.item_message_attachment.view.* import kotlinx.android.synthetic.main.item_message_attachment.view.*
class MessageAttachmentViewHolder( class MessageAttachmentViewHolder(
itemView: View, itemView: View,
listener: ActionsListener, listener: ActionsListener,
reactionListener: EmojiReactionListener? = null reactionListener: EmojiReactionListener? = null
) : BaseViewHolder<MessageAttachmentViewModel>(itemView, listener, reactionListener) { ) : BaseViewHolder<MessageAttachmentUiModel>(itemView, listener, reactionListener) {
init { init {
with(itemView) { with(itemView) {
...@@ -19,7 +19,7 @@ class MessageAttachmentViewHolder( ...@@ -19,7 +19,7 @@ class MessageAttachmentViewHolder(
} }
} }
override fun bindViews(data: MessageAttachmentViewModel) { override fun bindViews(data: MessageAttachmentUiModel) {
with(itemView) { with(itemView) {
text_message_time.text = data.time text_message_time.text = data.time
text_sender.text = data.senderName text_sender.text = data.senderName
......
package chat.rocket.android.chatroom.adapter package chat.rocket.android.chatroom.adapter
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 android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.chatroom.viewmodel.ReactionViewModel import chat.rocket.android.chatroom.uimodel.ReactionUiModel
import chat.rocket.android.dagger.DaggerLocalComponent import chat.rocket.android.dagger.DaggerLocalComponent
import chat.rocket.android.emoji.Emoji
import chat.rocket.android.emoji.EmojiKeyboardListener
import chat.rocket.android.emoji.EmojiPickerPopup
import chat.rocket.android.emoji.EmojiReactionListener
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.widget.emoji.Emoji
import chat.rocket.android.widget.emoji.EmojiListenerAdapter
import chat.rocket.android.widget.emoji.EmojiPickerPopup
import chat.rocket.android.widget.emoji.EmojiReactionListener
import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.CopyOnWriteArrayList
import javax.inject.Inject import javax.inject.Inject
...@@ -23,7 +23,7 @@ class MessageReactionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() ...@@ -23,7 +23,7 @@ class MessageReactionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>()
private const val ADD_REACTION_VIEW_TYPE = 1 private const val ADD_REACTION_VIEW_TYPE = 1
} }
private val reactions = CopyOnWriteArrayList<ReactionViewModel>() private val reactions = CopyOnWriteArrayList<ReactionUiModel>()
var listener: EmojiReactionListener? = null var listener: EmojiReactionListener? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
...@@ -59,7 +59,7 @@ class MessageReactionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() ...@@ -59,7 +59,7 @@ class MessageReactionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>()
return REACTION_VIEW_TYPE return REACTION_VIEW_TYPE
} }
fun addReactions(reactions: List<ReactionViewModel>) { fun addReactions(reactions: List<ReactionUiModel>) {
this.reactions.clear() this.reactions.clear()
this.reactions.addAllAbsent(reactions) this.reactions.addAllAbsent(reactions)
notifyItemRangeInserted(0, reactions.size) notifyItemRangeInserted(0, reactions.size)
...@@ -72,24 +72,26 @@ class MessageReactionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() ...@@ -72,24 +72,26 @@ class MessageReactionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>()
} }
fun contains(reactionShortname: String) = fun contains(reactionShortname: String) =
reactions.firstOrNull { it.shortname == reactionShortname} != null reactions.firstOrNull { it.shortname == reactionShortname } != null
class SingleReactionViewHolder(view: View, class SingleReactionViewHolder(view: View,
private val listener: EmojiReactionListener?) private val listener: EmojiReactionListener?)
: RecyclerView.ViewHolder(view), View.OnClickListener { : RecyclerView.ViewHolder(view), View.OnClickListener {
@Inject lateinit var localRepository: LocalRepository @Inject
@Volatile lateinit var reaction: ReactionViewModel lateinit var localRepository: LocalRepository
@Volatile
lateinit var reaction: ReactionUiModel
@Volatile @Volatile
var clickHandled = false var clickHandled = false
init { init {
DaggerLocalComponent.builder() DaggerLocalComponent.builder()
.context(itemView.context) .context(itemView.context)
.build() .build()
.inject(this) .inject(this)
} }
fun bind(reaction: ReactionViewModel) { fun bind(reaction: ReactionUiModel) {
clickHandled = false clickHandled = false
this.reaction = reaction this.reaction = reaction
with(itemView) { with(itemView) {
...@@ -125,7 +127,7 @@ class MessageReactionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() ...@@ -125,7 +127,7 @@ class MessageReactionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>()
itemView as ImageView itemView as ImageView
itemView.setOnClickListener { itemView.setOnClickListener {
val emojiPickerPopup = EmojiPickerPopup(itemView.context) val emojiPickerPopup = EmojiPickerPopup(itemView.context)
emojiPickerPopup.listener = object : EmojiListenerAdapter() { emojiPickerPopup.listener = object : EmojiKeyboardListener {
override fun onEmojiAdded(emoji: Emoji) { override fun onEmojiAdded(emoji: Emoji) {
listener?.onReactionAdded(messageId, emoji) listener?.onReactionAdded(messageId, emoji)
} }
......
package chat.rocket.android.chatroom.adapter package chat.rocket.android.chatroom.adapter
import android.view.View import android.view.View
import chat.rocket.android.chatroom.viewmodel.MessageReplyViewModel import chat.rocket.android.chatroom.uimodel.MessageReplyUiModel
import chat.rocket.android.widget.emoji.EmojiReactionListener import chat.rocket.android.emoji.EmojiReactionListener
import kotlinx.android.synthetic.main.item_message_reply.view.* import kotlinx.android.synthetic.main.item_message_reply.view.*
class MessageReplyViewHolder( class MessageReplyViewHolder(
...@@ -10,7 +10,7 @@ class MessageReplyViewHolder( ...@@ -10,7 +10,7 @@ class MessageReplyViewHolder(
listener: ActionsListener, listener: ActionsListener,
reactionListener: EmojiReactionListener? = null, reactionListener: EmojiReactionListener? = null,
private val replyCallback: (roomName: String, permalink: String) -> Unit private val replyCallback: (roomName: String, permalink: String) -> Unit
) : BaseViewHolder<MessageReplyViewModel>(itemView, listener, reactionListener) { ) : BaseViewHolder<MessageReplyUiModel>(itemView, listener, reactionListener) {
init { init {
with(itemView) { with(itemView) {
...@@ -18,7 +18,7 @@ class MessageReplyViewHolder( ...@@ -18,7 +18,7 @@ class MessageReplyViewHolder(
} }
} }
override fun bindViews(data: MessageReplyViewModel) { override fun bindViews(data: MessageReplyUiModel) {
with(itemView) { with(itemView) {
button_message_reply.setOnClickListener { button_message_reply.setOnClickListener {
with(data.rawData) { with(data.rawData) {
......
...@@ -4,9 +4,8 @@ import android.graphics.Color ...@@ -4,9 +4,8 @@ import android.graphics.Color
import android.text.method.LinkMovementMethod import android.text.method.LinkMovementMethod
import android.view.View import android.view.View
import androidx.core.view.isVisible import androidx.core.view.isVisible
import chat.rocket.android.chatroom.viewmodel.MessageViewModel import chat.rocket.android.chatroom.uimodel.MessageUiModel
import chat.rocket.android.util.extensions.setVisible import chat.rocket.android.emoji.EmojiReactionListener
import chat.rocket.android.widget.emoji.EmojiReactionListener
import chat.rocket.core.model.isSystemMessage import chat.rocket.core.model.isSystemMessage
import kotlinx.android.synthetic.main.avatar.view.* import kotlinx.android.synthetic.main.avatar.view.*
import kotlinx.android.synthetic.main.item_message.view.* import kotlinx.android.synthetic.main.item_message.view.*
...@@ -15,7 +14,7 @@ class MessageViewHolder( ...@@ -15,7 +14,7 @@ class MessageViewHolder(
itemView: View, itemView: View,
listener: ActionsListener, listener: ActionsListener,
reactionListener: EmojiReactionListener? = null reactionListener: EmojiReactionListener? = null
) : BaseViewHolder<MessageViewModel>(itemView, listener, reactionListener) { ) : BaseViewHolder<MessageUiModel>(itemView, listener, reactionListener) {
init { init {
with(itemView) { with(itemView) {
...@@ -24,7 +23,7 @@ class MessageViewHolder( ...@@ -24,7 +23,7 @@ class MessageViewHolder(
} }
} }
override fun bindViews(data: MessageViewModel) { override fun bindViews(data: MessageUiModel) {
with(itemView) { with(itemView) {
if (data.isFirstUnread) new_messages_notif.visibility = View.VISIBLE if (data.isFirstUnread) new_messages_notif.visibility = View.VISIBLE
else new_messages_notif.visibility = View.GONE else new_messages_notif.visibility = View.GONE
......
...@@ -9,7 +9,7 @@ import android.widget.ImageView ...@@ -9,7 +9,7 @@ import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.chatroom.adapter.PeopleSuggestionsAdapter.PeopleSuggestionViewHolder import chat.rocket.android.chatroom.adapter.PeopleSuggestionsAdapter.PeopleSuggestionViewHolder
import chat.rocket.android.chatroom.viewmodel.suggestion.PeopleSuggestionViewModel import chat.rocket.android.chatroom.uimodel.suggestion.PeopleSuggestionUiModel
import chat.rocket.android.util.extensions.setVisible import chat.rocket.android.util.extensions.setVisible
import chat.rocket.android.widget.autocompletion.model.SuggestionModel import chat.rocket.android.widget.autocompletion.model.SuggestionModel
import chat.rocket.android.widget.autocompletion.ui.BaseSuggestionViewHolder import chat.rocket.android.widget.autocompletion.ui.BaseSuggestionViewHolder
...@@ -22,14 +22,14 @@ class PeopleSuggestionsAdapter(context: Context) : SuggestionsAdapter<PeopleSugg ...@@ -22,14 +22,14 @@ class PeopleSuggestionsAdapter(context: Context) : SuggestionsAdapter<PeopleSugg
val allDescription = context.getString(R.string.suggest_all_description) val allDescription = context.getString(R.string.suggest_all_description)
val hereDescription = context.getString(R.string.suggest_here_description) val hereDescription = context.getString(R.string.suggest_here_description)
val pinnedList = listOf( val pinnedList = listOf(
PeopleSuggestionViewModel(imageUri = null, PeopleSuggestionUiModel(imageUri = null,
text = "all", text = "all",
username = "all", username = "all",
name = allDescription, name = allDescription,
status = null, status = null,
pinned = false, pinned = false,
searchList = listOf("all")), searchList = listOf("all")),
PeopleSuggestionViewModel(imageUri = null, PeopleSuggestionUiModel(imageUri = null,
text = "here", text = "here",
username = "here", username = "here",
name = hereDescription, name = hereDescription,
...@@ -49,7 +49,7 @@ class PeopleSuggestionsAdapter(context: Context) : SuggestionsAdapter<PeopleSugg ...@@ -49,7 +49,7 @@ class PeopleSuggestionsAdapter(context: Context) : SuggestionsAdapter<PeopleSugg
class PeopleSuggestionViewHolder(view: View) : BaseSuggestionViewHolder(view) { class PeopleSuggestionViewHolder(view: View) : BaseSuggestionViewHolder(view) {
override fun bind(item: SuggestionModel, itemClickListener: SuggestionsAdapter.ItemClickListener?) { override fun bind(item: SuggestionModel, itemClickListener: SuggestionsAdapter.ItemClickListener?) {
item as PeopleSuggestionViewModel item as PeopleSuggestionUiModel
with(itemView) { with(itemView) {
val username = itemView.findViewById<TextView>(R.id.text_username) val username = itemView.findViewById<TextView>(R.id.text_username)
val name = itemView.findViewById<TextView>(R.id.text_name) val name = itemView.findViewById<TextView>(R.id.text_name)
......
...@@ -6,7 +6,7 @@ import android.view.ViewGroup ...@@ -6,7 +6,7 @@ import android.view.ViewGroup
import android.widget.TextView import android.widget.TextView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.chatroom.adapter.RoomSuggestionsAdapter.RoomSuggestionsViewHolder import chat.rocket.android.chatroom.adapter.RoomSuggestionsAdapter.RoomSuggestionsViewHolder
import chat.rocket.android.chatroom.viewmodel.suggestion.ChatRoomSuggestionViewModel import chat.rocket.android.chatroom.uimodel.suggestion.ChatRoomSuggestionUiModel
import chat.rocket.android.widget.autocompletion.model.SuggestionModel import chat.rocket.android.widget.autocompletion.model.SuggestionModel
import chat.rocket.android.widget.autocompletion.ui.BaseSuggestionViewHolder import chat.rocket.android.widget.autocompletion.ui.BaseSuggestionViewHolder
import chat.rocket.android.widget.autocompletion.ui.SuggestionsAdapter import chat.rocket.android.widget.autocompletion.ui.SuggestionsAdapter
...@@ -22,7 +22,7 @@ class RoomSuggestionsAdapter : SuggestionsAdapter<RoomSuggestionsViewHolder>("#" ...@@ -22,7 +22,7 @@ class RoomSuggestionsAdapter : SuggestionsAdapter<RoomSuggestionsViewHolder>("#"
class RoomSuggestionsViewHolder(view: View) : BaseSuggestionViewHolder(view) { class RoomSuggestionsViewHolder(view: View) : BaseSuggestionViewHolder(view) {
override fun bind(item: SuggestionModel, itemClickListener: SuggestionsAdapter.ItemClickListener?) { override fun bind(item: SuggestionModel, itemClickListener: SuggestionsAdapter.ItemClickListener?) {
item as ChatRoomSuggestionViewModel item as ChatRoomSuggestionUiModel
with(itemView) { with(itemView) {
val fullname = itemView.findViewById<TextView>(R.id.text_fullname) val fullname = itemView.findViewById<TextView>(R.id.text_fullname)
val name = itemView.findViewById<TextView>(R.id.text_name) val name = itemView.findViewById<TextView>(R.id.text_name)
......
package chat.rocket.android.chatroom.adapter package chat.rocket.android.chatroom.adapter
import android.content.Intent
import android.net.Uri import android.net.Uri
import android.view.View import android.view.View
import chat.rocket.android.chatroom.viewmodel.UrlPreviewViewModel import androidx.core.view.isVisible
import chat.rocket.android.util.extensions.content import chat.rocket.android.chatroom.uimodel.UrlPreviewUiModel
import chat.rocket.android.util.extensions.openTabbedUrl import chat.rocket.android.util.extensions.openTabbedUrl
import chat.rocket.android.util.extensions.setVisible import chat.rocket.android.emoji.EmojiReactionListener
import chat.rocket.android.widget.emoji.EmojiReactionListener import chat.rocket.android.util.extensions.content
import kotlinx.android.synthetic.main.message_url_preview.view.* import kotlinx.android.synthetic.main.message_url_preview.view.*
class UrlPreviewViewHolder(itemView: View, class UrlPreviewViewHolder(itemView: View,
listener: ActionsListener, listener: ActionsListener,
reactionListener: EmojiReactionListener? = null) reactionListener: EmojiReactionListener? = null)
: BaseViewHolder<UrlPreviewViewModel>(itemView, listener, reactionListener) { : BaseViewHolder<UrlPreviewUiModel>(itemView, listener, reactionListener) {
init { init {
with(itemView) { with(itemView) {
...@@ -21,13 +20,13 @@ class UrlPreviewViewHolder(itemView: View, ...@@ -21,13 +20,13 @@ class UrlPreviewViewHolder(itemView: View,
} }
} }
override fun bindViews(data: UrlPreviewViewModel) { override fun bindViews(data: UrlPreviewUiModel) {
with(itemView) { with(itemView) {
if (data.thumbUrl.isNullOrEmpty()) { if (data.thumbUrl.isNullOrEmpty()) {
image_preview.setVisible(false) image_preview.isVisible = false
} else { } else {
image_preview.setImageURI(data.thumbUrl) image_preview.setImageURI(data.thumbUrl)
image_preview.setVisible(true) image_preview.isVisible = true
} }
text_host.content = data.hostname text_host.content = data.hostname
text_title.content = data.title text_title.content = data.title
......
package chat.rocket.android.chatroom.adapter package chat.rocket.android.chatroom.adapter
import android.view.View import android.view.View
import chat.rocket.android.chatroom.viewmodel.VideoAttachmentViewModel import androidx.core.view.isVisible
import chat.rocket.android.chatroom.uimodel.VideoAttachmentUiModel
import chat.rocket.android.player.PlayerActivity import chat.rocket.android.player.PlayerActivity
import chat.rocket.android.util.extensions.setVisible import chat.rocket.android.emoji.EmojiReactionListener
import chat.rocket.android.widget.emoji.EmojiReactionListener
import kotlinx.android.synthetic.main.message_attachment.view.* import kotlinx.android.synthetic.main.message_attachment.view.*
class VideoAttachmentViewHolder(itemView: View, class VideoAttachmentViewHolder(itemView: View,
listener: ActionsListener, listener: ActionsListener,
reactionListener: EmojiReactionListener? = null) reactionListener: EmojiReactionListener? = null)
: BaseViewHolder<VideoAttachmentViewModel>(itemView, listener, reactionListener) { : BaseViewHolder<VideoAttachmentUiModel>(itemView, listener, reactionListener) {
init { init {
with(itemView) { with(itemView) {
setupActionMenu(attachment_container) setupActionMenu(attachment_container)
image_attachment.setVisible(false) image_attachment.isVisible = false
audio_video_attachment.setVisible(true) audio_video_attachment.isVisible = true
} }
} }
override fun bindViews(data: VideoAttachmentViewModel) { override fun bindViews(data: VideoAttachmentUiModel) {
with(itemView) { with(itemView) {
file_name.text = data.attachmentTitle file_name.text = data.attachmentTitle
audio_video_attachment.setOnClickListener { view -> audio_video_attachment.setOnClickListener { view ->
......
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.ChatRoomNavigator
import chat.rocket.android.chatroom.presentation.ChatRoomView import chat.rocket.android.chatroom.presentation.ChatRoomView
import chat.rocket.android.chatroom.ui.ChatRoomActivity
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
import chat.rocket.android.dagger.scope.PerFragment import chat.rocket.android.dagger.scope.PerFragment
...@@ -12,20 +10,26 @@ import dagger.Provides ...@@ -12,20 +10,26 @@ import dagger.Provides
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.experimental.Job
@Module @Module
@PerFragment
class ChatRoomFragmentModule { class ChatRoomFragmentModule {
@Provides @Provides
@PerFragment
fun provideJob() = Job()
@Provides
@PerFragment
fun chatRoomView(frag: ChatRoomFragment): ChatRoomView { fun chatRoomView(frag: ChatRoomFragment): ChatRoomView {
return frag return frag
} }
@Provides @Provides
@PerFragment
fun provideLifecycleOwner(frag: ChatRoomFragment): LifecycleOwner { fun provideLifecycleOwner(frag: ChatRoomFragment): 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.chatroom.di package chat.rocket.android.chatroom.di
import chat.rocket.android.chatroom.ui.ChatRoomFragment import chat.rocket.android.chatroom.ui.ChatRoomFragment
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 ChatRoomFragmentProvider { abstract class ChatRoomFragmentProvider {
@ContributesAndroidInjector(modules = [ChatRoomFragmentModule::class]) @ContributesAndroidInjector(modules = [ChatRoomFragmentModule::class])
@PerFragment
abstract fun provideChatRoomFragment(): ChatRoomFragment abstract fun provideChatRoomFragment(): ChatRoomFragment
} }
\ No newline at end of file
...@@ -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
...@@ -5,26 +5,32 @@ import android.net.Uri ...@@ -5,26 +5,32 @@ import android.net.Uri
import chat.rocket.android.util.extensions.* import chat.rocket.android.util.extensions.*
import javax.inject.Inject import javax.inject.Inject
class UriInteractor @Inject constructor(private val context: Context) { class UriInteractor @Inject constructor(private val context: Context) {
/** /**
* Gets the file name from an [Uri]. * Returns the file name from the [Uri].
*/ */
fun getFileName(uri: Uri): String? = uri.getFileName(context) fun getFileName(uri: Uri): String? = uri.getFileName(context)
/** /**
* Gets the MimeType of an [Uri] * Returns the MimeType from the [Uri].
*/ */
fun getMimeType(uri: Uri): String = uri.getMimeType(context) fun getMimeType(uri: Uri): String = uri.getMimeType(context)
/** /**
* Gets the real path of an [Uri] * Returns the file size from the [Uri].
*/ */
fun getRealPath(uri: Uri): String? = uri.getRealPathFromURI(context)
fun getFileSize(uri: Uri) = uri.getFileSize(context) fun getFileSize(uri: Uri) = uri.getFileSize(context)
/**
* Returns the InputStream from the [Uri].
*/
fun getInputStream(uri: Uri) = uri.getInputStream(context) fun getInputStream(uri: Uri) = uri.getInputStream(context)
/**
* Returns the Bitmap from the [Uri].
*
* Note: It should be an image.
*/
fun getBitmap(uri: Uri) = uri.getBitmpap(context)
} }
\ No newline at end of file
...@@ -14,6 +14,12 @@ class ChatRoomNavigator(internal val activity: ChatRoomActivity) { ...@@ -14,6 +14,12 @@ class ChatRoomNavigator(internal val activity: ChatRoomActivity) {
} }
} }
fun toMentions(chatRoomId: String) {
activity.addFragmentBackStack("MentionsFragment", R.id.fragment_container) {
chat.rocket.android.mentions.ui.newInstance(chatRoomId)
}
}
fun toPinnedMessageList(chatRoomId: String) { fun toPinnedMessageList(chatRoomId: String) {
activity.addFragmentBackStack("PinnedMessages", R.id.fragment_container) { activity.addFragmentBackStack("PinnedMessages", R.id.fragment_container) {
chat.rocket.android.pinnedmessages.ui.newInstance(chatRoomId) chat.rocket.android.pinnedmessages.ui.newInstance(chatRoomId)
...@@ -37,16 +43,30 @@ class ChatRoomNavigator(internal val activity: ChatRoomActivity) { ...@@ -37,16 +43,30 @@ class ChatRoomNavigator(internal val activity: ChatRoomActivity) {
activity.finish() activity.finish()
} }
fun toDirectMessage(chatRoomId: String, fun toDirectMessage(
chatRoomName: String, chatRoomId: String,
chatRoomType: String, chatRoomName: String,
isChatRoomReadOnly: Boolean, chatRoomType: String,
chatRoomLastSeen: Long, isChatRoomReadOnly: Boolean,
isChatRoomSubscribed: Boolean, chatRoomLastSeen: Long,
isChatRoomCreator: Boolean, isChatRoomSubscribed: Boolean,
chatRoomMessage: String) { isChatRoomCreator: Boolean,
activity.startActivity(activity.chatRoomIntent(chatRoomId, chatRoomName, chatRoomType, isChatRoomFavorite: Boolean,
isChatRoomReadOnly, chatRoomLastSeen, isChatRoomSubscribed, isChatRoomCreator, chatRoomMessage)) chatRoomMessage: String
) {
activity.startActivity(
activity.chatRoomIntent(
chatRoomId,
chatRoomName,
chatRoomType,
isChatRoomReadOnly,
chatRoomLastSeen,
isChatRoomSubscribed,
isChatRoomCreator,
isChatRoomFavorite,
chatRoomMessage
)
)
activity.overridePendingTransition(R.anim.open_enter, R.anim.open_exit) activity.overridePendingTransition(R.anim.open_enter, R.anim.open_exit)
} }
} }
\ No newline at end of file
package chat.rocket.android.chatroom.presentation package chat.rocket.android.chatroom.presentation
import android.net.Uri import android.net.Uri
import chat.rocket.android.chatroom.viewmodel.BaseViewModel import chat.rocket.android.chatroom.uimodel.BaseUiModel
import chat.rocket.android.chatroom.viewmodel.suggestion.ChatRoomSuggestionViewModel import chat.rocket.android.chatroom.uimodel.suggestion.ChatRoomSuggestionUiModel
import chat.rocket.android.chatroom.viewmodel.suggestion.CommandSuggestionViewModel import chat.rocket.android.chatroom.uimodel.suggestion.CommandSuggestionUiModel
import chat.rocket.android.chatroom.viewmodel.suggestion.PeopleSuggestionViewModel import chat.rocket.android.chatroom.uimodel.suggestion.PeopleSuggestionUiModel
import chat.rocket.android.core.behaviours.LoadingView import chat.rocket.android.core.behaviours.LoadingView
import chat.rocket.android.core.behaviours.MessageView import chat.rocket.android.core.behaviours.MessageView
import chat.rocket.core.internal.realtime.socket.model.State import chat.rocket.core.internal.realtime.socket.model.State
...@@ -12,12 +12,19 @@ import chat.rocket.core.model.ChatRoom ...@@ -12,12 +12,19 @@ import chat.rocket.core.model.ChatRoom
interface ChatRoomView : LoadingView, MessageView { interface ChatRoomView : LoadingView, MessageView {
/**
* Shows the Favorite/Unfavorite chat room icon.
*
* @param isFavorite Shows the favorite icon if true, otherwise shows the unfavorite icon.
*/
fun showFavoriteIcon(isFavorite: Boolean)
/** /**
* Shows the chat room messages. * Shows the chat room messages.
* *
* @param dataSet The data set to show. * @param dataSet The data set to show.
*/ */
fun showMessages(dataSet: List<BaseViewModel<*>>) fun showMessages(dataSet: List<BaseUiModel<*>>)
/** /**
* Send a message to a chat room. * Send a message to a chat room.
...@@ -31,7 +38,7 @@ interface ChatRoomView : LoadingView, MessageView { ...@@ -31,7 +38,7 @@ interface ChatRoomView : LoadingView, MessageView {
* *
* @param usernameList The list of username to show. * @param usernameList The list of username to show.
*/ */
fun showTypingStatus(usernameList: ArrayList<String>) fun showTypingStatus(usernameList: List<String>)
/** /**
* Hides the typing status view. * Hides the typing status view.
...@@ -60,7 +67,7 @@ interface ChatRoomView : LoadingView, MessageView { ...@@ -60,7 +67,7 @@ interface ChatRoomView : LoadingView, MessageView {
* *
* @param message The (recent) message sent to a chat room. * @param message The (recent) message sent to a chat room.
*/ */
fun showNewMessage(message: List<BaseViewModel<*>>) fun showNewMessage(message: List<BaseUiModel<*>>)
/** /**
* Dispatch to the recycler views adapter that we should remove a message. * Dispatch to the recycler views adapter that we should remove a message.
...@@ -74,7 +81,7 @@ interface ChatRoomView : LoadingView, MessageView { ...@@ -74,7 +81,7 @@ interface ChatRoomView : LoadingView, MessageView {
* *
* @param index The index of the changed message * @param index The index of the changed message
*/ */
fun dispatchUpdateMessage(index: Int, message: List<BaseViewModel<*>>) fun dispatchUpdateMessage(index: Int, message: List<BaseUiModel<*>>)
/** /**
* Show reply status above the message composer. * Show reply status above the message composer.
...@@ -117,9 +124,9 @@ interface ChatRoomView : LoadingView, MessageView { ...@@ -117,9 +124,9 @@ interface ChatRoomView : LoadingView, MessageView {
fun showConnectionState(state: State) fun showConnectionState(state: State)
fun populatePeopleSuggestions(members: List<PeopleSuggestionViewModel>) fun populatePeopleSuggestions(members: List<PeopleSuggestionUiModel>)
fun populateRoomSuggestions(chatRooms: List<ChatRoomSuggestionViewModel>) fun populateRoomSuggestions(chatRooms: List<ChatRoomSuggestionUiModel>)
/** /**
* This user has joined the chat callback. * This user has joined the chat callback.
* *
...@@ -134,7 +141,7 @@ interface ChatRoomView : LoadingView, MessageView { ...@@ -134,7 +141,7 @@ interface ChatRoomView : LoadingView, MessageView {
* *
* @param commands The list of available commands. * @param commands The list of available commands.
*/ */
fun populateCommandSuggestions(commands: List<CommandSuggestionViewModel>) fun populateCommandSuggestions(commands: List<CommandSuggestionUiModel>)
/** /**
* Communicate whether it's a broadcast channel and if current user can post to it. * Communicate whether it's a broadcast channel and if current user can post to it.
......
...@@ -64,7 +64,7 @@ class MessageService : JobService() { ...@@ -64,7 +64,7 @@ class MessageService : JobService() {
Timber.e(ex) Timber.e(ex)
// TODO - remove the generic message when we implement :userId:/message subscription // TODO - remove the generic message when we implement :userId:/message subscription
if (ex is IllegalStateException) { if (ex is IllegalStateException) {
Timber.d(ex, "Probably a read-only problem...") Timber.e(ex, "Probably a read-only problem...")
// TODO: For now we are only going to reschedule when api is fixed. // TODO: For now we are only going to reschedule when api is fixed.
messageRepository.removeById(message.id) messageRepository.removeById(message.id)
jobFinished(params, false) jobFinished(params, false)
......
package chat.rocket.android.chatroom.ui package chat.rocket.android.chatroom.ui
import android.support.design.widget.BaseTransientBottomBar
import android.support.v4.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
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.core.view.setPadding
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.helper.MessageParser import chat.rocket.android.helper.MessageParser
import chat.rocket.android.util.extensions.content import chat.rocket.android.util.extensions.content
import com.google.android.material.snackbar.BaseTransientBottomBar
import kotlinx.android.synthetic.main.message_action_bar.view.*
import ru.noties.markwon.Markwon import ru.noties.markwon.Markwon
class ActionSnackbar : BaseTransientBottomBar<ActionSnackbar> { class ActionSnackbar private constructor(
parentViewGroup: ViewGroup, content:
View, contentViewCallback: com.google.android.material.snackbar.ContentViewCallback
) : BaseTransientBottomBar<ActionSnackbar>(parentViewGroup, content, contentViewCallback) {
companion object { companion object {
fun make(parentViewGroup: ViewGroup, content: String = "", parser: MessageParser): ActionSnackbar { fun make(parentViewGroup: ViewGroup, content: String = "", parser: MessageParser): ActionSnackbar {
val context = parentViewGroup.context val context = parentViewGroup.context
val view = LayoutInflater.from(context).inflate(R.layout.message_action_bar, parentViewGroup, false) val view = LayoutInflater.from(context).inflate(R.layout.message_action_bar, parentViewGroup, false)
val actionSnackbar = ActionSnackbar(parentViewGroup, view, CallbackImpl(view)) val actionSnackbar = ActionSnackbar(parentViewGroup, view, CallbackImpl(view))
actionSnackbar.parser = parser with(view) {
actionSnackbar.messageTextView = view.findViewById(R.id.text_view_action_text) as TextView actionSnackbar.getView().setPadding(0)
actionSnackbar.titleTextView = view.findViewById(R.id.text_view_action_title) as TextView actionSnackbar.getView().setBackgroundColor(ContextCompat.getColor(context, R.color.colorWhite))
actionSnackbar.cancelView = view.findViewById(R.id.image_view_action_cancel_quote) as ImageView actionSnackbar.parser = parser
actionSnackbar.duration = BaseTransientBottomBar.LENGTH_INDEFINITE actionSnackbar.messageTextView = text_view_action_text
val spannable = Markwon.markdown(context, content).trim() actionSnackbar.titleTextView = text_view_action_title
actionSnackbar.messageTextView.content = spannable actionSnackbar.cancelView = image_view_action_cancel_quote
actionSnackbar.duration = BaseTransientBottomBar.LENGTH_INDEFINITE
val spannable = Markwon.markdown(context, content).trim()
actionSnackbar.messageTextView.content = spannable
}
return actionSnackbar return actionSnackbar
} }
} }
...@@ -56,25 +65,22 @@ class ActionSnackbar : BaseTransientBottomBar<ActionSnackbar> { ...@@ -56,25 +65,22 @@ class ActionSnackbar : BaseTransientBottomBar<ActionSnackbar> {
title = "" title = ""
} }
private constructor(parentViewGroup: ViewGroup, content: View, contentViewCallback: BaseTransientBottomBar.ContentViewCallback) : class CallbackImpl(val content: View) : com.google.android.material.snackbar.ContentViewCallback {
super(parentViewGroup, content, contentViewCallback)
class CallbackImpl(val content: View) : BaseTransientBottomBar.ContentViewCallback {
override fun animateContentOut(delay: Int, duration: Int) { override fun animateContentOut(delay: Int, duration: Int) {
ViewCompat.setScaleY(content, 1f) content.scaleY = 1f
ViewCompat.animate(content) ViewCompat.animate(content)
.scaleY(0f) .scaleY(0f)
.setDuration(duration.toLong()) .setDuration(duration.toLong())
.startDelay = delay.toLong() .startDelay = delay.toLong()
} }
override fun animateContentIn(delay: Int, duration: Int) { override fun animateContentIn(delay: Int, duration: Int) {
ViewCompat.setScaleY(content, 0f) content.scaleY = 0f
ViewCompat.animate(content) ViewCompat.animate(content)
.scaleY(1f) .scaleY(1f)
.setDuration(duration.toLong()) .setDuration(duration.toLong())
.startDelay = delay.toLong() .startDelay = delay.toLong()
} }
} }
} }
\ No newline at end of file
...@@ -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
...@@ -27,20 +27,22 @@ fun Context.chatRoomIntent( ...@@ -27,20 +27,22 @@ fun Context.chatRoomIntent(
chatRoomId: String, chatRoomId: String,
chatRoomName: String, chatRoomName: String,
chatRoomType: String, chatRoomType: String,
isChatRoomReadOnly: Boolean, isReadOnly: Boolean,
chatRoomLastSeen: Long, chatRoomLastSeen: Long,
isChatRoomSubscribed: Boolean = true, isSubscribed: Boolean = true,
isChatRoomCreator: Boolean = false, isCreator: Boolean = false,
isFavorite: Boolean = false,
chatRoomMessage: String? = null chatRoomMessage: String? = null
): Intent { ): Intent {
return Intent(this, ChatRoomActivity::class.java).apply { return Intent(this, ChatRoomActivity::class.java).apply {
putExtra(INTENT_CHAT_ROOM_ID, chatRoomId) putExtra(INTENT_CHAT_ROOM_ID, chatRoomId)
putExtra(INTENT_CHAT_ROOM_NAME, chatRoomName) putExtra(INTENT_CHAT_ROOM_NAME, chatRoomName)
putExtra(INTENT_CHAT_ROOM_TYPE, chatRoomType) putExtra(INTENT_CHAT_ROOM_TYPE, chatRoomType)
putExtra(INTENT_CHAT_ROOM_IS_READ_ONLY, isChatRoomReadOnly) putExtra(INTENT_CHAT_ROOM_IS_READ_ONLY, isReadOnly)
putExtra(INTENT_CHAT_ROOM_LAST_SEEN, chatRoomLastSeen) putExtra(INTENT_CHAT_ROOM_LAST_SEEN, chatRoomLastSeen)
putExtra(INTENT_CHAT_IS_SUBSCRIBED, isChatRoomSubscribed) putExtra(INTENT_CHAT_IS_SUBSCRIBED, isSubscribed)
putExtra(INTENT_CHAT_ROOM_IS_CREATOR, isChatRoomCreator) putExtra(INTENT_CHAT_ROOM_IS_CREATOR, isCreator)
putExtra(INTENT_CHAT_ROOM_IS_FAVORITE, isFavorite)
putExtra(INTENT_CHAT_ROOM_MESSAGE, chatRoomMessage) putExtra(INTENT_CHAT_ROOM_MESSAGE, chatRoomMessage)
} }
} }
...@@ -50,6 +52,7 @@ private const val INTENT_CHAT_ROOM_NAME = "chat_room_name" ...@@ -50,6 +52,7 @@ private const val INTENT_CHAT_ROOM_NAME = "chat_room_name"
private const val INTENT_CHAT_ROOM_TYPE = "chat_room_type" private const val INTENT_CHAT_ROOM_TYPE = "chat_room_type"
private const val INTENT_CHAT_ROOM_IS_READ_ONLY = "chat_room_is_read_only" private const val INTENT_CHAT_ROOM_IS_READ_ONLY = "chat_room_is_read_only"
private const val INTENT_CHAT_ROOM_IS_CREATOR = "chat_room_is_creator" private const val INTENT_CHAT_ROOM_IS_CREATOR = "chat_room_is_creator"
private const val INTENT_CHAT_ROOM_IS_FAVORITE = "chat_room_is_favorite"
private const val INTENT_CHAT_ROOM_LAST_SEEN = "chat_room_last_seen" private const val INTENT_CHAT_ROOM_LAST_SEEN = "chat_room_last_seen"
private const val INTENT_CHAT_IS_SUBSCRIBED = "is_chat_room_subscribed" private const val INTENT_CHAT_IS_SUBSCRIBED = "is_chat_room_subscribed"
private const val INTENT_CHAT_ROOM_MESSAGE = "chat_room_message" private const val INTENT_CHAT_ROOM_MESSAGE = "chat_room_message"
...@@ -89,13 +92,15 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector { ...@@ -89,13 +92,15 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector {
val chatRoomType = intent.getStringExtra(INTENT_CHAT_ROOM_TYPE) val chatRoomType = intent.getStringExtra(INTENT_CHAT_ROOM_TYPE)
requireNotNull(chatRoomType) { "no chat_room_type provided in Intent extras" } requireNotNull(chatRoomType) { "no chat_room_type provided in Intent extras" }
val isChatRoomReadOnly = intent.getBooleanExtra(INTENT_CHAT_ROOM_IS_READ_ONLY, true) val isReadOnly = intent.getBooleanExtra(INTENT_CHAT_ROOM_IS_READ_ONLY, true)
val isChatRoomCreator = intent.getBooleanExtra(INTENT_CHAT_ROOM_IS_CREATOR, false) val isCreator = intent.getBooleanExtra(INTENT_CHAT_ROOM_IS_CREATOR, false)
val isFavorite = intent.getBooleanExtra(INTENT_CHAT_ROOM_IS_FAVORITE, false)
val chatRoomLastSeen = intent.getLongExtra(INTENT_CHAT_ROOM_LAST_SEEN, -1) val chatRoomLastSeen = intent.getLongExtra(INTENT_CHAT_ROOM_LAST_SEEN, -1)
val isChatRoomSubscribed = intent.getBooleanExtra(INTENT_CHAT_IS_SUBSCRIBED, true) val isSubscribed = intent.getBooleanExtra(INTENT_CHAT_IS_SUBSCRIBED, true)
val chatRoomMessage = intent.getStringExtra(INTENT_CHAT_ROOM_MESSAGE) val chatRoomMessage = intent.getStringExtra(INTENT_CHAT_ROOM_MESSAGE)
...@@ -104,8 +109,15 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector { ...@@ -104,8 +109,15 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector {
if (supportFragmentManager.findFragmentByTag(TAG_CHAT_ROOM_FRAGMENT) == null) { if (supportFragmentManager.findFragmentByTag(TAG_CHAT_ROOM_FRAGMENT) == null) {
addFragment(TAG_CHAT_ROOM_FRAGMENT, R.id.fragment_container) { addFragment(TAG_CHAT_ROOM_FRAGMENT, R.id.fragment_container) {
newInstance( newInstance(
chatRoomId, chatRoomName, chatRoomType, isChatRoomReadOnly, chatRoomLastSeen, chatRoomId,
isChatRoomSubscribed, isChatRoomCreator, chatRoomMessage chatRoomName,
chatRoomType,
isReadOnly,
chatRoomLastSeen,
isSubscribed,
isCreator,
isFavorite,
chatRoomMessage
) )
} }
} }
......
package chat.rocket.android.chatroom.ui package chat.rocket.android.chatroom.ui
import android.Manifest
import android.app.Activity import android.app.Activity
import android.content.ClipData import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Environment
import android.os.Handler import android.os.Handler
import android.support.annotation.DrawableRes import android.support.annotation.DrawableRes
import android.support.v4.app.Fragment import android.support.v4.app.Fragment
...@@ -30,8 +26,6 @@ import chat.rocket.android.chatroom.viewmodel.MessageViewModel ...@@ -30,8 +26,6 @@ import chat.rocket.android.chatroom.viewmodel.MessageViewModel
import chat.rocket.android.chatroom.viewmodel.suggestion.ChatRoomSuggestionViewModel import chat.rocket.android.chatroom.viewmodel.suggestion.ChatRoomSuggestionViewModel
import chat.rocket.android.chatroom.viewmodel.suggestion.CommandSuggestionViewModel import chat.rocket.android.chatroom.viewmodel.suggestion.CommandSuggestionViewModel
import chat.rocket.android.chatroom.viewmodel.suggestion.PeopleSuggestionViewModel import chat.rocket.android.chatroom.viewmodel.suggestion.PeopleSuggestionViewModel
import chat.rocket.android.draw.DrawingActivity
import chat.rocket.android.helper.AndroidPermissionsHelper
import chat.rocket.android.helper.EndlessRecyclerViewScrollListener import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import chat.rocket.android.helper.KeyboardHelper import chat.rocket.android.helper.KeyboardHelper
import chat.rocket.android.helper.MessageParser import chat.rocket.android.helper.MessageParser
...@@ -47,9 +41,6 @@ import kotlinx.android.synthetic.main.fragment_chat_room.* ...@@ -47,9 +41,6 @@ import kotlinx.android.synthetic.main.fragment_chat_room.*
import kotlinx.android.synthetic.main.message_attachment_options.* import kotlinx.android.synthetic.main.message_attachment_options.*
import kotlinx.android.synthetic.main.message_composer.* import kotlinx.android.synthetic.main.message_composer.*
import kotlinx.android.synthetic.main.message_list.* import kotlinx.android.synthetic.main.message_list.*
import java.io.File
import java.io.FileOutputStream
import java.util.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import javax.inject.Inject import javax.inject.Inject
...@@ -83,7 +74,6 @@ private const val BUNDLE_CHAT_ROOM_NAME = "chat_room_name" ...@@ -83,7 +74,6 @@ private const val BUNDLE_CHAT_ROOM_NAME = "chat_room_name"
private const val BUNDLE_CHAT_ROOM_TYPE = "chat_room_type" private const val BUNDLE_CHAT_ROOM_TYPE = "chat_room_type"
private const val BUNDLE_IS_CHAT_ROOM_READ_ONLY = "is_chat_room_read_only" private const val BUNDLE_IS_CHAT_ROOM_READ_ONLY = "is_chat_room_read_only"
private const val REQUEST_CODE_FOR_PERFORM_SAF = 42 private const val REQUEST_CODE_FOR_PERFORM_SAF = 42
private const val REQUEST_CODE_FOR_DRAW = 101
private const val BUNDLE_CHAT_ROOM_LAST_SEEN = "chat_room_last_seen" private const val BUNDLE_CHAT_ROOM_LAST_SEEN = "chat_room_last_seen"
private const val BUNDLE_CHAT_ROOM_IS_SUBSCRIBED = "chat_room_is_subscribed" private const val BUNDLE_CHAT_ROOM_IS_SUBSCRIBED = "chat_room_is_subscribed"
private const val BUNDLE_CHAT_ROOM_IS_CREATOR = "chat_room_is_creator" private const val BUNDLE_CHAT_ROOM_IS_CREATOR = "chat_room_is_creator"
...@@ -197,34 +187,13 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -197,34 +187,13 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
if (resultData != null && resultCode == Activity.RESULT_OK) { if (requestCode == REQUEST_CODE_FOR_PERFORM_SAF && resultCode == Activity.RESULT_OK) {
when(requestCode){ if (resultData != null) {
REQUEST_CODE_FOR_PERFORM_SAF -> { uploadFile(resultData.data)
uploadFile(resultData.data)
}
REQUEST_CODE_FOR_DRAW -> {
val result= resultData.getByteArrayExtra("bitmap")
val bitmap = BitmapFactory.decodeByteArray(result, 0, result.size)
val uri = saveImage(bitmap)
uploadFile(uri)
}
} }
} }
} }
private fun saveImage(bitmap: Bitmap): Uri {
val imageDir = "${Environment.DIRECTORY_PICTURES}/Rocket.Chat Images/"
val path = Environment.getExternalStoragePublicDirectory(imageDir)
val file = File(path, UUID.randomUUID().toString()+".png")
path.mkdirs()
file.createNewFile()
val outputStream = FileOutputStream(file)
bitmap.compress(Bitmap.CompressFormat.PNG,100,outputStream)
outputStream.flush()
outputStream.close()
return Uri.fromFile(file)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater) super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.chatroom_actions, menu) inflater.inflate(R.menu.chatroom_actions, menu)
...@@ -739,30 +708,6 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -739,30 +708,6 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
button_add_reaction.setOnClickListener { view -> button_add_reaction.setOnClickListener { view ->
openEmojiKeyboardPopup() openEmojiKeyboardPopup()
} }
button_drawing.setOnClickListener {
if (!canWriteToExternalStorage()) {
checkWritingPermission()
}else{
val intent = Intent(activity, DrawingActivity::class.java)
startActivityForResult(intent, REQUEST_CODE_FOR_DRAW)
}
handler.postDelayed({
hideAttachmentOptions()
}, 400)
}
}
}
private fun canWriteToExternalStorage(): Boolean {
return context?.let { AndroidPermissionsHelper.checkPermission(it, Manifest.permission.WRITE_EXTERNAL_STORAGE) }!!
}
private fun checkWritingPermission() {
activity?.let {
AndroidPermissionsHelper.requestPermission(it,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
AndroidPermissionsHelper.WRITE_EXTERNAL_STORAGE_CODE)
} }
} }
......
package chat.rocket.android.chatroom.ui.bottomsheet
import android.support.design.widget.BottomSheetDialog
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.view.MenuItem
import ru.whalemare.sheetmenu.SheetMenu
import ru.whalemare.sheetmenu.adapter.MenuAdapter
class BottomSheetMenu(adapter: MenuAdapter) : SheetMenu(adapter = adapter) {
override fun processRecycler(recycler: RecyclerView, dialog: BottomSheetDialog) {
if (layoutManager == null) {
layoutManager = LinearLayoutManager(recycler.context, LinearLayoutManager.VERTICAL, false)
}
// Superclass SheetMenu adapter property is nullable MenuAdapter? but this class enforces
// passing one at the constructor, so we assume it's always non-null.
val adapter = adapter!!
val callback = adapter.callback
adapter.callback = MenuItem.OnMenuItemClickListener {
callback?.onMenuItemClick(it)
dialog.cancel()
true
}
recycler.adapter = adapter
recycler.layoutManager = layoutManager
}
}
\ No newline at end of file
package chat.rocket.android.chatroom.ui.bottomsheet
import android.os.Bundle
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kotlinx.android.synthetic.main.message_action_item.view.*
import kotlinx.android.synthetic.main.message_bottomsheet.*
class MessageActionsBottomSheet : BottomSheetDialogFragment() {
private lateinit var adapter: MessageActionAdapter
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return inflater.inflate(R.layout.message_bottomsheet, container, false)
}
fun addItems(items: List<MenuItem>, itemClickListener: MenuItem.OnMenuItemClickListener) {
adapter = MessageActionAdapter()
adapter.addItems(items, ActionItemClickListener(dismissAction = { dismiss() },
itemClickListener = itemClickListener))
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
bottomsheet_recycler_view.layoutManager = LinearLayoutManager(context)
bottomsheet_recycler_view.adapter = adapter
}
private class ActionItemClickListener(
val dismissAction: () -> Unit,
val itemClickListener: MenuItem.OnMenuItemClickListener
)
private class MessageActionAdapter : RecyclerView.Adapter<MessageActionViewHolder>() {
private lateinit var itemClickListener: ActionItemClickListener
private val menuItems = mutableListOf<MenuItem>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MessageActionViewHolder {
return MessageActionViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.message_action_item, parent, false)
)
}
override fun getItemCount() = menuItems.size
override fun onBindViewHolder(holder: MessageActionViewHolder, position: Int) {
holder.bind(menuItems[position], itemClickListener)
}
fun addItems(items: List<MenuItem>, itemClickListener: ActionItemClickListener) {
this.itemClickListener = itemClickListener
menuItems.clear()
menuItems.addAll(items)
}
}
private class MessageActionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(item: MenuItem, itemClickListener: ActionItemClickListener) {
with(itemView) {
message_action_title.text = item.title
message_action_icon.setImageDrawable(item.icon)
setOnClickListener {
itemClickListener.itemClickListener.onMenuItemClick(item)
itemClickListener.dismissAction.invoke()
}
}
}
}
}
\ No newline at end of file
package chat.rocket.android.chatroom.ui.bottomsheet.adapter
import android.view.MenuItem
import chat.rocket.android.R
import chat.rocket.android.util.extensions.setVisible
/**
* An adapter for bottomsheet menu that lists all the actions that could be taken over a chat message.
*/
class ActionListAdapter(
menuItems: List<MenuItem> = emptyList(),
callback: MenuItem.OnMenuItemClickListener
) : ListBottomSheetAdapter(menuItems = menuItems, callback = callback) {
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = menuItems[position]
if (showIcons) {
holder.imageIcon.setVisible(item.icon != null)
} else {
holder.imageIcon.setVisible(false)
}
holder.imageIcon.setImageDrawable(item.icon)
holder.textTitle.text = item.title
holder.itemView.setOnClickListener {
callback?.onMenuItemClick(item)
}
val deleteTextColor = holder.itemView.context.resources.getColor(R.color.colorRed)
val color = if (item.itemId == R.id.action_message_delete) {
deleteTextColor
} else {
textColors.get(item.itemId)
}
holder.textTitle.setTextColor(color)
}
}
\ No newline at end of file
package chat.rocket.android.chatroom.ui.bottomsheet.adapter
import android.graphics.Color
import android.support.annotation.ColorInt
import android.support.annotation.IdRes
import android.util.SparseIntArray
import android.view.MenuItem
import chat.rocket.android.R
import ru.whalemare.sheetmenu.adapter.MenuAdapter
/**
* A regular bottomsheet adapter with added possibility to hide or show a menu item given its item id.
* Also added the possibility to change text colors for the menu items.
*/
open class ListBottomSheetAdapter(menuItems: List<MenuItem> = emptyList(), callback: MenuItem.OnMenuItemClickListener) :
MenuAdapter(menuItems = menuItems, callback = callback, itemLayoutId = R.layout.item_linear, showIcons = true) {
// Maps menu item ids to colors.
protected val textColors: SparseIntArray = SparseIntArray(menuItems.size)
init {
for (item in menuItems) {
textColors.put(item.itemId, Color.BLACK)
}
}
/**
* Hide a menu item and disable it.
*
* @param itemId The id of the menu item to disable and hide.
*/
fun hideMenuItem(@IdRes itemId: Int) {
menuItems.firstOrNull { it.itemId == itemId }?.apply {
setVisible(false)
setEnabled(false)
}
}
/**
* Show a menu item and enable it.
*
* @param itemId The id of the menu item to enable and show.
*/
fun showMenuItem(@IdRes itemId: Int) {
menuItems.firstOrNull { it.itemId == itemId }?.apply {
setVisible(true)
setEnabled(true)
}
}
/**
* Change a menu item text color given by its id to the given color.
*
* @param itemId The id of menu item.
* @param color The color (not the resource color id) of the menu item.
*/
fun setMenuItemTextColor(@IdRes itemId: Int, @ColorInt color: Int) {
val itemIndex = menuItems.indexOfFirst { it.itemId == itemId }
if (itemIndex > -1) {
textColors.put(itemId, color)
}
}
}
\ No newline at end of file
package chat.rocket.android.chatroom.viewmodel package chat.rocket.android.chatroom.uimodel
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
import chat.rocket.core.model.attachment.AudioAttachment import chat.rocket.core.model.attachment.AudioAttachment
data class AudioAttachmentViewModel( data class AudioAttachmentUiModel(
override val message: Message, override val message: Message,
override val rawData: AudioAttachment, override val rawData: AudioAttachment,
override val messageId: String, override val messageId: String,
override val attachmentUrl: String, override val attachmentUrl: String,
override val attachmentTitle: CharSequence, override val attachmentTitle: CharSequence,
override val id: Long, override val id: Long,
override var reactions: List<ReactionViewModel>, override var reactions: List<ReactionUiModel>,
override var nextDownStreamMessage: BaseViewModel<*>? = null, override var nextDownStreamMessage: BaseUiModel<*>? = null,
override var preview: Message? = null, override var preview: Message? = null,
override var isTemporary: Boolean = false override var isTemporary: Boolean = false
) : BaseFileAttachmentViewModel<AudioAttachment> { ) : BaseFileAttachmentUiModel<AudioAttachment> {
override val viewType: Int override val viewType: Int
get() = BaseViewModel.ViewType.AUDIO_ATTACHMENT.viewType get() = BaseUiModel.ViewType.AUDIO_ATTACHMENT.viewType
override val layoutId: Int override val layoutId: Int
get() = R.layout.message_attachment get() = R.layout.message_attachment
} }
\ No newline at end of file
package chat.rocket.android.chatroom.viewmodel package chat.rocket.android.chatroom.uimodel
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
import chat.rocket.core.model.attachment.AuthorAttachment import chat.rocket.core.model.attachment.AuthorAttachment
data class AuthorAttachmentViewModel( data class AuthorAttachmentUiModel(
override val attachmentUrl: String, override val attachmentUrl: String,
val id: Long, val id: Long,
val name: CharSequence?, val name: CharSequence?,
...@@ -13,13 +13,13 @@ data class AuthorAttachmentViewModel( ...@@ -13,13 +13,13 @@ data class AuthorAttachmentViewModel(
override val message: Message, override val message: Message,
override val rawData: AuthorAttachment, override val rawData: AuthorAttachment,
override val messageId: String, override val messageId: String,
override var reactions: List<ReactionViewModel>, override var reactions: List<ReactionUiModel>,
override var nextDownStreamMessage: BaseViewModel<*>? = null, override var nextDownStreamMessage: BaseUiModel<*>? = null,
override var preview: Message? = null, override var preview: Message? = null,
override var isTemporary: Boolean = false override var isTemporary: Boolean = false
) : BaseAttachmentViewModel<AuthorAttachment> { ) : BaseAttachmentUiModel<AuthorAttachment> {
override val viewType: Int override val viewType: Int
get() = BaseViewModel.ViewType.AUTHOR_ATTACHMENT.viewType get() = BaseUiModel.ViewType.AUTHOR_ATTACHMENT.viewType
override val layoutId: Int override val layoutId: Int
get() = R.layout.item_author_attachment get() = R.layout.item_author_attachment
} }
\ No newline at end of file
package chat.rocket.android.chatroom.uimodel
interface BaseAttachmentUiModel<out T> : BaseUiModel<T> {
val attachmentUrl: String
}
\ No newline at end of file
package chat.rocket.android.chatroom.viewmodel package chat.rocket.android.chatroom.uimodel
interface BaseFileAttachmentViewModel<out T> : BaseAttachmentViewModel<T> { interface BaseFileAttachmentUiModel<out T> : BaseAttachmentUiModel<T> {
val attachmentTitle: CharSequence val attachmentTitle: CharSequence
val id: Long val id: Long
} }
\ No newline at end of file
package chat.rocket.android.chatroom.viewmodel package chat.rocket.android.chatroom.uimodel
interface BaseMessageViewModel<out T> : BaseViewModel<T> { interface BaseMessageUiModel<out T> : BaseUiModel<T> {
val avatar: String val avatar: String
val time: CharSequence val time: CharSequence
val senderName: CharSequence val senderName: CharSequence
......
package chat.rocket.android.chatroom.viewmodel package chat.rocket.android.chatroom.uimodel
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
import java.security.InvalidParameterException import java.security.InvalidParameterException
interface BaseViewModel<out T> { interface BaseUiModel<out T> {
val message: Message val message: Message
val rawData: T val rawData: T
val messageId: String val messageId: String
val viewType: Int val viewType: Int
val layoutId: Int val layoutId: Int
var reactions: List<ReactionViewModel> var reactions: List<ReactionUiModel>
var nextDownStreamMessage: BaseViewModel<*>? var nextDownStreamMessage: BaseUiModel<*>?
var preview: Message? var preview: Message?
var isTemporary: Boolean var isTemporary: Boolean
...@@ -29,7 +29,7 @@ interface BaseViewModel<out T> { ...@@ -29,7 +29,7 @@ interface BaseViewModel<out T> {
} }
} }
internal fun Int.toViewType(): BaseViewModel.ViewType { internal fun Int.toViewType(): BaseUiModel.ViewType {
return BaseViewModel.ViewType.values().firstOrNull { it.viewType == this } return BaseUiModel.ViewType.values().firstOrNull { it.viewType == this }
?: throw InvalidParameterException("Invalid viewType: $this for BaseViewModel.ViewType") ?: throw InvalidParameterException("Invalid viewType: $this for BaseUiModel.ViewType")
} }
\ No newline at end of file
package chat.rocket.android.chatroom.uimodel
import chat.rocket.android.R
import chat.rocket.core.model.Message
import chat.rocket.core.model.attachment.ColorAttachment
data class ColorAttachmentUiModel(
override val attachmentUrl: String,
val id: Long,
val color: Int,
val text: CharSequence,
override val message: Message,
override val rawData: ColorAttachment,
override val messageId: String,
override var reactions: List<ReactionUiModel>,
override var nextDownStreamMessage: BaseUiModel<*>? = null,
override var preview: Message? = null,
override var isTemporary: Boolean = false
) : BaseAttachmentUiModel<ColorAttachment> {
override val viewType: Int
get() = BaseUiModel.ViewType.COLOR_ATTACHMENT.viewType
override val layoutId: Int
get() = R.layout.item_color_attachment
}
\ No newline at end of file
package chat.rocket.android.chatroom.viewmodel package chat.rocket.android.chatroom.uimodel
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
import chat.rocket.core.model.attachment.GenericFileAttachment import chat.rocket.core.model.attachment.GenericFileAttachment
import chat.rocket.core.model.attachment.ImageAttachment
data class GenericFileAttachmentViewModel( data class GenericFileAttachmentUiModel(
override val message: Message, override val message: Message,
override val rawData: GenericFileAttachment, override val rawData: GenericFileAttachment,
override val messageId: String, override val messageId: String,
override val attachmentUrl: String, override val attachmentUrl: String,
override val attachmentTitle: CharSequence, override val attachmentTitle: CharSequence,
override val id: Long, override val id: Long,
override var reactions: List<ReactionViewModel>, override var reactions: List<ReactionUiModel>,
override var nextDownStreamMessage: BaseViewModel<*>? = null, override var nextDownStreamMessage: BaseUiModel<*>? = null,
override var preview: Message? = null, override var preview: Message? = null,
override var isTemporary: Boolean = false override var isTemporary: Boolean = false
) : BaseFileAttachmentViewModel<GenericFileAttachment> { ) : BaseFileAttachmentUiModel<GenericFileAttachment> {
override val viewType: Int override val viewType: Int
get() = BaseViewModel.ViewType.GENERIC_FILE_ATTACHMENT.viewType get() = BaseUiModel.ViewType.GENERIC_FILE_ATTACHMENT.viewType
override val layoutId: Int override val layoutId: Int
get() = R.layout.item_file_attachment get() = R.layout.item_file_attachment
} }
\ No newline at end of file
package chat.rocket.android.chatroom.viewmodel package chat.rocket.android.chatroom.uimodel
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
import chat.rocket.core.model.attachment.ImageAttachment import chat.rocket.core.model.attachment.ImageAttachment
data class ImageAttachmentViewModel( data class ImageAttachmentUiModel(
override val message: Message, override val message: Message,
override val rawData: ImageAttachment, override val rawData: ImageAttachment,
override val messageId: String, override val messageId: String,
override val attachmentUrl: String, override val attachmentUrl: String,
override val attachmentTitle: CharSequence, override val attachmentTitle: CharSequence,
override val id: Long, override val id: Long,
override var reactions: List<ReactionViewModel>, override var reactions: List<ReactionUiModel>,
override var nextDownStreamMessage: BaseViewModel<*>? = null, override var nextDownStreamMessage: BaseUiModel<*>? = null,
override var preview: Message? = null, override var preview: Message? = null,
override var isTemporary: Boolean = false override var isTemporary: Boolean = false
) : BaseFileAttachmentViewModel<ImageAttachment> { ) : BaseFileAttachmentUiModel<ImageAttachment> {
override val viewType: Int override val viewType: Int
get() = BaseViewModel.ViewType.IMAGE_ATTACHMENT.viewType get() = BaseUiModel.ViewType.IMAGE_ATTACHMENT.viewType
override val layoutId: Int override val layoutId: Int
get() = R.layout.message_attachment get() = R.layout.message_attachment
} }
\ No newline at end of file
package chat.rocket.android.chatroom.viewmodel package chat.rocket.android.chatroom.uimodel
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
data class MessageAttachmentViewModel( data class MessageAttachmentUiModel(
override val message: Message, override val message: Message,
override val rawData: Message, override val rawData: Message,
override val messageId: String, override val messageId: String,
...@@ -11,14 +11,14 @@ data class MessageAttachmentViewModel( ...@@ -11,14 +11,14 @@ data class MessageAttachmentViewModel(
val time: CharSequence?, val time: CharSequence?,
val content: CharSequence, val content: CharSequence,
val isPinned: Boolean, val isPinned: Boolean,
override var reactions: List<ReactionViewModel>, override var reactions: List<ReactionUiModel>,
override var nextDownStreamMessage: BaseViewModel<*>? = null, override var nextDownStreamMessage: BaseUiModel<*>? = null,
var messageLink: String? = null, var messageLink: String? = null,
override var preview: Message? = null, override var preview: Message? = null,
override var isTemporary: Boolean = false override var isTemporary: Boolean = false
) : BaseViewModel<Message> { ) : BaseUiModel<Message> {
override val viewType: Int override val viewType: Int
get() = BaseViewModel.ViewType.MESSAGE_ATTACHMENT.viewType get() = BaseUiModel.ViewType.MESSAGE_ATTACHMENT.viewType
override val layoutId: Int override val layoutId: Int
get() = R.layout.item_message_attachment get() = R.layout.item_message_attachment
......
package chat.rocket.android.chatroom.viewmodel package chat.rocket.android.chatroom.uimodel
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.chatroom.domain.MessageReply import chat.rocket.android.chatroom.domain.MessageReply
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
data class MessageReplyViewModel( data class MessageReplyUiModel(
override val rawData: MessageReply, override val rawData: MessageReply,
override val messageId: String, override val messageId: String,
override var reactions: List<ReactionViewModel>, override var reactions: List<ReactionUiModel>,
override var nextDownStreamMessage: BaseViewModel<*>?, override var nextDownStreamMessage: BaseUiModel<*>?,
override var preview: Message?, override var preview: Message?,
override var isTemporary: Boolean = false, override var isTemporary: Boolean = false,
override val message: Message override val message: Message
) : BaseViewModel<MessageReply> { ) : BaseUiModel<MessageReply> {
override val viewType: Int override val viewType: Int
get() = BaseViewModel.ViewType.MESSAGE_REPLY.viewType get() = BaseUiModel.ViewType.MESSAGE_REPLY.viewType
override val layoutId: Int override val layoutId: Int
get() = R.layout.item_message_reply get() = R.layout.item_message_reply
} }
\ No newline at end of file
package chat.rocket.android.chatroom.viewmodel package chat.rocket.android.chatroom.uimodel
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
data class MessageViewModel( data class MessageUiModel(
override val message: Message, override val message: Message,
override val rawData: Message, override val rawData: Message,
override val messageId: String, override val messageId: String,
...@@ -12,14 +12,14 @@ data class MessageViewModel( ...@@ -12,14 +12,14 @@ data class MessageViewModel(
override val senderName: CharSequence, override val senderName: CharSequence,
override val content: CharSequence, override val content: CharSequence,
override val isPinned: Boolean, override val isPinned: Boolean,
override var reactions: List<ReactionViewModel>, override var reactions: List<ReactionUiModel>,
override var nextDownStreamMessage: BaseViewModel<*>? = null, override var nextDownStreamMessage: BaseUiModel<*>? = null,
override var preview: Message? = null, override var preview: Message? = null,
var isFirstUnread: Boolean, var isFirstUnread: Boolean,
override var isTemporary: Boolean = false override var isTemporary: Boolean = false
) : BaseMessageViewModel<Message> { ) : BaseMessageUiModel<Message> {
override val viewType: Int override val viewType: Int
get() = BaseViewModel.ViewType.MESSAGE.viewType get() = BaseUiModel.ViewType.MESSAGE.viewType
override val layoutId: Int override val layoutId: Int
get() = R.layout.item_message get() = R.layout.item_message
......
package chat.rocket.android.chatroom.uimodel
data class ReactionUiModel(
val messageId: String,
val shortname: String,
val unicode: CharSequence,
val count: Int,
val usernames: List<String> = emptyList()
)
\ No newline at end of file
package chat.rocket.android.chatroom.viewmodel package chat.rocket.android.chatroom.uimodel
import chat.rocket.core.model.ChatRoomRole import chat.rocket.core.model.ChatRoomRole
data class RoomViewModel( data class RoomUiModel(
val roles: List<ChatRoomRole>, val roles: List<ChatRoomRole>,
val isBroadcast: Boolean = false, val isBroadcast: Boolean = false,
val isRoom: Boolean = false val isRoom: Boolean = false
......
package chat.rocket.android.chatroom.viewmodel package chat.rocket.android.chatroom.uimodel
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
import chat.rocket.core.model.url.Url import chat.rocket.core.model.url.Url
data class UrlPreviewViewModel( data class UrlPreviewUiModel(
override val message: Message, override val message: Message,
override val rawData: Url, override val rawData: Url,
override val messageId: String, override val messageId: String,
...@@ -12,13 +12,13 @@ data class UrlPreviewViewModel( ...@@ -12,13 +12,13 @@ data class UrlPreviewViewModel(
val hostname: String, val hostname: String,
val description: CharSequence?, val description: CharSequence?,
val thumbUrl: String?, val thumbUrl: String?,
override var reactions: List<ReactionViewModel>, override var reactions: List<ReactionUiModel>,
override var nextDownStreamMessage: BaseViewModel<*>? = null, override var nextDownStreamMessage: BaseUiModel<*>? = null,
override var preview: Message? = null, override var preview: Message? = null,
override var isTemporary: Boolean = false override var isTemporary: Boolean = false
) : BaseViewModel<Url> { ) : BaseUiModel<Url> {
override val viewType: Int override val viewType: Int
get() = BaseViewModel.ViewType.URL_PREVIEW.viewType get() = BaseUiModel.ViewType.URL_PREVIEW.viewType
override val layoutId: Int override val layoutId: Int
get() = R.layout.message_url_preview get() = R.layout.message_url_preview
} }
\ No newline at end of file
package chat.rocket.android.chatroom.viewmodel package chat.rocket.android.chatroom.uimodel
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
import chat.rocket.core.model.attachment.VideoAttachment import chat.rocket.core.model.attachment.VideoAttachment
data class VideoAttachmentViewModel( data class VideoAttachmentUiModel(
override val message: Message, override val message: Message,
override val rawData: VideoAttachment, override val rawData: VideoAttachment,
override val messageId: String, override val messageId: String,
override val attachmentUrl: String, override val attachmentUrl: String,
override val attachmentTitle: CharSequence, override val attachmentTitle: CharSequence,
override val id: Long, override val id: Long,
override var reactions: List<ReactionViewModel>, override var reactions: List<ReactionUiModel>,
override var nextDownStreamMessage: BaseViewModel<*>? = null, override var nextDownStreamMessage: BaseUiModel<*>? = null,
override var preview: Message? = null, override var preview: Message? = null,
override var isTemporary: Boolean = false override var isTemporary: Boolean = false
) : BaseFileAttachmentViewModel<VideoAttachment> { ) : BaseFileAttachmentUiModel<VideoAttachment> {
override val viewType: Int override val viewType: Int
get() = BaseViewModel.ViewType.VIDEO_ATTACHMENT.viewType get() = BaseUiModel.ViewType.VIDEO_ATTACHMENT.viewType
override val layoutId: Int override val layoutId: Int
get() = R.layout.message_attachment get() = R.layout.message_attachment
} }
\ No newline at end of file
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.
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.
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