Unverified Commit 9dc946cc authored by divyanshu bhargava's avatar divyanshu bhargava Committed by GitHub

Merge branch 'develop' into empty-chat-view

parents 13a7e81b 1aa74e2e
Your Rocket.Chat.Android version: (make sure you are running the latest)
<!-- Version can be found by opening the side menu and then clicking on the chevron alongside username -->
## Description
<!-- 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 -->
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) -->
......@@ -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)
## 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`.
Since both the versions use `kotlin` for some or all of their classes, following are the common prerequisites for both versions:
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.
## 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.
- 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.
### Instructions specific to version
#### v1.0+
- After checking out to `develop` branch as mentioned above, simply import the project in Android Studio.
### SDK Instructions
#### 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`.
- 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
./build-sdk.sh
```
## How to run
### 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.
- 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`.
### 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.
## Bug report & Feature request
......
......@@ -13,8 +13,8 @@ android {
applicationId "chat.rocket.android"
minSdkVersion 21
targetSdkVersion versions.targetSdk
versionCode 2011
versionName "2.0.1"
versionCode 2014
versionName "2.0.4"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true
}
......@@ -47,6 +47,10 @@ android {
packagingOptions {
exclude 'META-INF/core.kotlin_module'
}
lintOptions{
disable 'MissingTranslation'
}
}
dependencies {
......
......@@ -5,7 +5,6 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<permission
......
......@@ -8,7 +8,6 @@ import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.*
import chat.rocket.android.server.domain.model.Account
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.retryIO
import chat.rocket.common.RocketChatAuthException
......@@ -27,12 +26,14 @@ private const val TYPE_LOGIN_USER_EMAIL = 0
private const val TYPE_LOGIN_CAS = 1
private const val TYPE_LOGIN_OAUTH = 2
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_GOOGLE = "google"
private const val SERVICE_NAME_LINKEDIN = "linkedin"
private const val SERVICE_NAME_GILAB = "gitlab"
class LoginPresenter @Inject constructor(private val view: LoginView,
class LoginPresenter @Inject constructor(
private val view: LoginView,
private val strategy: CancelStrategy,
private val navigator: AuthenticationNavigator,
private val tokenRepository: TokenRepository,
......@@ -41,14 +42,12 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
private val settingsInteractor: GetSettingsInteractor,
serverInteractor: GetCurrentServerInteractor,
private val saveAccountInteractor: SaveAccountInteractor,
private val factory: RocketChatClientFactory)
: CheckServerPresenter(strategy, factory, view) {
private val factory: RocketChatClientFactory
) {
// TODO - we should validate the current server when opening the app, and have a nonnull get()
private val currentServer = serverInteractor.get()!!
private lateinit var client: RocketChatClient
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 password: String
private lateinit var credentialToken: String
......@@ -62,7 +61,6 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
setupUserRegistrationView()
setupCasView()
setupOauthServicesView()
checkServerInfo(currentServer)
}
fun authenticateWithUserAndPassword(usernameOrEmail: String, password: String) {
......@@ -92,25 +90,14 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
doAuthentication(TYPE_LOGIN_OAUTH)
}
fun authenticadeWithDeepLink(deepLinkInfo: LoginDeepLinkInfo) {
fun authenticateWithDeepLink(deepLinkInfo: LoginDeepLinkInfo) {
val serverUrl = deepLinkInfo.url
setupConnectionInfo(serverUrl)
deepLinkUserId = deepLinkInfo.userId
deepLinkToken = deepLinkInfo.token
tokenRepository.save(serverUrl, Token(deepLinkUserId, deepLinkToken))
launchUI(strategy) {
try {
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")
}
}
doAuthentication(TYPE_LOGIN_DEEP_LINK)
}
private fun setupConnectionInfo(serverUrl: String) {
......@@ -156,9 +143,12 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
var totalSocialAccountsEnabled = 0
if (settings.isFacebookAuthenticationEnabled()) {
// //TODO: Remove until we have this implemented
// view.enableLoginByFacebook()
// totalSocialAccountsEnabled++
val clientId = getOauthClientId(services, SERVICE_NAME_FACEBOOK)
if (clientId != null) {
view.setupFacebookButtonListener(OauthHelper.getFacebookOauthUrl(clientId, currentServer, state), state)
view.enableLoginByFacebook()
totalSocialAccountsEnabled++
}
}
if (settings.isGithubAuthenticationEnabled()) {
val clientId = getOauthClientId(services, SERVICE_NAME_GITHUB)
......@@ -289,9 +279,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) }
?.get("appId")
?.get("clientId")
.toString()
}
private suspend fun saveAccount(username: String) {
......
......@@ -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.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.
......@@ -145,6 +145,14 @@ interface LoginView : LoadingView, MessageView, VersionCheckView {
*/
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.
*/
......
......@@ -72,7 +72,7 @@ class LoginFragment : Fragment(), LoginView {
}
deepLinkInfo?.let {
presenter.authenticadeWithDeepLink(it)
presenter.authenticateWithDeepLink(it)
}.ifNull {
presenter.setupView()
}
......@@ -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() {
ui {
button_github.isClickable = true
......@@ -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() {
social_accounts_container.postDelayed(300) {
ui {
......
......@@ -7,9 +7,10 @@ import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.server.domain.GetAccountsInteractor
import chat.rocket.android.server.domain.RefreshSettingsInteractor
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.launchUI
import chat.rocket.common.util.ifNull
import javax.inject.Inject
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 serverInteractor: SaveCurrentServerInteractor,
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) {
connectToServer(server) {
......@@ -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()) {
view.showInvalidServerUrlMessage()
} else {
......
......@@ -3,7 +3,7 @@ package chat.rocket.android.authentication.server.presentation
import chat.rocket.android.core.behaviours.LoadingView
import chat.rocket.android.core.behaviours.MessageView
interface ServerView : LoadingView, MessageView {
interface ServerView : LoadingView, MessageView, VersionCheckView {
/**
* Shows an invalid server URL message.
......
......@@ -10,4 +10,20 @@ interface VersionCheckView {
* Block user to proceed and alert him due to server having an unsupported server version.
*/
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
import android.app.AlertDialog
import android.net.Uri
import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
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.authentication.domain.model.LoginDeepLinkInfo
import chat.rocket.android.authentication.server.presentation.ServerPresenter
import chat.rocket.android.authentication.server.presentation.ServerView
import chat.rocket.android.helper.KeyboardHelper
import chat.rocket.android.util.extensions.*
import chat.rocket.common.util.ifNull
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_authentication_server.*
import javax.inject.Inject
class ServerFragment : Fragment(), ServerView {
@Inject lateinit var presenter: ServerPresenter
@Inject
lateinit var presenter: ServerPresenter
private var deepLinkInfo: LoginDeepLinkInfo? = null
private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener {
text_server_url.isCursorVisible = KeyboardHelper.isSoftKeyboardShown(relative_layout.rootView)
......@@ -33,6 +40,8 @@ class ServerFragment : Fragment(), ServerView {
}
}
private var protocol = "https://"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)
......@@ -49,8 +58,41 @@ class ServerFragment : Fragment(), ServerView {
setupOnClickListener()
deepLinkInfo?.let {
val uri = Uri.parse(it.url)
uri?.let { text_server_url.hintContent = it.host }
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() {
......@@ -74,7 +116,7 @@ class ServerFragment : Fragment(), ServerView {
}
}
override fun showMessage(resId: Int){
override fun showMessage(resId: Int) {
ui {
showToast(resId)
}
......@@ -90,15 +132,70 @@ class ServerFragment : Fragment(), ServerView {
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) {
button_connect.isEnabled = value
text_server_url.isEnabled = value
}
private fun setupOnClickListener() {
ui {
button_connect.setOnClickListener {
val url = text_server_url.textContent.ifEmpty(text_server_url.hintContent)
presenter.connect(text_server_protocol.textContent + url)
presenter.checkServer("${protocol}${url.sanitize()}")
}
}
}
}
\ No newline at end of file
......@@ -34,7 +34,7 @@ class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector {
val deepLinkInfo = intent.getLoginDeepLinkInfo()
launch(UI + job) {
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 ->
if (!authenticated) {
showServerInput(savedInstanceState, deepLinkInfo)
......
......@@ -15,9 +15,6 @@ import kotlinx.coroutines.experimental.Job
@PerFragment
class ChatRoomFragmentModule {
@Provides
fun provideChatRoomNavigator(activity: ChatRoomActivity) = ChatRoomNavigator(activity)
@Provides
fun chatRoomView(frag: ChatRoomFragment): ChatRoomView {
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
import chat.rocket.android.R
import chat.rocket.android.chatroom.ui.ChatRoomActivity
import chat.rocket.android.members.ui.newInstance
import chat.rocket.android.server.ui.changeServerIntent
import chat.rocket.android.util.extensions.addFragmentBackStack
class ChatRoomNavigator(internal val activity: ChatRoomActivity) {
......@@ -12,4 +13,9 @@ class ChatRoomNavigator(internal val activity: ChatRoomActivity) {
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
import chat.rocket.android.chatroom.viewmodel.suggestion.ChatRoomSuggestionViewModel
import chat.rocket.android.chatroom.viewmodel.suggestion.CommandSuggestionViewModel
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.infrastructure.LocalRepository
import chat.rocket.android.infrastructure.username
......@@ -172,7 +173,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
launchUI(strategy) {
view.showLoading()
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 fileSize = async { uriInteractor.getFileSize(uri) }.await()
val maxFileSize = settings.uploadMaxFileSize()
......@@ -189,12 +190,11 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
}
}
}
} catch (ex: RocketChatException) {
Timber.d(ex)
ex.message?.let {
view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
} catch (ex: Exception) {
Timber.d(ex, "Error uploading file")
when(ex) {
is RocketChatException -> view.showMessage(ex)
else -> view.showGenericErrorMessage()
}
} finally {
view.hideLoading()
......
......@@ -8,12 +8,14 @@ import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.app.AppCompatActivity
import chat.rocket.android.R
import chat.rocket.android.chatroom.presentation.ChatRoomNavigator
import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.util.extensions.addFragment
import chat.rocket.android.util.extensions.textContent
import chat.rocket.common.model.RoomType
import chat.rocket.common.model.roomTypeOf
import chat.rocket.common.util.ifNull
import dagger.android.AndroidInjection
import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector
......@@ -51,6 +53,7 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector {
// TODO - workaround for now... We will move to a single activity
@Inject lateinit var serverInteractor: GetCurrentServerInteractor
@Inject lateinit var navigator: ChatRoomNavigator
@Inject lateinit var managerFactory: ConnectionManagerFactory
private lateinit var chatRoomId: String
......@@ -66,7 +69,13 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector {
setContentView(R.layout.activity_chat_room)
// 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)
requireNotNull(chatRoomId) { "no chat_room_id provided in Intent extras" }
......
package chat.rocket.android.chatroom.ui
import android.Manifest
import android.app.Activity
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.support.annotation.DrawableRes
import android.support.v4.app.ActivityCompat
import android.support.v4.app.Fragment
import android.support.v4.content.ContextCompat
import android.support.v7.widget.DefaultItemAnimator
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
......@@ -482,31 +478,13 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
override fun showFileSelection(filter: Array<String>) {
ui {
if (ContextCompat.checkSelfPermission(it, Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(it,
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
1)
} else {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "*/*"
intent.putExtra(Intent.EXTRA_MIME_TYPES, filter)
intent.addCategory(Intent.CATEGORY_OPENABLE)
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)
}
}
}
}
override fun showInvalidFileSize(fileSize: Int, maxFileSize: Int) {
showMessage(getString(R.string.max_file_size_exceeded, fileSize, maxFileSize))
......
......@@ -118,6 +118,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
type = RoomType.DIRECT_MESSAGE,
user = SimpleUser(username = it.username, name = it.name, id = null),
name = it.name ?: "",
status = null,
fullName = it.name,
readonly = false,
updatedAt = null,
......@@ -144,6 +145,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
type = it.type,
user = it.user,
name = it.name ?: "",
status = null,
fullName = it.fullName,
readonly = it.readonly,
updatedAt = it.updatedAt,
......@@ -349,6 +351,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
type = room.type,
user = room.user ?: user,
name = room.name ?: name,
status = null,
fullName = room.fullName ?: fullName,
readonly = room.readonly,
updatedAt = room.updatedAt ?: updatedAt,
......@@ -382,6 +385,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
type = subscription.type,
user = subscription.user ?: user,
name = subscription.name,
status = null,
fullName = subscription.fullName ?: fullName,
readonly = subscription.readonly ?: readonly,
updatedAt = subscription.updatedAt ?: updatedAt,
......
......@@ -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.ui.AuthenticationActivity
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.ui.ChatRoomActivity
import chat.rocket.android.chatroom.ui.PinnedMessagesActivity
......@@ -45,7 +46,9 @@ abstract class ActivityBuilder {
abstract fun bindMainActivity(): MainActivity
@PerActivity
@ContributesAndroidInjector(modules = [ChatRoomFragmentProvider::class, MembersFragmentProvider::class])
@ContributesAndroidInjector(modules = [ChatRoomModule::class,
ChatRoomFragmentProvider::class,
MembersFragmentProvider::class])
abstract fun bindChatRoomActivity(): ChatRoomActivity
@PerActivity
......
......@@ -67,4 +67,21 @@ object OauthHelper {
"&response_type=code" +
"&scope=read_user"
}
/**
* 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"
}
}
......@@ -28,14 +28,21 @@ class ProfilePresenter @Inject constructor(private val view: ProfileView,
view.showLoading()
try {
val myself = retryIO("me") { client.me() }
myselfId = myself.id!!
val avatarUrl = serverUrl.avatarUrl(myself.username!!)
val id = myself.id
val username = myself.username
if (id == null || username == null) {
view.showGenericErrorMessage()
} else {
myselfId = id
val avatarUrl = serverUrl.avatarUrl(username)
val email = myself.emails?.getOrNull(0)?.address
view.showProfile(
avatarUrl,
myself.name ?: "",
myself.username ?: "",
myself.emails?.get(0)?.address!!
email
)
}
} catch (exception: RocketChatException) {
view.showMessage(exception)
} finally {
......
......@@ -2,7 +2,6 @@ package chat.rocket.android.profile.presentation
import chat.rocket.android.core.behaviours.LoadingView
import chat.rocket.android.core.behaviours.MessageView
import chat.rocket.core.model.Myself
interface ProfileView : LoadingView, MessageView {
......@@ -14,7 +13,7 @@ interface ProfileView : LoadingView, MessageView {
* @param username The user username.
* @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
......
......@@ -55,18 +55,18 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
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 {
image_avatar.setImageURI(avatarUrl)
text_name.textContent = name
text_username.textContent = username
text_email.textContent = email
text_email.textContent = email ?: ""
text_avatar_url.textContent = ""
currentName = name
currentUsername = username
currentEmail = email
currentEmail = email ?: ""
currentAvatar = avatarUrl
profile_container.setVisible(true)
......
......@@ -2,15 +2,15 @@ package chat.rocket.android.server.infraestructure
import chat.rocket.common.model.BaseRoom
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.subscribeRoomMessages
import chat.rocket.core.internal.realtime.unsubscribe
import chat.rocket.core.internal.realtime.socket.connect
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.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.model.Message
import chat.rocket.core.model.Myself
......@@ -60,7 +60,7 @@ class ConnectionManager(internal val client: RocketChatClient) {
Timber.d("Subscribed to rooms: $id")
roomsId = id
}
client.subscribeUserDataChanges { _, id ->
client.subscribeUserData { _, id ->
Timber.d("Subscribed to the user: $id")
userId = id
}
......
......@@ -7,6 +7,7 @@ import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.VersionInfo
import chat.rocket.android.util.extensions.launchUI
import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatInvalidProtocolException
import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.serverInfo
import kotlinx.coroutines.experimental.Deferred
......@@ -18,17 +19,18 @@ abstract class CheckServerPresenter constructor(private val strategy: CancelStra
private val factory: RocketChatClientFactory,
private val view: VersionCheckView) {
private lateinit var currentServer: String
private val client: RocketChatClient by lazy {
factory.create(currentServer)
}
private lateinit var client: RocketChatClient
internal fun checkServerInfo(serverUrl: String): Job {
return launchUI(strategy) {
try {
currentServer = serverUrl
client = factory.create(currentServer)
val version = checkServerVersion(serverUrl).await()
when (version) {
is Version.VersionOk -> {
Timber.i("Your version is nice! (Requires: 0.62.0, Yours: ${version.version})")
view.versionOk()
}
is Version.RecommendedVersionWarning -> {
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
}
} catch (ex: Exception) {
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"
private const val INTENT_CHAT_ROOM_NAME = "INTENT_CHAT_ROOM_NAME"
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 {
serverUrl?.let { url ->
putExtra(INTENT_SERVER_URL, url)
......
......@@ -4,12 +4,17 @@ import android.util.Patterns
fun String.removeTrailingSlash(): String {
return if (isNotEmpty() && this[length - 1] == '/') {
this.replace("/+$", "")
this.substring(0, length - 1)
} else {
this
}
}
fun String.sanitize(): String {
val tmp = this.trim()
return tmp.removeTrailingSlash()
}
fun String.avatarUrl(avatar: String, isGroupOrChannel: Boolean = false, format: String = "jpeg"): String {
return if (isGroupOrChannel) {
"${removeTrailingSlash()}/avatar/%23${avatar.removeTrailingSlash()}?format=$format"
......
......@@ -30,7 +30,7 @@ private const val JSON_CREDENTIAL_SECRET = "credentialSecret"
const val INTENT_OAUTH_CREDENTIAL_TOKEN = "credential_token"
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() {
private lateinit var webPageUrl: String
private lateinit var state: String
......
......@@ -18,24 +18,30 @@
android:id="@+id/text_server_url"
style="@style/Authentication.EditText"
android:layout_below="@id/text_headline"
android:layout_marginStart="-4dp"
android:layout_marginStart="-6dp"
android:layout_marginTop="32dp"
android:layout_toEndOf="@id/text_server_protocol"
android:layout_toEndOf="@id/protocol_container"
android:cursorVisible="false"
android:hint="@string/default_server"
android:imeOptions="actionDone"
android:digits="0123456789abcdefghijklmnopqrstuvwxyz.-/:"
android:inputType="textUri"
android:paddingEnd="0dp"
android:paddingStart="4dp" />
android:inputType="text|textUri"
android:paddingEnd="0dp" />
<TextView
android:id="@+id/text_server_protocol"
style="@style/Authentication.TextView"
<FrameLayout
android:id="@+id/protocol_container"
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_marginTop="32dp"
android:gravity="center_vertical"
android:text="@string/default_protocol" />
android:layout_marginTop="32dp">
<Spinner
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
android:id="@+id/view_loading"
......
<resources>
<resources
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="MissingTranslation">
<!-- Titles -->
<string name="title_sign_in_your_server">अपने सर्वर में साइन इन करें</string>
......
......@@ -91,6 +91,12 @@
</string>
<string name="msg_no_chat_title">Nenhuma mensagem de chat</string>
<string name="msg_no_chat_description">Comece a conversar para ver suas\nmensagens aqui.</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 -->
<string name="message_room_name_changed">Nome da sala alterado para: %1$s por %2$s</string>
......@@ -132,12 +138,12 @@
<string name="max_file_size_exceeded">Tamanho de arquivo (%1$d bytes) excedeu tamanho máximo de upload (%2$d bytes)</string>
<!-- Socket status -->
<string name="status_connected">conectado</string>
<string name="status_disconnected">desconetado</string>
<string name="status_connecting">conectando</string>
<string name="status_authenticating">autenticando</string>
<string name="status_disconnecting">desconectando</string>
<string name="status_waiting">conectando em %d segundos</string>
<string name="status_connected">Conectado</string>
<string name="status_disconnected">Desconetado</string>
<string name="status_connecting">Conectando</string>
<string name="status_authenticating">Autenticando</string>
<string name="status_disconnecting">Desconectando</string>
<string name="status_waiting">Conectando em %d segundos</string>
<!--Suggestions-->
<string name="suggest_all_description">Notifica todos nesta sala</string>
......
......@@ -92,6 +92,12 @@
</string>
<string name="msg_no_chat_title">No chat messages</string>
<string name="msg_no_chat_description">Start conversing to see your\nmessages here.</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 -->
<string name="message_room_name_changed">Room name changed to: %1$s by %2$s</string>
......@@ -133,12 +139,12 @@
<string name="max_file_size_exceeded">File size %1$d bytes exceeded max upload size of %2$d bytes</string>
<!-- Socket status -->
<string name="status_connected">connected</string>
<string name="status_disconnected">disconnected</string>
<string name="status_connecting">connecting</string>
<string name="status_authenticating">authenticating</string>
<string name="status_disconnecting">disconnecting</string>
<string name="status_waiting">connecting in %d seconds</string>
<string name="status_connected">Connected</string>
<string name="status_disconnected">Disconnected</string>
<string name="status_connecting">Connecting</string>
<string name="status_authenticating">Authenticating</string>
<string name="status_disconnecting">Disconnecting</string>
<string name="status_waiting">Connecting in %d seconds</string>
<!--Suggestions-->
<string name="suggest_all_description">Notify all in this room</string>
......
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