Unverified Commit 68a1626c authored by Rafael Kellermann Streit's avatar Rafael Kellermann Streit Committed by GitHub

Merge pull request #1490 from RocketChat/beta

[RELEASE] Merge BETA into MASTER
parents 1bb668cb 8dd30e58
...@@ -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.
......
...@@ -3,7 +3,6 @@ apply plugin: 'io.fabric' ...@@ -3,7 +3,6 @@ apply plugin: 'io.fabric'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-kapt'
apply plugin: 'realm-android'
android { android {
compileSdkVersion versions.compileSdk compileSdkVersion versions.compileSdk
...@@ -11,12 +10,18 @@ android { ...@@ -11,12 +10,18 @@ android {
defaultConfig { defaultConfig {
applicationId "chat.rocket.android" applicationId "chat.rocket.android"
minSdkVersion 21 minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk targetSdkVersion versions.targetSdk
versionCode 2027 versionCode 2034
versionName "2.4.0" versionName "2.5.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true multiDexEnabled true
javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
}
}
} }
signingConfigs { signingConfigs {
...@@ -62,6 +67,10 @@ dependencies { ...@@ -62,6 +67,10 @@ dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation project(':player') implementation project(':player')
implementation project(':emoji')
implementation project(':draw')
implementation project(':util')
implementation project(':core')
implementation libraries.kotlin implementation libraries.kotlin
implementation libraries.coroutines implementation libraries.coroutines
...@@ -69,11 +78,11 @@ dependencies { ...@@ -69,11 +78,11 @@ dependencies {
implementation libraries.appCompat implementation libraries.appCompat
implementation libraries.recyclerview implementation libraries.recyclerview
implementation libraries.design implementation libraries.material
implementation libraries.constraintLayout implementation libraries.constraintlayout
implementation libraries.cardView implementation libraries.cardview
implementation libraries.flexbox implementation libraries.flexbox
implementation libraries.customTabs implementation libraries.browser
implementation libraries.androidKtx implementation libraries.androidKtx
...@@ -87,7 +96,6 @@ dependencies { ...@@ -87,7 +96,6 @@ dependencies {
implementation libraries.room implementation libraries.room
kapt libraries.roomProcessor kapt libraries.roomProcessor
implementation libraries.roomRxjava
implementation libraries.lifecycleExtensions implementation libraries.lifecycleExtensions
kapt libraries.lifecycleCompiler kapt libraries.lifecycleCompiler
...@@ -100,7 +108,6 @@ dependencies { ...@@ -100,7 +108,6 @@ dependencies {
implementation libraries.timber implementation libraries.timber
implementation libraries.threeTenABP implementation libraries.threeTenABP
implementation libraries.rxBinding
implementation libraries.fresco implementation libraries.fresco
api libraries.frescoOkHttp api libraries.frescoOkHttp
...@@ -115,11 +122,11 @@ dependencies { ...@@ -115,11 +122,11 @@ dependencies {
implementation libraries.markwon implementation libraries.markwon
implementation libraries.sheetMenu
implementation libraries.aVLoadingIndicatorView implementation libraries.aVLoadingIndicatorView
implementation('com.crashlytics.sdk.android:crashlytics:2.6.8@aar') { implementation "com.github.luciofm:livedata-ktx:b1e8bbc25a"
implementation('com.crashlytics.sdk.android:crashlytics:2.9.2@aar') {
transitive = true transitive = true
} }
......
{
"formatVersion": 1,
"database": {
"version": 3,
"identityHash": "06359a8c2943365dd094bc5dff210203",
"entities": [
{
"tableName": "users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `username` TEXT, `name` TEXT, `status` TEXT NOT NULL, `utcOffset` REAL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "username",
"columnName": "username",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "status",
"columnName": "status",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "utcOffset",
"columnName": "utcOffset",
"affinity": "REAL",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_users_username",
"unique": false,
"columnNames": [
"username"
],
"createSql": "CREATE INDEX `index_users_username` ON `${TABLE_NAME}` (`username`)"
}
],
"foreignKeys": []
},
{
"tableName": "chatrooms",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `subscriptionId` TEXT NOT NULL, `type` TEXT NOT NULL, `name` TEXT NOT NULL, `fullname` TEXT, `userId` TEXT, `ownerId` TEXT, `readonly` INTEGER, `isDefault` INTEGER, `favorite` INTEGER, `open` INTEGER NOT NULL, `alert` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `userMentions` INTEGER, `groupMentions` INTEGER, `updatedAt` INTEGER, `timestamp` INTEGER, `lastSeen` INTEGER, `lastMessageText` TEXT, `lastMessageUserId` TEXT, `lastMessageTimestamp` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`ownerId`) REFERENCES `users`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`userId`) REFERENCES `users`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`lastMessageUserId`) REFERENCES `users`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "subscriptionId",
"columnName": "subscriptionId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "fullname",
"columnName": "fullname",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "ownerId",
"columnName": "ownerId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "readonly",
"columnName": "readonly",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "isDefault",
"columnName": "isDefault",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "favorite",
"columnName": "favorite",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "open",
"columnName": "open",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "alert",
"columnName": "alert",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "unread",
"columnName": "unread",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userMentions",
"columnName": "userMentions",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "groupMentions",
"columnName": "groupMentions",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "updatedAt",
"columnName": "updatedAt",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "timestamp",
"columnName": "timestamp",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "lastSeen",
"columnName": "lastSeen",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "lastMessageText",
"columnName": "lastMessageText",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "lastMessageUserId",
"columnName": "lastMessageUserId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "lastMessageTimestamp",
"columnName": "lastMessageTimestamp",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_chatrooms_userId",
"unique": false,
"columnNames": [
"userId"
],
"createSql": "CREATE INDEX `index_chatrooms_userId` ON `${TABLE_NAME}` (`userId`)"
},
{
"name": "index_chatrooms_ownerId",
"unique": false,
"columnNames": [
"ownerId"
],
"createSql": "CREATE INDEX `index_chatrooms_ownerId` ON `${TABLE_NAME}` (`ownerId`)"
},
{
"name": "index_chatrooms_subscriptionId",
"unique": true,
"columnNames": [
"subscriptionId"
],
"createSql": "CREATE UNIQUE INDEX `index_chatrooms_subscriptionId` ON `${TABLE_NAME}` (`subscriptionId`)"
},
{
"name": "index_chatrooms_updatedAt",
"unique": false,
"columnNames": [
"updatedAt"
],
"createSql": "CREATE INDEX `index_chatrooms_updatedAt` ON `${TABLE_NAME}` (`updatedAt`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"ownerId"
],
"referencedColumns": [
"id"
]
},
{
"table": "users",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"userId"
],
"referencedColumns": [
"id"
]
},
{
"table": "users",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"lastMessageUserId"
],
"referencedColumns": [
"id"
]
}
]
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"06359a8c2943365dd094bc5dff210203\")"
]
}
}
\ No newline at end of file
{
"formatVersion": 1,
"database": {
"version": 4,
"identityHash": "e389d26bfb975f00c75dc6fc5d06d012",
"entities": [
{
"tableName": "users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `username` TEXT, `name` TEXT, `status` TEXT NOT NULL, `utcOffset` REAL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "username",
"columnName": "username",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "status",
"columnName": "status",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "utcOffset",
"columnName": "utcOffset",
"affinity": "REAL",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_users_username",
"unique": false,
"columnNames": [
"username"
],
"createSql": "CREATE INDEX `index_users_username` ON `${TABLE_NAME}` (`username`)"
}
],
"foreignKeys": []
},
{
"tableName": "chatrooms",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `subscriptionId` TEXT NOT NULL, `type` TEXT NOT NULL, `name` TEXT NOT NULL, `fullname` TEXT, `userId` TEXT, `ownerId` TEXT, `readonly` INTEGER, `isDefault` INTEGER, `favorite` INTEGER, `open` INTEGER NOT NULL, `alert` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `userMentions` INTEGER, `groupMentions` INTEGER, `updatedAt` INTEGER, `timestamp` INTEGER, `lastSeen` INTEGER, `lastMessageText` TEXT, `lastMessageUserId` TEXT, `lastMessageTimestamp` INTEGER, `broadcast` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`ownerId`) REFERENCES `users`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`userId`) REFERENCES `users`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`lastMessageUserId`) REFERENCES `users`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "subscriptionId",
"columnName": "subscriptionId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "fullname",
"columnName": "fullname",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "ownerId",
"columnName": "ownerId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "readonly",
"columnName": "readonly",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "isDefault",
"columnName": "isDefault",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "favorite",
"columnName": "favorite",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "open",
"columnName": "open",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "alert",
"columnName": "alert",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "unread",
"columnName": "unread",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userMentions",
"columnName": "userMentions",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "groupMentions",
"columnName": "groupMentions",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "updatedAt",
"columnName": "updatedAt",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "timestamp",
"columnName": "timestamp",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "lastSeen",
"columnName": "lastSeen",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "lastMessageText",
"columnName": "lastMessageText",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "lastMessageUserId",
"columnName": "lastMessageUserId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "lastMessageTimestamp",
"columnName": "lastMessageTimestamp",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "broadcast",
"columnName": "broadcast",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_chatrooms_userId",
"unique": false,
"columnNames": [
"userId"
],
"createSql": "CREATE INDEX `index_chatrooms_userId` ON `${TABLE_NAME}` (`userId`)"
},
{
"name": "index_chatrooms_ownerId",
"unique": false,
"columnNames": [
"ownerId"
],
"createSql": "CREATE INDEX `index_chatrooms_ownerId` ON `${TABLE_NAME}` (`ownerId`)"
},
{
"name": "index_chatrooms_subscriptionId",
"unique": true,
"columnNames": [
"subscriptionId"
],
"createSql": "CREATE UNIQUE INDEX `index_chatrooms_subscriptionId` ON `${TABLE_NAME}` (`subscriptionId`)"
},
{
"name": "index_chatrooms_updatedAt",
"unique": false,
"columnNames": [
"updatedAt"
],
"createSql": "CREATE INDEX `index_chatrooms_updatedAt` ON `${TABLE_NAME}` (`updatedAt`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"ownerId"
],
"referencedColumns": [
"id"
]
},
{
"table": "users",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"userId"
],
"referencedColumns": [
"id"
]
},
{
"table": "users",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"lastMessageUserId"
],
"referencedColumns": [
"id"
]
}
]
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"e389d26bfb975f00c75dc6fc5d06d012\")"
]
}
}
\ No newline at end of file
{
"formatVersion": 1,
"database": {
"version": 5,
"identityHash": "47a0c30e2696ae09bc86df16cc37279d",
"entities": [
{
"tableName": "users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `username` TEXT, `name` TEXT, `status` TEXT NOT NULL, `utcOffset` REAL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "username",
"columnName": "username",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "status",
"columnName": "status",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "utcOffset",
"columnName": "utcOffset",
"affinity": "REAL",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_users_username",
"unique": false,
"columnNames": [
"username"
],
"createSql": "CREATE INDEX `index_users_username` ON `${TABLE_NAME}` (`username`)"
}
],
"foreignKeys": []
},
{
"tableName": "chatrooms",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `subscriptionId` TEXT NOT NULL, `type` TEXT NOT NULL, `name` TEXT NOT NULL, `fullname` TEXT, `userId` TEXT, `ownerId` TEXT, `readonly` INTEGER, `isDefault` INTEGER, `favorite` INTEGER, `open` INTEGER NOT NULL, `alert` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `userMentions` INTEGER, `groupMentions` INTEGER, `updatedAt` INTEGER, `timestamp` INTEGER, `lastSeen` INTEGER, `lastMessageText` TEXT, `lastMessageUserId` TEXT, `lastMessageTimestamp` INTEGER, `broadcast` INTEGER, PRIMARY KEY(`id`), FOREIGN KEY(`ownerId`) REFERENCES `users`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`userId`) REFERENCES `users`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`lastMessageUserId`) REFERENCES `users`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "subscriptionId",
"columnName": "subscriptionId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "fullname",
"columnName": "fullname",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "ownerId",
"columnName": "ownerId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "readonly",
"columnName": "readonly",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "isDefault",
"columnName": "isDefault",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "favorite",
"columnName": "favorite",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "open",
"columnName": "open",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "alert",
"columnName": "alert",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "unread",
"columnName": "unread",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userMentions",
"columnName": "userMentions",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "groupMentions",
"columnName": "groupMentions",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "updatedAt",
"columnName": "updatedAt",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "timestamp",
"columnName": "timestamp",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "lastSeen",
"columnName": "lastSeen",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "lastMessageText",
"columnName": "lastMessageText",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "lastMessageUserId",
"columnName": "lastMessageUserId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "lastMessageTimestamp",
"columnName": "lastMessageTimestamp",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "broadcast",
"columnName": "broadcast",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_chatrooms_userId",
"unique": false,
"columnNames": [
"userId"
],
"createSql": "CREATE INDEX `index_chatrooms_userId` ON `${TABLE_NAME}` (`userId`)"
},
{
"name": "index_chatrooms_ownerId",
"unique": false,
"columnNames": [
"ownerId"
],
"createSql": "CREATE INDEX `index_chatrooms_ownerId` ON `${TABLE_NAME}` (`ownerId`)"
},
{
"name": "index_chatrooms_subscriptionId",
"unique": true,
"columnNames": [
"subscriptionId"
],
"createSql": "CREATE UNIQUE INDEX `index_chatrooms_subscriptionId` ON `${TABLE_NAME}` (`subscriptionId`)"
},
{
"name": "index_chatrooms_updatedAt",
"unique": false,
"columnNames": [
"updatedAt"
],
"createSql": "CREATE INDEX `index_chatrooms_updatedAt` ON `${TABLE_NAME}` (`updatedAt`)"
},
{
"name": "index_chatrooms_lastMessageUserId",
"unique": false,
"columnNames": [
"lastMessageUserId"
],
"createSql": "CREATE INDEX `index_chatrooms_lastMessageUserId` ON `${TABLE_NAME}` (`lastMessageUserId`)"
}
],
"foreignKeys": [
{
"table": "users",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"ownerId"
],
"referencedColumns": [
"id"
]
},
{
"table": "users",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"userId"
],
"referencedColumns": [
"id"
]
},
{
"table": "users",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"lastMessageUserId"
],
"referencedColumns": [
"id"
]
}
]
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"47a0c30e2696ae09bc86df16cc37279d\")"
]
}
}
\ No newline at end of file
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
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="chat.rocket.android"> package="chat.rocket.android">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
...@@ -8,6 +9,7 @@ ...@@ -8,6 +9,7 @@
<application <application
android:name=".app.RocketChatApplication" android:name=".app.RocketChatApplication"
tools:replace="android:name"
android:allowBackup="true" android:allowBackup="true"
android:fullBackupContent="@xml/backup_config" android:fullBackupContent="@xml/backup_config"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
...@@ -22,13 +24,13 @@ ...@@ -22,13 +24,13 @@
android:screenOrientation="portrait" android:screenOrientation="portrait"
android:theme="@style/AuthenticationTheme" android:theme="@style/AuthenticationTheme"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
...@@ -60,7 +62,7 @@ ...@@ -60,7 +62,7 @@
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" /> android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<activity <activity
android:name=".webview.cas.ui.CasWebViewActivity" android:name=".webview.sso.ui.SsoWebViewActivity"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" /> android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
...@@ -84,11 +86,6 @@ ...@@ -84,11 +86,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 <receiver
android:name=".push.DirectReplyReceiver" android:name=".push.DirectReplyReceiver"
android:enabled="true" android:enabled="true"
......
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.ConnectionManagerFactory
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.model.UserStatus import chat.rocket.common.model.UserStatus
...@@ -15,8 +16,7 @@ import javax.inject.Inject ...@@ -15,8 +16,7 @@ import javax.inject.Inject
class AppLifecycleObserver @Inject constructor( class AppLifecycleObserver @Inject constructor(
private val serverInteractor: GetCurrentServerInteractor, private val serverInteractor: GetCurrentServerInteractor,
private val factory: RocketChatClientFactory, private val factory: ConnectionManagerFactory
private val getAccountInteractor: GetAccountInteractor
) : LifecycleObserver { ) : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START) @OnLifecycleEvent(Lifecycle.Event.ON_START)
...@@ -31,14 +31,8 @@ class AppLifecycleObserver @Inject constructor( ...@@ -31,14 +31,8 @@ class AppLifecycleObserver @Inject constructor(
private fun changeTemporaryStatus(userStatus: UserStatus) { private fun changeTemporaryStatus(userStatus: UserStatus) {
launch { launch {
val currentServer = serverInteractor.get() serverInteractor.get()?.let { currentServer ->
val account = currentServer?.let { getAccountInteractor.get(currentServer) } factory.create(currentServer).setTemporaryStatus(userStatus)
val client = account?.let { factory.create(currentServer) }
try {
client?.setTemporaryStatus(userStatus)
} catch (exception: RocketChatException) {
Timber.e(exception)
} }
} }
} }
......
...@@ -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 {
...@@ -44,39 +44,48 @@ object DateTimeHelper { ...@@ -44,39 +44,48 @@ object DateTimeHelper {
} }
} }
/** fun getFormattedDateForMessages(localDateTime: LocalDateTime, context: Context): String {
val localDate = localDateTime.toLocalDate()
return when (localDate) {
today -> context.getString(R.string.msg_today)
yesterday -> context.getString(R.string.msg_yesterday)
else -> formatLocalDate(localDate)
}
}
/**
* Returns a time from a [LocalDateTime]. * Returns a time from a [LocalDateTime].
* *
* @param localDateTime The [LocalDateTime]. * @param localDateTime The [LocalDateTime].
* @return The time from a [LocalDateTime]. * @return The time from a [LocalDateTime].
*/ */
fun getTime(localDateTime: LocalDateTime): String { fun getTime(localDateTime: LocalDateTime): String {
val formatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT) val formatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)
return localDateTime.toLocalTime().format(formatter).toString() return localDateTime.toLocalTime().format(formatter).toString()
} }
/** /**
* Returns a date time from a [LocalDateTime]. * Returns a date time from a [LocalDateTime].
* *
* @param localDateTime The [LocalDateTime]. * @param localDateTime The [LocalDateTime].
* @return The time from a [LocalDateTime]. * @return The time from a [LocalDateTime].
*/ */
fun getDateTime(localDateTime: LocalDateTime): String { fun getDateTime(localDateTime: LocalDateTime): String {
return formatLocalDateTime(localDateTime) return formatLocalDateTime(localDateTime)
} }
private fun formatLocalDateTime(localDateTime: LocalDateTime): String { private fun formatLocalDateTime(localDateTime: LocalDateTime): String {
val formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT) val formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)
return localDateTime.format(formatter).toString() return localDateTime.format(formatter).toString()
} }
private fun formatLocalDate(localDate: LocalDate): String { private fun formatLocalDate(localDate: LocalDate): String {
val formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT) val formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
return localDate.format(formatter).toString() return localDate.format(formatter).toString()
} }
private fun formatLocalTime(localTime: LocalTime): String { private fun formatLocalTime(localTime: LocalTime): String {
val formatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT) val formatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)
return localTime.format(formatter).toString() return localTime.format(formatter).toString()
} }
} }
\ No newline at end of file
import android.content.Context import android.content.Context
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.support.v4.content.ContextCompat import androidx.core.content.ContextCompat
import android.support.v4.graphics.drawable.DrawableCompat import androidx.core.graphics.drawable.DrawableCompat
import android.widget.TextView import android.widget.TextView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.common.model.UserStatus import chat.rocket.common.model.UserStatus
......
...@@ -3,19 +3,12 @@ package chat.rocket.android.app ...@@ -3,19 +3,12 @@ package chat.rocket.android.app
import android.app.Activity import android.app.Activity
import android.app.Application import android.app.Application
import android.app.Service import android.app.Service
import android.arch.lifecycle.ProcessLifecycleOwner
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import androidx.core.content.edit import androidx.core.content.edit
import androidx.lifecycle.ProcessLifecycleOwner
import chat.rocket.android.BuildConfig import chat.rocket.android.BuildConfig
import chat.rocket.android.app.migration.RealmMigration
import chat.rocket.android.app.migration.RocketChatLibraryModule
import chat.rocket.android.app.migration.RocketChatServerModule
import chat.rocket.android.app.migration.model.RealmBasedServerInfo
import chat.rocket.android.app.migration.model.RealmPublicSetting
import chat.rocket.android.app.migration.model.RealmSession
import chat.rocket.android.app.migration.model.RealmUser
import chat.rocket.android.dagger.DaggerAppComponent import chat.rocket.android.dagger.DaggerAppComponent
import chat.rocket.android.dagger.qualifier.ForMessages import chat.rocket.android.dagger.qualifier.ForMessages
import chat.rocket.android.helper.CrashlyticsTree import chat.rocket.android.helper.CrashlyticsTree
...@@ -24,19 +17,9 @@ import chat.rocket.android.infrastructure.installCrashlyticsWrapper ...@@ -24,19 +17,9 @@ import chat.rocket.android.infrastructure.installCrashlyticsWrapper
import chat.rocket.android.server.domain.AccountsRepository import chat.rocket.android.server.domain.AccountsRepository
import chat.rocket.android.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.SITE_URL import chat.rocket.android.server.domain.SITE_URL
import chat.rocket.android.server.domain.SaveCurrentServerInteractor
import chat.rocket.android.server.domain.SettingsRepository
import chat.rocket.android.server.domain.TokenRepository import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.server.domain.favicon import chat.rocket.android.emoji.EmojiRepository
import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.domain.wideTile
import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.serverLogoUrl
import chat.rocket.android.widget.emoji.EmojiRepository
import chat.rocket.common.model.Token
import chat.rocket.core.model.Value
import com.crashlytics.android.Crashlytics import com.crashlytics.android.Crashlytics
import com.crashlytics.android.core.CrashlyticsCore import com.crashlytics.android.core.CrashlyticsCore
import com.facebook.drawee.backends.pipeline.DraweeConfig import com.facebook.drawee.backends.pipeline.DraweeConfig
...@@ -49,10 +32,6 @@ import dagger.android.HasActivityInjector ...@@ -49,10 +32,6 @@ import dagger.android.HasActivityInjector
import dagger.android.HasBroadcastReceiverInjector import dagger.android.HasBroadcastReceiverInjector
import dagger.android.HasServiceInjector import dagger.android.HasServiceInjector
import io.fabric.sdk.android.Fabric import io.fabric.sdk.android.Fabric
import io.realm.Realm
import io.realm.RealmConfiguration
import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.launch
import timber.log.Timber import timber.log.Timber
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import javax.inject.Inject import javax.inject.Inject
...@@ -83,17 +62,11 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -83,17 +62,11 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
@Inject @Inject
lateinit var settingsInteractor: GetSettingsInteractor lateinit var settingsInteractor: GetSettingsInteractor
@Inject @Inject
lateinit var settingsRepository: SettingsRepository
@Inject
lateinit var tokenRepository: TokenRepository lateinit var tokenRepository: TokenRepository
@Inject @Inject
lateinit var accountRepository: AccountsRepository
@Inject
lateinit var saveCurrentServerRepository: SaveCurrentServerInteractor
@Inject
lateinit var prefs: SharedPreferences
@Inject
lateinit var localRepository: LocalRepository lateinit var localRepository: LocalRepository
@Inject
lateinit var accountRepository: AccountsRepository
@Inject @Inject
@field:ForMessages @field:ForMessages
...@@ -127,15 +100,7 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -127,15 +100,7 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
localRepository.setOldMessagesCleanedUp() localRepository.setOldMessagesCleanedUp()
} }
// TODO - remove this and all realm stuff when we got to 80% in 2.0 // TODO - remove REALM files.
try {
if (!localRepository.hasMigrated()) {
migrateFromLegacy()
}
} catch (ex: Exception) {
Timber.d(ex, "Error migrating old accounts")
}
// TODO - remove this // TODO - remove this
checkCurrentServer() checkCurrentServer()
} }
...@@ -160,110 +125,6 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -160,110 +125,6 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
} }
} }
private fun migrateFromLegacy() {
Realm.init(this)
val serveListConfiguration = RealmConfiguration.Builder()
.name("server.list.realm")
.schemaVersion(6)
.migration(RealmMigration())
.modules(RocketChatServerModule())
.build()
val serverRealm = Realm.getInstance(serveListConfiguration)
val serversInfoList = serverRealm.where(RealmBasedServerInfo::class.java).findAll().toList()
serversInfoList.forEach { server ->
val hostname = server.hostname
val url = if (server.insecure) "http://$hostname" else "https://$hostname"
val config = RealmConfiguration.Builder()
.name("${server.hostname}.realm")
.schemaVersion(6)
.migration(RealmMigration())
.modules(RocketChatLibraryModule())
.build()
val realm = Realm.getInstance(config)
val user = realm.where(RealmUser::class.java)
.isNotEmpty(RealmUser.EMAILS).findFirst()
val session = realm.where(RealmSession::class.java).findFirst()
migratePublicSettings(url, realm)
if (user != null && session != null) {
val authToken = session.token
settingsRepository.get(url)
migrateServerInfo(url, authToken!!, settingsRepository.get(url), user)
}
realm.close()
}
migrateCurrentServer(serversInfoList)
serverRealm.close()
localRepository.setMigrated(true)
}
private fun migrateServerInfo(url: String, authToken: String, settings: PublicSettings, user: RealmUser) {
val userId = user._id
val avatar = url.avatarUrl(user.username!!)
val icon = settings.favicon()?.let {
url.serverLogoUrl(it)
}
val logo = settings.wideTile()?.let {
url.serverLogoUrl(it)
}
val account = Account(url, icon, logo, user.username!!, avatar)
launch(CommonPool) {
tokenRepository.save(url, Token(userId!!, authToken))
accountRepository.save(account)
}
}
private fun migratePublicSettings(url: String, realm: Realm) {
val settings = realm.where(RealmPublicSetting::class.java).findAll()
val serverSettings = hashMapOf<String, Value<Any>>()
settings.toList().forEach { setting ->
val type = setting.type!!
val value = setting.value!!
val convertedSetting = when (type) {
"string" -> Value(value)
"language" -> Value(value)
"boolean" -> Value(value.toBoolean())
"int" -> try {
Value(value.toInt())
} catch (ex: NumberFormatException) {
Value(0)
}
else -> null // ignore
}
if (convertedSetting != null) {
val id = setting._id!!
serverSettings.put(id, convertedSetting)
}
}
settingsRepository.save(url, serverSettings)
}
private fun migrateCurrentServer(serversList: List<RealmBasedServerInfo>) {
if (getCurrentServerInteractor.get() == null) {
var currentServer = getSharedPreferences("cache", Context.MODE_PRIVATE)
.getString("KEY_SELECTED_SERVER_HOSTNAME", null)
currentServer = if (serversList.isNotEmpty()) {
val server = serversList.find { it.hostname == currentServer }
val hostname = server!!.hostname
if (server.insecure) {
"http://$hostname"
} else {
"https://$hostname"
}
} else {
"http://$currentServer"
}
saveCurrentServerRepository.save(currentServer)
}
}
private fun setupCrashlytics() { private fun setupCrashlytics() {
val core = CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build() val core = CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build()
Fabric.with(this, Crashlytics.Builder().core(core).build()) Fabric.with(this, Crashlytics.Builder().core(core).build())
...@@ -305,11 +166,6 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -305,11 +166,6 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
} }
} }
private fun LocalRepository.setMigrated(migrated: Boolean) {
save(LocalRepository.MIGRATION_FINISHED_KEY, migrated)
}
private fun LocalRepository.hasMigrated() = getBoolean(LocalRepository.MIGRATION_FINISHED_KEY)
private fun LocalRepository.needOldMessagesCleanUp() = getBoolean(CLEANUP_OLD_MESSAGES_NEEDED, true) private fun LocalRepository.needOldMessagesCleanUp() = getBoolean(CLEANUP_OLD_MESSAGES_NEEDED, true)
private fun LocalRepository.setOldMessagesCleanedUp() = save(CLEANUP_OLD_MESSAGES_NEEDED, false) private fun LocalRepository.setOldMessagesCleanedUp() = save(CLEANUP_OLD_MESSAGES_NEEDED, false)
......
package chat.rocket.android.app
import android.arch.persistence.room.Database
import android.arch.persistence.room.RoomDatabase
import chat.rocket.android.server.infraestructure.ServerDao
import chat.rocket.android.server.infraestructure.ServerEntity
@Database(entities = arrayOf(ServerEntity::class), version = 1, exportSchema = false)
abstract class RocketChatDatabase : RoomDatabase() {
abstract fun serverDao(): ServerDao
}
package chat.rocket.android.app.migration
import chat.rocket.android.BuildConfig
import chat.rocket.android.app.migration.model.RealmUser
import io.realm.DynamicRealm
import io.realm.RealmMigration
class RealmMigration : RealmMigration {
override fun migrate(dynamicRealm: DynamicRealm, oldVersion: Long, newVersion: Long) {
var oldVersion = oldVersion
val schema = dynamicRealm.schema
if (oldVersion == 0L) {
// NOOP
oldVersion++
}
if (oldVersion == 1L) {
oldVersion++
}
if (oldVersion == 2L) {
oldVersion++
}
if (oldVersion == 3L) {
oldVersion++
}
if (oldVersion == 4L) {
oldVersion++
}
if (oldVersion == 5L) {
val userSchema = schema.get("RealmUser")
try {
userSchema?.addField(RealmUser.NAME, String::class.java)
} catch (e: IllegalArgumentException) {
if (BuildConfig.DEBUG) {
e.printStackTrace()
}
// ignore; it makes here if the schema for this model was already update before without migration
}
}
}
// hack around to avoid "new different configuration cannot access the same file" error
override fun hashCode(): Int {
return 37
}
override fun equals(o: Any?): Boolean {
return o is chat.rocket.android.app.migration.RealmMigration
}
// end hack
}
\ No newline at end of file
package chat.rocket.android.app.migration
import io.realm.annotations.RealmModule
@RealmModule(library = true, allClasses = true)
class RocketChatLibraryModule
\ No newline at end of file
package chat.rocket.android.app.migration
import chat.rocket.android.app.migration.model.RealmBasedServerInfo
import io.realm.annotations.RealmModule
@RealmModule(library = true, classes = arrayOf(RealmBasedServerInfo::class))
class RocketChatServerModule
\ No newline at end of file
package chat.rocket.android.app.migration.model
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
open class RealmBasedServerInfo : RealmObject() {
@PrimaryKey
@JvmField var hostname: String? = null
@JvmField var name: String? = null
@JvmField var session: String? = null
@JvmField var insecure: Boolean = false
}
\ No newline at end of file
package chat.rocket.android.app.migration.model
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
open class RealmEmail : RealmObject() {
@PrimaryKey
@JvmField
var address: String? = null
@JvmField
var verified: Boolean = false
}
\ No newline at end of file
package chat.rocket.android.app.migration.model
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
open class RealmPreferences : RealmObject() {
@PrimaryKey
@JvmField
var id: String? = null
@JvmField
var newRoomNotification: String? = null
@JvmField
var newMessageNotification: String? = null
@JvmField
var useEmojis: Boolean = false
@JvmField
var convertAsciiEmoji: Boolean = false
@JvmField
var saveMobileBandwidth: Boolean = false
@JvmField
var collapseMediaByDefault: Boolean = false
@JvmField
var unreadRoomsMode: Boolean = false
@JvmField
var autoImageLoad: Boolean = false
@JvmField
var emailNotificationMode: String? = null
@JvmField
var unreadAlert: Boolean = false
@JvmField
var desktopNotificationDuration: Int = 0
@JvmField
var viewMode: Int = 0
@JvmField
var hideUsernames: Boolean = false
@JvmField
var hideAvatars: Boolean = false
@JvmField
var hideFlexTab: Boolean = false
}
\ No newline at end of file
package chat.rocket.android.app.migration.model
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
open class RealmPublicSetting : RealmObject() {
@PrimaryKey
@JvmField
var _id: String? = null
@JvmField
var group: String? = null
@JvmField
var type: String? = null
@JvmField
var value: String? = null
@JvmField
var _updatedAt: Long = 0
@JvmField
var meta: String? = null
}
\ No newline at end of file
package chat.rocket.android.app.migration.model
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
open class RealmSession : RealmObject() {
@JvmField
@PrimaryKey
var sessionId: Int = 0 //only 0 is used!
@JvmField
var token: String? = null
@JvmField
var tokenVerified: Boolean = false
@JvmField
var error: String? = null
}
\ No newline at end of file
package chat.rocket.android.app.migration.model
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
open class RealmSettings : RealmObject() {
@PrimaryKey
@JvmField
var id: String? = null
@JvmField
var preferences: RealmPreferences? = null
}
\ No newline at end of file
package chat.rocket.android.app.migration.model
import io.realm.RealmList
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
open class RealmUser : RealmObject() {
companion object {
const val ID = "_id"
const val NAME = "name"
const val USERNAME = "username"
const val STATUS = "status"
const val UTC_OFFSET = "utcOffset"
const val EMAILS = "emails"
const val SETTINGS = "settings"
const val STATUS_ONLINE = "online"
const val STATUS_BUSY = "busy"
const val STATUS_AWAY = "away"
const val STATUS_OFFLINE = "offline"
}
@PrimaryKey
@JvmField
var _id: String? = null
@JvmField
var name: String? = null
@JvmField
var username: String? = null
@JvmField
var status: String? = null
@JvmField
var utcOffset: Double = 0.toDouble()
@JvmField
var emails: RealmList<RealmEmail>? = null
@JvmField
var settings: RealmSettings? = null
}
\ No newline at end of file
package chat.rocket.android.authentication.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
...@@ -87,7 +87,7 @@ interface LoginView : LoadingView, MessageView { ...@@ -87,7 +87,7 @@ interface LoginView : LoadingView, MessageView {
* Enables and shows the oauth view if there is login via social accounts enabled by the server settings. * Enables and shows the oauth view if there is login via social accounts enabled by the server settings.
* *
* REMARK: We must show at maximum *three* social accounts views ([enableLoginByFacebook], [enableLoginByGithub], [enableLoginByGoogle], * REMARK: We must show at maximum *three* social accounts views ([enableLoginByFacebook], [enableLoginByGithub], [enableLoginByGoogle],
* [enableLoginByLinkedin], [enableLoginByMeteor], [enableLoginByTwitter], [enableLoginByGitlab] or [addCustomOauthServiceButton]) for the oauth view. * [enableLoginByLinkedin], [enableLoginByMeteor], [enableLoginByTwitter], [enableLoginByGitlab], [addCustomOauthServiceButton] or [addSamlServiceButton]) for the oauth view.
* If the possibility of login via social accounts exceeds 3 different ways we should set up the FAB ([setupFabListener]) to show the remaining view(s). * If the possibility of login via social accounts exceeds 3 different ways we should set up the FAB ([setupFabListener]) to show the remaining view(s).
*/ */
fun enableOauthView() fun enableOauthView()
...@@ -197,7 +197,7 @@ interface LoginView : LoadingView, MessageView { ...@@ -197,7 +197,7 @@ interface LoginView : LoadingView, MessageView {
* @state A random string generated by the app, which you'll verify later (to protect against forgery attacks). * @state A random string generated by the app, which you'll verify later (to protect against forgery attacks).
* @serviceName The custom OAuth service name. * @serviceName The custom OAuth service name.
* @serviceNameColor The custom OAuth service name color (just stylizing). * @serviceNameColor The custom OAuth service name color (just stylizing).
* @buttonColor The color of the custom OAuth button (just stylizing). * @buttonColor The custom OAuth button color (just stylizing).
* @see [enableOauthView] * @see [enableOauthView]
*/ */
fun addCustomOauthServiceButton( fun addCustomOauthServiceButton(
...@@ -208,6 +208,23 @@ interface LoginView : LoadingView, MessageView { ...@@ -208,6 +208,23 @@ interface LoginView : LoadingView, MessageView {
buttonColor: Int buttonColor: Int
) )
/**
* Adds a SAML button in the oauth view.
*
* @samlUrl The SAML url to sets up the button (the listener).
* @serviceName The SAML service name.
* @serviceNameColor The SAML service name color (just stylizing).
* @buttonColor The SAML button color (just stylizing).
* @see [enableOauthView]
*/
fun addSamlServiceButton(
samlUrl: String,
samlToken: String,
serviceName: String,
serviceNameColor: Int,
buttonColor: Int
)
/** /**
* Setups the FloatingActionButton to show more social accounts views (expanding the oauth view interface to show the remaining view(s)). * Setups the FloatingActionButton to show more social accounts views (expanding the oauth view interface to show the remaining view(s)).
*/ */
......
...@@ -6,7 +6,7 @@ import android.content.Intent ...@@ -6,7 +6,7 @@ import android.content.Intent
import android.graphics.PorterDuff import android.graphics.PorterDuff
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.Fragment import androidx.fragment.app.Fragment
import android.text.style.ClickableSpan import android.text.style.ClickableSpan
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
...@@ -24,8 +24,8 @@ import chat.rocket.android.authentication.login.presentation.LoginPresenter ...@@ -24,8 +24,8 @@ import chat.rocket.android.authentication.login.presentation.LoginPresenter
import chat.rocket.android.authentication.login.presentation.LoginView import chat.rocket.android.authentication.login.presentation.LoginView
import chat.rocket.android.helper.* import chat.rocket.android.helper.*
import chat.rocket.android.util.extensions.* import chat.rocket.android.util.extensions.*
import chat.rocket.android.webview.cas.ui.INTENT_CAS_TOKEN import chat.rocket.android.webview.sso.ui.INTENT_SSO_TOKEN
import chat.rocket.android.webview.cas.ui.casWebViewIntent import chat.rocket.android.webview.sso.ui.ssoWebViewIntent
import chat.rocket.android.webview.oauth.ui.INTENT_OAUTH_CREDENTIAL_SECRET import chat.rocket.android.webview.oauth.ui.INTENT_OAUTH_CREDENTIAL_SECRET
import chat.rocket.android.webview.oauth.ui.INTENT_OAUTH_CREDENTIAL_TOKEN import chat.rocket.android.webview.oauth.ui.INTENT_OAUTH_CREDENTIAL_TOKEN
import chat.rocket.android.webview.oauth.ui.oauthWebViewIntent import chat.rocket.android.webview.oauth.ui.oauthWebViewIntent
...@@ -36,7 +36,8 @@ import kotlinx.android.synthetic.main.fragment_authentication_log_in.* ...@@ -36,7 +36,8 @@ import kotlinx.android.synthetic.main.fragment_authentication_log_in.*
import javax.inject.Inject import javax.inject.Inject
internal const val REQUEST_CODE_FOR_CAS = 4 internal const val REQUEST_CODE_FOR_CAS = 4
internal const val REQUEST_CODE_FOR_OAUTH = 5 internal const val REQUEST_CODE_FOR_SAML = 5
internal const val REQUEST_CODE_FOR_OAUTH = 6
class LoginFragment : Fragment(), LoginView { class LoginFragment : Fragment(), LoginView {
@Inject @Inject
...@@ -111,7 +112,10 @@ class LoginFragment : Fragment(), LoginView { ...@@ -111,7 +112,10 @@ class LoginFragment : Fragment(), LoginView {
showMessage(getString(R.string.message_credentials_saved_successfully)) showMessage(getString(R.string.message_credentials_saved_successfully))
} }
REQUEST_CODE_FOR_CAS -> { REQUEST_CODE_FOR_CAS -> {
presenter.authenticateWithCas(data.getStringExtra(INTENT_CAS_TOKEN)) presenter.authenticateWithCas(data.getStringExtra(INTENT_SSO_TOKEN))
}
REQUEST_CODE_FOR_SAML -> data.apply {
presenter.authenticateWithSaml(getStringExtra(INTENT_SSO_TOKEN))
} }
REQUEST_CODE_FOR_OAUTH -> { REQUEST_CODE_FOR_OAUTH -> {
presenter.authenticateWithOauth( presenter.authenticateWithOauth(
...@@ -252,7 +256,7 @@ class LoginFragment : Fragment(), LoginView { ...@@ -252,7 +256,7 @@ class LoginFragment : Fragment(), LoginView {
ui { activity -> ui { activity ->
button_cas.setOnClickListener { button_cas.setOnClickListener {
startActivityForResult( startActivityForResult(
activity.casWebViewIntent(casUrl, casToken), activity.ssoWebViewIntent(casUrl, casToken),
REQUEST_CODE_FOR_CAS REQUEST_CODE_FOR_CAS
) )
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold) activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
...@@ -447,7 +451,7 @@ class LoginFragment : Fragment(), LoginView { ...@@ -447,7 +451,7 @@ class LoginFragment : Fragment(), LoginView {
buttonColor: Int buttonColor: Int
) { ) {
ui { activity -> ui { activity ->
val button = getCustomOauthButton(serviceName, serviceNameColor, buttonColor) val button = getCustomServiceButton(serviceName, serviceNameColor, buttonColor)
social_accounts_container.addView(button) social_accounts_container.addView(button)
button.setOnClickListener { button.setOnClickListener {
...@@ -460,6 +464,27 @@ class LoginFragment : Fragment(), LoginView { ...@@ -460,6 +464,27 @@ class LoginFragment : Fragment(), LoginView {
} }
} }
override fun addSamlServiceButton(
samlUrl: String,
samlToken: String,
serviceName: String,
serviceNameColor: Int,
buttonColor: Int
) {
ui { activity ->
val button = getCustomServiceButton(serviceName, serviceNameColor, buttonColor)
social_accounts_container.addView(button)
button.setOnClickListener {
startActivityForResult(
activity.ssoWebViewIntent(samlUrl, samlToken),
REQUEST_CODE_FOR_SAML
)
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
}
}
}
override fun setupFabListener() { override fun setupFabListener() {
ui { ui {
button_fab.isVisible = true button_fab.isVisible = true
...@@ -577,9 +602,9 @@ class LoginFragment : Fragment(), LoginView { ...@@ -577,9 +602,9 @@ class LoginFragment : Fragment(), LoginView {
} }
/** /**
* Gets a stylized custom OAuth button. * Gets a stylized custom service button.
*/ */
private fun getCustomOauthButton( private fun getCustomServiceButton(
buttonText: String, buttonText: String,
buttonTextColor: Int, buttonTextColor: Int,
buttonBgColor: Int buttonBgColor: Int
......
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
...@@ -7,7 +7,7 @@ import chat.rocket.android.server.domain.* ...@@ -7,7 +7,7 @@ import chat.rocket.android.server.domain.*
import chat.rocket.android.server.domain.model.Account import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.avatarUrl import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.registerPushToken import chat.rocket.android.util.extensions.registerPushToken
import chat.rocket.android.util.extensions.serverLogoUrl import chat.rocket.android.util.extensions.serverLogoUrl
import chat.rocket.android.util.retryIO import chat.rocket.android.util.retryIO
......
...@@ -3,7 +3,7 @@ package chat.rocket.android.authentication.registerusername.ui ...@@ -3,7 +3,7 @@ package chat.rocket.android.authentication.registerusername.ui
import DrawableHelper import DrawableHelper
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.Fragment import androidx.fragment.app.Fragment
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
......
package chat.rocket.android.authentication.resetpassword.di package chat.rocket.android.authentication.resetpassword.di
import android.arch.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.authentication.resetpassword.presentation.ResetPasswordView import chat.rocket.android.authentication.resetpassword.presentation.ResetPasswordView
import chat.rocket.android.authentication.resetpassword.ui.ResetPasswordFragment import chat.rocket.android.authentication.resetpassword.ui.ResetPasswordFragment
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
...@@ -10,20 +10,26 @@ import dagger.Provides ...@@ -10,20 +10,26 @@ import dagger.Provides
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.experimental.Job
@Module @Module
@PerFragment
class ResetPasswordFragmentModule { class ResetPasswordFragmentModule {
@Provides @Provides
@PerFragment
fun provideJob() = Job()
@Provides
@PerFragment
fun resetPasswordView(frag: ResetPasswordFragment): ResetPasswordView { fun resetPasswordView(frag: ResetPasswordFragment): ResetPasswordView {
return frag return frag
} }
@Provides @Provides
@PerFragment
fun provideLifecycleOwner(frag: ResetPasswordFragment): LifecycleOwner { fun provideLifecycleOwner(frag: ResetPasswordFragment): LifecycleOwner {
return frag return frag
} }
@Provides @Provides
@PerFragment
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy { fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs) return CancelStrategy(owner, jobs)
} }
......
package chat.rocket.android.authentication.resetpassword.di package chat.rocket.android.authentication.resetpassword.di
import chat.rocket.android.authentication.resetpassword.ui.ResetPasswordFragment import chat.rocket.android.authentication.resetpassword.ui.ResetPasswordFragment
import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
...@@ -8,5 +9,6 @@ import dagger.android.ContributesAndroidInjector ...@@ -8,5 +9,6 @@ import dagger.android.ContributesAndroidInjector
abstract class ResetPasswordFragmentProvider { abstract class ResetPasswordFragmentProvider {
@ContributesAndroidInjector(modules = [ResetPasswordFragmentModule::class]) @ContributesAndroidInjector(modules = [ResetPasswordFragmentModule::class])
@PerFragment
abstract fun provideResetPasswordFragment(): ResetPasswordFragment abstract fun provideResetPasswordFragment(): ResetPasswordFragment
} }
\ No newline at end of file
...@@ -3,10 +3,9 @@ package chat.rocket.android.authentication.resetpassword.presentation ...@@ -3,10 +3,9 @@ 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.GetConnectingServerInteractor
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
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.retryIO import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.RocketChatInvalidResponseException import chat.rocket.common.RocketChatInvalidResponseException
......
...@@ -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
...@@ -10,10 +10,11 @@ import chat.rocket.android.server.domain.SaveConnectingServerInteractor ...@@ -10,10 +10,11 @@ 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
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extension.launchUI
import javax.inject.Inject 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: SaveConnectingServerInteractor, private val serverInteractor: SaveConnectingServerInteractor,
......
...@@ -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
...@@ -6,6 +6,7 @@ import chat.rocket.android.infrastructure.LocalRepository ...@@ -6,6 +6,7 @@ import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.* import chat.rocket.android.server.domain.*
import chat.rocket.android.server.domain.model.Account import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.* import chat.rocket.android.util.extensions.*
import chat.rocket.android.util.retryIO import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
......
...@@ -5,7 +5,7 @@ import android.app.Activity ...@@ -5,7 +5,7 @@ import android.app.Activity
import android.content.Intent import android.content.Intent
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.Fragment import androidx.fragment.app.Fragment
import android.text.style.ClickableSpan import android.text.style.ClickableSpan
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
......
package chat.rocket.android.authentication.twofactor.di package chat.rocket.android.authentication.twofactor.di
import android.arch.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.authentication.twofactor.presentation.TwoFAView import chat.rocket.android.authentication.twofactor.presentation.TwoFAView
import chat.rocket.android.authentication.twofactor.ui.TwoFAFragment import chat.rocket.android.authentication.twofactor.ui.TwoFAFragment
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
...@@ -10,20 +10,26 @@ import dagger.Provides ...@@ -10,20 +10,26 @@ import dagger.Provides
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.experimental.Job
@Module @Module
@PerFragment
class TwoFAFragmentModule { class TwoFAFragmentModule {
@Provides @Provides
@PerFragment
fun provideJob() = Job()
@Provides
@PerFragment
fun loginView(frag: TwoFAFragment): TwoFAView { fun loginView(frag: TwoFAFragment): TwoFAView {
return frag return frag
} }
@Provides @Provides
@PerFragment
fun provideLifecycleOwner(frag: TwoFAFragment): LifecycleOwner { fun provideLifecycleOwner(frag: TwoFAFragment): LifecycleOwner {
return frag return frag
} }
@Provides @Provides
@PerFragment
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy { fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs) return CancelStrategy(owner, jobs)
} }
......
package chat.rocket.android.authentication.twofactor.di package chat.rocket.android.authentication.twofactor.di
import chat.rocket.android.authentication.twofactor.ui.TwoFAFragment import chat.rocket.android.authentication.twofactor.ui.TwoFAFragment
import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
@Module abstract class TwoFAFragmentProvider { @Module abstract class TwoFAFragmentProvider {
@ContributesAndroidInjector(modules = [TwoFAFragmentModule::class]) @ContributesAndroidInjector(modules = [TwoFAFragmentModule::class])
@PerFragment
abstract fun provideTwoFAFragment(): TwoFAFragment abstract fun provideTwoFAFragment(): TwoFAFragment
} }
...@@ -7,7 +7,7 @@ import chat.rocket.android.server.domain.* ...@@ -7,7 +7,7 @@ import chat.rocket.android.server.domain.*
import chat.rocket.android.server.domain.model.Account import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.avatarUrl import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.registerPushToken import chat.rocket.android.util.extensions.registerPushToken
import chat.rocket.android.util.extensions.serverLogoUrl import chat.rocket.android.util.extensions.serverLogoUrl
import chat.rocket.android.util.retryIO import chat.rocket.android.util.retryIO
......
...@@ -4,7 +4,7 @@ import DrawableHelper ...@@ -4,7 +4,7 @@ import DrawableHelper
import android.content.Context import android.content.Context
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.Fragment import androidx.fragment.app.Fragment
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
......
...@@ -3,8 +3,8 @@ package chat.rocket.android.authentication.ui ...@@ -3,8 +3,8 @@ package chat.rocket.android.authentication.ui
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.Fragment import androidx.fragment.app.Fragment
import android.support.v7.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.authentication.domain.model.LoginDeepLinkInfo import chat.rocket.android.authentication.domain.model.LoginDeepLinkInfo
import chat.rocket.android.authentication.domain.model.getLoginDeepLinkInfo import chat.rocket.android.authentication.domain.model.getLoginDeepLinkInfo
......
package chat.rocket.android.chatinformation.adapter package chat.rocket.android.chatinformation.adapter
import android.support.v7.widget.RecyclerView
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.chatinformation.adapter.ReadReceiptAdapter.ReadReceiptViewHolder import chat.rocket.android.chatinformation.adapter.ReadReceiptAdapter.ReadReceiptViewHolder
import chat.rocket.android.chatinformation.viewmodel.ReadReceiptViewModel import chat.rocket.android.chatinformation.viewmodel.ReadReceiptViewModel
......
package chat.rocket.android.chatinformation.di package chat.rocket.android.chatinformation.di
import android.arch.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import chat.rocket.android.chatinformation.presentation.MessageInfoView import chat.rocket.android.chatinformation.presentation.MessageInfoView
import chat.rocket.android.chatinformation.ui.MessageInfoFragment import chat.rocket.android.chatinformation.ui.MessageInfoFragment
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 MessageInfoFragmentModule { class MessageInfoFragmentModule {
@Provides @Provides
@PerFragment
fun provideJob() = Job()
@Provides
@PerFragment
fun messageInfoView(frag: MessageInfoFragment): MessageInfoView { fun messageInfoView(frag: MessageInfoFragment): MessageInfoView {
return frag return frag
} }
@Provides @Provides
@PerFragment
fun provideLifecycleOwner(frag: MessageInfoFragment): LifecycleOwner { fun provideLifecycleOwner(frag: MessageInfoFragment): 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.chatinformation.presentation package chat.rocket.android.chatinformation.presentation
import chat.rocket.android.chatinformation.viewmodel.ReadReceiptViewModel import chat.rocket.android.chatroom.uimodel.UiModelMapper
import chat.rocket.android.chatroom.viewmodel.ViewModelMapper
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.UserHelper
import chat.rocket.android.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.MessagesRepository
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.retryIO import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.core.internal.rest.getMessageReadReceipts import chat.rocket.core.internal.rest.getMessageReadReceipts
import chat.rocket.core.internal.rest.queryUsers
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
class MessageInfoPresenter @Inject constructor( class MessageInfoPresenter @Inject constructor(
private val view: MessageInfoView, private val view: MessageInfoView,
private val strategy: CancelStrategy, private val strategy: CancelStrategy,
private val mapper: ViewModelMapper, private val mapper: UiModelMapper,
serverInteractor: GetCurrentServerInteractor, serverInteractor: GetCurrentServerInteractor,
factory: ConnectionManagerFactory factory: ConnectionManagerFactory
) { ) {
......
...@@ -3,8 +3,8 @@ package chat.rocket.android.chatinformation.ui ...@@ -3,8 +3,8 @@ package chat.rocket.android.chatinformation.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.appcompat.app.AppCompatActivity
import android.support.v7.app.AppCompatActivity import androidx.fragment.app.Fragment
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.chatinformation.ui.MessageInfoFragment.Companion.TAG_MESSAGE_INFO_FRAGMENT import chat.rocket.android.chatinformation.ui.MessageInfoFragment.Companion.TAG_MESSAGE_INFO_FRAGMENT
import chat.rocket.android.util.extensions.addFragment import chat.rocket.android.util.extensions.addFragment
......
package chat.rocket.android.chatinformation.ui package chat.rocket.android.chatinformation.ui
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.widget.DefaultItemAnimator
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.chatinformation.adapter.ReadReceiptAdapter import chat.rocket.android.chatinformation.adapter.ReadReceiptAdapter
import chat.rocket.android.chatinformation.presentation.MessageInfoPresenter import chat.rocket.android.chatinformation.presentation.MessageInfoPresenter
...@@ -37,7 +37,6 @@ class MessageInfoFragment : Fragment(), MessageInfoView { ...@@ -37,7 +37,6 @@ class MessageInfoFragment : Fragment(), MessageInfoView {
lateinit var presenter: MessageInfoPresenter lateinit var presenter: MessageInfoPresenter
private lateinit var adapter: ReadReceiptAdapter private lateinit var adapter: ReadReceiptAdapter
private lateinit var endlessRecyclerViewScrollListener: EndlessRecyclerViewScrollListener
private lateinit var messageId: String private lateinit var messageId: String
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
...@@ -67,24 +66,14 @@ class MessageInfoFragment : Fragment(), MessageInfoView { ...@@ -67,24 +66,14 @@ class MessageInfoFragment : Fragment(), MessageInfoView {
presenter.loadReadReceipts(messageId = messageId) presenter.loadReadReceipts(messageId = messageId)
} }
override fun onDestroyView() {
super.onDestroyView()
receipt_list.removeOnScrollListener(endlessRecyclerViewScrollListener)
}
private fun setupRecyclerView() { private fun setupRecyclerView() {
// Initialize the endlessRecyclerViewScrollListener so we don't NPE at onDestroyView // Initialize the endlessRecyclerViewScrollListener so we don't NPE at onDestroyView
val linearLayoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, true) val linearLayoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, true)
adapter = ReadReceiptAdapter() adapter = ReadReceiptAdapter()
linearLayoutManager.stackFromEnd = true linearLayoutManager.stackFromEnd = true
receipt_list.layoutManager = linearLayoutManager receipt_list.layoutManager = linearLayoutManager
receipt_list.itemAnimator = DefaultItemAnimator() receipt_list.itemAnimator = DefaultItemAnimator()
receipt_list.adapter = adapter receipt_list.adapter = adapter
endlessRecyclerViewScrollListener = object :
EndlessRecyclerViewScrollListener(receipt_list.layoutManager as LinearLayoutManager) {
override fun onLoadMore(page: Int, totalItemsCount: Int, recyclerView: RecyclerView?) {
}
}
} }
override fun showGenericErrorMessage() { override fun showGenericErrorMessage() {
......
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
...@@ -90,10 +91,15 @@ abstract class BaseViewHolder<T : BaseViewModel<*>>( ...@@ -90,10 +91,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 = menuItems.filterNot { view.context?.let {
vm.menuItemsToHide.contains(it.itemId) if (it is ContextThemeWrapper && it.baseContext is AppCompatActivity) {
}, callback = this@BaseViewHolder) with(it.baseContext as AppCompatActivity) {
BottomSheetMenu(adapter).show(view.context) 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.AudioAttachmentViewModel import chat.rocket.android.chatroom.uimodel.*
import chat.rocket.android.chatroom.viewmodel.AuthorAttachmentViewModel
import chat.rocket.android.chatroom.viewmodel.BaseFileAttachmentViewModel
import chat.rocket.android.chatroom.viewmodel.BaseViewModel
import chat.rocket.android.chatroom.viewmodel.ColorAttachmentViewModel
import chat.rocket.android.chatroom.viewmodel.GenericFileAttachmentViewModel
import chat.rocket.android.chatroom.viewmodel.ImageAttachmentViewModel
import chat.rocket.android.chatroom.viewmodel.MessageAttachmentViewModel
import chat.rocket.android.chatroom.viewmodel.MessageReplyViewModel
import chat.rocket.android.chatroom.viewmodel.MessageViewModel
import chat.rocket.android.chatroom.viewmodel.UrlPreviewViewModel
import chat.rocket.android.chatroom.viewmodel.VideoAttachmentViewModel
import chat.rocket.android.chatroom.viewmodel.toViewType
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
...@@ -30,9 +20,10 @@ class ChatRoomAdapter( ...@@ -30,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)
...@@ -40,43 +31,43 @@ class ChatRoomAdapter( ...@@ -40,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)
...@@ -116,45 +107,50 @@ class ChatRoomAdapter( ...@@ -116,45 +107,50 @@ 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 clearData() {
dataSet.clear()
notifyDataSetChanged()
}
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
} }
...@@ -174,7 +170,7 @@ class ChatRoomAdapter( ...@@ -174,7 +170,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")
...@@ -248,7 +244,16 @@ class ChatRoomAdapter( ...@@ -248,7 +244,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)
...@@ -28,6 +28,8 @@ class ImageAttachmentViewHolder( ...@@ -28,6 +28,8 @@ class ImageAttachmentViewHolder(
}.build() }.build()
image_attachment.controller = controller image_attachment.controller = controller
file_name.text = data.attachmentTitle file_name.text = data.attachmentTitle
file_description.text = data.attachmentDescription
file_text.text = data.attachmentText
image_attachment.setOnClickListener { image_attachment.setOnClickListener {
ImageHelper.openImage( ImageHelper.openImage(
context, context,
......
...@@ -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,13 +72,15 @@ class MessageReactionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() ...@@ -72,13 +72,15 @@ 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
...@@ -89,7 +91,7 @@ class MessageReactionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() ...@@ -89,7 +91,7 @@ class MessageReactionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>()
.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) {
......
...@@ -5,9 +5,8 @@ import android.text.method.LinkMovementMethod ...@@ -5,9 +5,8 @@ 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.R import chat.rocket.android.R
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.*
...@@ -16,7 +15,7 @@ class MessageViewHolder( ...@@ -16,7 +15,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) {
...@@ -25,24 +24,34 @@ class MessageViewHolder( ...@@ -25,24 +24,34 @@ 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 day_marker_layout.visibility = if (data.showDayMarker) {
else new_messages_notif.visibility = View.GONE day.text = data.currentDayMarkerText
View.VISIBLE
} else {
View.GONE
}
if (data.isFirstUnread) {
new_messages_notif.visibility = View.VISIBLE
} else {
new_messages_notif.visibility = View.GONE
}
text_message_time.text = data.time text_message_time.text = data.time
text_sender.text = data.senderName text_sender.text = data.senderName
text_content.text = data.content text_content.text = data.content
image_avatar.setImageURI(data.avatar) image_avatar.setImageURI(data.avatar)
text_content.setTextColor( text_content.setTextColor(if (data.isTemporary) Color.GRAY else Color.BLACK)
if (data.isTemporary) Color.GRAY else Color.BLACK
)
data.message.let { data.message.let {
text_edit_indicator.isVisible = !it.isSystemMessage() && it.editedBy != null text_edit_indicator.isVisible = !it.isSystemMessage() && it.editedBy != null
image_star_indicator.isVisible = it.starred?.isNotEmpty() ?: false image_star_indicator.isVisible = it.starred?.isNotEmpty() ?: false
} }
if (data.unread == null) { if (data.unread == null) {
read_receipt_view.setVisible(false) read_receipt_view.isVisible = false
} else { } else {
read_receipt_view.setImageResource( read_receipt_view.setImageResource(
if (data.unread == true) { if (data.unread == true) {
...@@ -51,7 +60,7 @@ class MessageViewHolder( ...@@ -51,7 +60,7 @@ class MessageViewHolder(
R.drawable.ic_check_read_24dp R.drawable.ic_check_read_24dp
} }
) )
read_receipt_view.setVisible(true) read_receipt_view.isVisible = true
} }
} }
} }
......
...@@ -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.ChatRoomView import chat.rocket.android.chatroom.presentation.ChatRoomView
import chat.rocket.android.chatroom.ui.ChatRoomFragment import chat.rocket.android.chatroom.ui.ChatRoomFragment
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
...@@ -12,6 +12,10 @@ import kotlinx.coroutines.experimental.Job ...@@ -12,6 +12,10 @@ import kotlinx.coroutines.experimental.Job
@Module @Module
class ChatRoomFragmentModule { class ChatRoomFragmentModule {
@Provides
@PerFragment
fun provideJob() = Job()
@Provides @Provides
@PerFragment @PerFragment
fun chatRoomView(frag: ChatRoomFragment): ChatRoomView { fun chatRoomView(frag: ChatRoomFragment): ChatRoomView {
......
...@@ -7,8 +7,8 @@ import dagger.Module ...@@ -7,8 +7,8 @@ import dagger.Module
import dagger.Provides import dagger.Provides
@Module @Module
@PerActivity
class ChatRoomModule { class ChatRoomModule {
@Provides @Provides
@PerActivity
fun provideChatRoomNavigator(activity: ChatRoomActivity) = ChatRoomNavigator(activity) fun provideChatRoomNavigator(activity: ChatRoomActivity) = ChatRoomNavigator(activity)
} }
\ No newline at end of file
package chat.rocket.android.chatroom.domain package chat.rocket.android.chatroom.domain
data class MessageReply( data class MessageReply(val roomName: String, val permalink: String)
val roomName: String, \ No newline at end of file
val permalink: String
)
\ 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
...@@ -15,6 +15,12 @@ class ChatRoomNavigator(internal val activity: ChatRoomActivity) { ...@@ -15,6 +15,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)
...@@ -38,16 +44,30 @@ class ChatRoomNavigator(internal val activity: ChatRoomActivity) { ...@@ -38,16 +44,30 @@ class ChatRoomNavigator(internal val activity: ChatRoomActivity) {
activity.finish() activity.finish()
} }
fun toDirectMessage(chatRoomId: String, fun toDirectMessage(
chatRoomId: String,
chatRoomName: String, chatRoomName: String,
chatRoomType: String, chatRoomType: String,
isChatRoomReadOnly: Boolean, isChatRoomReadOnly: Boolean,
chatRoomLastSeen: Long, chatRoomLastSeen: Long,
isChatRoomSubscribed: Boolean, isChatRoomSubscribed: Boolean,
isChatRoomCreator: Boolean, isChatRoomCreator: Boolean,
chatRoomMessage: String) { isChatRoomFavorite: Boolean,
activity.startActivity(activity.chatRoomIntent(chatRoomId, chatRoomName, chatRoomType, chatRoomMessage: String
isChatRoomReadOnly, chatRoomLastSeen, isChatRoomSubscribed, isChatRoomCreator, chatRoomMessage)) ) {
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)
} }
......
package chat.rocket.android.chatroom.presentation package chat.rocket.android.chatroom.presentation
import android.net.Uri import chat.rocket.android.chatroom.uimodel.BaseUiModel
import chat.rocket.android.chatroom.viewmodel.BaseViewModel import chat.rocket.android.chatroom.uimodel.suggestion.ChatRoomSuggestionUiModel
import chat.rocket.android.chatroom.viewmodel.suggestion.ChatRoomSuggestionViewModel import chat.rocket.android.chatroom.uimodel.suggestion.CommandSuggestionUiModel
import chat.rocket.android.chatroom.viewmodel.suggestion.CommandSuggestionViewModel import chat.rocket.android.chatroom.uimodel.suggestion.PeopleSuggestionUiModel
import chat.rocket.android.chatroom.viewmodel.suggestion.PeopleSuggestionViewModel
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 +11,27 @@ import chat.rocket.core.model.ChatRoom ...@@ -12,12 +11,27 @@ 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.
* @param clearDataSet If true it will clear the previous data set.
*/ */
fun showMessages(dataSet: List<BaseViewModel<*>>) fun showMessages(dataSet: List<BaseUiModel<*>>, clearDataSet: Boolean)
/**
* Shows the chat room messages in the basis of a search term.
*
* @param dataSet The data set to show.
*/
fun showSearchedMessages(dataSet: List<BaseUiModel<*>>)
/** /**
* Send a message to a chat room. * Send a message to a chat room.
...@@ -43,13 +57,6 @@ interface ChatRoomView : LoadingView, MessageView { ...@@ -43,13 +57,6 @@ interface ChatRoomView : LoadingView, MessageView {
*/ */
fun showFileSelection(filter: Array<String>?) fun showFileSelection(filter: Array<String>?)
/**
* Uploads a file to a chat room.
*
* @param uri The file URI to send.
*/
fun uploadFile(uri: Uri)
/** /**
* Shows a invalid file message. * Shows a invalid file message.
*/ */
...@@ -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<*>>, isMessageReceived: Boolean)
/** /**
* 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.
...@@ -111,15 +118,15 @@ interface ChatRoomView : LoadingView, MessageView { ...@@ -111,15 +118,15 @@ interface ChatRoomView : LoadingView, MessageView {
/** /**
* Clears the message composition. * Clears the message composition.
*/ */
fun clearMessageComposition() fun clearMessageComposition(deleteMessage: Boolean)
fun showInvalidFileSize(fileSize: Int, maxFileSize: Int) fun showInvalidFileSize(fileSize: Int, maxFileSize: Int)
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.
......
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))
with(view) {
actionSnackbar.getView().setPadding(0)
actionSnackbar.getView().setBackgroundColor(ContextCompat.getColor(context, R.color.colorWhite))
actionSnackbar.parser = parser actionSnackbar.parser = parser
actionSnackbar.messageTextView = view.findViewById(R.id.text_view_action_text) as TextView actionSnackbar.messageTextView = text_view_action_text
actionSnackbar.titleTextView = view.findViewById(R.id.text_view_action_title) as TextView actionSnackbar.titleTextView = text_view_action_title
actionSnackbar.cancelView = view.findViewById(R.id.image_view_action_cancel_quote) as ImageView actionSnackbar.cancelView = image_view_action_cancel_quote
actionSnackbar.duration = BaseTransientBottomBar.LENGTH_INDEFINITE actionSnackbar.duration = BaseTransientBottomBar.LENGTH_INDEFINITE
val spannable = Markwon.markdown(context, content).trim() val spannable = Markwon.markdown(context, content).trim()
actionSnackbar.messageTextView.content = spannable actionSnackbar.messageTextView.content = spannable
}
return actionSnackbar return actionSnackbar
} }
} }
...@@ -56,13 +65,10 @@ class ActionSnackbar : BaseTransientBottomBar<ActionSnackbar> { ...@@ -56,13 +65,10 @@ 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())
...@@ -70,7 +76,7 @@ class ActionSnackbar : BaseTransientBottomBar<ActionSnackbar> { ...@@ -70,7 +76,7 @@ class ActionSnackbar : BaseTransientBottomBar<ActionSnackbar> {
} }
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())
......
...@@ -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
import android.graphics.drawable.Drawable
import android.net.Uri
import androidx.core.view.isVisible
import chat.rocket.android.util.extensions.getFileName
import chat.rocket.android.util.extensions.getMimeType
fun ChatRoomFragment.showFileAttachmentDialog(uri: Uri) {
activity?.let { fragmentActivity ->
uri.getMimeType(fragmentActivity).let { mimeType ->
description.text.clear()
when {
mimeType.startsWith("image") -> {
imagePreview.isVisible = true
imagePreview.setImageURI(uri)
}
mimeType.startsWith("video") -> {
audioVideoAttachment.isVisible = true
}
else -> {
textFile.isVisible = true
textFile.text = uri.getFileName(fragmentActivity)
}
}
}
}
sendButton.setOnClickListener {
presenter.uploadFile(chatRoomId, uri, (citation ?: "") + description.text.toString())
alertDialog.dismiss()
}
cancelButton.setOnClickListener { alertDialog.dismiss() }
alertDialog.show()
}
fun ChatRoomFragment.showDrawAttachmentDialog(byteArray: ByteArray) {
description.text.clear()
imagePreview.isVisible = true
imagePreview.setImageDrawable(Drawable.createFromStream(byteArray.inputStream(), ""))
sendButton.setOnClickListener {
presenter.uploadDrawingImage(
chatRoomId,
byteArray,
(citation ?: "") + description.text.toString()
)
alertDialog.dismiss()
}
cancelButton.setOnClickListener { alertDialog.dismiss() }
alertDialog.show()
}
\ No newline at end of file
This diff is collapsed.
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
......
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.
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.
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