Unverified Commit c54037ca authored by divyanshu bhargava's avatar divyanshu bhargava Committed by GitHub

Merge pull request #10 from RocketChat/develop

merge
parents 46eff2d8 9f49e746
...@@ -57,7 +57,7 @@ jobs: ...@@ -57,7 +57,7 @@ jobs:
command: ./gradlew lint command: ./gradlew lint
- run: - run:
name: Run Unit test name: Run Unit test
command: echo ./gradlew test # TODO: Fix unit test errors soon... command: ./gradlew test
- store_artifacts: - store_artifacts:
path: app/build/reports/ path: app/build/reports/
destination: reports destination: reports
......
Your Rocket.Chat.Android version: (make sure you are running the latest) ## Description
<!-- Version can be found by opening the side menu and then clicking on the chevron alongside username -->
<!-- Please, describe what's the issue here. -->
## Devices and Versions
<!-- Version can be found by opening the side menu and then clicking on "Settings" and then "About" -->
Your Rocket.Chat.Android version: (e.g. 2.1.0)
Your Rocket.Chat Server version: (e.g. 0.63.1-develop)
<!-- Found a bug? List all devices that reproduced it and all that doesn't --> <!-- Found a bug? List all devices that reproduced it and all that doesn't -->
Mobile device model and OS version: (e.g. "Nexus 7 - Android 6.0.1") Mobile device model and OS version: (e.g. "Nexus 7 - Android 6.0.1")
<!-- Don't forget to list the steps to reproduce. Stack traces may help too :) -->
## Steps to reproduce
<!-- In case it is a bug, can you describe the steps to reproduce it please? -->
## Logs
<!-- Do you have any logs? It can help the developers indentifying the cause in case it's a bug. -->
<!-- To get the logs, you can use [Logcat](https://developer.android.com/studio/debug/am-logcat.html) in Android Studio or you can use [Pidcat](https://github.com/JakeWharton/pidcat) -->
#.travis.yml
language: android
jdk: oraclejdk8
sudo: required
android:
components: # Cookbooks version: https://github.com/travis-ci/travis-cookbooks/tree/9c6cd11
- tools # Update preinstalled tools from revision 24.0.2 to 24.4.1
- build-tools-25.0.3 # Match build-tools version used in build.gradle
- platform-tools # Update platform-tools to revision 25.0.3+
- tools # Update tools from revision 24.4.1 to 25.2.5
env:
global:
- API=26 # Android API level 26 by default
- TAG=google_apis # Google APIs by default, alternatively use default
- ABI=armeabi-v7a # ARM ABI v7a by default
- QEMU_AUDIO_DRV=none # Disable emulator audio to avoid warning
- ANDROID_HOME=/usr/local/android-sdk # Depends on the cookbooks version used in the VM
- TOOLS=${ANDROID_HOME}/tools # PATH order matters, exists more than one emulator script
- PATH=${ANDROID_HOME}:${ANDROID_HOME}/emulator:${TOOLS}:${TOOLS}/bin:${ANDROID_HOME}/platform-tools:${PATH}
- ADB_INSTALL_TIMEOUT=20 # minutes (2 minutes by default)
install:
# List and delete unnecessary components to free space
- sdkmanager --list || true
- sdkmanager --uninstall "system-images;android-15;default;armeabi-v7a"
# Update sdk tools to latest version and install/update components
- echo yes | sdkmanager "tools"
- echo yes | sdkmanager "platforms;android-26" # Latest platform required by SDK tools
- echo yes | sdkmanager "platforms;android-${API}" # Android platform required by emulator
- echo yes | sdkmanager "extras;android;m2repository"
- echo yes | sdkmanager "extras;google;m2repository"
- echo yes | sdkmanager "extras;m2repository;com;android;support;constraint;constraint-layout;1.0.2"
- echo yes | sdkmanager "extras;m2repository;com;android;support;constraint;constraint-layout-solver;1.0.2"
# - echo yes | sdkmanager "$EMULATOR" # Install emulator system image
# Create and start emulator
# - echo no | avdmanager create avd -n acib -k "$EMULATOR" -f --abi "$ABI" --tag "$TAG"
# - emulator -avd acib -engine classic -no-window -verbose -qemu -m 512 &
before_script:
# - echo y | android update sdk --no-ui --all --filter tools,platform-tools
# - echo y | android update sdk --no-ui --all --filter android-25
# - echo y | android update sdk --no-ui --all --filter extra-android-m2repository,extra-android-support
# - echo y | android update sdk --no-ui --all --filter extra-google-m2repository,extra-google-google_play_services
# - echo y | android update sdk --no-ui --all --filter build-tools-25.0.3
# - echo yes | sdkmanager "extras;m2repository;com;android;support;constraint;constraint-layout;1.0.2"
# - echo yes | sdkmanager "extras;m2repository;com;android;support;constraint;constraint-layout-solver;1.0.2"
- ./gradlew dependencies
script:
- ./gradlew checkstyle findbugs pmd
...@@ -5,34 +5,36 @@ ...@@ -5,34 +5,36 @@
[![CircleCI](https://circleci.com/gh/RocketChat/Rocket.Chat.Android/tree/develop.svg?style=shield)](https://circleci.com/gh/RocketChat/Rocket.Chat.Android/tree/develop) [![Build Status](https://travis-ci.org/RocketChat/Rocket.Chat.Android.svg?branch=develop)](https://travis-ci.org/RocketChat/Rocket.Chat.Android) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/a81156a8682e4649994270d3670c3c83)](https://www.codacy.com/app/matheusjardimb/Rocket.Chat.Android) [![CircleCI](https://circleci.com/gh/RocketChat/Rocket.Chat.Android/tree/develop.svg?style=shield)](https://circleci.com/gh/RocketChat/Rocket.Chat.Android/tree/develop) [![Build Status](https://travis-ci.org/RocketChat/Rocket.Chat.Android.svg?branch=develop)](https://travis-ci.org/RocketChat/Rocket.Chat.Android) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/a81156a8682e4649994270d3670c3c83)](https://www.codacy.com/app/matheusjardimb/Rocket.Chat.Android)
## Description ## Description
Currently, the app is maintained in two branches, namely `v1+` and `v2+`. The `v1+` is maintained in the `develop` branch and the `v2+` is maintained in the `develop-2.x` branch. The older version is written partially in `java` and `kotlin`, but we intend to write the latest version completely in `kotlin`.
Clone the repository by running `git clone https://github.com/RocketChat/Rocket.Chat.Android.git` in your terminal. To build the v1.0+ of the app, run `git checkout develop` and to build the v2.0+, run `git checkout develop-2.x`. This repository contains all the code related to the Android native application of [Rocket.Chat](https://github.com/RocketChat/Rocket.Chat/#about-rocketchat). To send new pull-requests, always use the branch `develop` as base and open an issue with the description of what you want/need to accomplish, if the issue wasn't created yet.
Since both the versions use `kotlin` for some or all of their classes, following are the common prerequisites for both versions:
## 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. - 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.
- 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.
### Instructions specific to version ### SDK Instructions
#### v1.0+
- After checking out to `develop` branch as mentioned above, simply import the project in Android Studio.
#### v2+
- This version requires the [Kotlin SDK](https://github.com/RocketChat/Rocket.Chat.Kotlin.SDK) for Rocket.Chat. Clone the Kotlin SDK in by running `git clone https://github.com/RocketChat/Rocket.Chat.Kotlin.SDK.git`. - This version requires the [Kotlin SDK](https://github.com/RocketChat/Rocket.Chat.Kotlin.SDK) for Rocket.Chat. Clone the Kotlin SDK in by running `git clone https://github.com/RocketChat/Rocket.Chat.Kotlin.SDK.git`.
- First, a build is required for the SDK, so that required jar files are generated. Make sure that the android repository and the kotlin sdk have the same immediate parent directory. Change the current directory to `Rocket.Chat.Android/app` and run the `build-sdk.sh` which will result in creating of the required jar file `core*.jar` and `common*.jar` in `Rocket.Chat.Android/app/libs`,by the following steps in your terminal window: - First, a build is required for the SDK, so that required jar files are generated. Make sure that the android repository and the kotlin sdk have the same immediate parent directory. Change the current directory to `Rocket.Chat.Android/app` and run the `build-sdk.sh` which will result in creating of the required jar file `core*.jar` and `common*.jar` in `Rocket.Chat.Android/app/libs`,by the following steps in your terminal window:
``` ```
cd Rocket.Chat.Android/app cd Rocket.Chat.Android/app
./build-sdk.sh ./build-sdk.sh
``` ```
## How to run ## How to run
### Command Line ### Command Line
- Connect your physical device to your pc via USB or start an emulator. Run `adb devices` in terminal. You should see your device in the list of devices. - Connect your physical device to your pc via USB or start an emulator. Run `adb devices` in terminal. You should see your device in the list of devices.
- In order to build the debug apk, run `./gradlew assembleDebug`. This would generate a debug apk which can be found under `Rocket.Chat.Android/app/build/outputs/apk/debug` folder with the name `app-debug.apk`. - In order to build the debug apk, run `./gradlew assembleDebug`. This would generate a debug apk which can be found under `Rocket.Chat.Android/app/build/outputs/apk/debug` folder with the name `app-debug.apk`.
- In order to build and install the apk directly to the connected device, run `./gradlew installDebug`. - In order to build and install the apk directly to the connected device, run `./gradlew installDebug`.
### Android Studio ### Android Studio
- After importing the project in android studio, go to `Run > Run app` and then select your device, or create a new virtual device by following the wizard. - After importing the project in android studio, go to `Run > Run app` and then select your device, or create a new virtual device by following the wizard.
## Bug report & Feature request ## Bug report & Feature request
......
...@@ -13,8 +13,8 @@ android { ...@@ -13,8 +13,8 @@ android {
applicationId "chat.rocket.android" applicationId "chat.rocket.android"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion versions.targetSdk targetSdkVersion versions.targetSdk
versionCode 2011 versionCode 2015
versionName "2.0.1" versionName "2.1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true multiDexEnabled true
} }
...@@ -78,6 +78,8 @@ dependencies { ...@@ -78,6 +78,8 @@ dependencies {
implementation libraries.room implementation libraries.room
kapt libraries.roomProcessor kapt libraries.roomProcessor
implementation libraries.roomRxjava implementation libraries.roomRxjava
implementation libraries.lifecycleExtensions
kapt libraries.lifecycleCompiler
implementation libraries.rxKotlin implementation libraries.rxKotlin
implementation libraries.rxAndroid implementation libraries.rxAndroid
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" /> <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<permission <permission
...@@ -50,37 +49,52 @@ ...@@ -50,37 +49,52 @@
android:scheme="https" /> android:scheme="https" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
android:name=".server.ui.ChangeServerActivity" android:name=".server.ui.ChangeServerActivity"
android:theme="@style/AuthenticationTheme" /> android:theme="@style/AuthenticationTheme" />
<activity <activity
android:name=".main.ui.MainActivity" android:name=".main.ui.MainActivity"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" /> android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<activity <activity
android:name=".webview.ui.WebViewActivity" android:name=".webview.ui.WebViewActivity"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" /> android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<activity <activity
android:name=".webview.cas.ui.CasWebViewActivity" android:name=".webview.cas.ui.CasWebViewActivity"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" /> android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<activity <activity
android:name=".webview.oauth.ui.OauthWebViewActivity" android:name=".webview.oauth.ui.OauthWebViewActivity"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" /> android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<activity <activity
android:name=".chatroom.ui.ChatRoomActivity" android:name=".chatroom.ui.ChatRoomActivity"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" /> android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<!-- TODO: Change to fragment-->
<activity <activity
android:name=".chatroom.ui.PinnedMessagesActivity" android:name=".chatroom.ui.PinnedMessagesActivity"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" /> android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
<!-- TODO: Change to fragment-->
<activity <activity
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="com.google.android.gms.gcm.GcmReceiver" android:name="com.google.android.gms.gcm.GcmReceiver"
android:exported="true" android:exported="true"
...@@ -124,10 +138,6 @@ ...@@ -124,10 +138,6 @@
<meta-data <meta-data
android:name="io.fabric.ApiKey" android:name="io.fabric.ApiKey"
android:value="12ac6e94f850aaffcdff52001af77ca415d06a43" /> android:value="12ac6e94f850aaffcdff52001af77ca415d06a43" />
<activity
android:name=".settings.about.ui.AboutActivity"
android:theme="@style/AppTheme" />
</application> </application>
</manifest> </manifest>
package chat.rocket.android.app
import android.arch.lifecycle.Lifecycle
import android.arch.lifecycle.LifecycleObserver
import android.arch.lifecycle.OnLifecycleEvent
import chat.rocket.android.server.domain.GetAccountInteractor
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.common.RocketChatException
import chat.rocket.common.model.UserStatus
import chat.rocket.core.internal.realtime.setTemporaryStatus
import kotlinx.coroutines.experimental.launch
import timber.log.Timber
import javax.inject.Inject
class AppLifecycleObserver @Inject constructor(
private val serverInteractor: GetCurrentServerInteractor,
private val factory: RocketChatClientFactory,
private val getAccountInteractor: GetAccountInteractor
) : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onEnterForeground() {
changeTemporaryStatus(UserStatus.Online())
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onEnterBackground() {
changeTemporaryStatus(UserStatus.Away())
}
private fun changeTemporaryStatus(userStatus: UserStatus) {
launch {
val currentServer = serverInteractor.get()
val account = currentServer?.let { getAccountInteractor.get(currentServer) }
val client = account?.let { factory.create(currentServer) }
try {
client?.setTemporaryStatus(userStatus)
} catch (exception: RocketChatException) {
Timber.e(exception)
}
}
}
}
\ No newline at end of file
...@@ -108,18 +108,12 @@ object DrawableHelper { ...@@ -108,18 +108,12 @@ object DrawableHelper {
* @see [UserStatus] * @see [UserStatus]
* @return The user status drawable. * @return The user status drawable.
*/ */
fun getUserStatusDrawable(userStatus: UserStatus, context: Context): Drawable { fun getUserStatusDrawable(userStatus: UserStatus?, context: Context): Drawable {
return when (userStatus) { return when (userStatus) {
is UserStatus.Online -> { is UserStatus.Online -> getDrawableFromId(R.drawable.ic_status_online_12dp, context)
getDrawableFromId(R.drawable.ic_status_online_24dp, context) is UserStatus.Away -> getDrawableFromId(R.drawable.ic_status_away_12dp, context)
} is UserStatus.Busy -> getDrawableFromId(R.drawable.ic_status_busy_12dp, context)
is UserStatus.Away -> { else -> getDrawableFromId(R.drawable.ic_status_invisible_12dp, context)
getDrawableFromId(R.drawable.ic_status_away_24dp, context)
}
is UserStatus.Busy -> {
getDrawableFromId(R.drawable.ic_status_busy_24dp, context)
}
else -> getDrawableFromId(R.drawable.ic_status_invisible_24dp, context)
} }
} }
} }
\ No newline at end of file
...@@ -3,6 +3,7 @@ package chat.rocket.android.app ...@@ -3,6 +3,7 @@ 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
...@@ -43,9 +44,11 @@ import timber.log.Timber ...@@ -43,9 +44,11 @@ import timber.log.Timber
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import javax.inject.Inject import javax.inject.Inject
class RocketChatApplication : Application(), HasActivityInjector, HasServiceInjector, class RocketChatApplication : Application(), HasActivityInjector, HasServiceInjector,
HasBroadcastReceiverInjector { HasBroadcastReceiverInjector {
@Inject
lateinit var appLifecycleObserver: AppLifecycleObserver
@Inject @Inject
lateinit var activityDispatchingAndroidInjector: DispatchingAndroidInjector<Activity> lateinit var activityDispatchingAndroidInjector: DispatchingAndroidInjector<Activity>
...@@ -84,7 +87,14 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -84,7 +87,14 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
DaggerAppComponent.builder().application(this).build().inject(this) DaggerAppComponent.builder()
.application(this)
.build()
.inject(this)
ProcessLifecycleOwner.get()
.lifecycle
.addObserver(appLifecycleObserver)
// TODO - remove this on the future, temporary migration stuff for pre-release versions. // TODO - remove this on the future, temporary migration stuff for pre-release versions.
migrateInternalTokens() migrateInternalTokens()
......
...@@ -8,7 +8,6 @@ import chat.rocket.android.infrastructure.LocalRepository ...@@ -8,7 +8,6 @@ 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.server.presentation.CheckServerPresenter
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.RocketChatAuthException import chat.rocket.common.RocketChatAuthException
...@@ -27,28 +26,28 @@ private const val TYPE_LOGIN_USER_EMAIL = 0 ...@@ -27,28 +26,28 @@ private const val TYPE_LOGIN_USER_EMAIL = 0
private const val TYPE_LOGIN_CAS = 1 private const val TYPE_LOGIN_CAS = 1
private const val TYPE_LOGIN_OAUTH = 2 private const val TYPE_LOGIN_OAUTH = 2
private const val TYPE_LOGIN_DEEP_LINK = 3 private const val TYPE_LOGIN_DEEP_LINK = 3
private const val SERVICE_NAME_FACEBOOK = "facebook"
private const val SERVICE_NAME_GITHUB = "github" private const val SERVICE_NAME_GITHUB = "github"
private const val SERVICE_NAME_GOOGLE = "google" private const val SERVICE_NAME_GOOGLE = "google"
private const val SERVICE_NAME_LINKEDIN = "linkedin" private const val SERVICE_NAME_LINKEDIN = "linkedin"
private const val SERVICE_NAME_GILAB = "gitlab" private const val SERVICE_NAME_GILAB = "gitlab"
class LoginPresenter @Inject constructor(private val view: LoginView, class LoginPresenter @Inject constructor(
private val strategy: CancelStrategy, private val view: LoginView,
private val navigator: AuthenticationNavigator, private val strategy: CancelStrategy,
private val tokenRepository: TokenRepository, private val navigator: AuthenticationNavigator,
private val localRepository: LocalRepository, private val tokenRepository: TokenRepository,
private val getAccountsInteractor: GetAccountsInteractor, private val localRepository: LocalRepository,
private val settingsInteractor: GetSettingsInteractor, private val getAccountsInteractor: GetAccountsInteractor,
serverInteractor: GetCurrentServerInteractor, private val settingsInteractor: GetSettingsInteractor,
private val saveAccountInteractor: SaveAccountInteractor, serverInteractor: GetCurrentServerInteractor,
private val factory: RocketChatClientFactory) private val saveAccountInteractor: SaveAccountInteractor,
: CheckServerPresenter(strategy, factory, view) { private val factory: RocketChatClientFactory
) {
// TODO - we should validate the current server when opening the app, and have a nonnull get() // TODO - we should validate the current server when opening the app, and have a nonnull get()
private val currentServer = serverInteractor.get()!! private val currentServer = serverInteractor.get()!!
private lateinit var client: RocketChatClient private lateinit var client: RocketChatClient
private lateinit var settings: PublicSettings private lateinit var settings: PublicSettings
//private val client: RocketChatClient = factory.create(currentServer)
//private val settings: PublicSettings = settingsInteractor.get(currentServer)
private lateinit var usernameOrEmail: String private lateinit var usernameOrEmail: String
private lateinit var password: String private lateinit var password: String
private lateinit var credentialToken: String private lateinit var credentialToken: String
...@@ -62,7 +61,6 @@ class LoginPresenter @Inject constructor(private val view: LoginView, ...@@ -62,7 +61,6 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
setupUserRegistrationView() setupUserRegistrationView()
setupCasView() setupCasView()
setupOauthServicesView() setupOauthServicesView()
checkServerInfo(currentServer)
} }
fun authenticateWithUserAndPassword(usernameOrEmail: String, password: String) { fun authenticateWithUserAndPassword(usernameOrEmail: String, password: String) {
...@@ -92,25 +90,14 @@ class LoginPresenter @Inject constructor(private val view: LoginView, ...@@ -92,25 +90,14 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
doAuthentication(TYPE_LOGIN_OAUTH) doAuthentication(TYPE_LOGIN_OAUTH)
} }
fun authenticadeWithDeepLink(deepLinkInfo: LoginDeepLinkInfo) { fun authenticateWithDeepLink(deepLinkInfo: LoginDeepLinkInfo) {
val serverUrl = deepLinkInfo.url val serverUrl = deepLinkInfo.url
setupConnectionInfo(serverUrl) setupConnectionInfo(serverUrl)
deepLinkUserId = deepLinkInfo.userId deepLinkUserId = deepLinkInfo.userId
deepLinkToken = deepLinkInfo.token deepLinkToken = deepLinkInfo.token
tokenRepository.save(serverUrl, Token(deepLinkUserId, deepLinkToken)) tokenRepository.save(serverUrl, Token(deepLinkUserId, deepLinkToken))
launchUI(strategy) {
try { doAuthentication(TYPE_LOGIN_DEEP_LINK)
val version = checkServerVersion(serverUrl).await()
when (version) {
is Version.OutOfDateError -> {
view.blockAndAlertNotRequiredVersion()
}
else -> doAuthentication(TYPE_LOGIN_DEEP_LINK)
}
} catch (ex: Exception) {
Timber.d(ex, "Error performing deep link login")
}
}
} }
private fun setupConnectionInfo(serverUrl: String) { private fun setupConnectionInfo(serverUrl: String) {
...@@ -139,7 +126,7 @@ class LoginPresenter @Inject constructor(private val view: LoginView, ...@@ -139,7 +126,7 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
} }
private fun setupUserRegistrationView() { private fun setupUserRegistrationView() {
if (settings.isRegistrationEnabledForNewUsers()) { if (settings.isRegistrationEnabledForNewUsers() && settings.isLoginFormEnabled()) {
view.showSignUpView() view.showSignUpView()
view.setupSignUpView() view.setupSignUpView()
} }
...@@ -156,9 +143,12 @@ class LoginPresenter @Inject constructor(private val view: LoginView, ...@@ -156,9 +143,12 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
var totalSocialAccountsEnabled = 0 var totalSocialAccountsEnabled = 0
if (settings.isFacebookAuthenticationEnabled()) { if (settings.isFacebookAuthenticationEnabled()) {
// //TODO: Remove until we have this implemented val clientId = getOauthClientId(services, SERVICE_NAME_FACEBOOK)
// view.enableLoginByFacebook() if (clientId != null) {
// totalSocialAccountsEnabled++ view.setupFacebookButtonListener(OauthHelper.getFacebookOauthUrl(clientId, currentServer, state), state)
view.enableLoginByFacebook()
totalSocialAccountsEnabled++
}
} }
if (settings.isGithubAuthenticationEnabled()) { if (settings.isGithubAuthenticationEnabled()) {
val clientId = getOauthClientId(services, SERVICE_NAME_GITHUB) val clientId = getOauthClientId(services, SERVICE_NAME_GITHUB)
...@@ -197,7 +187,21 @@ class LoginPresenter @Inject constructor(private val view: LoginView, ...@@ -197,7 +187,21 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
if (settings.isGitlabAuthenticationEnabled()) { if (settings.isGitlabAuthenticationEnabled()) {
val clientId = getOauthClientId(services, SERVICE_NAME_GILAB) val clientId = getOauthClientId(services, SERVICE_NAME_GILAB)
if (clientId != null) { if (clientId != null) {
view.setupGitlabButtonListener(OauthHelper.getGitlabOauthUrl(clientId, currentServer, state), state) val gitlabOauthUrl = if (settings.gitlabUrl() != null) {
OauthHelper.getGitlabOauthUrl(
host = settings.gitlabUrl(),
clientId = clientId,
serverUrl = currentServer,
state = state
)
} else {
OauthHelper.getGitlabOauthUrl(
clientId = clientId,
serverUrl = currentServer,
state = state
)
}
view.setupGitlabButtonListener(gitlabOauthUrl, state)
view.enableLoginByGitlab() view.enableLoginByGitlab()
totalSocialAccountsEnabled++ totalSocialAccountsEnabled++
} }
...@@ -289,9 +293,10 @@ class LoginPresenter @Inject constructor(private val view: LoginView, ...@@ -289,9 +293,10 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
} }
} }
private fun getOauthClientId(listMap: List<Map<String, String>>, serviceName: String): String? { private fun getOauthClientId(listMap: List<Map<String, Any>>, serviceName: String): String? {
return listMap.find { map -> map.containsValue(serviceName) } return listMap.find { map -> map.containsValue(serviceName) }?.let {
?.get("appId") it["clientId"] ?: it["appId"]
}.toString()
} }
private suspend fun saveAccount(username: String) { private suspend fun saveAccount(username: String) {
......
...@@ -4,7 +4,7 @@ import chat.rocket.android.authentication.server.presentation.VersionCheckView ...@@ -4,7 +4,7 @@ import chat.rocket.android.authentication.server.presentation.VersionCheckView
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
interface LoginView : LoadingView, MessageView, VersionCheckView { interface LoginView : LoadingView, MessageView {
/** /**
* Shows the form view (i.e the username/email and password fields) if it is enabled by the server settings. * Shows the form view (i.e the username/email and password fields) if it is enabled by the server settings.
...@@ -145,6 +145,14 @@ interface LoginView : LoadingView, MessageView, VersionCheckView { ...@@ -145,6 +145,14 @@ interface LoginView : LoadingView, MessageView, VersionCheckView {
*/ */
fun setupLinkedinButtonListener(linkedinUrl: String, state: String) fun setupLinkedinButtonListener(linkedinUrl: String, state: String)
/**
* Setups the Facebook button when tapped.
*
* @param facebookOauthUrl The Facebook OAuth URL to authenticate with.
* @param state A random string generated by the app, which you'll verify later (to protect against forgery attacks).
*/
fun setupFacebookButtonListener(facebookOauthUrl: String, state: String)
/** /**
* Shows the "login by Meteor" view if it is enable by the server settings. * Shows the "login by Meteor" view if it is enable by the server settings.
*/ */
......
...@@ -72,7 +72,7 @@ class LoginFragment : Fragment(), LoginView { ...@@ -72,7 +72,7 @@ class LoginFragment : Fragment(), LoginView {
} }
deepLinkInfo?.let { deepLinkInfo?.let {
presenter.authenticadeWithDeepLink(it) presenter.authenticateWithDeepLink(it)
}.ifNull { }.ifNull {
presenter.setupView() presenter.setupView()
} }
...@@ -261,6 +261,15 @@ class LoginFragment : Fragment(), LoginView { ...@@ -261,6 +261,15 @@ class LoginFragment : Fragment(), LoginView {
} }
} }
override fun setupFacebookButtonListener(facebookOauthUrl: String, state: String) {
ui { activity ->
button_facebook.setOnClickListener {
startActivityForResult(activity.oauthWebViewIntent(facebookOauthUrl, state), REQUEST_CODE_FOR_OAUTH)
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
}
}
}
override fun enableLoginByGithub() { override fun enableLoginByGithub() {
ui { ui {
button_github.isClickable = true button_github.isClickable = true
...@@ -370,27 +379,6 @@ class LoginFragment : Fragment(), LoginView { ...@@ -370,27 +379,6 @@ class LoginFragment : Fragment(), LoginView {
} }
} }
override fun alertNotRecommendedVersion() {
ui {
AlertDialog.Builder(it)
.setMessage(getString(R.string.msg_ver_not_recommended, BuildConfig.RECOMMENDED_SERVER_VERSION))
.setPositiveButton(R.string.msg_ok, null)
.create()
.show()
}
}
override fun blockAndAlertNotRequiredVersion() {
ui {
AlertDialog.Builder(it)
.setMessage(getString(R.string.msg_ver_not_minimum, BuildConfig.REQUIRED_SERVER_VERSION))
.setOnDismissListener { activity?.onBackPressed() }
.setPositiveButton(R.string.msg_ok, null)
.create()
.show()
}
}
private fun showRemainingSocialAccountsView() { private fun showRemainingSocialAccountsView() {
social_accounts_container.postDelayed(300) { social_accounts_container.postDelayed(300) {
ui { ui {
......
...@@ -7,9 +7,10 @@ import chat.rocket.android.core.lifecycle.CancelStrategy ...@@ -7,9 +7,10 @@ import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.server.domain.GetAccountsInteractor import chat.rocket.android.server.domain.GetAccountsInteractor
import chat.rocket.android.server.domain.RefreshSettingsInteractor import chat.rocket.android.server.domain.RefreshSettingsInteractor
import chat.rocket.android.server.domain.SaveCurrentServerInteractor import chat.rocket.android.server.domain.SaveCurrentServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
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.extensions.launchUI
import chat.rocket.common.util.ifNull
import javax.inject.Inject import javax.inject.Inject
class ServerPresenter @Inject constructor(private val view: ServerView, class ServerPresenter @Inject constructor(private val view: ServerView,
...@@ -17,7 +18,18 @@ class ServerPresenter @Inject constructor(private val view: ServerView, ...@@ -17,7 +18,18 @@ class ServerPresenter @Inject constructor(private val view: ServerView,
private val navigator: AuthenticationNavigator, private val navigator: AuthenticationNavigator,
private val serverInteractor: SaveCurrentServerInteractor, private val serverInteractor: SaveCurrentServerInteractor,
private val refreshSettingsInteractor: RefreshSettingsInteractor, private val refreshSettingsInteractor: RefreshSettingsInteractor,
private val getAccountsInteractor: GetAccountsInteractor) { private val getAccountsInteractor: GetAccountsInteractor,
factory: RocketChatClientFactory
) : CheckServerPresenter(strategy, factory, view) {
fun checkServer(server: String) {
if (!server.isValidUrl()) {
view.showInvalidServerUrlMessage()
} else {
view.showLoading()
checkServerInfo(server)
}
}
fun connect(server: String) { fun connect(server: String) {
connectToServer(server) { connectToServer(server) {
...@@ -25,7 +37,7 @@ class ServerPresenter @Inject constructor(private val view: ServerView, ...@@ -25,7 +37,7 @@ class ServerPresenter @Inject constructor(private val view: ServerView,
} }
} }
fun connectToServer(server: String, block: () -> Unit) { private fun connectToServer(server: String, block: () -> Unit) {
if (!server.isValidUrl()) { if (!server.isValidUrl()) {
view.showInvalidServerUrlMessage() view.showInvalidServerUrlMessage()
} else { } else {
......
...@@ -3,7 +3,7 @@ package chat.rocket.android.authentication.server.presentation ...@@ -3,7 +3,7 @@ package chat.rocket.android.authentication.server.presentation
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
interface ServerView : LoadingView, MessageView { interface ServerView : LoadingView, MessageView, VersionCheckView {
/** /**
* Shows an invalid server URL message. * Shows an invalid server URL message.
......
...@@ -10,4 +10,20 @@ interface VersionCheckView { ...@@ -10,4 +10,20 @@ interface VersionCheckView {
* Block user to proceed and alert him due to server having an unsupported server version. * Block user to proceed and alert him due to server having an unsupported server version.
*/ */
fun blockAndAlertNotRequiredVersion() fun blockAndAlertNotRequiredVersion()
/**
* Alerts the user that an error has occurred while checking the server version
* This is optional.
*/
fun errorCheckingServerVersion() {}
/**
* Do some action if version is ok. This is optional.
*/
fun versionOk() {}
/**
* Alters the user this protocol is invalid. This is optional.
*/
fun errorInvalidProtocol() {}
} }
\ No newline at end of file
package chat.rocket.android.authentication.server.ui package chat.rocket.android.authentication.server.ui
import android.app.AlertDialog
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.Fragment import android.support.v4.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
import android.view.ViewTreeObserver import android.view.ViewTreeObserver
import android.widget.AdapterView
import android.widget.ArrayAdapter
import chat.rocket.android.BuildConfig
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.server.presentation.ServerPresenter import chat.rocket.android.authentication.server.presentation.ServerPresenter
import chat.rocket.android.authentication.server.presentation.ServerView import chat.rocket.android.authentication.server.presentation.ServerView
import chat.rocket.android.helper.KeyboardHelper import chat.rocket.android.helper.KeyboardHelper
import chat.rocket.android.util.extensions.* import chat.rocket.android.util.extensions.*
import chat.rocket.common.util.ifNull
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_authentication_server.* import kotlinx.android.synthetic.main.fragment_authentication_server.*
import javax.inject.Inject import javax.inject.Inject
class ServerFragment : Fragment(), ServerView { class ServerFragment : Fragment(), ServerView {
@Inject lateinit var presenter: ServerPresenter @Inject
lateinit var presenter: ServerPresenter
private var deepLinkInfo: LoginDeepLinkInfo? = null private var deepLinkInfo: LoginDeepLinkInfo? = null
private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener { private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener {
text_server_url.isCursorVisible = KeyboardHelper.isSoftKeyboardShown(relative_layout.rootView) text_server_url.isCursorVisible = KeyboardHelper.isSoftKeyboardShown(relative_layout.rootView)
...@@ -33,6 +40,8 @@ class ServerFragment : Fragment(), ServerView { ...@@ -33,6 +40,8 @@ class ServerFragment : Fragment(), ServerView {
} }
} }
private var protocol = "https://"
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this) AndroidSupportInjection.inject(this)
...@@ -49,8 +58,41 @@ class ServerFragment : Fragment(), ServerView { ...@@ -49,8 +58,41 @@ class ServerFragment : Fragment(), ServerView {
setupOnClickListener() setupOnClickListener()
deepLinkInfo?.let { deepLinkInfo?.let {
val uri = Uri.parse(it.url)
uri?.let { text_server_url.hintContent = it.host }
presenter.deepLink(it) presenter.deepLink(it)
} }
text_server_protocol.adapter = ArrayAdapter<String>(activity,
android.R.layout.simple_dropdown_item_1line, arrayOf("https://", "http://"))
text_server_protocol.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
when(position) {
0 -> {
protocol = "https://"
}
1 -> {
ui{
AlertDialog.Builder(it)
.setTitle(R.string.msg_warning)
.setMessage(R.string.msg_http_insecure)
.setPositiveButton(R.string.msg_proceed) { _, _ ->
protocol = "http://"
}
.setNegativeButton(R.string.msg_cancel) { _, _ ->
text_server_protocol.setSelection(0)
}
.setCancelable(false)
.create()
.show()
}
}
}
}
override fun onNothingSelected(parent: AdapterView<*>?) {
}
}
} }
override fun onDestroyView() { override fun onDestroyView() {
...@@ -74,7 +116,7 @@ class ServerFragment : Fragment(), ServerView { ...@@ -74,7 +116,7 @@ class ServerFragment : Fragment(), ServerView {
} }
} }
override fun showMessage(resId: Int){ override fun showMessage(resId: Int) {
ui { ui {
showToast(resId) showToast(resId)
} }
...@@ -90,15 +132,70 @@ class ServerFragment : Fragment(), ServerView { ...@@ -90,15 +132,70 @@ class ServerFragment : Fragment(), ServerView {
showMessage(getString(R.string.msg_generic_error)) showMessage(getString(R.string.msg_generic_error))
} }
override fun alertNotRecommendedVersion() {
ui {
hideLoading()
AlertDialog.Builder(it)
.setMessage(getString(R.string.msg_ver_not_recommended, BuildConfig.RECOMMENDED_SERVER_VERSION))
.setPositiveButton(R.string.msg_ok, { _, _ ->
performConnect()
})
.create()
.show()
}
}
override fun blockAndAlertNotRequiredVersion() {
ui {
hideLoading()
AlertDialog.Builder(it)
.setMessage(getString(R.string.msg_ver_not_minimum, BuildConfig.REQUIRED_SERVER_VERSION))
.setPositiveButton(R.string.msg_ok, null)
.setOnDismissListener {
// reset the deeplink info, so the user can log to another server...
deepLinkInfo = null
}
.create()
.show()
}
}
override fun versionOk() {
performConnect()
}
override fun errorCheckingServerVersion() {
hideLoading()
showMessage(R.string.msg_error_checking_server_version)
}
override fun errorInvalidProtocol() {
hideLoading()
showMessage(R.string.msg_invalid_server_protocol)
}
private fun performConnect() {
ui {
deepLinkInfo?.let {
presenter.deepLink(it)
}.ifNull {
val url = text_server_url.textContent.ifEmpty(text_server_url.hintContent)
presenter.connect("${protocol}${url.sanitize()}")
}
}
}
private fun enableUserInput(value: Boolean) { private fun enableUserInput(value: Boolean) {
button_connect.isEnabled = value button_connect.isEnabled = value
text_server_url.isEnabled = value text_server_url.isEnabled = value
} }
private fun setupOnClickListener() { private fun setupOnClickListener() {
button_connect.setOnClickListener { ui {
val url = text_server_url.textContent.ifEmpty(text_server_url.hintContent) button_connect.setOnClickListener {
presenter.connect(text_server_protocol.textContent + url) val url = text_server_url.textContent.ifEmpty(text_server_url.hintContent)
presenter.checkServer("${protocol}${url.sanitize()}")
}
} }
} }
} }
\ No newline at end of file
...@@ -34,7 +34,7 @@ class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector { ...@@ -34,7 +34,7 @@ class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector {
val deepLinkInfo = intent.getLoginDeepLinkInfo() val deepLinkInfo = intent.getLoginDeepLinkInfo()
launch(UI + job) { launch(UI + job) {
val newServer = intent.getBooleanExtra(INTENT_ADD_NEW_SERVER, false) val newServer = intent.getBooleanExtra(INTENT_ADD_NEW_SERVER, false)
// if we got authenticadeWithDeepLink information, pass true to newServer also // if we got authenticateWithDeepLink information, pass true to newServer also
presenter.loadCredentials(newServer || deepLinkInfo != null) { authenticated -> presenter.loadCredentials(newServer || deepLinkInfo != null) { authenticated ->
if (!authenticated) { if (!authenticated) {
showServerInput(savedInstanceState, deepLinkInfo) showServerInput(savedInstanceState, deepLinkInfo)
......
...@@ -15,9 +15,6 @@ import kotlinx.coroutines.experimental.Job ...@@ -15,9 +15,6 @@ import kotlinx.coroutines.experimental.Job
@PerFragment @PerFragment
class ChatRoomFragmentModule { class ChatRoomFragmentModule {
@Provides
fun provideChatRoomNavigator(activity: ChatRoomActivity) = ChatRoomNavigator(activity)
@Provides @Provides
fun chatRoomView(frag: ChatRoomFragment): ChatRoomView { fun chatRoomView(frag: ChatRoomFragment): ChatRoomView {
return frag return frag
......
package chat.rocket.android.chatroom.di
import chat.rocket.android.chatroom.presentation.ChatRoomNavigator
import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.dagger.scope.PerActivity
import dagger.Module
import dagger.Provides
@Module
@PerActivity
class ChatRoomModule {
@Provides
fun provideChatRoomNavigator(activity: ChatRoomActivity) = ChatRoomNavigator(activity)
}
\ No newline at end of file
...@@ -3,6 +3,7 @@ package chat.rocket.android.chatroom.presentation ...@@ -3,6 +3,7 @@ package chat.rocket.android.chatroom.presentation
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.chatroom.ui.ChatRoomActivity import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.members.ui.newInstance import chat.rocket.android.members.ui.newInstance
import chat.rocket.android.server.ui.changeServerIntent
import chat.rocket.android.util.extensions.addFragmentBackStack import chat.rocket.android.util.extensions.addFragmentBackStack
class ChatRoomNavigator(internal val activity: ChatRoomActivity) { class ChatRoomNavigator(internal val activity: ChatRoomActivity) {
...@@ -12,4 +13,9 @@ class ChatRoomNavigator(internal val activity: ChatRoomActivity) { ...@@ -12,4 +13,9 @@ class ChatRoomNavigator(internal val activity: ChatRoomActivity) {
newInstance(chatRoomId, chatRoomType) newInstance(chatRoomId, chatRoomType)
} }
} }
fun toNewServer() {
activity.startActivity(activity.changeServerIntent())
activity.finish()
}
} }
\ No newline at end of file
...@@ -10,6 +10,7 @@ import chat.rocket.android.chatroom.viewmodel.ViewModelMapper ...@@ -10,6 +10,7 @@ import chat.rocket.android.chatroom.viewmodel.ViewModelMapper
import chat.rocket.android.chatroom.viewmodel.suggestion.ChatRoomSuggestionViewModel import chat.rocket.android.chatroom.viewmodel.suggestion.ChatRoomSuggestionViewModel
import chat.rocket.android.chatroom.viewmodel.suggestion.CommandSuggestionViewModel import chat.rocket.android.chatroom.viewmodel.suggestion.CommandSuggestionViewModel
import chat.rocket.android.chatroom.viewmodel.suggestion.PeopleSuggestionViewModel import chat.rocket.android.chatroom.viewmodel.suggestion.PeopleSuggestionViewModel
import chat.rocket.android.core.behaviours.showMessage
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.infrastructure.username import chat.rocket.android.infrastructure.username
...@@ -41,21 +42,23 @@ import timber.log.Timber ...@@ -41,21 +42,23 @@ import timber.log.Timber
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, class ChatRoomPresenter @Inject constructor(
private val navigator: ChatRoomNavigator, private val view: ChatRoomView,
private val strategy: CancelStrategy, private val navigator: ChatRoomNavigator,
getSettingsInteractor: GetSettingsInteractor, private val strategy: CancelStrategy,
serverInteractor: GetCurrentServerInteractor, getSettingsInteractor: GetSettingsInteractor,
private val getChatRoomsInteractor: GetChatRoomsInteractor, serverInteractor: GetCurrentServerInteractor,
private val permissions: GetPermissionsInteractor, private val getChatRoomsInteractor: GetChatRoomsInteractor,
private val uriInteractor: UriInteractor, private val permissions: GetPermissionsInteractor,
private val messagesRepository: MessagesRepository, private val uriInteractor: UriInteractor,
private val usersRepository: UsersRepository, private val messagesRepository: MessagesRepository,
private val roomsRepository: RoomRepository, private val usersRepository: UsersRepository,
private val localRepository: LocalRepository, private val roomsRepository: RoomRepository,
factory: ConnectionManagerFactory, private val localRepository: LocalRepository,
private val mapper: ViewModelMapper, factory: ConnectionManagerFactory,
private val jobSchedulerInteractor: JobSchedulerInteractor) { private val mapper: ViewModelMapper,
private val jobSchedulerInteractor: JobSchedulerInteractor
) {
private val currentServer = serverInteractor.get()!! private val currentServer = serverInteractor.get()!!
private val manager = factory.create(currentServer) private val manager = factory.create(currentServer)
private val client = manager.client private val client = manager.client
...@@ -172,7 +175,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -172,7 +175,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
launchUI(strategy) { launchUI(strategy) {
view.showLoading() view.showLoading()
try { try {
val fileName = async { uriInteractor.getFileName(uri) }.await() val fileName = async { uriInteractor.getFileName(uri) }.await() ?: uri.toString()
val mimeType = async { uriInteractor.getMimeType(uri) }.await() val mimeType = async { uriInteractor.getMimeType(uri) }.await()
val fileSize = async { uriInteractor.getFileSize(uri) }.await() val fileSize = async { uriInteractor.getFileSize(uri) }.await()
val maxFileSize = settings.uploadMaxFileSize() val maxFileSize = settings.uploadMaxFileSize()
...@@ -189,12 +192,11 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -189,12 +192,11 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
} }
} }
} }
} catch (ex: RocketChatException) { } catch (ex: Exception) {
Timber.d(ex) Timber.d(ex, "Error uploading file")
ex.message?.let { when(ex) {
view.showMessage(it) is RocketChatException -> view.showMessage(ex)
}.ifNull { else -> view.showGenericErrorMessage()
view.showGenericErrorMessage()
} }
} finally { } finally {
view.hideLoading() view.hideLoading()
...@@ -510,7 +512,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -510,7 +512,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
fun loadChatRooms() { fun loadChatRooms() {
launchUI(strategy) { launchUI(strategy) {
try { try {
val chatRooms = getChatRoomsInteractor.get(currentServer) val chatRooms = getChatRoomsInteractor.getAll(currentServer)
.filterNot { .filterNot {
it.type is RoomType.DirectMessage || it.type is RoomType.Livechat it.type is RoomType.DirectMessage || it.type is RoomType.Livechat
} }
......
...@@ -4,27 +4,24 @@ import chat.rocket.android.chatroom.viewmodel.ViewModelMapper ...@@ -4,27 +4,24 @@ 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.server.domain.GetChatRoomsInteractor import chat.rocket.android.server.domain.GetChatRoomsInteractor
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.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extensions.launchUI
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
import chat.rocket.core.internal.rest.getRoomPinnedMessages import chat.rocket.core.internal.rest.getRoomPinnedMessages
import chat.rocket.core.model.Value
import chat.rocket.core.model.isSystemMessage import chat.rocket.core.model.isSystemMessage
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
class PinnedMessagesPresenter @Inject constructor(private val view: PinnedMessagesView, class PinnedMessagesPresenter @Inject constructor(
private val strategy: CancelStrategy, private val view: PinnedMessagesView,
private val serverInteractor: GetCurrentServerInteractor, private val strategy: CancelStrategy,
private val roomsInteractor: GetChatRoomsInteractor, private val serverInteractor: GetCurrentServerInteractor,
private val mapper: ViewModelMapper, private val roomsInteractor: GetChatRoomsInteractor,
factory: RocketChatClientFactory, private val mapper: ViewModelMapper,
getSettingsInteractor: GetSettingsInteractor) { factory: RocketChatClientFactory
) {
private val client = factory.create(serverInteractor.get()!!) private val client = factory.create(serverInteractor.get()!!)
private var settings: Map<String, Value<Any>> = getSettingsInteractor.get(serverInteractor.get()!!)
private var pinnedMessagesListOffset: Int = 0 private var pinnedMessagesListOffset: Int = 0
/** /**
......
...@@ -3,11 +3,11 @@ package chat.rocket.android.chatroom.ui ...@@ -3,11 +3,11 @@ package chat.rocket.android.chatroom.ui
import DrawableHelper import DrawableHelper
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.drawable.Drawable
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.Fragment import android.support.v4.app.Fragment
import android.support.v7.app.AppCompatActivity import android.support.v7.app.AppCompatActivity
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.chatroom.presentation.ChatRoomNavigator
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.ConnectionManagerFactory
import chat.rocket.android.util.extensions.addFragment import chat.rocket.android.util.extensions.addFragment
...@@ -20,15 +20,15 @@ import dagger.android.DispatchingAndroidInjector ...@@ -20,15 +20,15 @@ import dagger.android.DispatchingAndroidInjector
import dagger.android.support.HasSupportFragmentInjector import dagger.android.support.HasSupportFragmentInjector
import kotlinx.android.synthetic.main.app_bar_chat_room.* import kotlinx.android.synthetic.main.app_bar_chat_room.*
import javax.inject.Inject import javax.inject.Inject
import timber.log.Timber
fun Context.chatRoomIntent(
fun Context.chatRoomIntent(chatRoomId: String, chatRoomId: String,
chatRoomName: String, chatRoomName: String,
chatRoomType: String, chatRoomType: String,
isChatRoomReadOnly: Boolean, isChatRoomReadOnly: Boolean,
chatRoomLastSeen: Long, chatRoomLastSeen: Long,
isChatRoomSubscribed: Boolean = true): Intent { isChatRoomSubscribed: Boolean = true
): 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)
...@@ -51,6 +51,7 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector { ...@@ -51,6 +51,7 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector {
// TODO - workaround for now... We will move to a single activity // TODO - workaround for now... We will move to a single activity
@Inject lateinit var serverInteractor: GetCurrentServerInteractor @Inject lateinit var serverInteractor: GetCurrentServerInteractor
@Inject lateinit var navigator: ChatRoomNavigator
@Inject lateinit var managerFactory: ConnectionManagerFactory @Inject lateinit var managerFactory: ConnectionManagerFactory
private lateinit var chatRoomId: String private lateinit var chatRoomId: String
...@@ -66,7 +67,13 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector { ...@@ -66,7 +67,13 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector {
setContentView(R.layout.activity_chat_room) setContentView(R.layout.activity_chat_room)
// Workaround for when we are coming to the app via the recents app and the app was killed. // Workaround for when we are coming to the app via the recents app and the app was killed.
managerFactory.create(serverInteractor.get()!!).connect() val serverUrl = serverInteractor.get()
if (serverUrl != null) {
managerFactory.create(serverUrl).connect()
} else {
navigator.toNewServer()
return
}
chatRoomId = intent.getStringExtra(INTENT_CHAT_ROOM_ID) chatRoomId = intent.getStringExtra(INTENT_CHAT_ROOM_ID)
requireNotNull(chatRoomId) { "no chat_room_id provided in Intent extras" } requireNotNull(chatRoomId) { "no chat_room_id provided in Intent extras" }
......
package chat.rocket.android.chatroom.ui package chat.rocket.android.chatroom.ui
import android.Manifest
import android.app.Activity import android.app.Activity
import android.content.ClipData import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.support.annotation.DrawableRes import android.support.annotation.DrawableRes
import android.support.v4.app.ActivityCompat
import android.support.v4.app.Fragment import android.support.v4.app.Fragment
import android.support.v4.content.ContextCompat
import android.support.v7.widget.DefaultItemAnimator import android.support.v7.widget.DefaultItemAnimator
import android.support.v7.widget.LinearLayoutManager import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView import android.support.v7.widget.RecyclerView
...@@ -42,12 +38,14 @@ import kotlinx.android.synthetic.main.message_list.* ...@@ -42,12 +38,14 @@ import kotlinx.android.synthetic.main.message_list.*
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import javax.inject.Inject import javax.inject.Inject
fun newInstance(chatRoomId: String, fun newInstance(
chatRoomName: String, chatRoomId: String,
chatRoomType: String, chatRoomName: String,
isChatRoomReadOnly: Boolean, chatRoomType: String,
chatRoomLastSeen: Long, isChatRoomReadOnly: Boolean,
isSubscribed: Boolean = true): Fragment { chatRoomLastSeen: Long,
isSubscribed: Boolean = true
): Fragment {
return ChatRoomFragment().apply { return ChatRoomFragment().apply {
arguments = Bundle(1).apply { arguments = Bundle(1).apply {
putString(BUNDLE_CHAT_ROOM_ID, chatRoomId) putString(BUNDLE_CHAT_ROOM_ID, chatRoomId)
...@@ -114,7 +112,13 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -114,7 +112,13 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
setHasOptionsMenu(true) setHasOptionsMenu(true)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = container?.inflate(R.layout.fragment_chat_room) override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return container?.inflate(R.layout.fragment_chat_room)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
...@@ -468,29 +472,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -468,29 +472,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
override fun showFileSelection(filter: Array<String>) { override fun showFileSelection(filter: Array<String>) {
ui { ui {
if (ContextCompat.checkSelfPermission(it, Manifest.permission.READ_EXTERNAL_STORAGE) val intent = Intent(Intent.ACTION_GET_CONTENT)
!= PackageManager.PERMISSION_GRANTED) { intent.type = "*/*"
ActivityCompat.requestPermissions(it, intent.putExtra(Intent.EXTRA_MIME_TYPES, filter)
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), intent.addCategory(Intent.CATEGORY_OPENABLE)
1) startActivityForResult(intent, REQUEST_CODE_FOR_PERFORM_SAF)
} else {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "*/*"
intent.putExtra(Intent.EXTRA_MIME_TYPES, filter)
startActivityForResult(intent, REQUEST_CODE_FOR_PERFORM_SAF)
}
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
when (requestCode) {
1 -> {
if (!(grantResults.isNotEmpty() && grantResults.first() == PackageManager.PERMISSION_GRANTED)) {
handler.postDelayed({
ui { hideAttachmentOptions() }
}, 400)
}
}
} }
} }
......
...@@ -10,6 +10,7 @@ import android.text.SpannableStringBuilder ...@@ -10,6 +10,7 @@ import android.text.SpannableStringBuilder
import android.text.style.ForegroundColorSpan import android.text.style.ForegroundColorSpan
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 chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
...@@ -49,6 +50,7 @@ class ChatRoomsAdapter(private val context: Context, ...@@ -49,6 +50,7 @@ class ChatRoomsAdapter(private val context: Context,
fun bind(chatRoom: ChatRoom) = with(itemView) { fun bind(chatRoom: ChatRoom) = with(itemView) {
bindAvatar(chatRoom, image_avatar) bindAvatar(chatRoom, image_avatar)
bindName(chatRoom, text_chat_name) bindName(chatRoom, text_chat_name)
bindIcon(chatRoom, image_chat_icon)
bindLastMessageDateTime(chatRoom, text_last_message_date_time) bindLastMessageDateTime(chatRoom, text_last_message_date_time)
bindLastMessage(chatRoom, text_last_message) bindLastMessage(chatRoom, text_last_message)
bindUnreadMessages(chatRoom, text_total_unread_messages) bindUnreadMessages(chatRoom, text_total_unread_messages)
...@@ -80,34 +82,39 @@ class ChatRoomsAdapter(private val context: Context, ...@@ -80,34 +82,39 @@ class ChatRoomsAdapter(private val context: Context,
} }
} }
private fun bindName(chatRoom: ChatRoom, textView: TextView) { private fun bindIcon(chatRoom: ChatRoom, imageView: ImageView) {
textView.textContent = chatRoom.name
val drawable = when (chatRoom.type) { val drawable = when (chatRoom.type) {
is RoomType.Channel -> { is RoomType.Channel -> DrawableHelper.getDrawableFromId(
DrawableHelper.getDrawableFromId(R.drawable.ic_room_channel, context) R.drawable.ic_hashtag_12dp,
} context
is RoomType.PrivateGroup -> { )
DrawableHelper.getDrawableFromId(R.drawable.ic_room_lock, context) is RoomType.PrivateGroup -> DrawableHelper.getDrawableFromId(
} R.drawable.ic_lock_12_dp,
is RoomType.DirectMessage -> { context
DrawableHelper.getDrawableFromId(R.drawable.ic_room_dm, context) )
} is RoomType.DirectMessage -> DrawableHelper.getUserStatusDrawable(
chatRoom.status,
context
)
else -> null else -> null
} }
drawable?.let { drawable?.let {
val wrappedDrawable = DrawableHelper.wrapDrawable(it) val mutateDrawable = DrawableHelper.wrapDrawable(it).mutate()
val mutableDrawable = wrappedDrawable.mutate() if (chatRoom.type !is RoomType.DirectMessage) {
val color = when (chatRoom.alert || chatRoom.unread > 0) { val color = when (chatRoom.alert || chatRoom.unread > 0) {
true -> R.color.colorPrimaryText true -> R.color.colorPrimaryText
false -> R.color.colorSecondaryText false -> R.color.colorSecondaryText
}
DrawableHelper.tintDrawable(mutateDrawable, context, color)
} }
DrawableHelper.tintDrawable(mutableDrawable, context, color) imageView.setImageDrawable(mutateDrawable)
DrawableHelper.compoundDrawable(textView, mutableDrawable)
} }
} }
private fun bindName(chatRoom: ChatRoom, textView: TextView) {
textView.textContent = chatRoom.name
}
private fun bindLastMessageDateTime(chatRoom: ChatRoom, textView: TextView) { private fun bindLastMessageDateTime(chatRoom: ChatRoom, textView: TextView) {
val lastMessage = chatRoom.lastMessage val lastMessage = chatRoom.lastMessage
if (lastMessage != null) { if (lastMessage != null) {
......
...@@ -32,12 +32,9 @@ import dagger.android.support.AndroidSupportInjection ...@@ -32,12 +32,9 @@ import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_chat_rooms.* import kotlinx.android.synthetic.main.fragment_chat_rooms.*
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.NonCancellable.isActive import kotlinx.coroutines.experimental.NonCancellable.isActive
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.launch
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
class ChatRoomsFragment : Fragment(), ChatRoomsView { class ChatRoomsFragment : Fragment(), ChatRoomsView {
@Inject lateinit var presenter: ChatRoomsPresenter @Inject lateinit var presenter: ChatRoomsPresenter
@Inject lateinit var serverInteractor: GetCurrentServerInteractor @Inject lateinit var serverInteractor: GetCurrentServerInteractor
...@@ -67,7 +64,11 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView { ...@@ -67,7 +64,11 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
super.onDestroy() super.onDestroy()
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = container?.inflate(R.layout.fragment_chat_rooms) override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = container?.inflate(R.layout.fragment_chat_rooms)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
...@@ -100,7 +101,6 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView { ...@@ -100,7 +101,6 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
}) })
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) { when (item.itemId) {
R.id.action_sort -> { R.id.action_sort -> {
......
package chat.rocket.android.dagger package chat.rocket.android.dagger
import android.app.Application import android.app.Application
import chat.rocket.android.app.AppLifecycleObserver
import chat.rocket.android.app.RocketChatApplication import chat.rocket.android.app.RocketChatApplication
import chat.rocket.android.chatroom.service.MessageService import chat.rocket.android.chatroom.service.MessageService
import chat.rocket.android.dagger.module.ActivityBuilder import chat.rocket.android.dagger.module.ActivityBuilder
......
...@@ -8,6 +8,7 @@ import chat.rocket.android.authentication.signup.di.SignupFragmentProvider ...@@ -8,6 +8,7 @@ import chat.rocket.android.authentication.signup.di.SignupFragmentProvider
import chat.rocket.android.authentication.twofactor.di.TwoFAFragmentProvider import chat.rocket.android.authentication.twofactor.di.TwoFAFragmentProvider
import chat.rocket.android.authentication.ui.AuthenticationActivity import chat.rocket.android.authentication.ui.AuthenticationActivity
import chat.rocket.android.chatroom.di.ChatRoomFragmentProvider import chat.rocket.android.chatroom.di.ChatRoomFragmentProvider
import chat.rocket.android.chatroom.di.ChatRoomModule
import chat.rocket.android.chatroom.di.PinnedMessagesFragmentProvider import chat.rocket.android.chatroom.di.PinnedMessagesFragmentProvider
import chat.rocket.android.chatroom.ui.ChatRoomActivity import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.chatroom.ui.PinnedMessagesActivity import chat.rocket.android.chatroom.ui.PinnedMessagesActivity
...@@ -45,7 +46,9 @@ abstract class ActivityBuilder { ...@@ -45,7 +46,9 @@ abstract class ActivityBuilder {
abstract fun bindMainActivity(): MainActivity abstract fun bindMainActivity(): MainActivity
@PerActivity @PerActivity
@ContributesAndroidInjector(modules = [ChatRoomFragmentProvider::class, MembersFragmentProvider::class]) @ContributesAndroidInjector(modules = [ChatRoomModule::class,
ChatRoomFragmentProvider::class,
MembersFragmentProvider::class])
abstract fun bindChatRoomActivity(): ChatRoomActivity abstract fun bindChatRoomActivity(): ChatRoomActivity
@PerActivity @PerActivity
......
...@@ -22,29 +22,8 @@ import chat.rocket.android.infrastructure.LocalRepository ...@@ -22,29 +22,8 @@ import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.infrastructure.SharedPrefsLocalRepository import chat.rocket.android.infrastructure.SharedPrefsLocalRepository
import chat.rocket.android.push.GroupedPush import chat.rocket.android.push.GroupedPush
import chat.rocket.android.push.PushManager import chat.rocket.android.push.PushManager
import chat.rocket.android.server.domain.AccountsRepository import chat.rocket.android.server.domain.*
import chat.rocket.android.server.domain.ChatRoomsRepository import chat.rocket.android.server.infraestructure.*
import chat.rocket.android.server.domain.CurrentServerRepository
import chat.rocket.android.server.domain.GetAccountInteractor
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.GetPermissionsInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.JobSchedulerInteractor
import chat.rocket.android.server.domain.MessagesRepository
import chat.rocket.android.server.domain.MultiServerTokenRepository
import chat.rocket.android.server.domain.RoomRepository
import chat.rocket.android.server.domain.SettingsRepository
import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.server.domain.UsersRepository
import chat.rocket.android.server.infraestructure.JobSchedulerInteractorImpl
import chat.rocket.android.server.infraestructure.MemoryChatRoomsRepository
import chat.rocket.android.server.infraestructure.MemoryRoomRepository
import chat.rocket.android.server.infraestructure.MemoryUsersRepository
import chat.rocket.android.server.infraestructure.ServerDao
import chat.rocket.android.server.infraestructure.SharedPreferencesAccountsRepository
import chat.rocket.android.server.infraestructure.SharedPreferencesMessagesRepository
import chat.rocket.android.server.infraestructure.SharedPreferencesSettingsRepository
import chat.rocket.android.server.infraestructure.SharedPrefsCurrentServerRepository
import chat.rocket.android.util.AppJsonAdapterFactory import chat.rocket.android.util.AppJsonAdapterFactory
import chat.rocket.android.util.TimberLogger import chat.rocket.android.util.TimberLogger
import chat.rocket.common.internal.FallbackSealedClassJsonAdapter import chat.rocket.common.internal.FallbackSealedClassJsonAdapter
...@@ -55,6 +34,7 @@ import chat.rocket.common.util.Logger ...@@ -55,6 +34,7 @@ import chat.rocket.common.util.Logger
import chat.rocket.common.util.PlatformLogger import chat.rocket.common.util.PlatformLogger
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.AttachmentAdapterFactory import chat.rocket.core.internal.AttachmentAdapterFactory
import chat.rocket.core.internal.ReactionsAdapter
import com.facebook.drawee.backends.pipeline.DraweeConfig import com.facebook.drawee.backends.pipeline.DraweeConfig
import com.facebook.imagepipeline.backends.okhttp3.OkHttpImagePipelineConfigFactory import com.facebook.imagepipeline.backends.okhttp3.OkHttpImagePipelineConfigFactory
import com.facebook.imagepipeline.core.ImagePipelineConfig import com.facebook.imagepipeline.core.ImagePipelineConfig
...@@ -164,10 +144,10 @@ class AppModule { ...@@ -164,10 +144,10 @@ class AppModule {
listeners.add(RequestLoggingListener()) listeners.add(RequestLoggingListener())
return OkHttpImagePipelineConfigFactory.newBuilder(context, okHttpClient) return OkHttpImagePipelineConfigFactory.newBuilder(context, okHttpClient)
.setRequestListeners(listeners) .setRequestListeners(listeners)
.setDownsampleEnabled(true) .setDownsampleEnabled(true)
//.experiment().setBitmapPrepareToDraw(true).experiment() //.experiment().setBitmapPrepareToDraw(true).experiment()
.experiment().setPartialImageCachingEnabled(true).build() .experiment().setPartialImageCachingEnabled(true).build()
} }
@Provides @Provides
...@@ -196,7 +176,7 @@ class AppModule { ...@@ -196,7 +176,7 @@ class AppModule {
@Provides @Provides
@ForMessages @ForMessages
fun provideMessagesSharedPreferences(context: Application) = fun provideMessagesSharedPreferences(context: Application) =
context.getSharedPreferences("messages", Context.MODE_PRIVATE) context.getSharedPreferences("messages", Context.MODE_PRIVATE)
@Provides @Provides
@Singleton @Singleton
...@@ -230,17 +210,33 @@ class AppModule { ...@@ -230,17 +210,33 @@ class AppModule {
@Provides @Provides
@Singleton @Singleton
fun provideMoshi(logger: PlatformLogger, fun provideActiveUsersRepository(): ActiveUsersRepository {
currentServerInteractor: GetCurrentServerInteractor): return MemoryActiveUsersRepository()
Moshi { }
@Provides
@Singleton
fun provideMoshi(
logger: PlatformLogger,
currentServerInteractor: GetCurrentServerInteractor
): Moshi {
val url = currentServerInteractor.get() ?: "" val url = currentServerInteractor.get() ?: ""
return Moshi.Builder() return Moshi.Builder()
.add(FallbackSealedClassJsonAdapter.ADAPTER_FACTORY) .add(FallbackSealedClassJsonAdapter.ADAPTER_FACTORY)
.add(AppJsonAdapterFactory.INSTANCE) .add(AppJsonAdapterFactory.INSTANCE)
.add(AttachmentAdapterFactory(Logger(logger, url))) .add(AttachmentAdapterFactory(Logger(logger, url)))
.add(java.lang.Long::class.java, ISO8601Date::class.java, TimestampAdapter(CalendarISO8601Converter())) .add(
.add(Long::class.java, ISO8601Date::class.java, TimestampAdapter(CalendarISO8601Converter())) java.lang.Long::class.java,
.build() ISO8601Date::class.java,
TimestampAdapter(CalendarISO8601Converter())
)
.add(
Long::class.java,
ISO8601Date::class.java,
TimestampAdapter(CalendarISO8601Converter())
)
.add(ReactionsAdapter())
.build()
} }
@Provides @Provides
...@@ -268,15 +264,15 @@ class AppModule { ...@@ -268,15 +264,15 @@ class AppModule {
fun provideConfiguration(context: Application, client: OkHttpClient): SpannableConfiguration { fun provideConfiguration(context: Application, client: OkHttpClient): SpannableConfiguration {
val res = context.resources val res = context.resources
return SpannableConfiguration.builder(context) return SpannableConfiguration.builder(context)
.asyncDrawableLoader(AsyncDrawableLoader.builder() .asyncDrawableLoader(AsyncDrawableLoader.builder()
.client(client) .client(client)
.executorService(Executors.newCachedThreadPool()) .executorService(Executors.newCachedThreadPool())
.resources(res) .resources(res)
.build()) .build())
.theme(SpannableTheme.builder() .theme(SpannableTheme.builder()
.linkColor(res.getColor(R.color.colorAccent)) .linkColor(res.getColor(R.color.colorAccent))
.build()) .build())
.build() .build()
} }
@Provides @Provides
...@@ -294,11 +290,11 @@ class AppModule { ...@@ -294,11 +290,11 @@ class AppModule {
@Provides @Provides
@Singleton @Singleton
fun provideAccountsRepository(preferences: SharedPreferences, moshi: Moshi): AccountsRepository = fun provideAccountsRepository(preferences: SharedPreferences, moshi: Moshi): AccountsRepository =
SharedPreferencesAccountsRepository(preferences, moshi) SharedPreferencesAccountsRepository(preferences, moshi)
@Provides @Provides
fun provideNotificationManager(context: Application) = fun provideNotificationManager(context: Application) =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
@Provides @Provides
@Singleton @Singleton
...@@ -307,12 +303,12 @@ class AppModule { ...@@ -307,12 +303,12 @@ class AppModule {
@Provides @Provides
@Singleton @Singleton
fun providePushManager( fun providePushManager(
context: Application, context: Application,
groupedPushes: GroupedPush, groupedPushes: GroupedPush,
manager: NotificationManager, manager: NotificationManager,
moshi: Moshi, moshi: Moshi,
getAccountInteractor: GetAccountInteractor, getAccountInteractor: GetAccountInteractor,
getSettingsInteractor: GetSettingsInteractor): PushManager { getSettingsInteractor: GetSettingsInteractor): PushManager {
return PushManager(groupedPushes, manager, moshi, getAccountInteractor, getSettingsInteractor, context) return PushManager(groupedPushes, manager, moshi, getAccountInteractor, getSettingsInteractor, context)
} }
...@@ -324,9 +320,9 @@ class AppModule { ...@@ -324,9 +320,9 @@ class AppModule {
@Provides @Provides
fun provideSendMessageJob(context: Application): JobInfo { fun provideSendMessageJob(context: Application): JobInfo {
return JobInfo.Builder(MessageService.RETRY_SEND_MESSAGE_ID, return JobInfo.Builder(MessageService.RETRY_SEND_MESSAGE_ID,
ComponentName(context, MessageService::class.java)) ComponentName(context, MessageService::class.java))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.build() .build()
} }
@Provides @Provides
......
...@@ -54,17 +54,41 @@ object OauthHelper { ...@@ -54,17 +54,41 @@ object OauthHelper {
/** /**
* Returns the Gitlab Oauth URL. * Returns the Gitlab Oauth URL.
* *
* @param host The Gitlab host.
* @param clientId The Gitlab client ID. * @param clientId The Gitlab client ID.
* @param serverUrl The server URL. * @param serverUrl The server URL.
* @param state An unguessable random string used to protect against forgery attacks. * @param state An unguessable random string used to protect against forgery attacks.
* @return The Gitlab Oauth URL. * @return The Gitlab Oauth URL.
*/ */
fun getGitlabOauthUrl(clientId: String, serverUrl: String, state: String): String { fun getGitlabOauthUrl(
return "https://gitlab.com/oauth/authorize" + host: String? = "https://gitlab.com",
clientId: String,
serverUrl: String,
state: String
): String {
return host +
"/oauth/authorize" +
"?client_id=$clientId" + "?client_id=$clientId" +
"&redirect_uri=${serverUrl.removeTrailingSlash()}/_oauth/gitlab?close" + "&redirect_uri=${serverUrl.removeTrailingSlash()}/_oauth/gitlab?close" +
"&state=$state" + "&state=$state" +
"&response_type=code" + "&response_type=code" +
"&scope=read_user" "&scope=read_user"
} }
}
\ No newline at end of file /**
* Returns the Facebook Oauth URL.
*
* @param clientId The Facebook client ID.
* @param serverUrl The server URL.
* @param state An unguessable random string used to protect against forgery attacks.
* @return The Facebook Oauth URL.
*/
fun getFacebookOauthUrl(clientId: String, serverUrl: String, state: String): String {
return "https://facebook.com/v2.9/dialog/oauth" +
"?client_id=$clientId" +
"&redirect_uri=${serverUrl.removeTrailingSlash()}/_oauth/facebook?close" +
"&state=$state" +
"&response_type=code" +
"&scope=email"
}
}
...@@ -135,7 +135,7 @@ class MainPresenter @Inject constructor( ...@@ -135,7 +135,7 @@ class MainPresenter @Inject constructor(
navigator.toServerScreen() navigator.toServerScreen()
} }
fun changeStatus(userStatus: UserStatus) { fun changeDefaultStatus(userStatus: UserStatus) {
launchUI(strategy) { launchUI(strategy) {
try { try {
client.setDefaultStatus(userStatus) client.setDefaultStatus(userStatus)
...@@ -186,9 +186,9 @@ class MainPresenter @Inject constructor( ...@@ -186,9 +186,9 @@ class MainPresenter @Inject constructor(
private suspend fun subscribeMyselfUpdates() { private suspend fun subscribeMyselfUpdates() {
manager.addUserDataChannel(userDataChannel) manager.addUserDataChannel(userDataChannel)
for (myself in userDataChannel) { for (myself in userDataChannel) {
updateMyself(myself) updateMyself(myself)
} }
} }
private suspend fun updateMyself(myself: Myself) { private suspend fun updateMyself(myself: Myself) {
......
...@@ -84,10 +84,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp ...@@ -84,10 +84,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp
override fun showUserStatus(userStatus: UserStatus) { override fun showUserStatus(userStatus: UserStatus) {
headerLayout.apply { headerLayout.apply {
image_user_status.setImageDrawable( image_user_status.setImageDrawable(
DrawableHelper.getUserStatusDrawable( DrawableHelper.getUserStatusDrawable(userStatus, this.context)
userStatus,
this.context
)
) )
} }
} }
...@@ -141,7 +138,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp ...@@ -141,7 +138,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp
accounts_list.layoutManager = LinearLayoutManager(this) accounts_list.layoutManager = LinearLayoutManager(this)
accounts_list.adapter = AccountsAdapter(accounts, object : Selector { accounts_list.adapter = AccountsAdapter(accounts, object : Selector {
override fun onStatusSelected(userStatus: UserStatus) { override fun onStatusSelected(userStatus: UserStatus) {
presenter.changeStatus(userStatus) presenter.changeDefaultStatus(userStatus)
} }
override fun onAccountSelected(serverUrl: String) { override fun onAccountSelected(serverUrl: String) {
......
...@@ -14,12 +14,14 @@ import chat.rocket.core.RocketChatClient ...@@ -14,12 +14,14 @@ import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.getMembers import chat.rocket.core.internal.rest.getMembers
import javax.inject.Inject import javax.inject.Inject
class MembersPresenter @Inject constructor(private val view: MembersView, class MembersPresenter @Inject constructor(
private val navigator: MembersNavigator, private val view: MembersView,
private val strategy: CancelStrategy, private val navigator: MembersNavigator,
private val serverInteractor: GetCurrentServerInteractor, private val strategy: CancelStrategy,
factory: RocketChatClientFactory, serverInteractor: GetCurrentServerInteractor,
private val mapper: MemberViewModelMapper) { factory: RocketChatClientFactory,
private val mapper: MemberViewModelMapper
) {
private val client: RocketChatClient = factory.create(serverInteractor.get()!!) private val client: RocketChatClient = factory.create(serverInteractor.get()!!)
fun loadChatRoomsMembers(chatRoomId: String, chatRoomType: String, offset: Long = 0) { fun loadChatRoomsMembers(chatRoomId: String, chatRoomType: String, offset: Long = 0) {
...@@ -49,7 +51,7 @@ class MembersPresenter @Inject constructor(private val view: MembersView, ...@@ -49,7 +51,7 @@ class MembersPresenter @Inject constructor(private val view: MembersView,
val realName = memberViewModel.realName.toString() val realName = memberViewModel.realName.toString()
val username = "@${memberViewModel.username}" val username = "@${memberViewModel.username}"
val email = memberViewModel.email ?: "" val email = memberViewModel.email ?: ""
val utcOffset = memberViewModel.utcOffset.toString() val utcOffset = memberViewModel.utcOffset.toString()
navigator.toMemberDetails(avatarUri, realName, username, email, utcOffset) navigator.toMemberDetails(avatarUri, realName, username, email, utcOffset)
} }
......
...@@ -25,7 +25,6 @@ import dagger.android.support.AndroidSupportInjection ...@@ -25,7 +25,6 @@ import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_members.* import kotlinx.android.synthetic.main.fragment_members.*
import javax.inject.Inject import javax.inject.Inject
fun newInstance(chatRoomId: String, chatRoomType: String): Fragment { fun newInstance(chatRoomId: String, chatRoomType: String): Fragment {
return MembersFragment().apply { return MembersFragment().apply {
arguments = Bundle(1).apply { arguments = Bundle(1).apply {
......
...@@ -28,14 +28,21 @@ class ProfilePresenter @Inject constructor(private val view: ProfileView, ...@@ -28,14 +28,21 @@ class ProfilePresenter @Inject constructor(private val view: ProfileView,
view.showLoading() view.showLoading()
try { try {
val myself = retryIO("me") { client.me() } val myself = retryIO("me") { client.me() }
myselfId = myself.id!! val id = myself.id
val avatarUrl = serverUrl.avatarUrl(myself.username!!) val username = myself.username
view.showProfile( if (id == null || username == null) {
avatarUrl, view.showGenericErrorMessage()
myself.name ?: "", } else {
myself.username ?: "", myselfId = id
myself.emails?.get(0)?.address!! val avatarUrl = serverUrl.avatarUrl(username)
) val email = myself.emails?.getOrNull(0)?.address
view.showProfile(
avatarUrl,
myself.name ?: "",
myself.username ?: "",
email
)
}
} catch (exception: RocketChatException) { } catch (exception: RocketChatException) {
view.showMessage(exception) view.showMessage(exception)
} finally { } finally {
......
...@@ -2,7 +2,6 @@ package chat.rocket.android.profile.presentation ...@@ -2,7 +2,6 @@ package chat.rocket.android.profile.presentation
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.model.Myself
interface ProfileView : LoadingView, MessageView { interface ProfileView : LoadingView, MessageView {
...@@ -14,7 +13,7 @@ interface ProfileView : LoadingView, MessageView { ...@@ -14,7 +13,7 @@ interface ProfileView : LoadingView, MessageView {
* @param username The user username. * @param username The user username.
* @param email The user email. * @param email The user email.
*/ */
fun showProfile(avatarUrl: String, name: String, username: String, email: String) fun showProfile(avatarUrl: String, name: String, username: String, email: String?)
/** /**
* Shows a profile update successfully message * Shows a profile update successfully message
......
...@@ -55,18 +55,18 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback { ...@@ -55,18 +55,18 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
super.onDestroyView() super.onDestroyView()
} }
override fun showProfile(avatarUrl: String, name: String, username: String, email: String) { override fun showProfile(avatarUrl: String, name: String, username: String, email: String?) {
ui { ui {
image_avatar.setImageURI(avatarUrl) image_avatar.setImageURI(avatarUrl)
text_name.textContent = name text_name.textContent = name
text_username.textContent = username text_username.textContent = username
text_email.textContent = email text_email.textContent = email ?: ""
text_avatar_url.textContent = "" text_avatar_url.textContent = ""
currentName = name currentName = name
currentUsername = username currentUsername = username
currentEmail = email currentEmail = email ?: ""
currentAvatar = avatarUrl currentAvatar = avatarUrl
profile_container.setVisible(true) profile_container.setVisible(true)
......
package chat.rocket.android.server.domain
import chat.rocket.common.model.User
interface ActiveUsersRepository {
fun save(url: String, activeUsers: List<User>)
fun get(url: String): List<User>
}
\ No newline at end of file
package chat.rocket.android.server.domain
import chat.rocket.common.model.User
import javax.inject.Inject
class GetActiveUsersInteractor @Inject constructor(private val repository: ActiveUsersRepository) {
fun isActiveUserOnRepository(url: String, user: User): Boolean {
return repository.get(url).any { user_ -> user_.id == user.id }
}
fun getAllActiveUsers(url: String): List<User> {
return repository.get(url)
}
fun getActiveUserById(url: String, id: String): User? {
return repository.get(url).find { user -> user.id == id }
}
fun getActiveUserByUsername(url: String, username: String): User? {
return repository.get(url).find { user -> user.username == username }
}
}
\ No newline at end of file
...@@ -8,13 +8,13 @@ import javax.inject.Inject ...@@ -8,13 +8,13 @@ import javax.inject.Inject
class GetChatRoomsInteractor @Inject constructor(private val repository: ChatRoomsRepository) { class GetChatRoomsInteractor @Inject constructor(private val repository: ChatRoomsRepository) {
/** /**
* Get all ChatRoom objects. * Get all [ChatRoom].
* *
* @param url The server url. * @param url The server url.
* *
* @return All the ChatRoom objects. * @return All the [ChatRoom] objects.
*/ */
fun get(url: String) = repository.get(url) fun getAll(url: String) = repository.get(url)
/** /**
* Get a list of chat rooms that contains the name parameter. * Get a list of chat rooms that contains the name parameter.
...@@ -23,7 +23,7 @@ class GetChatRoomsInteractor @Inject constructor(private val repository: ChatRoo ...@@ -23,7 +23,7 @@ class GetChatRoomsInteractor @Inject constructor(private val repository: ChatRoo
* @param name The name of chat room to look for or a chat room that contains this name. * @param name The name of chat room to look for or a chat room that contains this name.
* @return A list of ChatRoom objects with the given name. * @return A list of ChatRoom objects with the given name.
*/ */
suspend fun getByName(url: String, name: String): List<ChatRoom> = withContext(CommonPool) { suspend fun getAllByName(url: String, name: String): List<ChatRoom> = withContext(CommonPool) {
val allChatRooms = repository.get(url) val allChatRooms = repository.get(url)
if (name.isEmpty()) { if (name.isEmpty()) {
return@withContext allChatRooms return@withContext allChatRooms
...@@ -34,11 +34,11 @@ class GetChatRoomsInteractor @Inject constructor(private val repository: ChatRoo ...@@ -34,11 +34,11 @@ class GetChatRoomsInteractor @Inject constructor(private val repository: ChatRoo
} }
/** /**
* Get a specific room by its id. * Get a specific [ChatRoom] by its id.
* *
* @param serverUrl The server url where the room is. * @param serverUrl The server url where the room is.
* @param roomId The id of the room to get. * @param roomId The id of the room to get.
* @return The ChatRoom object or null if we couldn't find any. * @return The [ChatRoom] object or null if we couldn't find any.
*/ */
suspend fun getById(serverUrl: String, roomId: String): ChatRoom? = withContext(CommonPool) { suspend fun getById(serverUrl: String, roomId: String): ChatRoom? = withContext(CommonPool) {
val allChatRooms = repository.get(serverUrl) val allChatRooms = repository.get(serverUrl)
...@@ -46,4 +46,43 @@ class GetChatRoomsInteractor @Inject constructor(private val repository: ChatRoo ...@@ -46,4 +46,43 @@ class GetChatRoomsInteractor @Inject constructor(private val repository: ChatRoo
it.id == roomId it.id == roomId
} }
} }
/**
* Get a specific [ChatRoom] by its name.
*
* @param serverUrl The server url where the room is.
* @param name The name of the room to get.
* @return The [ChatRoom] object or null if we couldn't find any.
*/
fun getByName(serverUrl: String, name: String): ChatRoom? {
return getAll(serverUrl).toMutableList().find { chatRoom -> chatRoom.name == name }
}
/**
* Add a [ChatRoom].
*
* @param url The server url.
* @param chatRoom The [ChatRoom] to be added to the list.
*/
fun add(url: String, chatRoom: ChatRoom) {
val chatRooms: MutableList<ChatRoom> = getAll(url).toMutableList()
synchronized(this) {
chatRooms.add(chatRoom)
}
repository.save(url, chatRooms)
}
/**
* Removes a [ChatRoom].
*
* @param url The server url.
* @param chatRoom The [ChatRoom] to be removed from the list.
*/
fun remove(url: String, chatRoom: ChatRoom) {
val chatRooms: MutableList<ChatRoom> = getAll(url).toMutableList()
synchronized(this) {
chatRooms.removeAll { chatRoom_ -> chatRoom_.id == chatRoom.id }
}
repository.save(url, chatRooms)
}
} }
\ No newline at end of file
...@@ -8,15 +8,17 @@ import kotlinx.coroutines.experimental.async ...@@ -8,15 +8,17 @@ import kotlinx.coroutines.experimental.async
import kotlinx.coroutines.experimental.withContext import kotlinx.coroutines.experimental.withContext
import javax.inject.Inject import javax.inject.Inject
class RefreshSettingsInteractor @Inject constructor(private val factory: RocketChatClientFactory, class RefreshSettingsInteractor @Inject constructor(
private val repository: SettingsRepository) { private val factory: RocketChatClientFactory,
private val repository: SettingsRepository
) {
private var settingsFilter = arrayOf( private var settingsFilter = arrayOf(
LDAP_ENABLE, CAS_ENABLE, CAS_LOGIN_URL, LDAP_ENABLE, CAS_ENABLE, CAS_LOGIN_URL,
ACCOUNT_REGISTRATION, ACCOUNT_LOGIN_FORM, ACCOUNT_PASSWORD_RESET, ACCOUNT_CUSTOM_FIELDS, ACCOUNT_REGISTRATION, ACCOUNT_LOGIN_FORM, ACCOUNT_PASSWORD_RESET, ACCOUNT_CUSTOM_FIELDS,
ACCOUNT_GOOGLE, ACCOUNT_FACEBOOK, ACCOUNT_GITHUB, ACCOUNT_LINKEDIN, ACCOUNT_METEOR, ACCOUNT_GOOGLE, ACCOUNT_FACEBOOK, ACCOUNT_GITHUB, ACCOUNT_LINKEDIN, ACCOUNT_METEOR,
ACCOUNT_TWITTER, ACCOUNT_WORDPRESS, ACCOUNT_GITLAB, ACCOUNT_TWITTER, ACCOUNT_WORDPRESS, ACCOUNT_GITLAB, ACCOUNT_GITLAB_URL,
SITE_URL, SITE_NAME, FAVICON_512, FAVICON_196, USE_REALNAME, ALLOW_ROOM_NAME_SPECIAL_CHARS, SITE_URL, SITE_NAME, FAVICON_512, FAVICON_196, USE_REALNAME, ALLOW_ROOM_NAME_SPECIAL_CHARS,
FAVORITE_ROOMS, UPLOAD_STORAGE_TYPE, UPLOAD_MAX_FILE_SIZE, UPLOAD_WHITELIST_MIMETYPES, FAVORITE_ROOMS, UPLOAD_STORAGE_TYPE, UPLOAD_MAX_FILE_SIZE, UPLOAD_WHITELIST_MIMETYPES,
......
package chat.rocket.android.server.domain
import chat.rocket.common.model.User
import javax.inject.Inject
class SaveActiveUsersInteractor @Inject constructor(
private val repository: ActiveUsersRepository,
private val getActiveUsersInteractor: GetActiveUsersInteractor
) {
fun save(url: String, activeUsers: List<User>) {
repository.save(url, activeUsers)
}
fun addActiveUser(url: String, user: User) {
val activeUserList: MutableList<User> =
getActiveUsersInteractor.getAllActiveUsers(url).toMutableList()
synchronized(this) {
activeUserList.add(user)
}
save(url, activeUserList)
}
fun updateActiveUser(url: String, user: User) {
getActiveUsersInteractor.getActiveUserById(url, user.id)?.let {
val newUser = User(
id = user.id,
name = user.name ?: it.name,
username = user.username ?: it.username,
status = user.status ?: it.status,
emails = user.emails ?: it.emails,
utcOffset = user.utcOffset ?: it.utcOffset
)
val activeUserList: MutableList<User> =
getActiveUsersInteractor.getAllActiveUsers(url).toMutableList()
synchronized(this) {
activeUserList.removeAll { user_ -> user_.id == user.id }
}
activeUserList.add(newUser)
save(url, activeUserList)
}
}
}
\ No newline at end of file
...@@ -26,6 +26,7 @@ const val ACCOUNT_METEOR = "Accounts_OAuth_Meteor" ...@@ -26,6 +26,7 @@ const val ACCOUNT_METEOR = "Accounts_OAuth_Meteor"
const val ACCOUNT_TWITTER = "Accounts_OAuth_Twitter" const val ACCOUNT_TWITTER = "Accounts_OAuth_Twitter"
const val ACCOUNT_WORDPRESS = "Accounts_OAuth_Wordpress" const val ACCOUNT_WORDPRESS = "Accounts_OAuth_Wordpress"
const val ACCOUNT_GITLAB = "Accounts_OAuth_Gitlab" const val ACCOUNT_GITLAB = "Accounts_OAuth_Gitlab"
const val ACCOUNT_GITLAB_URL = "API_Gitlab_URL"
const val SITE_URL = "Site_Url" const val SITE_URL = "Site_Url"
const val SITE_NAME = "Site_Name" const val SITE_NAME = "Site_Name"
...@@ -68,6 +69,7 @@ fun PublicSettings.isLinkedinAuthenticationEnabled(): Boolean = this[ACCOUNT_LIN ...@@ -68,6 +69,7 @@ fun PublicSettings.isLinkedinAuthenticationEnabled(): Boolean = this[ACCOUNT_LIN
fun PublicSettings.isMeteorAuthenticationEnabled(): Boolean = this[ACCOUNT_METEOR]?.value == true fun PublicSettings.isMeteorAuthenticationEnabled(): Boolean = this[ACCOUNT_METEOR]?.value == true
fun PublicSettings.isTwitterAuthenticationEnabled(): Boolean = this[ACCOUNT_TWITTER]?.value == true fun PublicSettings.isTwitterAuthenticationEnabled(): Boolean = this[ACCOUNT_TWITTER]?.value == true
fun PublicSettings.isGitlabAuthenticationEnabled(): Boolean = this[ACCOUNT_GITLAB]?.value == true fun PublicSettings.isGitlabAuthenticationEnabled(): Boolean = this[ACCOUNT_GITLAB]?.value == true
fun PublicSettings.gitlabUrl(): String? = this[ACCOUNT_GITLAB_URL]?.value as String?
fun PublicSettings.isWordpressAuthenticationEnabled(): Boolean = this[ACCOUNT_WORDPRESS]?.value == true fun PublicSettings.isWordpressAuthenticationEnabled(): Boolean = this[ACCOUNT_WORDPRESS]?.value == true
fun PublicSettings.useRealName(): Boolean = this[USE_REALNAME]?.value == true fun PublicSettings.useRealName(): Boolean = this[USE_REALNAME]?.value == true
......
package chat.rocket.android.server.infraestructure package chat.rocket.android.server.infraestructure
import chat.rocket.common.model.BaseRoom import chat.rocket.common.model.BaseRoom
import chat.rocket.common.model.User
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.realtime.subscribeSubscriptions
import chat.rocket.core.internal.realtime.subscribeRooms
import chat.rocket.core.internal.realtime.subscribeUserData
import chat.rocket.core.internal.realtime.subscribeActiveUsers
import chat.rocket.core.internal.realtime.subscribeRoomMessages
import chat.rocket.core.internal.realtime.unsubscribe import chat.rocket.core.internal.realtime.unsubscribe
import chat.rocket.core.internal.realtime.socket.connect import chat.rocket.core.internal.realtime.socket.connect
import chat.rocket.core.internal.realtime.socket.disconnect import chat.rocket.core.internal.realtime.socket.disconnect
import chat.rocket.core.internal.realtime.socket.model.State import chat.rocket.core.internal.realtime.socket.model.State
import chat.rocket.core.internal.realtime.socket.model.StreamMessage import chat.rocket.core.internal.realtime.socket.model.StreamMessage
import chat.rocket.core.internal.realtime.subscribeRoomMessages
import chat.rocket.core.internal.realtime.subscribeRooms
import chat.rocket.core.internal.realtime.subscribeSubscriptions
import chat.rocket.core.internal.realtime.subscribeUserDataChanges
import chat.rocket.core.internal.rest.chatRooms import chat.rocket.core.internal.rest.chatRooms
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
import chat.rocket.core.model.Myself import chat.rocket.core.model.Myself
...@@ -28,11 +30,13 @@ class ConnectionManager(internal val client: RocketChatClient) { ...@@ -28,11 +30,13 @@ class ConnectionManager(internal val client: RocketChatClient) {
private val roomAndSubscriptionChannels = ArrayList<Channel<StreamMessage<BaseRoom>>>() private val roomAndSubscriptionChannels = ArrayList<Channel<StreamMessage<BaseRoom>>>()
private val roomMessagesChannels = LinkedHashMap<String, Channel<Message>>() private val roomMessagesChannels = LinkedHashMap<String, Channel<Message>>()
private val userDataChannels = ArrayList<Channel<Myself>>() private val userDataChannels = ArrayList<Channel<Myself>>()
private val activeUsersChannels = ArrayList<Channel<User>>()
private val subscriptionIdMap = HashMap<String, String>() private val subscriptionIdMap = HashMap<String, String>()
private var subscriptionId: String? = null private var subscriptionId: String? = null
private var roomsId: String? = null private var roomsId: String? = null
private var userId: String? = null private var userDataId: String? = null
private var activeUserId: String? = null
fun connect() { fun connect() {
if (connectJob?.isActive == true && (state !is State.Disconnected)) { if (connectJob?.isActive == true && (state !is State.Disconnected)) {
...@@ -60,9 +64,13 @@ class ConnectionManager(internal val client: RocketChatClient) { ...@@ -60,9 +64,13 @@ class ConnectionManager(internal val client: RocketChatClient) {
Timber.d("Subscribed to rooms: $id") Timber.d("Subscribed to rooms: $id")
roomsId = id roomsId = id
} }
client.subscribeUserDataChanges { _, id -> client.subscribeUserData { _, id ->
Timber.d("Subscribed to the user: $id") Timber.d("Subscribed to the userData id: $id")
userId = id userDataId = id
}
client.subscribeActiveUsers { _, id ->
Timber.d("Subscribed to the activeUser id: $id")
activeUserId = id
} }
resubscribeRooms() resubscribeRooms()
...@@ -115,6 +123,14 @@ class ConnectionManager(internal val client: RocketChatClient) { ...@@ -115,6 +123,14 @@ class ConnectionManager(internal val client: RocketChatClient) {
} }
} }
launch(parent = connectJob) {
for (user in client.activeUsersChannel) {
Timber.d("Got activeUsers")
for (channel in activeUsersChannels) {
channel.send(user)
}
}
}
client.connect() client.connect()
// Broadcast initial state... // Broadcast initial state...
...@@ -154,6 +170,10 @@ class ConnectionManager(internal val client: RocketChatClient) { ...@@ -154,6 +170,10 @@ class ConnectionManager(internal val client: RocketChatClient) {
fun removeUserDataChannel(channel: Channel<Myself>) = userDataChannels.remove(channel) fun removeUserDataChannel(channel: Channel<Myself>) = userDataChannels.remove(channel)
fun addActiveUserChannel(channel: Channel<User>) = activeUsersChannels.add(channel)
fun removeActiveUserChannel(channel: Channel<User>) = activeUsersChannels.remove(channel)
fun subscribeRoomMessages(roomId: String, channel: Channel<Message>) { fun subscribeRoomMessages(roomId: String, channel: Channel<Message>) {
val oldSub = roomMessagesChannels.put(roomId, channel) val oldSub = roomMessagesChannels.put(roomId, channel)
if (oldSub != null) { if (oldSub != null) {
......
package chat.rocket.android.server.infraestructure
import chat.rocket.android.server.domain.ActiveUsersRepository
import chat.rocket.common.model.User
class MemoryActiveUsersRepository : ActiveUsersRepository {
val cache = HashMap<String, List<User>>()
override fun save(url: String, activeUsers: List<User>) {
cache[url] = activeUsers
}
override fun get(url: String): List<User> = cache[url] ?: emptyList()
}
\ No newline at end of file
...@@ -7,6 +7,7 @@ import chat.rocket.android.server.infraestructure.RocketChatClientFactory ...@@ -7,6 +7,7 @@ import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.VersionInfo import chat.rocket.android.util.VersionInfo
import chat.rocket.android.util.extensions.launchUI import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.retryIO import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatInvalidProtocolException
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.serverInfo import chat.rocket.core.internal.rest.serverInfo
import kotlinx.coroutines.experimental.Deferred import kotlinx.coroutines.experimental.Deferred
...@@ -18,17 +19,18 @@ abstract class CheckServerPresenter constructor(private val strategy: CancelStra ...@@ -18,17 +19,18 @@ abstract class CheckServerPresenter constructor(private val strategy: CancelStra
private val factory: RocketChatClientFactory, private val factory: RocketChatClientFactory,
private val view: VersionCheckView) { private val view: VersionCheckView) {
private lateinit var currentServer: String private lateinit var currentServer: String
private val client: RocketChatClient by lazy { private lateinit var client: RocketChatClient
factory.create(currentServer)
}
internal fun checkServerInfo(serverUrl: String): Job { internal fun checkServerInfo(serverUrl: String): Job {
return launchUI(strategy) { return launchUI(strategy) {
try { try {
currentServer = serverUrl
client = factory.create(currentServer)
val version = checkServerVersion(serverUrl).await() val version = checkServerVersion(serverUrl).await()
when (version) { when (version) {
is Version.VersionOk -> { is Version.VersionOk -> {
Timber.i("Your version is nice! (Requires: 0.62.0, Yours: ${version.version})") Timber.i("Your version is nice! (Requires: 0.62.0, Yours: ${version.version})")
view.versionOk()
} }
is Version.RecommendedVersionWarning -> { is Version.RecommendedVersionWarning -> {
Timber.i("Your server ${version.version} is bellow recommended version ${BuildConfig.RECOMMENDED_SERVER_VERSION}") Timber.i("Your server ${version.version} is bellow recommended version ${BuildConfig.RECOMMENDED_SERVER_VERSION}")
...@@ -41,6 +43,14 @@ abstract class CheckServerPresenter constructor(private val strategy: CancelStra ...@@ -41,6 +43,14 @@ abstract class CheckServerPresenter constructor(private val strategy: CancelStra
} }
} catch (ex: Exception) { } catch (ex: Exception) {
Timber.d(ex, "Error getting server info") Timber.d(ex, "Error getting server info")
when(ex) {
is RocketChatInvalidProtocolException -> {
view.errorInvalidProtocol()
}
else -> {
view.errorCheckingServerVersion()
}
}
} }
} }
} }
......
...@@ -41,7 +41,7 @@ private const val INTENT_SERVER_URL = "INTENT_SERVER_URL" ...@@ -41,7 +41,7 @@ private const val INTENT_SERVER_URL = "INTENT_SERVER_URL"
private const val INTENT_CHAT_ROOM_NAME = "INTENT_CHAT_ROOM_NAME" private const val INTENT_CHAT_ROOM_NAME = "INTENT_CHAT_ROOM_NAME"
private const val INTENT_CHAT_ROOM_TYPE = "INTENT_CHAT_ROOM_TYPE" private const val INTENT_CHAT_ROOM_TYPE = "INTENT_CHAT_ROOM_TYPE"
fun Context.changeServerIntent(serverUrl: String?): Intent { fun Context.changeServerIntent(serverUrl: String? = null): Intent {
return Intent(this, ChangeServerActivity::class.java).apply { return Intent(this, ChangeServerActivity::class.java).apply {
serverUrl?.let { url -> serverUrl?.let { url ->
putExtra(INTENT_SERVER_URL, url) putExtra(INTENT_SERVER_URL, url)
......
package chat.rocket.android.settings.about.ui package chat.rocket.android.settings.about.ui
import android.support.v7.app.AppCompatActivity
import android.os.Bundle import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import chat.rocket.android.BuildConfig import chat.rocket.android.BuildConfig
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.util.extensions.textContent import chat.rocket.android.util.extensions.textContent
...@@ -19,15 +19,13 @@ class AboutActivity : AppCompatActivity() { ...@@ -19,15 +19,13 @@ class AboutActivity : AppCompatActivity() {
} }
private fun setupViews() { private fun setupViews() {
val versionName = resources.getString(R.string.msg_version) +" "+BuildConfig.VERSION_NAME text_version_name.text = getString(R.string.msg_version, BuildConfig.VERSION_NAME)
val versionCode = resources.getString(R.string.msg_build)+" #"+ BuildConfig.VERSION_CODE text_build_number.text = getString(R.string.msg_build, BuildConfig.VERSION_CODE)
text_version_name.text = versionName
text_build_number.text = versionCode
} }
private fun setupToolbar() { private fun setupToolbar() {
setSupportActionBar(toolbar) setSupportActionBar(toolbar)
text_change_password.textContent = resources.getString(R.string.title_about) text_change_password.textContent = getString(R.string.title_about)
} }
override fun onBackPressed() { override fun onBackPressed() {
......
...@@ -11,10 +11,12 @@ import chat.rocket.core.internal.rest.me ...@@ -11,10 +11,12 @@ import chat.rocket.core.internal.rest.me
import chat.rocket.core.internal.rest.updateProfile import chat.rocket.core.internal.rest.updateProfile
import javax.inject.Inject import javax.inject.Inject
class PasswordPresenter @Inject constructor (private val view: PasswordView, class PasswordPresenter @Inject constructor(
private val strategy: CancelStrategy, private val view: PasswordView,
serverInteractor: GetCurrentServerInteractor, private val strategy: CancelStrategy,
factory: RocketChatClientFactory){ serverInteractor: GetCurrentServerInteractor,
factory: RocketChatClientFactory
) {
private val serverUrl = serverInteractor.get()!! private val serverUrl = serverInteractor.get()!!
private val client: RocketChatClient = factory.create(serverUrl) private val client: RocketChatClient = factory.create(serverUrl)
......
...@@ -4,12 +4,17 @@ import android.util.Patterns ...@@ -4,12 +4,17 @@ import android.util.Patterns
fun String.removeTrailingSlash(): String { fun String.removeTrailingSlash(): String {
return if (isNotEmpty() && this[length - 1] == '/') { return if (isNotEmpty() && this[length - 1] == '/') {
this.replace("/+$", "") this.substring(0, length - 1)
} else { } else {
this this
} }
} }
fun String.sanitize(): String {
val tmp = this.trim()
return tmp.removeTrailingSlash()
}
fun String.avatarUrl(avatar: String, isGroupOrChannel: Boolean = false, format: String = "jpeg"): String { fun String.avatarUrl(avatar: String, isGroupOrChannel: Boolean = false, format: String = "jpeg"): String {
return if (isGroupOrChannel) { return if (isGroupOrChannel) {
"${removeTrailingSlash()}/avatar/%23${avatar.removeTrailingSlash()}?format=$format" "${removeTrailingSlash()}/avatar/%23${avatar.removeTrailingSlash()}?format=$format"
......
...@@ -30,7 +30,7 @@ private const val JSON_CREDENTIAL_SECRET = "credentialSecret" ...@@ -30,7 +30,7 @@ private const val JSON_CREDENTIAL_SECRET = "credentialSecret"
const val INTENT_OAUTH_CREDENTIAL_TOKEN = "credential_token" const val INTENT_OAUTH_CREDENTIAL_TOKEN = "credential_token"
const val INTENT_OAUTH_CREDENTIAL_SECRET = "credential_secret" const val INTENT_OAUTH_CREDENTIAL_SECRET = "credential_secret"
// Shows a WebView to the user authenticate with your Gitlab credentials. // Shows a WebView to the user authenticate with its OAuth credential.
class OauthWebViewActivity : AppCompatActivity() { class OauthWebViewActivity : AppCompatActivity() {
private lateinit var webPageUrl: String private lateinit var webPageUrl: String
private lateinit var state: String private lateinit var state: String
......
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="12dp"
android:height="12dp"
android:viewportHeight="12"
android:viewportWidth="12">
<path
android:fillColor="#9EA2A8"
android:fillType="evenOdd"
android:pathData="M2.4,0h1.2v12h-1.2z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#9EA2A8"
android:fillType="evenOdd"
android:pathData="M0,2.4h12v1.2h-12z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#9EA2A8"
android:fillType="evenOdd"
android:pathData="M0,8.4h12v1.2h-12z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
<path
android:fillColor="#9EA2A8"
android:fillType="evenOdd"
android:pathData="M8.4,0h1.2v12h-1.2z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
</vector>
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="12dp"
android:height="12dp"
android:viewportWidth="12"
android:viewportHeight="12">
<path
android:pathData="M1.5,5.5h9v6h-9z"
android:strokeWidth="1"
android:fillColor="#00000000"
android:strokeColor="#9EA2A8"
android:fillType="evenOdd"/>
<path
android:pathData="M2.5,5.5L9.5,5.5L9.5,4C9.5,2.067 7.933,0.5 6,0.5C4.067,0.5 2.5,2.067 2.5,4L2.5,5.5Z"
android:strokeWidth="1"
android:fillColor="#00000000"
android:strokeColor="#9EA2A8"
android:fillType="evenOdd"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="12dp"
android:height="24dp" android:height="12dp"
android:viewportHeight="24" android:viewportHeight="12"
android:viewportWidth="24"> android:viewportWidth="12">
<path <path
android:fillColor="#FFFFD100" android:fillColor="#FFFFD100"
android:fillType="evenOdd" android:fillType="evenOdd"
android:pathData="M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0" android:pathData="M6,6m-5,0a5,5 0,1 1,10 0a5,5 0,1 1,-10 0"
android:strokeColor="#00000000" android:strokeColor="#00000000"
android:strokeWidth="1" /> android:strokeWidth="1" />
<path </vector>
android:fillColor="#00000000" \ No newline at end of file
android:fillType="evenOdd"
android:pathData="M12,12m-11,0a11,11 0,1 1,22 0a11,11 0,1 1,-22 0"
android:strokeColor="#FFFFFFFF"
android:strokeWidth="2" />
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="12dp"
android:height="24dp" android:height="12dp"
android:viewportHeight="24" android:viewportHeight="12"
android:viewportWidth="24"> android:viewportWidth="12">
<path <path
android:fillColor="#FFFF2A57" android:fillColor="#FFFF2A57"
android:fillType="evenOdd" android:fillType="evenOdd"
android:pathData="M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0" android:pathData="M6,6m-5,0a5,5 0,1 1,10 0a5,5 0,1 1,-10 0"
android:strokeColor="#00000000" android:strokeColor="#00000000"
android:strokeWidth="1" /> android:strokeWidth="1" />
<path </vector>
android:fillColor="#00000000" \ No newline at end of file
android:fillType="evenOdd"
android:pathData="M12,12m-11,0a11,11 0,1 1,22 0a11,11 0,1 1,-22 0"
android:strokeColor="#FFFFFFFF"
android:strokeWidth="2" />
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="12dp"
android:height="24dp" android:height="12dp"
android:viewportHeight="24" android:viewportHeight="12"
android:viewportWidth="24"> android:viewportWidth="12">
<path <path
android:fillColor="#FFCBCED1" android:fillColor="#FFCBCED1"
android:fillType="evenOdd" android:fillType="evenOdd"
android:pathData="M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0" android:pathData="M6,6m-5,0a5,5 0,1 1,10 0a5,5 0,1 1,-10 0"
android:strokeColor="#00000000" android:strokeColor="#00000000"
android:strokeWidth="1" /> android:strokeWidth="1" />
<path </vector>
android:fillColor="#00000000" \ No newline at end of file
android:fillType="evenOdd"
android:pathData="M12,12m-11,0a11,11 0,1 1,22 0a11,11 0,1 1,-22 0"
android:strokeColor="#FFFFFFFF"
android:strokeWidth="2" />
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="12dp"
android:height="24dp" android:height="12dp"
android:viewportHeight="24" android:viewportHeight="12"
android:viewportWidth="24"> android:viewportWidth="12">
<path <path
android:fillColor="#FF2DE0A5" android:fillColor="#FF2DE0A5"
android:fillType="evenOdd" android:fillType="evenOdd"
android:pathData="M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0" android:pathData="M6,6m-5,0a5,5 0,1 1,10 0a5,5 0,1 1,-10 0"
android:strokeColor="#00000000" android:strokeColor="#00000000"
android:strokeWidth="1" /> android:strokeWidth="1" />
<path </vector>
android:fillColor="#00000000" \ No newline at end of file
android:fillType="evenOdd"
android:pathData="M12,12m-11,0a11,11 0,1 1,22 0a11,11 0,1 1,-22 0"
android:strokeColor="#FFFFFFFF"
android:strokeWidth="2" />
</vector>
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="10dp"
android:height="10dp"
android:viewportWidth="10.0"
android:viewportHeight="10.0">
<path
android:pathData="M5,5m-5,0a5,5 0,1 1,10 0a5,5 0,1 1,-10 0"
android:fillType="evenOdd"
android:fillColor="#FFFFFF"
android:strokeWidth="1"/>
</vector>
\ No newline at end of file
...@@ -18,24 +18,30 @@ ...@@ -18,24 +18,30 @@
android:id="@+id/text_server_url" android:id="@+id/text_server_url"
style="@style/Authentication.EditText" style="@style/Authentication.EditText"
android:layout_below="@id/text_headline" android:layout_below="@id/text_headline"
android:layout_marginStart="-4dp" android:layout_marginStart="-6dp"
android:layout_marginTop="32dp" android:layout_marginTop="32dp"
android:layout_toEndOf="@id/text_server_protocol" android:layout_toEndOf="@id/protocol_container"
android:cursorVisible="false" android:cursorVisible="false"
android:hint="@string/default_server" android:hint="@string/default_server"
android:imeOptions="actionDone" android:imeOptions="actionDone"
android:digits="0123456789abcdefghijklmnopqrstuvwxyz.-/:" android:inputType="text|textUri"
android:inputType="textUri" android:paddingEnd="0dp" />
android:paddingEnd="0dp"
android:paddingStart="4dp" />
<TextView <FrameLayout
android:id="@+id/text_server_protocol" android:id="@+id/protocol_container"
style="@style/Authentication.TextView" android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/style_edit_text_authentication"
android:layout_marginStart="@dimen/screen_edge_left_and_right_margins"
android:layout_below="@id/text_headline" android:layout_below="@id/text_headline"
android:layout_marginTop="32dp" android:layout_marginTop="32dp">
android:gravity="center_vertical" <Spinner
android:text="@string/default_protocol" /> android:id="@+id/text_server_protocol"
android:spinnerMode="dropdown"
android:layout_width="120dp"
android:layout_height="50dp"
android:backgroundTint="@color/actionMenuColor" />
</FrameLayout>
<com.wang.avi.AVLoadingIndicatorView <com.wang.avi.AVLoadingIndicatorView
android:id="@+id/view_loading" android:id="@+id/view_loading"
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerInParent="true" android:layout_centerInParent="true"
android:visibility="gone"
app:indicatorColor="@color/black" app:indicatorColor="@color/black"
app:indicatorName="BallPulseIndicator" /> app:indicatorName="BallPulseIndicator" />
...@@ -33,15 +34,15 @@ ...@@ -33,15 +34,15 @@
android:id="@+id/connection_status_text" android:id="@+id/connection_status_text"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="32dp" android:layout_height="32dp"
android:alpha="0"
android:background="@color/colorPrimary" android:background="@color/colorPrimary"
android:elevation="4dp" android:elevation="4dp"
android:textColor="@color/white"
android:gravity="center" android:gravity="center"
android:textAppearance="@style/TextAppearance.AppCompat.Body2" android:textAppearance="@style/TextAppearance.AppCompat.Body2"
android:textColor="@color/white"
android:visibility="gone" android:visibility="gone"
android:alpha="0"
tools:alpha="1" tools:alpha="1"
tools:visibility="visible" tools:text="connected"
tools:text="connected"/> tools:visibility="visible" />
</RelativeLayout> </RelativeLayout>
\ No newline at end of file
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
android:paddingStart="16dp" android:paddingStart="16dp"
android:paddingTop="8dp" android:paddingTop="8dp"
android:drawablePadding="10dp" android:drawablePadding="10dp"
android:drawableStart="@drawable/ic_status_online_24dp" android:drawableStart="@drawable/ic_status_online_12dp"
android:text="@string/action_online" android:text="@string/action_online"
android:background="?selectableItemBackground"/> android:background="?selectableItemBackground"/>
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
android:paddingStart="16dp" android:paddingStart="16dp"
android:paddingTop="8dp" android:paddingTop="8dp"
android:drawablePadding="10dp" android:drawablePadding="10dp"
android:drawableStart="@drawable/ic_status_away_24dp" android:drawableStart="@drawable/ic_status_away_12dp"
android:text="@string/action_away" android:text="@string/action_away"
android:background="?selectableItemBackground"/> android:background="?selectableItemBackground"/>
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
android:paddingStart="16dp" android:paddingStart="16dp"
android:paddingTop="8dp" android:paddingTop="8dp"
android:drawablePadding="10dp" android:drawablePadding="10dp"
android:drawableStart="@drawable/ic_status_busy_24dp" android:drawableStart="@drawable/ic_status_busy_12dp"
android:text="@string/action_busy" android:text="@string/action_busy"
android:background="?selectableItemBackground"/> android:background="?selectableItemBackground"/>
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
android:paddingStart="16dp" android:paddingStart="16dp"
android:paddingTop="8dp" android:paddingTop="8dp"
android:drawablePadding="10dp" android:drawablePadding="10dp"
android:drawableStart="@drawable/ic_status_invisible_24dp" android:drawableStart="@drawable/ic_status_invisible_12dp"
android:text="@string/action_invisible" android:text="@string/action_invisible"
android:background="?selectableItemBackground"/> android:background="?selectableItemBackground"/>
......
...@@ -5,62 +5,70 @@ ...@@ -5,62 +5,70 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground" android:background="?android:attr/selectableItemBackground"
android:paddingStart="@dimen/screen_edge_left_and_right_padding" android:paddingBottom="@dimen/chat_item_top_and_bottom_padding"
android:paddingEnd="@dimen/screen_edge_left_and_right_padding" android:paddingEnd="@dimen/screen_edge_left_and_right_padding"
android:paddingTop="@dimen/chat_item_top_and_bottom_padding" android:paddingStart="@dimen/screen_edge_left_and_right_padding"
android:paddingBottom="@dimen/chat_item_top_and_bottom_padding"> android:paddingTop="@dimen/chat_item_top_and_bottom_padding">
<com.facebook.drawee.view.SimpleDraweeView <com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/image_avatar" android:id="@+id/image_avatar"
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="40dp" android:layout_height="40dp"
app:roundedCornerRadius="3dp" android:layout_marginTop="5dp"
android:layout_marginTop="6dp"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/> app:layout_constraintTop_toTopOf="parent"
app:roundedCornerRadius="3dp" />
<ImageView
android:id="@+id/image_chat_icon"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginStart="16dp"
app:layout_constraintStart_toEndOf="@+id/image_avatar"
app:layout_constraintTop_toTopOf="@+id/image_avatar"
tools:src="@drawable/ic_hashtag_12dp" />
<TextView <TextView
android:id="@+id/text_chat_name" android:id="@+id/text_chat_name"
style="@style/ChatRoom.Name.TextView" style="@style/ChatRoom.Name.TextView"
android:layout_width="0dp" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="8dp"
app:layout_constraintStart_toEndOf="@id/image_avatar"
android:textDirection="locale" android:textDirection="locale"
tools:text="General"/> app:layout_constraintStart_toEndOf="@+id/image_chat_icon"
tools:text="general" />
<TextView <TextView
android:id="@+id/text_last_message_date_time" android:id="@+id/text_last_message_date_time"
style="@style/Timestamp.TextView" style="@style/Timestamp.TextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="5dp" android:layout_marginStart="8dp"
android:layout_marginEnd="5dp" app:layout_constraintBottom_toBottomOf="@+id/text_chat_name"
app:layout_constraintBaseline_toBaselineOf="@+id/text_chat_name"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/text_chat_name"
tools:text="11:45 AM" /> tools:text="11:45 AM" />
<TextView <TextView
android:id="@+id/text_last_message" android:id="@+id/text_last_message"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginTop="2dp"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="2" android:maxLines="2"
android:layout_marginTop="2dp"
app:layout_constraintStart_toStartOf="@id/text_chat_name"
app:layout_constraintTop_toBottomOf="@id/text_chat_name"
app:layout_constraintEnd_toStartOf="@id/layout_unread_messages_badge"
android:textDirection="locale" android:textDirection="locale"
tools:text="You: Type something that is very big and need at least to lines, or maybe even more"/> app:layout_constraintEnd_toStartOf="@+id/layout_unread_messages_badge"
app:layout_constraintStart_toStartOf="@+id/image_chat_icon"
app:layout_constraintTop_toBottomOf="@+id/text_chat_name"
tools:text="Filipe de Lima Brito: Type something that is very big and need at least to lines, or maybe even more" />
<include <include
android:id="@+id/layout_unread_messages_badge" android:id="@+id/layout_unread_messages_badge"
layout="@layout/unread_messages_badge" layout="@layout/unread_messages_badge"
android:layout_width="18dp" android:layout_width="18dp"
android:layout_height="18dp" android:layout_height="18dp"
android:layout_marginStart="5dp" app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="5dp" app:layout_constraintTop_toTopOf="@+id/text_last_message" />
app:layout_constraintTop_toTopOf="@id/text_last_message"
app:layout_constraintEnd_toEndOf="parent"/>
</android.support.constraint.ConstraintLayout> </android.support.constraint.ConstraintLayout>
\ No newline at end of file
...@@ -43,8 +43,8 @@ ...@@ -43,8 +43,8 @@
<ImageView <ImageView
android:id="@+id/image_user_status" android:id="@+id/image_user_status"
android:layout_width="14dp" android:layout_width="12dp"
android:layout_height="14dp" android:layout_height="12dp"
app:layout_constraintStart_toStartOf="parent" /> app:layout_constraintStart_toStartOf="parent" />
<TextView <TextView
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="2dp" android:layout_marginBottom="2dp"
android:layout_marginEnd="2dp" android:layout_marginEnd="2dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="2dp"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginTop="2dp" android:layout_marginTop="2dp"
android:background="@color/suggestion_background_color"> android:background="@color/suggestion_background_color">
<FrameLayout <com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/image_avatar_container" android:id="@+id/image_avatar"
android:layout_width="24dp"
android:layout_height="24dp"
app:roundedCornerRadius="3dp"
tools:src="@tools:sample/avatars" />
<ImageView
android:id="@+id/image_status"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginStart="5dp"
app:layout_constraintBottom_toBottomOf="@+id/image_avatar"
app:layout_constraintStart_toEndOf="@+id/image_avatar"
app:layout_constraintTop_toTopOf="@+id/image_avatar" />
<TextView
android:id="@+id/text_username"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerVertical="true"> android:layout_marginStart="5dp"
android:maxLines="1"
<com.facebook.drawee.view.SimpleDraweeView android:textColor="@color/black"
android:id="@+id/image_avatar" android:textSize="16sp"
android:layout_width="24dp" app:layout_constraintBottom_toBottomOf="parent"
android:layout_height="24dp" app:layout_constraintStart_toEndOf="@+id/image_status"
android:layout_marginTop="4dp" app:layout_constraintTop_toTopOf="parent"
android:layout_marginBottom="4dp" tools:text="@tools:sample/full_names" />
app:roundedCornerRadius="3dp"
tools:src="@tools:sample/avatars" /> <TextView
android:id="@+id/text_name"
<ImageView android:layout_width="wrap_content"
android:id="@+id/image_status"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_gravity="bottom|end"
android:background="@drawable/user_status_white"
android:padding="2dp" />
</FrameLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerVertical="true" android:layout_marginStart="5dp"
android:layout_toEndOf="@id/image_avatar_container" android:ellipsize="end"
android:layout_toRightOf="@id/image_avatar_container" android:maxLines="1"
android:background="@color/suggestion_background_color"> android:textColor="@color/gray_material"
android:textSize="16sp"
<TextView app:layout_constraintBottom_toBottomOf="parent"
android:id="@+id/text_username" app:layout_constraintStart_toEndOf="@+id/text_username"
android:layout_width="wrap_content" app:layout_constraintTop_toTopOf="parent"
android:layout_height="wrap_content" tools:text="@tools:sample/full_names" />
android:layout_centerVertical="true"
android:maxLines="1" </android.support.constraint.ConstraintLayout>
android:textColor="@color/black" \ No newline at end of file
android:textSize="16sp"
tools:text="@tools:sample/full_names" />
<TextView
android:id="@+id/text_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/text_username"
android:layout_toRightOf="@+id/text_username"
android:maxLines="1"
android:paddingLeft="8dp"
android:paddingStart="8dp"
android:textColor="@color/gray_material"
android:textSize="16sp"
tools:text="@tools:sample/full_names" />
</RelativeLayout>
</RelativeLayout>
\ No newline at end of file
...@@ -3,43 +3,30 @@ ...@@ -3,43 +3,30 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="2dp" android:layout_marginBottom="2dp"
android:layout_marginEnd="2dp"
android:layout_marginStart="8dp"
android:layout_marginTop="2dp"
android:background="@color/suggestion_background_color"> android:background="@color/suggestion_background_color">
<RelativeLayout <TextView
android:layout_width="match_parent" android:id="@+id/text_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerVertical="true" android:maxLines="1"
android:layout_toEndOf="@id/image_avatar_container" android:textColor="@color/black"
android:layout_toRightOf="@id/image_avatar_container" android:textSize="16sp"
android:background="@color/suggestion_background_color"> tools:text="@tools:sample/full_names" />
<TextView <TextView
android:id="@+id/text_name" android:id="@+id/text_fullname"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerVertical="true" android:layout_toEndOf="@+id/text_name"
android:maxLines="1" android:maxLines="1"
android:textColor="@color/black" android:layout_marginStart="8dp"
android:textSize="16sp" android:textColor="@color/gray_material"
tools:text="@tools:sample/full_names" /> android:textSize="16sp"
tools:text="@tools:sample/full_names" />
<TextView
android:id="@+id/text_fullname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/text_name"
android:layout_toRightOf="@+id/text_name"
android:maxLines="1"
android:paddingLeft="8dp"
android:paddingStart="8dp"
android:textColor="@color/gray_material"
android:textSize="16sp"
tools:text="@tools:sample/full_names" />
</RelativeLayout>
</RelativeLayout> </RelativeLayout>
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
...@@ -80,6 +80,8 @@ ...@@ -80,6 +80,8 @@
<string name="msg_preview_photo">तस्वीरें</string> <string name="msg_preview_photo">तस्वीरें</string>
<string name="msg_unread_messages">अपठित संदेश</string> <string name="msg_unread_messages">अपठित संदेश</string>
<string name="msg_no_messages_yet">अभी तक कोई पोस्ट नहीं</string> <string name="msg_no_messages_yet">अभी तक कोई पोस्ट नहीं</string>
<string name="msg_version">वर्शन %1$s</string>
<string name="msg_build">बिल्ड %1$d</string>
<string name="msg_ok">OK</string> <string name="msg_ok">OK</string>
<string name="msg_ver_not_recommended"> <string name="msg_ver_not_recommended">
ऐसा लगता है कि आपका सर्वर संस्करण अनुशंसित संस्करण %1$s के नीचे है।\nआप अभी भी लॉगिन कर सकते हैं लेकिन आप अप्रत्याशित व्यवहार का अनुभव कर सकते हैं ऐसा लगता है कि आपका सर्वर संस्करण अनुशंसित संस्करण %1$s के नीचे है।\nआप अभी भी लॉगिन कर सकते हैं लेकिन आप अप्रत्याशित व्यवहार का अनुभव कर सकते हैं
...@@ -87,8 +89,12 @@ ...@@ -87,8 +89,12 @@
<string name="msg_ver_not_minimum"> <string name="msg_ver_not_minimum">
ऐसा लगता है कि आपका सर्वर संस्करण न्यूनतम आवश्यक संस्करण %1$s से कम है।\nकृपया लॉगिन करने के लिए अपने सर्वर को अपग्रेड करें! ऐसा लगता है कि आपका सर्वर संस्करण न्यूनतम आवश्यक संस्करण %1$s से कम है।\nकृपया लॉगिन करने के लिए अपने सर्वर को अपग्रेड करें!
</string> </string>
<string name="msg_version">वर्शन</string> <string name="msg_proceed">आगे बढ़ें</string>
<string name="msg_build">बिल्ड</string> <string name="msg_cancel">रद्द करना</string>
<string name="msg_warning">चेतावनी</string>
<string name="msg_http_insecure">HTTP का उपयोग करते समय, आप एक असुरक्षित सर्वर से कनेक्ट हो रहे हैं। हम आपको ऐसा करने की सलाह नहीं देते हैं।</string>
<string name="msg_error_checking_server_version">आपके सर्वर संस्करण की जांच करते समय एक त्रुटि आई है, कृपया पुनः प्रयास करें</string>
<string name="msg_invalid_server_protocol">चयनित प्रोटोकॉल इस सर्वर द्वारा स्वीकार नहीं किया गया है, HTTPS का उपयोग करने का प्रयास करें</string>
<!-- System messages --> <!-- System messages -->
<string name="message_room_name_changed">%2$s ने रूम का नाम बदलकर %1$s किया</string> <string name="message_room_name_changed">%2$s ने रूम का नाम बदलकर %1$s किया</string>
...@@ -187,4 +193,4 @@ ...@@ -187,4 +193,4 @@
<string name="notif_action_reply_hint">जवाब</string> <string name="notif_action_reply_hint">जवाब</string>
<string name="notif_error_sending">उत्तर विफल हुआ है। कृपया फिर से प्रयास करें।</string> <string name="notif_error_sending">उत्तर विफल हुआ है। कृपया फिर से प्रयास करें।</string>
<string name="notif_success_sending">संदेश भेजा गया %1$s!</string> <string name="notif_success_sending">संदेश भेजा गया %1$s!</string>
</resources> </resources>
\ No newline at end of file
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
<!-- Actions --> <!-- Actions -->
<string name="action_connect">Conectar</string> <string name="action_connect">Conectar</string>
<string name="action_use_this_username">Usar este nome de usuário</string> <string name="action_use_this_username">Usar este nome de usuário</string>
<string name="action_login_or_sign_up">Toque este botão para fazer login ou criar uma conta</string> <string name="action_login_or_sign_up">Toque neste botão para fazer login ou criar uma conta</string>
<string name="action_terms_of_service">Termos de Serviço</string> <string name="action_terms_of_service">Termos de Serviço</string>
<string name="action_privacy_policy">Política de Privacidade</string> <string name="action_privacy_policy">Política de Privacidade</string>
<string name="action_search">Pesquisar</string> <string name="action_search">Pesquisar</string>
...@@ -59,7 +59,7 @@ ...@@ -59,7 +59,7 @@
<string name="msg_this_room_is_read_only">Este chat é apenas de leitura</string> <string name="msg_this_room_is_read_only">Este chat é apenas de leitura</string>
<string name="msg_invalid_2fa_code">Código 2FA inválido</string> <string name="msg_invalid_2fa_code">Código 2FA inválido</string>
<string name="msg_invalid_file">Arquivo inválido</string> <string name="msg_invalid_file">Arquivo inválido</string>
<string name="msg_invalid_server_url">URL de servidor inválida</string> <string name="msg_invalid_server_url">URL de servidor inválido</string>
<string name="msg_content_description_log_in_using_facebook">Fazer login através do Facebook</string> <string name="msg_content_description_log_in_using_facebook">Fazer login através do Facebook</string>
<string name="msg_content_description_log_in_using_github">Fazer login através do Github</string> <string name="msg_content_description_log_in_using_github">Fazer login através do Github</string>
<string name="msg_content_description_log_in_using_google">Fazer login através do Google</string> <string name="msg_content_description_log_in_using_google">Fazer login através do Google</string>
...@@ -80,8 +80,8 @@ ...@@ -80,8 +80,8 @@
<string name="msg_preview_audio">Audio</string> <string name="msg_preview_audio">Audio</string>
<string name="msg_preview_photo">Foto</string> <string name="msg_preview_photo">Foto</string>
<string name="msg_no_messages_yet">Nenhuma mensagem ainda</string> <string name="msg_no_messages_yet">Nenhuma mensagem ainda</string>
<string name="msg_version">Versão</string> <string name="msg_version">Versão %1$s</string>
<string name="msg_build">Build</string> <string name="msg_build">Build %1$d</string>
<string name="msg_ok">OK</string> <string name="msg_ok">OK</string>
<string name="msg_ver_not_recommended"> <string name="msg_ver_not_recommended">
Parece que a versão do seu servidor está abaixo da recomendada %1$s.\nVocê ainda assim pode logar e continuar mas podem ocorrer alguns problemas inesperados. Parece que a versão do seu servidor está abaixo da recomendada %1$s.\nVocê ainda assim pode logar e continuar mas podem ocorrer alguns problemas inesperados.
...@@ -89,13 +89,19 @@ ...@@ -89,13 +89,19 @@
<string name="msg_ver_not_minimum"> <string name="msg_ver_not_minimum">
Parece que a versão do seu servidor está abaixo da mínima requerida %1$s.\nPor favor, atualize seus servidores antes de continuar! Parece que a versão do seu servidor está abaixo da mínima requerida %1$s.\nPor favor, atualize seus servidores antes de continuar!
</string> </string>
<string name="msg_proceed">CONTINUAR</string>
<string name="msg_cancel">CANCELAR</string>
<string name="msg_warning">AVISO</string>
<string name="msg_http_insecure">Usando HTTP, você estará conectando a um servidor não seguro, não recomendamos sua utilização.</string>
<string name="msg_error_checking_server_version">Ocorreu um erro verificando a versão do servidor, por favor tente novamente</string>
<string name="msg_invalid_server_protocol">O protocolo selecionado não é suportado pelo servidor, por favor utilize HTTPS e tente novamente</string>
<!-- System messages --> <!-- System messages -->
<string name="message_room_name_changed">Nome da sala alterado para: %1$s por %2$s</string> <string name="message_room_name_changed">Nome da sala alterado para: %1$s por %2$s</string>
<string name="message_user_added_by">Usuário %1$s adicionado por %2$s</string> <string name="message_user_added_by">Usuário %1$s adicionado por %2$s</string>
<string name="message_user_removed_by">Usuário %1$s removido por %2$s</string> <string name="message_user_removed_by">Usuário %1$s removido por %2$s</string>
<string name="message_user_left">Saiu da sala.</string> <string name="message_user_left">Saiu da sala.</string>
<string name="message_user_joined_channel">Entrou no sala.</string> <string name="message_user_joined_channel">Entrou na sala.</string>
<string name="message_welcome">Bem-vindo, %s</string> <string name="message_welcome">Bem-vindo, %s</string>
<string name="message_removed">Mensagem removida</string> <string name="message_removed">Mensagem removida</string>
<string name="message_pinned">Pinou uma mensagem:</string> <string name="message_pinned">Pinou uma mensagem:</string>
...@@ -116,7 +122,7 @@ ...@@ -116,7 +122,7 @@
<!-- Permission messages --> <!-- Permission messages -->
<string name="permission_editing_not_allowed">Edição não permitida</string> <string name="permission_editing_not_allowed">Edição não permitida</string>
<string name="permission_deleting_not_allowed">Remoção não permitida</string> <string name="permission_deleting_not_allowed">Remoção não permitida</string>
<string name="permission_pinning_not_allowed">Fixar não permitido</string> <string name="permission_pinning_not_allowed">Pinagem não permitida</string>
<!-- Members List --> <!-- Members List -->
<string name="title_members_list">Lista de Membros</string> <string name="title_members_list">Lista de Membros</string>
...@@ -130,12 +136,12 @@ ...@@ -130,12 +136,12 @@
<string name="max_file_size_exceeded">Tamanho de arquivo (%1$d bytes) excedeu tamanho máximo de upload (%2$d bytes)</string> <string name="max_file_size_exceeded">Tamanho de arquivo (%1$d bytes) excedeu tamanho máximo de upload (%2$d bytes)</string>
<!-- Socket status --> <!-- Socket status -->
<string name="status_connected">conectado</string> <string name="status_connected">Conectado</string>
<string name="status_disconnected">desconetado</string> <string name="status_disconnected">Desconetado</string>
<string name="status_connecting">conectando</string> <string name="status_connecting">Conectando</string>
<string name="status_authenticating">autenticando</string> <string name="status_authenticating">Autenticando</string>
<string name="status_disconnecting">desconectando</string> <string name="status_disconnecting">Desconectando</string>
<string name="status_waiting">conectando em %d segundos</string> <string name="status_waiting">Conectando em %d segundos</string>
<!--Suggestions--> <!--Suggestions-->
<string name="suggest_all_description">Notifica todos nesta sala</string> <string name="suggest_all_description">Notifica todos nesta sala</string>
...@@ -156,8 +162,8 @@ ...@@ -156,8 +162,8 @@
<string name="Leave_the_current_channel">Sair do canal atual</string> <string name="Leave_the_current_channel">Sair do canal atual</string>
<string name="Displays_action_text">Exibir texto de ação</string> <string name="Displays_action_text">Exibir texto de ação</string>
<string name="Direct_message_someone">Enviar DM para alguém</string> <string name="Direct_message_someone">Enviar DM para alguém</string>
<string name="Mute_someone_in_room">Mutar alguém</string> <string name="Mute_someone_in_room">Silenciar alguém</string>
<string name="Unmute_someone_in_room">Desmutar alguém na sala</string> <string name="Unmute_someone_in_room">De-silenciar alguém na sala</string>
<string name="Invite_user_to_join_channel">Convidar algum usuário para entrar neste canal</string> <string name="Invite_user_to_join_channel">Convidar algum usuário para entrar neste canal</string>
<string name="Unarchive">Desarquivar</string> <string name="Unarchive">Desarquivar</string>
<string name="Join_the_given_channel">Entrar no canal especificado</string> <string name="Join_the_given_channel">Entrar no canal especificado</string>
...@@ -187,4 +193,4 @@ ...@@ -187,4 +193,4 @@
<string name="notif_action_reply_hint">RESPONDER</string> <string name="notif_action_reply_hint">RESPONDER</string>
<string name="notif_error_sending">Falha ao enviar a mensagem.</string> <string name="notif_error_sending">Falha ao enviar a mensagem.</string>
<string name="notif_success_sending">Mensagem enviada para %1$s!</string> <string name="notif_success_sending">Mensagem enviada para %1$s!</string>
</resources> </resources>
\ No newline at end of file
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
<dimen name="screen_edge_left_and_right_margins">16dp</dimen> <dimen name="screen_edge_left_and_right_margins">16dp</dimen>
<dimen name="screen_edge_left_and_right_padding">16dp</dimen> <dimen name="screen_edge_left_and_right_padding">16dp</dimen>
<dimen name="chat_item_top_and_bottom_padding">12dp</dimen>
<dimen name="message_item_top_and_bottom_padding">6dp</dimen> <dimen name="message_item_top_and_bottom_padding">6dp</dimen>
<dimen name="member_item_top_and_bottom_padding">6dp</dimen> <dimen name="member_item_top_and_bottom_padding">6dp</dimen>
...@@ -23,6 +22,10 @@ ...@@ -23,6 +22,10 @@
<dimen name="nav_header_height">140dp</dimen> <dimen name="nav_header_height">140dp</dimen>
<!-- ChatRoom -->
<dimen name="chat_item_top_and_bottom_padding">12dp</dimen>
<!-- Emoji --> <!-- Emoji -->
<dimen name="picker_padding_bottom">16dp</dimen> <dimen name="picker_padding_bottom">16dp</dimen>
<dimen name="supposed_keyboard_height">252dp</dimen> <dimen name="supposed_keyboard_height">252dp</dimen>
......
...@@ -82,14 +82,20 @@ ...@@ -82,14 +82,20 @@
<string name="msg_preview_audio">Audio</string> <string name="msg_preview_audio">Audio</string>
<string name="msg_preview_photo">Photo</string> <string name="msg_preview_photo">Photo</string>
<string name="msg_no_messages_yet">No messages yet</string> <string name="msg_no_messages_yet">No messages yet</string>
<string name="msg_version">Version</string> <string name="msg_version">Version %1$s</string>
<string name="msg_build">Build</string> <string name="msg_build">Build %1$d</string>
<string name="msg_ok">OK</string> <string name="msg_ok">OK</string>
<string name="msg_ver_not_recommended"> <string name="msg_ver_not_recommended">
Looks like your server version is below the recommended version %1$s.\nYou can still login but you may experience unexpected behaviors.</string> Looks like your server version is below the recommended version %1$s.\nYou can still login but you may experience unexpected behaviors.</string>
<string name="msg_ver_not_minimum"> <string name="msg_ver_not_minimum">
Looks like your server version is below the minimum required version %1$s.\nPlease upgrade your server to login! Looks like your server version is below the minimum required version %1$s.\nPlease upgrade your server to login!
</string> </string>
<string name="msg_proceed">PROCEED</string>
<string name="msg_cancel">CANCEL</string>
<string name="msg_warning">WARNING</string>
<string name="msg_http_insecure">When using HTTP, you\'re connecting to an insecure server. We don\'t recommend you doing that.</string>
<string name="msg_error_checking_server_version">An error has occurred while checking your server version, please try again</string>
<string name="msg_invalid_server_protocol">The selected protocol is not accepted by this server, try using HTTPS</string>
<!-- System messages --> <!-- System messages -->
<string name="message_room_name_changed">Room name changed to: %1$s by %2$s</string> <string name="message_room_name_changed">Room name changed to: %1$s by %2$s</string>
...@@ -131,12 +137,12 @@ ...@@ -131,12 +137,12 @@
<string name="max_file_size_exceeded">File size %1$d bytes exceeded max upload size of %2$d bytes</string> <string name="max_file_size_exceeded">File size %1$d bytes exceeded max upload size of %2$d bytes</string>
<!-- Socket status --> <!-- Socket status -->
<string name="status_connected">connected</string> <string name="status_connected">Connected</string>
<string name="status_disconnected">disconnected</string> <string name="status_disconnected">Disconnected</string>
<string name="status_connecting">connecting</string> <string name="status_connecting">Connecting</string>
<string name="status_authenticating">authenticating</string> <string name="status_authenticating">Authenticating</string>
<string name="status_disconnecting">disconnecting</string> <string name="status_disconnecting">Disconnecting</string>
<string name="status_waiting">connecting in %d seconds</string> <string name="status_waiting">Connecting in %d seconds</string>
<!--Suggestions--> <!--Suggestions-->
<string name="suggest_all_description">Notify all in this room</string> <string name="suggest_all_description">Notify all in this room</string>
......
...@@ -3,6 +3,7 @@ package chat.rocket.android ...@@ -3,6 +3,7 @@ package chat.rocket.android
import chat.rocket.android.server.infraestructure.MemoryMessagesRepository import chat.rocket.android.server.infraestructure.MemoryMessagesRepository
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
import chat.rocket.core.model.MessageType import chat.rocket.core.model.MessageType
import kotlinx.coroutines.experimental.runBlocking
import org.hamcrest.CoreMatchers.notNullValue import org.hamcrest.CoreMatchers.notNullValue
import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.MatcherAssert.assertThat
import org.junit.Before import org.junit.Before
...@@ -30,7 +31,9 @@ class MemoryMessagesRepositoryTest { ...@@ -30,7 +31,9 @@ class MemoryMessagesRepositoryTest {
senderAlias = null, senderAlias = null,
type = MessageType.MessageRemoved(), type = MessageType.MessageRemoved(),
updatedAt = 1511443964815, updatedAt = 1511443964815,
urls = null urls = null,
pinned = false,
reactions = null
) )
val msg2 = Message( val msg2 = Message(
...@@ -50,86 +53,98 @@ class MemoryMessagesRepositoryTest { ...@@ -50,86 +53,98 @@ class MemoryMessagesRepositoryTest {
senderAlias = null, senderAlias = null,
type = MessageType.MessageRemoved(), type = MessageType.MessageRemoved(),
updatedAt = 1511443964818, updatedAt = 1511443964818,
urls = null urls = null,
pinned = false,
reactions = null
) )
@Before @Before
fun setup() { fun setup() {
repository.clear() runBlocking {
repository.clear()
}
} }
@Test @Test
fun `save() should save a single message`() { fun `save() should save a single message`() {
assertThat(repository.getAll().size, isEqualTo(0)) runBlocking {
repository.save(msg) assertThat(repository.getAll().size, isEqualTo(0))
val allMessages = repository.getAll() repository.save(msg)
assertThat(allMessages.size, isEqualTo(1)) val allMessages = repository.getAll()
allMessages[0].apply { assertThat(allMessages.size, isEqualTo(1))
assertThat(id, isEqualTo("messageId")) allMessages[0].apply {
assertThat(message, isEqualTo("Beam me up, Scotty.")) assertThat(id, isEqualTo("messageId"))
assertThat(roomId, isEqualTo("GENERAL")) assertThat(message, isEqualTo("Beam me up, Scotty."))
assertThat(roomId, isEqualTo("GENERAL"))
}
} }
} }
@Test @Test
fun `saveAll() should all saved messages`() { fun `saveAll() should all saved messages`() {
assertThat(repository.getAll().size, isEqualTo(0)) runBlocking {
repository.saveAll(listOf(msg, msg2)) assertThat(repository.getAll().size, isEqualTo(0))
val allMessages = repository.getAll() repository.saveAll(listOf(msg, msg2))
assertThat(allMessages.size, isEqualTo(2)) val allMessages = repository.getAll()
allMessages[0].apply { assertThat(allMessages.size, isEqualTo(2))
assertThat(id, isEqualTo("messageId")) allMessages[0].apply {
assertThat(message, isEqualTo("Beam me up, Scotty.")) assertThat(id, isEqualTo("messageId"))
assertThat(roomId, isEqualTo("GENERAL")) assertThat(message, isEqualTo("Beam me up, Scotty."))
} assertThat(roomId, isEqualTo("GENERAL"))
}
allMessages[1].apply { allMessages[1].apply {
assertThat(id, isEqualTo("messageId2")) assertThat(id, isEqualTo("messageId2"))
assertThat(message, isEqualTo("Highly Illogical")) assertThat(message, isEqualTo("Highly Illogical"))
assertThat(roomId, isEqualTo("sandbox")) assertThat(roomId, isEqualTo("sandbox"))
}
} }
} }
@Test @Test
fun `getById() should return a single message`() { fun `getById() should return a single message`() {
repository.saveAll(listOf(msg, msg2)) runBlocking {
var singleMsg = repository.getById("messageId") repository.saveAll(listOf(msg, msg2))
assertThat(singleMsg, notNullValue()) var singleMsg = repository.getById("messageId")
singleMsg!!.apply { assertThat(singleMsg, notNullValue())
assertThat(id, isEqualTo("messageId")) singleMsg!!.apply {
assertThat(message, isEqualTo("Beam me up, Scotty.")) assertThat(id, isEqualTo("messageId"))
assertThat(roomId, isEqualTo("GENERAL")) assertThat(message, isEqualTo("Beam me up, Scotty."))
} assertThat(roomId, isEqualTo("GENERAL"))
}
singleMsg = repository.getById("messageId2") singleMsg = repository.getById("messageId2")
assertThat(singleMsg, notNullValue()) assertThat(singleMsg, notNullValue())
singleMsg!!.apply { singleMsg!!.apply {
assertThat(id, isEqualTo("messageId2")) assertThat(id, isEqualTo("messageId2"))
assertThat(message, isEqualTo("Highly Illogical")) assertThat(message, isEqualTo("Highly Illogical"))
assertThat(roomId, isEqualTo("sandbox")) assertThat(roomId, isEqualTo("sandbox"))
}
} }
} }
@Test @Test
fun `getByRoomId() should return all messages for room id or an empty list`() { fun `getByRoomId() should return all messages for room id or an empty list`() {
repository.saveAll(listOf(msg, msg2)) runBlocking {
var roomMessages = repository.getByRoomId("faAad32fkasods2") repository.saveAll(listOf(msg, msg2))
assertThat(roomMessages.isEmpty(), isEqualTo(true)) var roomMessages = repository.getByRoomId("faAad32fkasods2")
assertThat(roomMessages.isEmpty(), isEqualTo(true))
roomMessages = repository.getByRoomId("sandbox") roomMessages = repository.getByRoomId("sandbox")
assertThat(roomMessages.size, isEqualTo(1)) assertThat(roomMessages.size, isEqualTo(1))
roomMessages[0].apply { roomMessages[0].apply {
assertThat(id, isEqualTo("messageId2")) assertThat(id, isEqualTo("messageId2"))
assertThat(message, isEqualTo("Highly Illogical")) assertThat(message, isEqualTo("Highly Illogical"))
assertThat(roomId, isEqualTo("sandbox")) assertThat(roomId, isEqualTo("sandbox"))
} }
roomMessages = repository.getByRoomId("GENERAL") roomMessages = repository.getByRoomId("GENERAL")
assertThat(roomMessages.size, isEqualTo(1)) assertThat(roomMessages.size, isEqualTo(1))
roomMessages[0].apply { roomMessages[0].apply {
assertThat(id, isEqualTo("messageId")) assertThat(id, isEqualTo("messageId"))
assertThat(message, isEqualTo("Beam me up, Scotty.")) assertThat(message, isEqualTo("Beam me up, Scotty."))
assertThat(roomId, isEqualTo("GENERAL")) assertThat(roomId, isEqualTo("GENERAL"))
}
} }
} }
} }
\ No newline at end of file
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