From ac5ee24a7b68f8d3d57937230810fac20152fc32 Mon Sep 17 00:00:00 2001 From: aniket Date: Tue, 15 May 2018 03:40:07 +0530 Subject: [PATCH 01/45] adds basic cases for google smart lock --- app/build.gradle | 1 + .../authentication/login/ui/LoginFragment.kt | 164 +++++++++++++----- .../server/presentation/ServerPresenter.kt | 2 + dependencies.gradle | 1 + 4 files changed, 120 insertions(+), 48 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index e8a8563bdd..291c15ed8e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -74,6 +74,7 @@ dependencies { kapt libraries.daggerAndroidApt implementation libraries.playServicesGcm + implementation libraries.playServicesAuth implementation libraries.room kapt libraries.roomProcessor diff --git a/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt b/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt index b71a1780c6..65d5665a42 100644 --- a/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt +++ b/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt @@ -31,14 +31,31 @@ import chat.rocket.android.webview.oauth.ui.INTENT_OAUTH_CREDENTIAL_SECRET import chat.rocket.android.webview.oauth.ui.INTENT_OAUTH_CREDENTIAL_TOKEN import chat.rocket.android.webview.oauth.ui.oauthWebViewIntent import chat.rocket.common.util.ifNull +import com.google.android.gms.auth.api.Auth +import com.google.android.gms.auth.api.credentials.Credential +import com.google.android.gms.auth.api.credentials.CredentialRequest +import com.google.android.gms.auth.api.credentials.IdentityProviders +import com.google.android.gms.common.api.CommonStatusCodes +import com.google.android.gms.common.api.GoogleApiClient +import com.google.android.gms.common.api.Status import dagger.android.support.AndroidSupportInjection import kotlinx.android.synthetic.main.fragment_authentication_log_in.* import javax.inject.Inject + internal const val REQUEST_CODE_FOR_CAS = 1 internal const val REQUEST_CODE_FOR_OAUTH = 2 +internal const val MULTIPLE_CREDENTIALS_READ = 3 + +class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks { + override fun onConnected(p0: Bundle?) { + requestCredentials() + } + + override fun onConnectionSuspended(p0: Int) { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } -class LoginFragment : Fragment(), LoginView { @Inject lateinit var presenter: LoginPresenter private var isOauthViewEnable = false @@ -47,6 +64,7 @@ class LoginFragment : Fragment(), LoginView { } private var isGlobalLayoutListenerSetUp = false private var deepLinkInfo: LoginDeepLinkInfo? = null + private var googleApiClient: GoogleApiClient? = null companion object { private const val DEEP_LINK_INFO = "DeepLinkInfo" @@ -61,15 +79,16 @@ class LoginFragment : Fragment(), LoginView { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) AndroidSupportInjection.inject(this) + buildGoogleApiClient() deepLinkInfo = arguments?.getParcelable(DEEP_LINK_INFO) } override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? ): View? = - container?.inflate(R.layout.fragment_authentication_log_in) + container?.inflate(R.layout.fragment_authentication_log_in) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -102,26 +121,75 @@ class LoginFragment : Fragment(), LoginView { } else if (requestCode == REQUEST_CODE_FOR_OAUTH) { data?.apply { presenter.authenticateWithOauth( - getStringExtra(INTENT_OAUTH_CREDENTIAL_TOKEN), - getStringExtra(INTENT_OAUTH_CREDENTIAL_SECRET) + getStringExtra(INTENT_OAUTH_CREDENTIAL_TOKEN), + getStringExtra(INTENT_OAUTH_CREDENTIAL_SECRET) ) } + } else if (requestCode == MULTIPLE_CREDENTIALS_READ) { + var loginCredentials: Credential = data!!.getParcelableExtra(Credential.EXTRA_KEY) + handleCredential(loginCredentials) + } + } + } + + private fun buildGoogleApiClient() { + googleApiClient = GoogleApiClient.Builder(context!!) + .addConnectionCallbacks(this) + .addApi(Auth.CREDENTIALS_API) + .build() + } + + private fun requestCredentials() { + var request: CredentialRequest = CredentialRequest.Builder() + .setPasswordLoginSupported(true) + //add account types for custom login methods + /*.setAccountTypes(IdentityProviders.GOOGLE,IdentityProviders.FACEBOOK)*/ + .build() + + Auth.CredentialsApi.request(googleApiClient, request).setResultCallback { credentialRequestResult -> + //hideProgress() + val status = credentialRequestResult.getStatus() + if (status.isSuccess) { + // Auto sign-in success + handleCredential(credentialRequestResult.getCredential()) + } else if (status.statusCode == CommonStatusCodes.RESOLUTION_REQUIRED) { + // Getting credential needs to show some UI, start resolution + resolveResult(status, MULTIPLE_CREDENTIALS_READ) } } } + private fun handleCredential(loginCredentials: Credential) { + if (loginCredentials.accountType == IdentityProviders.GOOGLE) { + //TODO add SL code for google as custom login method + } else if (loginCredentials.accountType == IdentityProviders.FACEBOOK) { + //TODO add SL code for facebook as custom login method + } else if (loginCredentials.accountType == IdentityProviders.TWITTER) { + //TODO add SL code for twitter as custom login method + } else if (loginCredentials.accountType == IdentityProviders.LINKEDIN) { + //TODO add SL code for linkedin as custom login method + } else { + presenter.authenticateWithUserAndPassword(loginCredentials.id, loginCredentials.password!!) + } + } + + private fun resolveResult(status: Status, requestCode: Int) { + //TODO surround with a try/catch block + status.startResolutionForResult(activity, requestCode) + } + private fun tintEditTextDrawableStart() { ui { val personDrawable = - DrawableHelper.getDrawableFromId(R.drawable.ic_assignment_ind_black_24dp, it) + DrawableHelper.getDrawableFromId(R.drawable.ic_assignment_ind_black_24dp, it) val lockDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_lock_black_24dp, it) val drawables = arrayOf(personDrawable, lockDrawable) DrawableHelper.wrapDrawables(drawables) DrawableHelper.tintDrawables(drawables, it, R.color.colorDrawableTintGrey) DrawableHelper.compoundDrawables( - arrayOf(text_username_or_email, text_password), - drawables + arrayOf(text_username_or_email, text_password), + drawables ) } } @@ -172,8 +240,8 @@ class LoginFragment : Fragment(), LoginView { ui { button_log_in.setOnClickListener { presenter.authenticateWithUserAndPassword( - text_username_or_email.textContent, - text_password.textContent + text_username_or_email.textContent, + text_password.textContent ) } } @@ -211,8 +279,8 @@ class LoginFragment : Fragment(), LoginView { ui { activity -> button_cas.setOnClickListener { startActivityForResult( - activity.casWebViewIntent(casUrl, casToken), - REQUEST_CODE_FOR_CAS + activity.casWebViewIntent(casUrl, casToken), + REQUEST_CODE_FOR_CAS ) activity.overridePendingTransition(R.anim.slide_up, R.anim.hold) } @@ -304,8 +372,8 @@ class LoginFragment : Fragment(), LoginView { ui { activity -> button_facebook.setOnClickListener { startActivityForResult( - activity.oauthWebViewIntent(facebookOauthUrl, state), - REQUEST_CODE_FOR_OAUTH + activity.oauthWebViewIntent(facebookOauthUrl, state), + REQUEST_CODE_FOR_OAUTH ) activity.overridePendingTransition(R.anim.slide_up, R.anim.hold) } @@ -322,8 +390,8 @@ class LoginFragment : Fragment(), LoginView { ui { activity -> button_github.setOnClickListener { startActivityForResult( - activity.oauthWebViewIntent(githubUrl, state), - REQUEST_CODE_FOR_OAUTH + activity.oauthWebViewIntent(githubUrl, state), + REQUEST_CODE_FOR_OAUTH ) activity.overridePendingTransition(R.anim.slide_up, R.anim.hold) } @@ -342,8 +410,8 @@ class LoginFragment : Fragment(), LoginView { ui { activity -> button_google.setOnClickListener { startActivityForResult( - activity.oauthWebViewIntent(googleUrl, state), - REQUEST_CODE_FOR_OAUTH + activity.oauthWebViewIntent(googleUrl, state), + REQUEST_CODE_FOR_OAUTH ) activity.overridePendingTransition(R.anim.slide_up, R.anim.hold) } @@ -360,8 +428,8 @@ class LoginFragment : Fragment(), LoginView { ui { activity -> button_linkedin.setOnClickListener { startActivityForResult( - activity.oauthWebViewIntent(linkedinUrl, state), - REQUEST_CODE_FOR_OAUTH + activity.oauthWebViewIntent(linkedinUrl, state), + REQUEST_CODE_FOR_OAUTH ) activity.overridePendingTransition(R.anim.slide_up, R.anim.hold) } @@ -390,8 +458,8 @@ class LoginFragment : Fragment(), LoginView { ui { activity -> button_gitlab.setOnClickListener { startActivityForResult( - activity.oauthWebViewIntent(gitlabUrl, state), - REQUEST_CODE_FOR_OAUTH + activity.oauthWebViewIntent(gitlabUrl, state), + REQUEST_CODE_FOR_OAUTH ) activity.overridePendingTransition(R.anim.slide_up, R.anim.hold) } @@ -399,11 +467,11 @@ class LoginFragment : Fragment(), LoginView { } override fun addCustomOauthServiceButton( - customOauthUrl: String, - state: String, - serviceName: String, - serviceNameColor: Int, - buttonColor: Int + customOauthUrl: String, + state: String, + serviceName: String, + serviceNameColor: Int, + buttonColor: Int ) { ui { activity -> val button = getCustomOauthButton(serviceName, serviceNameColor, buttonColor) @@ -411,8 +479,8 @@ class LoginFragment : Fragment(), LoginView { button.setOnClickListener { startActivityForResult( - activity.oauthWebViewIntent(customOauthUrl, state), - REQUEST_CODE_FOR_OAUTH + activity.oauthWebViewIntent(customOauthUrl, state), + REQUEST_CODE_FOR_OAUTH ) activity.overridePendingTransition(R.anim.slide_up, R.anim.hold) } @@ -460,9 +528,9 @@ class LoginFragment : Fragment(), LoginView { social_accounts_container.postDelayed(300) { ui { (0..social_accounts_container.childCount) - .mapNotNull { social_accounts_container.getChildAt(it) as? ImageButton } - .filter { it.isClickable } - .forEach { it.isVisible = true } + .mapNotNull { social_accounts_container.getChildAt(it) as? ImageButton } + .filter { it.isClickable } + .forEach { it.isVisible = true } } } } @@ -496,10 +564,10 @@ class LoginFragment : Fragment(), LoginView { private fun showThreeSocialAccountsMethods() { (0..social_accounts_container.childCount) - .mapNotNull { social_accounts_container.getChildAt(it) as? ImageButton } - .filter { it.isClickable } - .take(3) - .forEach { it.isVisible = true } + .mapNotNull { social_accounts_container.getChildAt(it) as? ImageButton } + .filter { it.isClickable } + .take(3) + .forEach { it.isVisible = true } } private fun showOauthView() { @@ -524,28 +592,28 @@ class LoginFragment : Fragment(), LoginView { private fun enabledOauthAccountsImageButtons(): Int { return (0..social_accounts_container.childCount) - .mapNotNull { social_accounts_container.getChildAt(it) as? ImageButton } - .filter { it.isClickable } - .size + .mapNotNull { social_accounts_container.getChildAt(it) as? ImageButton } + .filter { it.isClickable } + .size } private fun enabledServicesAccountsButtons(): Int { return (0..social_accounts_container.childCount) - .mapNotNull { social_accounts_container.getChildAt(it) as? Button } - .size + .mapNotNull { social_accounts_container.getChildAt(it) as? Button } + .size } /** * Gets a stylized custom OAuth button. */ private fun getCustomOauthButton( - buttonText: String, - buttonTextColor: Int, - buttonBgColor: Int + buttonText: String, + buttonTextColor: Int, + buttonBgColor: Int ): Button { val params: LinearLayout.LayoutParams = LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, - LinearLayout.LayoutParams.WRAP_CONTENT + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT ) val margin = resources.getDimensionPixelSize(R.dimen.screen_edge_left_and_right_margins) diff --git a/app/src/main/java/chat/rocket/android/authentication/server/presentation/ServerPresenter.kt b/app/src/main/java/chat/rocket/android/authentication/server/presentation/ServerPresenter.kt index c05e87b4a7..8350b8dd3b 100644 --- a/app/src/main/java/chat/rocket/android/authentication/server/presentation/ServerPresenter.kt +++ b/app/src/main/java/chat/rocket/android/authentication/server/presentation/ServerPresenter.kt @@ -32,6 +32,7 @@ class ServerPresenter @Inject constructor(private val view: ServerView, } fun connect(server: String) { + //code that leads to login screen (smart lock will be implemented after this) connectToServer(server) { navigator.toLogin() } @@ -64,6 +65,7 @@ class ServerPresenter @Inject constructor(private val view: ServerView, } fun deepLink(deepLinkInfo: LoginDeepLinkInfo) { + //code that leads to login screen (smart lock will be implemented after this) connectToServer(deepLinkInfo.url) { navigator.toLogin(deepLinkInfo) } diff --git a/dependencies.gradle b/dependencies.gradle index 942e23a34c..04d563469c 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -59,6 +59,7 @@ ext { daggerProcessor : "com.google.dagger:dagger-compiler:${versions.dagger}", daggerAndroidApt : "com.google.dagger:dagger-android-processor:${versions.dagger}", playServicesGcm : "com.google.android.gms:play-services-gcm:${versions.playServices}", + playServicesAuth : "com.google.android.gms:play-services-auth:${versions.playServices}", exoPlayer : "com.google.android.exoplayer:exoplayer:${versions.exoPlayer}", room : "android.arch.persistence.room:runtime:${versions.room}", From 8ed04ad5873b01931b2b35f60516108187467b60 Mon Sep 17 00:00:00 2001 From: aniket Date: Tue, 15 May 2018 18:10:16 +0530 Subject: [PATCH 02/45] deals with more edge cases --- .../login/presentation/LoginPresenter.kt | 1 + .../login/presentation/LoginView.kt | 6 ++ .../authentication/login/ui/LoginFragment.kt | 91 +++++++++++++++---- .../signup/presentation/SignupPresenter.kt | 5 + .../signup/presentation/SignupView.kt | 6 ++ .../signup/ui/SignupFragment.kt | 75 ++++++++++++++- 6 files changed, 166 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginPresenter.kt b/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginPresenter.kt index 46452ff2bc..4c2052f3a5 100644 --- a/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginPresenter.kt +++ b/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginPresenter.kt @@ -303,6 +303,7 @@ class LoginPresenter @Inject constructor( saveAccount(username) saveToken(token) registerPushToken() + navigator.toChatList() } else if (loginType == TYPE_LOGIN_OAUTH) { navigator.toRegisterUsername(token.userId, token.authToken) diff --git a/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginView.kt b/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginView.kt index 98a2d6be1c..7eecdac651 100644 --- a/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginView.kt +++ b/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginView.kt @@ -2,6 +2,7 @@ package chat.rocket.android.authentication.login.presentation import chat.rocket.android.core.behaviours.LoadingView import chat.rocket.android.core.behaviours.MessageView +import com.google.android.gms.auth.api.credentials.Credential interface LoginView : LoadingView, MessageView { @@ -223,4 +224,9 @@ interface LoginView : LoadingView, MessageView { * Alerts the user about a wrong inputted password. */ fun alertWrongPassword() + + /** + * Save credentials via google smart lock + */ + fun saveSmartLockCredentials(loginCredential: Credential) } \ No newline at end of file diff --git a/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt b/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt index 65d5665a42..af41f85933 100644 --- a/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt +++ b/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt @@ -2,12 +2,15 @@ package chat.rocket.android.authentication.login.ui import DrawableHelper import android.app.Activity +import android.app.PendingIntent import android.content.Intent +import android.content.IntentSender import android.graphics.PorterDuff import android.os.Build import android.os.Bundle import android.support.v4.app.Fragment import android.text.style.ClickableSpan +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -32,9 +35,7 @@ import chat.rocket.android.webview.oauth.ui.INTENT_OAUTH_CREDENTIAL_TOKEN import chat.rocket.android.webview.oauth.ui.oauthWebViewIntent import chat.rocket.common.util.ifNull import com.google.android.gms.auth.api.Auth -import com.google.android.gms.auth.api.credentials.Credential -import com.google.android.gms.auth.api.credentials.CredentialRequest -import com.google.android.gms.auth.api.credentials.IdentityProviders +import com.google.android.gms.auth.api.credentials.* import com.google.android.gms.common.api.CommonStatusCodes import com.google.android.gms.common.api.GoogleApiClient import com.google.android.gms.common.api.Status @@ -46,16 +47,9 @@ import javax.inject.Inject internal const val REQUEST_CODE_FOR_CAS = 1 internal const val REQUEST_CODE_FOR_OAUTH = 2 internal const val MULTIPLE_CREDENTIALS_READ = 3 +internal const val NO_CREDENTIALS_EXIST = 4 class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks { - override fun onConnected(p0: Bundle?) { - requestCredentials() - } - - override fun onConnectionSuspended(p0: Int) { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - @Inject lateinit var presenter: LoginPresenter private var isOauthViewEnable = false @@ -65,6 +59,7 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks private var isGlobalLayoutListenerSetUp = false private var deepLinkInfo: LoginDeepLinkInfo? = null private var googleApiClient: GoogleApiClient? = null + private var credentialsToBeSaved: Credential? = null companion object { private const val DEEP_LINK_INFO = "DeepLinkInfo" @@ -76,6 +71,14 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks } } + override fun onConnected(p0: Bundle?) { + //add call to the save credentials function here just like in sign up fragment + } + + override fun onConnectionSuspended(p0: Int) { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) AndroidSupportInjection.inject(this) @@ -128,17 +131,55 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks } else if (requestCode == MULTIPLE_CREDENTIALS_READ) { var loginCredentials: Credential = data!!.getParcelableExtra(Credential.EXTRA_KEY) handleCredential(loginCredentials) + } else if (requestCode == NO_CREDENTIALS_EXIST) { + //use the hints to autofill some info in the sign up or sign in forms + var loginCredentials: Credential = data!!.getParcelableExtra(Credential.EXTRA_KEY) + //pass these info to sign up view to autofill forms + var name = loginCredentials.name + var email = loginCredentials.id + var password = loginCredentials.password + + text_username_or_email.setText(email) + text_password.setText(password) } } + //cancelled + else if (resultCode == Activity.RESULT_CANCELED) { + + } + //create new account to use it as login account (deal with this case carefully, many edge cases) + else if (resultCode == CredentialsApi.ACTIVITY_RESULT_ADD_ACCOUNT) { + //save credentials in this case after user signs up as well as in that case when user signs in + // with a new account other than those saved by smart lock. Also delete and disableAutoSignIn + // need to be implemented. Refer docs. + } + //no hints for user id's exist + else if (resultCode == CredentialsApi.ACTIVITY_RESULT_NO_HINTS_AVAILABLE) { + + } + } + + override fun onDestroy() { + super.onDestroy() + googleApiClient!!.stopAutoManage(activity!!) + googleApiClient!!.disconnect() } private fun buildGoogleApiClient() { googleApiClient = GoogleApiClient.Builder(context!!) + .enableAutoManage(activity!!, { + Log.d("STATUS", "ERROR: connection to client failed") + }) .addConnectionCallbacks(this) .addApi(Auth.CREDENTIALS_API) .build() } + override fun onStart() { + super.onStart() + requestCredentials() + } + private fun requestCredentials() { var request: CredentialRequest = CredentialRequest.Builder() .setPasswordLoginSupported(true) @@ -148,19 +189,39 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks Auth.CredentialsApi.request(googleApiClient, request).setResultCallback { credentialRequestResult -> //hideProgress() - val status = credentialRequestResult.getStatus() + val status = credentialRequestResult.status if (status.isSuccess) { // Auto sign-in success - handleCredential(credentialRequestResult.getCredential()) + handleCredential(credentialRequestResult.credential) } else if (status.statusCode == CommonStatusCodes.RESOLUTION_REQUIRED) { // Getting credential needs to show some UI, start resolution resolveResult(status, MULTIPLE_CREDENTIALS_READ) + } else if (status.statusCode == CommonStatusCodes.SIGN_IN_REQUIRED) { + //resolveResult(status, NO_CREDENTIALS_EXIST) + //build a dialog for possible account hints + var hintRequest: HintRequest = HintRequest.Builder() + .setHintPickerConfig(CredentialPickerConfig.Builder() + .setShowCancelButton(true) + .build()) + .setEmailAddressIdentifierSupported(true) + .setAccountTypes(IdentityProviders.GOOGLE) + .build() + var intent: PendingIntent = Auth.CredentialsApi.getHintPickerIntent(googleApiClient, hintRequest) + try { + startIntentSenderForResult(intent.intentSender, NO_CREDENTIALS_EXIST, null, 0, 0, 0, null) + } catch (e: IntentSender.SendIntentException) { + Log.e("STATUS", "ERROR: Could not start hint picker Intent", e); + } + } else { + Log.d("STATUS", "ERROR: nothing happening") } } } private fun handleCredential(loginCredentials: Credential) { - if (loginCredentials.accountType == IdentityProviders.GOOGLE) { + if (loginCredentials.accountType == null) { + presenter.authenticateWithUserAndPassword(loginCredentials.id, loginCredentials.password!!) + } else if (loginCredentials.accountType == IdentityProviders.GOOGLE) { //TODO add SL code for google as custom login method } else if (loginCredentials.accountType == IdentityProviders.FACEBOOK) { //TODO add SL code for facebook as custom login method @@ -168,8 +229,6 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks //TODO add SL code for twitter as custom login method } else if (loginCredentials.accountType == IdentityProviders.LINKEDIN) { //TODO add SL code for linkedin as custom login method - } else { - presenter.authenticateWithUserAndPassword(loginCredentials.id, loginCredentials.password!!) } } diff --git a/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupPresenter.kt b/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupPresenter.kt index 183bda5144..4890999dc2 100644 --- a/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupPresenter.kt +++ b/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupPresenter.kt @@ -20,6 +20,7 @@ import chat.rocket.core.internal.rest.login import chat.rocket.core.internal.rest.me import chat.rocket.core.internal.rest.signup import chat.rocket.core.model.Myself +import com.google.android.gms.auth.api.credentials.Credential import javax.inject.Inject class SignupPresenter @Inject constructor(private val view: SignupView, @@ -66,6 +67,10 @@ class SignupPresenter @Inject constructor(private val view: SignupView, localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, me.username) saveAccount(me) registerPushToken() + var loginCredentials: Credential = Credential.Builder(email) + .setPassword(password) + .build() + view.saveSmartLockCredentials(loginCredentials) navigator.toChatList() } catch (exception: RocketChatException) { exception.message?.let { diff --git a/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupView.kt b/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupView.kt index 3c0d3d50f0..8730e84b1b 100644 --- a/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupView.kt +++ b/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupView.kt @@ -2,6 +2,7 @@ package chat.rocket.android.authentication.signup.presentation import chat.rocket.android.core.behaviours.LoadingView import chat.rocket.android.core.behaviours.MessageView +import com.google.android.gms.auth.api.credentials.Credential interface SignupView : LoadingView, MessageView { @@ -24,4 +25,9 @@ interface SignupView : LoadingView, MessageView { * Alerts the user about a blank email. */ fun alertBlankEmail() + + /** + * Save credentials via google smart lock + */ + fun saveSmartLockCredentials(loginCredential: Credential) } \ No newline at end of file diff --git a/app/src/main/java/chat/rocket/android/authentication/signup/ui/SignupFragment.kt b/app/src/main/java/chat/rocket/android/authentication/signup/ui/SignupFragment.kt index b45e595f78..8e1727bcd9 100644 --- a/app/src/main/java/chat/rocket/android/authentication/signup/ui/SignupFragment.kt +++ b/app/src/main/java/chat/rocket/android/authentication/signup/ui/SignupFragment.kt @@ -1,10 +1,13 @@ package chat.rocket.android.authentication.signup.ui import DrawableHelper +import android.app.Activity.RESULT_OK +import android.content.Intent import android.os.Build import android.os.Bundle import android.support.v4.app.Fragment import android.text.style.ClickableSpan +import android.util.Log import android.view.* import android.widget.Toast import chat.rocket.android.R @@ -13,12 +16,30 @@ import chat.rocket.android.authentication.signup.presentation.SignupView import chat.rocket.android.helper.KeyboardHelper import chat.rocket.android.helper.TextHelper import chat.rocket.android.util.extensions.* +import com.google.android.gms.auth.api.Auth +import com.google.android.gms.auth.api.credentials.Credential +import com.google.android.gms.common.api.GoogleApiClient +import com.google.android.gms.common.api.ResolvingResultCallbacks +import com.google.android.gms.common.api.Status import dagger.android.support.AndroidSupportInjection import kotlinx.android.synthetic.main.fragment_authentication_sign_up.* import javax.inject.Inject -class SignupFragment : Fragment(), SignupView { - @Inject lateinit var presenter: SignupPresenter +internal const val SAVE_CREDENTIALS = 1 + +class SignupFragment : Fragment(), SignupView, GoogleApiClient.ConnectionCallbacks { + override fun onConnected(p0: Bundle?) { + saveCredentials() + } + + override fun onConnectionSuspended(p0: Int) { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + @Inject + lateinit var presenter: SignupPresenter + private var googleApiClient: GoogleApiClient? = null + private var credentialsToBeSaved: Credential? = null private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener { if (KeyboardHelper.isSoftKeyboardShown(relative_layout.rootView)) { bottom_container.setVisible(false) @@ -58,6 +79,12 @@ class SignupFragment : Fragment(), SignupView { } } + override fun onDestroy() { + super.onDestroy() + googleApiClient!!.stopAutoManage(activity!!) + googleApiClient!!.disconnect() + } + override fun onDestroyView() { relative_layout.viewTreeObserver.removeOnGlobalLayoutListener(layoutListener) super.onDestroyView() @@ -95,6 +122,50 @@ class SignupFragment : Fragment(), SignupView { } } + override fun saveSmartLockCredentials(loginCredential: Credential) { + credentialsToBeSaved = loginCredential + googleApiClient = GoogleApiClient.Builder(context!!) + .enableAutoManage(activity!!, { + Log.d("STATUS", "ERROR: connection to client failed") + }) + .addConnectionCallbacks(this) + .addApi(Auth.CREDENTIALS_API) + .build() + } + + override fun onStart() { + super.onStart() + saveCredentials() + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + if (requestCode == SAVE_CREDENTIALS) { + if (resultCode == RESULT_OK) { + Toast.makeText(context, "Credentials saved successfully", Toast.LENGTH_SHORT).show() + } else { + Log.e("STATUS", "ERROR: Cancelled by user") + } + } + } + + private fun saveCredentials() { + if (credentialsToBeSaved == null) { + return + } + Auth.CredentialsApi.save(googleApiClient, credentialsToBeSaved).setResultCallback( + object : ResolvingResultCallbacks(activity!!, SAVE_CREDENTIALS) { + override fun onSuccess(status: Status) { + Log.d("STATUS", "save:SUCCESS:$status") + credentialsToBeSaved = null + } + + override fun onUnresolvableFailure(status: Status) { + Log.w("STATUS", "save:FAILURE:$status") + credentialsToBeSaved = null + } + }) + } + override fun showLoading() { ui { enableUserInput(false) From e528cdf2dc7fbb9e518207cf8f17820da0b53b19 Mon Sep 17 00:00:00 2001 From: aniket Date: Wed, 16 May 2018 00:03:01 +0530 Subject: [PATCH 03/45] deals with all email and password cases --- .../login/presentation/LoginPresenter.kt | 6 +- .../login/presentation/LoginView.kt | 2 +- .../authentication/login/ui/LoginFragment.kt | 66 +++++++++++++------ .../signup/presentation/SignupView.kt | 2 +- .../signup/ui/SignupFragment.kt | 40 +++-------- .../ui/AuthenticationActivity.kt | 8 +++ .../main/presentation/MainPresenter.kt | 3 +- .../android/main/presentation/MainView.kt | 5 ++ .../rocket/android/main/ui/MainActivity.kt | 51 ++++++++++++-- 9 files changed, 123 insertions(+), 60 deletions(-) diff --git a/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginPresenter.kt b/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginPresenter.kt index 4c2052f3a5..1205da28f4 100644 --- a/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginPresenter.kt +++ b/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginPresenter.kt @@ -17,6 +17,7 @@ import chat.rocket.common.model.Token import chat.rocket.common.util.ifNull import chat.rocket.core.RocketChatClient import chat.rocket.core.internal.rest.* +import com.google.android.gms.auth.api.credentials.Credential import kotlinx.coroutines.experimental.delay import timber.log.Timber import java.util.concurrent.TimeUnit @@ -303,7 +304,10 @@ class LoginPresenter @Inject constructor( saveAccount(username) saveToken(token) registerPushToken() - + var loginCredentials: Credential = Credential.Builder(usernameOrEmail) + .setPassword(password) + .build() + view.saveSmartLockCredentials(loginCredentials) navigator.toChatList() } else if (loginType == TYPE_LOGIN_OAUTH) { navigator.toRegisterUsername(token.userId, token.authToken) diff --git a/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginView.kt b/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginView.kt index 7eecdac651..ff33b7e18c 100644 --- a/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginView.kt +++ b/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginView.kt @@ -228,5 +228,5 @@ interface LoginView : LoadingView, MessageView { /** * Save credentials via google smart lock */ - fun saveSmartLockCredentials(loginCredential: Credential) + fun saveSmartLockCredentials(loginCredential: Credential?) } \ No newline at end of file diff --git a/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt b/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt index af41f85933..bb5c3156e2 100644 --- a/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt +++ b/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt @@ -15,10 +15,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.ViewTreeObserver -import android.widget.Button -import android.widget.ImageButton -import android.widget.LinearLayout -import android.widget.ScrollView +import android.widget.* import androidx.core.view.isVisible import androidx.core.view.postDelayed import chat.rocket.android.R @@ -38,6 +35,7 @@ import com.google.android.gms.auth.api.Auth import com.google.android.gms.auth.api.credentials.* import com.google.android.gms.common.api.CommonStatusCodes import com.google.android.gms.common.api.GoogleApiClient +import com.google.android.gms.common.api.ResolvingResultCallbacks import com.google.android.gms.common.api.Status import dagger.android.support.AndroidSupportInjection import kotlinx.android.synthetic.main.fragment_authentication_log_in.* @@ -48,6 +46,9 @@ internal const val REQUEST_CODE_FOR_CAS = 1 internal const val REQUEST_CODE_FOR_OAUTH = 2 internal const val MULTIPLE_CREDENTIALS_READ = 3 internal const val NO_CREDENTIALS_EXIST = 4 +internal const val SAVE_CREDENTIALS = 5 + +var googleApiClient: GoogleApiClient? = null class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks { @Inject @@ -58,7 +59,6 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks } private var isGlobalLayoutListenerSetUp = false private var deepLinkInfo: LoginDeepLinkInfo? = null - private var googleApiClient: GoogleApiClient? = null private var credentialsToBeSaved: Credential? = null companion object { @@ -72,7 +72,7 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks } override fun onConnected(p0: Bundle?) { - //add call to the save credentials function here just like in sign up fragment + saveSmartLockCredentials(credentialsToBeSaved) } override fun onConnectionSuspended(p0: Int) { @@ -132,30 +132,35 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks var loginCredentials: Credential = data!!.getParcelableExtra(Credential.EXTRA_KEY) handleCredential(loginCredentials) } else if (requestCode == NO_CREDENTIALS_EXIST) { - //use the hints to autofill some info in the sign up or sign in forms + //use the hints to autofill sign in forms to reduce the info to be filled var loginCredentials: Credential = data!!.getParcelableExtra(Credential.EXTRA_KEY) - //pass these info to sign up view to autofill forms - var name = loginCredentials.name var email = loginCredentials.id var password = loginCredentials.password text_username_or_email.setText(email) text_password.setText(password) + } else if (requestCode == SAVE_CREDENTIALS) { + Toast.makeText(context, "Credentials saved successfully", Toast.LENGTH_SHORT).show() } + } else if (requestCode == SAVE_CREDENTIALS) { + Log.e("STATUS", "ERROR: Cancelled by user") + } else if (requestCode == MULTIPLE_CREDENTIALS_READ) { + Log.d("STATUS", "failed ") } - //cancelled - else if (resultCode == Activity.RESULT_CANCELED) { + //cancel button pressed by the user in case of reading from smart lock + else if (resultCode == Activity.RESULT_CANCELED) { + //add shared preference so that the dialog is not shown always } + //create new account to use it as login account (deal with this case carefully, many edge cases) else if (resultCode == CredentialsApi.ACTIVITY_RESULT_ADD_ACCOUNT) { - //save credentials in this case after user signs up as well as in that case when user signs in - // with a new account other than those saved by smart lock. Also delete and disableAutoSignIn - // need to be implemented. Refer docs. } + //no hints for user id's exist else if (resultCode == CredentialsApi.ACTIVITY_RESULT_NO_HINTS_AVAILABLE) { - + } else { + Log.d("Status", "nothing happening") } } @@ -188,16 +193,14 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks .build() Auth.CredentialsApi.request(googleApiClient, request).setResultCallback { credentialRequestResult -> - //hideProgress() val status = credentialRequestResult.status if (status.isSuccess) { // Auto sign-in success handleCredential(credentialRequestResult.credential) } else if (status.statusCode == CommonStatusCodes.RESOLUTION_REQUIRED) { - // Getting credential needs to show some UI, start resolution resolveResult(status, MULTIPLE_CREDENTIALS_READ) } else if (status.statusCode == CommonStatusCodes.SIGN_IN_REQUIRED) { - //resolveResult(status, NO_CREDENTIALS_EXIST) + //build a dialog for possible account hints var hintRequest: HintRequest = HintRequest.Builder() .setHintPickerConfig(CredentialPickerConfig.Builder() @@ -233,8 +236,31 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks } private fun resolveResult(status: Status, requestCode: Int) { - //TODO surround with a try/catch block - status.startResolutionForResult(activity, requestCode) + try { + status.startResolutionForResult(activity, requestCode) + } catch (e: IntentSender.SendIntentException) { + Log.e("STATUS", "Failed to send Credentials intent.", e) + } + } + + override fun saveSmartLockCredentials(loginCredential: Credential?) { + credentialsToBeSaved = loginCredential + if (credentialsToBeSaved == null) { + return + } + + Auth.CredentialsApi.save(googleApiClient, credentialsToBeSaved).setResultCallback( + object : ResolvingResultCallbacks(activity!!, SAVE_CREDENTIALS) { + override fun onSuccess(status: Status) { + Log.d("STATUS", "save:SUCCESS:$status") + credentialsToBeSaved = null + } + + override fun onUnresolvableFailure(status: Status) { + Log.w("STATUS", "save:FAILURE:$status") + credentialsToBeSaved = null + } + }) } private fun tintEditTextDrawableStart() { diff --git a/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupView.kt b/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupView.kt index 8730e84b1b..caf2d54ce7 100644 --- a/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupView.kt +++ b/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupView.kt @@ -29,5 +29,5 @@ interface SignupView : LoadingView, MessageView { /** * Save credentials via google smart lock */ - fun saveSmartLockCredentials(loginCredential: Credential) + fun saveSmartLockCredentials(loginCredential: Credential?) } \ No newline at end of file diff --git a/app/src/main/java/chat/rocket/android/authentication/signup/ui/SignupFragment.kt b/app/src/main/java/chat/rocket/android/authentication/signup/ui/SignupFragment.kt index 8e1727bcd9..09f2e19874 100644 --- a/app/src/main/java/chat/rocket/android/authentication/signup/ui/SignupFragment.kt +++ b/app/src/main/java/chat/rocket/android/authentication/signup/ui/SignupFragment.kt @@ -8,9 +8,13 @@ import android.os.Bundle import android.support.v4.app.Fragment import android.text.style.ClickableSpan import android.util.Log -import android.view.* +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.ViewTreeObserver import android.widget.Toast import chat.rocket.android.R +import chat.rocket.android.authentication.login.ui.googleApiClient import chat.rocket.android.authentication.signup.presentation.SignupPresenter import chat.rocket.android.authentication.signup.presentation.SignupView import chat.rocket.android.helper.KeyboardHelper @@ -18,7 +22,6 @@ import chat.rocket.android.helper.TextHelper import chat.rocket.android.util.extensions.* import com.google.android.gms.auth.api.Auth import com.google.android.gms.auth.api.credentials.Credential -import com.google.android.gms.common.api.GoogleApiClient import com.google.android.gms.common.api.ResolvingResultCallbacks import com.google.android.gms.common.api.Status import dagger.android.support.AndroidSupportInjection @@ -27,18 +30,10 @@ import javax.inject.Inject internal const val SAVE_CREDENTIALS = 1 -class SignupFragment : Fragment(), SignupView, GoogleApiClient.ConnectionCallbacks { - override fun onConnected(p0: Bundle?) { - saveCredentials() - } - - override fun onConnectionSuspended(p0: Int) { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } +class SignupFragment : Fragment(), SignupView { @Inject lateinit var presenter: SignupPresenter - private var googleApiClient: GoogleApiClient? = null private var credentialsToBeSaved: Credential? = null private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener { if (KeyboardHelper.isSoftKeyboardShown(relative_layout.rootView)) { @@ -79,12 +74,6 @@ class SignupFragment : Fragment(), SignupView, GoogleApiClient.ConnectionCallbac } } - override fun onDestroy() { - super.onDestroy() - googleApiClient!!.stopAutoManage(activity!!) - googleApiClient!!.disconnect() - } - override fun onDestroyView() { relative_layout.viewTreeObserver.removeOnGlobalLayoutListener(layoutListener) super.onDestroyView() @@ -122,20 +111,11 @@ class SignupFragment : Fragment(), SignupView, GoogleApiClient.ConnectionCallbac } } - override fun saveSmartLockCredentials(loginCredential: Credential) { + override fun saveSmartLockCredentials(loginCredential: Credential?) { credentialsToBeSaved = loginCredential - googleApiClient = GoogleApiClient.Builder(context!!) - .enableAutoManage(activity!!, { - Log.d("STATUS", "ERROR: connection to client failed") - }) - .addConnectionCallbacks(this) - .addApi(Auth.CREDENTIALS_API) - .build() - } - - override fun onStart() { - super.onStart() - saveCredentials() + if (googleApiClient!!.isConnected) { + saveCredentials() + } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { diff --git a/app/src/main/java/chat/rocket/android/authentication/ui/AuthenticationActivity.kt b/app/src/main/java/chat/rocket/android/authentication/ui/AuthenticationActivity.kt index 5c9d86f9de..f02c73bb58 100644 --- a/app/src/main/java/chat/rocket/android/authentication/ui/AuthenticationActivity.kt +++ b/app/src/main/java/chat/rocket/android/authentication/ui/AuthenticationActivity.kt @@ -43,6 +43,14 @@ class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector { } } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + val currentFragment = supportFragmentManager.findFragmentById(R.id.fragment_container) + if (currentFragment!=null){ + currentFragment.onActivityResult(requestCode, resultCode, data) + } + } + override fun onDestroy() { job.cancel() super.onDestroy() diff --git a/app/src/main/java/chat/rocket/android/main/presentation/MainPresenter.kt b/app/src/main/java/chat/rocket/android/main/presentation/MainPresenter.kt index 32f446c2ed..86a4c02ea6 100644 --- a/app/src/main/java/chat/rocket/android/main/presentation/MainPresenter.kt +++ b/app/src/main/java/chat/rocket/android/main/presentation/MainPresenter.kt @@ -105,11 +105,12 @@ class MainPresenter @Inject constructor( disconnect() removeAccountInteractor.remove(currentServer) tokenRepository.remove(currentServer) + view.disableAutoSignIn() navigator.toNewServer() } catch (ex: Exception) { Timber.d(ex, "Error cleaning up the session...") } - + view.disableAutoSignIn() navigator.toNewServer() } } diff --git a/app/src/main/java/chat/rocket/android/main/presentation/MainView.kt b/app/src/main/java/chat/rocket/android/main/presentation/MainView.kt index 7c1a69f9af..1235ec42b5 100644 --- a/app/src/main/java/chat/rocket/android/main/presentation/MainView.kt +++ b/app/src/main/java/chat/rocket/android/main/presentation/MainView.kt @@ -24,4 +24,9 @@ interface MainView : MessageView, VersionCheckView { fun setupNavHeader(viewModel: NavHeaderViewModel, accounts: List) fun closeServerSelection() + + /** + * callback to disable auto sign in for google smart lock when the user logs out + */ + fun disableAutoSignIn() } \ No newline at end of file diff --git a/app/src/main/java/chat/rocket/android/main/ui/MainActivity.kt b/app/src/main/java/chat/rocket/android/main/ui/MainActivity.kt index 0b0a156d32..696a98f120 100644 --- a/app/src/main/java/chat/rocket/android/main/ui/MainActivity.kt +++ b/app/src/main/java/chat/rocket/android/main/ui/MainActivity.kt @@ -6,6 +6,7 @@ import android.os.Bundle import android.support.v4.app.Fragment import android.support.v7.app.AppCompatActivity import android.support.v7.widget.LinearLayoutManager +import android.util.Log import android.view.Gravity import android.view.MenuItem import android.view.View @@ -22,6 +23,8 @@ import chat.rocket.android.util.extensions.fadeOut import chat.rocket.android.util.extensions.rotateBy import chat.rocket.android.util.extensions.showToast import chat.rocket.common.model.UserStatus +import com.google.android.gms.auth.api.Auth +import com.google.android.gms.common.api.GoogleApiClient import com.google.android.gms.gcm.GoogleCloudMessaging import com.google.android.gms.iid.InstanceID import dagger.android.AndroidInjection @@ -37,18 +40,29 @@ import kotlinx.coroutines.experimental.launch import timber.log.Timber import javax.inject.Inject -class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupportFragmentInjector { - @Inject lateinit var activityDispatchingAndroidInjector: DispatchingAndroidInjector - @Inject lateinit var fragmentDispatchingAndroidInjector: DispatchingAndroidInjector - @Inject lateinit var presenter: MainPresenter +class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupportFragmentInjector, GoogleApiClient.ConnectionCallbacks { + override fun onConnected(p0: Bundle?) { + } + + override fun onConnectionSuspended(p0: Int) { + } + + @Inject + lateinit var activityDispatchingAndroidInjector: DispatchingAndroidInjector + @Inject + lateinit var fragmentDispatchingAndroidInjector: DispatchingAndroidInjector + @Inject + lateinit var presenter: MainPresenter private var isFragmentAdded: Boolean = false private var expanded = false + private var googleApiClient: GoogleApiClient? = null private val headerLayout by lazy { view_navigation.getHeaderView(0) } override fun onCreate(savedInstanceState: Bundle?) { AndroidInjection.inject(this) super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) + buildGoogleApiClient() launch(CommonPool) { try { @@ -66,6 +80,31 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp setupNavigationView() } + private fun buildGoogleApiClient() { + googleApiClient = GoogleApiClient.Builder(this) + .enableAutoManage(this, { + Log.d("STATUS", "ERROR: connection to client failed") + }) + .addConnectionCallbacks(this) + .addApi(Auth.CREDENTIALS_API) + .build() + } + + override fun onStart() { + super.onStart() + if (googleApiClient!!.isConnected) { + Log.d("STATUS", "google api client connected successfully") + } + } + + override fun disableAutoSignIn() { + if (googleApiClient!!.isConnected) { + Auth.CredentialsApi.disableAutoSignIn(googleApiClient) + } else { + Log.e("STATUS", "Failed to disable auto sign in") + } + } + override fun onResume() { super.onResume() if (!isFragmentAdded) { @@ -84,7 +123,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp override fun showUserStatus(userStatus: UserStatus) { headerLayout.apply { image_user_status.setImageDrawable( - DrawableHelper.getUserStatusDrawable(userStatus, this.context) + DrawableHelper.getUserStatusDrawable(userStatus, this.context) ) } } @@ -95,7 +134,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp with(viewModel) { if (userStatus != null) { image_user_status.setImageDrawable( - DrawableHelper.getUserStatusDrawable(userStatus, context) + DrawableHelper.getUserStatusDrawable(userStatus, context) ) } if (userDisplayName != null) { From 1f5a213961db2e7947a4576f695fc1ee2a103443 Mon Sep 17 00:00:00 2001 From: Divyanshu Bhargava Date: Wed, 16 May 2018 23:59:46 +0530 Subject: [PATCH 04/45] bug fix --- .../chat/rocket/android/chatroom/adapter/MessageViewHolder.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/chat/rocket/android/chatroom/adapter/MessageViewHolder.kt b/app/src/main/java/chat/rocket/android/chatroom/adapter/MessageViewHolder.kt index 8819dd5167..e4215f6f7b 100644 --- a/app/src/main/java/chat/rocket/android/chatroom/adapter/MessageViewHolder.kt +++ b/app/src/main/java/chat/rocket/android/chatroom/adapter/MessageViewHolder.kt @@ -37,7 +37,7 @@ class MessageViewHolder( if (data.isTemporary) Color.GRAY else Color.BLACK ) data.message.let { - text_edit_indicator.isVisible = it.isSystemMessage() && it.editedBy != null + text_edit_indicator.isVisible = !it.isSystemMessage() && it.editedBy != null image_star_indicator.isVisible = it.starred?.isNotEmpty() ?: false } } From fd7f81d62af5ca2616bd8c2ea8d96b1f35e88b95 Mon Sep 17 00:00:00 2001 From: aniket Date: Thu, 17 May 2018 01:34:39 +0530 Subject: [PATCH 05/45] adds wear module --- app/src/main/AndroidManifest.xml | 19 +++------ settings.gradle | 2 +- wear/.gitignore | 1 + wear/build.gradle | 38 ++++++++++++++++++ wear/proguard-rules.pro | 21 ++++++++++ wear/src/main/AndroidManifest.xml | 37 +++++++++++++++++ .../chat/rocket/android/wear/MainActivity.kt | 15 +++++++ wear/src/main/res/layout/activity_main.xml | 27 +++++++++++++ wear/src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3418 bytes wear/src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2206 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 4842 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 7718 bytes wear/src/main/res/values-round/strings.xml | 3 ++ wear/src/main/res/values/dimens.xml | 15 +++++++ wear/src/main/res/values/strings.xml | 8 ++++ 15 files changed, 172 insertions(+), 14 deletions(-) create mode 100644 wear/.gitignore create mode 100644 wear/build.gradle create mode 100644 wear/proguard-rules.pro create mode 100644 wear/src/main/AndroidManifest.xml create mode 100644 wear/src/main/java/chat/rocket/android/wear/MainActivity.kt create mode 100644 wear/src/main/res/layout/activity_main.xml create mode 100644 wear/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 wear/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 wear/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 wear/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 wear/src/main/res/values-round/strings.xml create mode 100644 wear/src/main/res/values/dimens.xml create mode 100644 wear/src/main/res/values/strings.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 11306f2ce1..5a56e49193 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -20,10 +20,9 @@ android:fullBackupContent="@xml/backup_config" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" - android:roundIcon="@mipmap/ic_launcher_round" android:networkSecurityConfig="@xml/network_security_config" + android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true"> - - + + @@ -50,42 +50,36 @@ android:scheme="https" /> - - - - - - - + - + @@ -101,7 +95,6 @@ - - + \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 4457826772..700dcc220e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':app', ':player' \ No newline at end of file +include ':app', ':player', ':wear' \ No newline at end of file diff --git a/wear/.gitignore b/wear/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/wear/.gitignore @@ -0,0 +1 @@ +/build diff --git a/wear/build.gradle b/wear/build.gradle new file mode 100644 index 0000000000..f7754ff312 --- /dev/null +++ b/wear/build.gradle @@ -0,0 +1,38 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' +android { + compileSdkVersion 26 + + + defaultConfig { + applicationId "chat.rocket.android.wear" + minSdkVersion 23 + targetSdkVersion versions.targetSdk + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + postprocessing { + removeUnusedCode false + removeUnusedResources false + obfuscate false + optimizeCode false + proguardFile 'proguard-rules.pro' + } + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation libraries.kotlin + implementation 'com.google.android.support:wearable:2.3.0' + implementation 'com.google.android.gms:play-services-wearable:15.0.1' + implementation 'com.android.support:percent:26.1.0' + implementation 'com.android.support:support-v4:26.1.0' + implementation 'com.android.support:recyclerview-v7:26.1.0' + implementation 'com.android.support:wear:26.1.0' + compileOnly 'com.google.android.wearable:wearable:2.3.0' +} diff --git a/wear/proguard-rules.pro b/wear/proguard-rules.pro new file mode 100644 index 0000000000..f1b424510d --- /dev/null +++ b/wear/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/wear/src/main/AndroidManifest.xml b/wear/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..ee1483864f --- /dev/null +++ b/wear/src/main/AndroidManifest.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/wear/src/main/java/chat/rocket/android/wear/MainActivity.kt b/wear/src/main/java/chat/rocket/android/wear/MainActivity.kt new file mode 100644 index 0000000000..42985241ce --- /dev/null +++ b/wear/src/main/java/chat/rocket/android/wear/MainActivity.kt @@ -0,0 +1,15 @@ +package chat.rocket.android.wear + +import android.os.Bundle +import android.support.wearable.activity.WearableActivity + +class MainActivity : WearableActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + + // Enables Always-on + setAmbientEnabled() + } +} diff --git a/wear/src/main/res/layout/activity_main.xml b/wear/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000000..3bc9c6bf8c --- /dev/null +++ b/wear/src/main/res/layout/activity_main.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + diff --git a/wear/src/main/res/mipmap-hdpi/ic_launcher.png b/wear/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..cde69bcccec65160d92116f20ffce4fce0b5245c GIT binary patch literal 3418 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 literal 0 HcmV?d00001 diff --git a/wear/src/main/res/mipmap-xhdpi/ic_launcher.png b/wear/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..bfa42f0e7b91d006d22352c9ff2f134e504e3c1d GIT binary patch literal 4842 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 literal 0 HcmV?d00001 diff --git a/wear/src/main/res/mipmap-xxhdpi/ic_launcher.png b/wear/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..324e72cdd7480cb983fa1bcc7ce686e51ef87fe7 GIT binary patch literal 7718 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 literal 0 HcmV?d00001 diff --git a/wear/src/main/res/values-round/strings.xml b/wear/src/main/res/values-round/strings.xml new file mode 100644 index 0000000000..452e3358d9 --- /dev/null +++ b/wear/src/main/res/values-round/strings.xml @@ -0,0 +1,3 @@ + + Hello Round World! + diff --git a/wear/src/main/res/values/dimens.xml b/wear/src/main/res/values/dimens.xml new file mode 100644 index 0000000000..e865b412be --- /dev/null +++ b/wear/src/main/res/values/dimens.xml @@ -0,0 +1,15 @@ + + + + 0dp + + + 5dp + diff --git a/wear/src/main/res/values/strings.xml b/wear/src/main/res/values/strings.xml new file mode 100644 index 0000000000..e0468e0514 --- /dev/null +++ b/wear/src/main/res/values/strings.xml @@ -0,0 +1,8 @@ + + Wear + + Hello Square World! + From d1d45a7d58d5950b36887ca0f448fc22650030bc Mon Sep 17 00:00:00 2001 From: Divyanshu Bhargava Date: Fri, 18 May 2018 10:56:41 +0530 Subject: [PATCH 06/45] bug fix --- .../android/MemoryMessagesRepositoryTest.kt | 150 ------------------ 1 file changed, 150 deletions(-) delete mode 100644 app/src/test/java/chat/rocket/android/MemoryMessagesRepositoryTest.kt diff --git a/app/src/test/java/chat/rocket/android/MemoryMessagesRepositoryTest.kt b/app/src/test/java/chat/rocket/android/MemoryMessagesRepositoryTest.kt deleted file mode 100644 index 0939d18873..0000000000 --- a/app/src/test/java/chat/rocket/android/MemoryMessagesRepositoryTest.kt +++ /dev/null @@ -1,150 +0,0 @@ -package chat.rocket.android - -import chat.rocket.android.server.infraestructure.MemoryMessagesRepository -import chat.rocket.core.model.Message -import chat.rocket.core.model.MessageType -import kotlinx.coroutines.experimental.runBlocking -import org.hamcrest.CoreMatchers.notNullValue -import org.hamcrest.MatcherAssert.assertThat -import org.junit.Before -import org.junit.Test -import org.hamcrest.CoreMatchers.`is` as isEqualTo - -class MemoryMessagesRepositoryTest { - - val repository = MemoryMessagesRepository() - - val msg = Message( - id = "messageId", - roomId = "GENERAL", - message = "Beam me up, Scotty.", - timestamp = 1511443964815, - attachments = null, - sender = null, - avatar = null, - channels = null, - editedAt = null, - editedBy = null, - groupable = true, - mentions = null, - parseUrls = false, - senderAlias = null, - type = MessageType.MessageRemoved(), - updatedAt = 1511443964815, - urls = null, - pinned = false, - reactions = null - ) - - val msg2 = Message( - id = "messageId2", - roomId = "sandbox", - message = "Highly Illogical", - timestamp = 1511443964818, - attachments = null, - sender = null, - avatar = null, - channels = null, - editedAt = null, - editedBy = null, - groupable = true, - mentions = null, - parseUrls = false, - senderAlias = null, - type = MessageType.MessageRemoved(), - updatedAt = 1511443964818, - urls = null, - pinned = false, - reactions = null - ) - - @Before - fun setup() { - runBlocking { - repository.clear() - } - } - - @Test - fun `save() should save a single message`() { - runBlocking { - assertThat(repository.getAll().size, isEqualTo(0)) - repository.save(msg) - val allMessages = repository.getAll() - assertThat(allMessages.size, isEqualTo(1)) - allMessages[0].apply { - assertThat(id, isEqualTo("messageId")) - assertThat(message, isEqualTo("Beam me up, Scotty.")) - assertThat(roomId, isEqualTo("GENERAL")) - } - } - } - - @Test - fun `saveAll() should all saved messages`() { - runBlocking { - assertThat(repository.getAll().size, isEqualTo(0)) - repository.saveAll(listOf(msg, msg2)) - val allMessages = repository.getAll() - assertThat(allMessages.size, isEqualTo(2)) - allMessages[0].apply { - assertThat(id, isEqualTo("messageId")) - assertThat(message, isEqualTo("Beam me up, Scotty.")) - assertThat(roomId, isEqualTo("GENERAL")) - } - - allMessages[1].apply { - assertThat(id, isEqualTo("messageId2")) - assertThat(message, isEqualTo("Highly Illogical")) - assertThat(roomId, isEqualTo("sandbox")) - } - } - } - - @Test - fun `getById() should return a single message`() { - runBlocking { - repository.saveAll(listOf(msg, msg2)) - var singleMsg = repository.getById("messageId") - assertThat(singleMsg, notNullValue()) - singleMsg!!.apply { - assertThat(id, isEqualTo("messageId")) - assertThat(message, isEqualTo("Beam me up, Scotty.")) - assertThat(roomId, isEqualTo("GENERAL")) - } - - singleMsg = repository.getById("messageId2") - assertThat(singleMsg, notNullValue()) - singleMsg!!.apply { - assertThat(id, isEqualTo("messageId2")) - assertThat(message, isEqualTo("Highly Illogical")) - assertThat(roomId, isEqualTo("sandbox")) - } - } - } - - @Test - fun `getByRoomId() should return all messages for room id or an empty list`() { - runBlocking { - repository.saveAll(listOf(msg, msg2)) - var roomMessages = repository.getByRoomId("faAad32fkasods2") - assertThat(roomMessages.isEmpty(), isEqualTo(true)) - - roomMessages = repository.getByRoomId("sandbox") - assertThat(roomMessages.size, isEqualTo(1)) - roomMessages[0].apply { - assertThat(id, isEqualTo("messageId2")) - assertThat(message, isEqualTo("Highly Illogical")) - assertThat(roomId, isEqualTo("sandbox")) - } - - roomMessages = repository.getByRoomId("GENERAL") - assertThat(roomMessages.size, isEqualTo(1)) - roomMessages[0].apply { - assertThat(id, isEqualTo("messageId")) - assertThat(message, isEqualTo("Beam me up, Scotty.")) - assertThat(roomId, isEqualTo("GENERAL")) - } - } - } -} \ No newline at end of file From 8a165290eb6300f92835032e27f4b01d14465f48 Mon Sep 17 00:00:00 2001 From: Poussinou Date: Sun, 20 May 2018 18:15:15 +0200 Subject: [PATCH 07/45] Update gradle-wrapper.properties --- gradle/wrapper/gradle-wrapper.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index fdfb7e318b..21ba6136d0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -4,3 +4,4 @@ distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionSha256Sum=9af7345c199f1731c187c96d3fe3d31f5405192a42046bafa71d846c3d9adacb From f4c434e2c0283ca06b968a72d8387b54e53a9897 Mon Sep 17 00:00:00 2001 From: aniket Date: Tue, 22 May 2018 02:37:12 +0530 Subject: [PATCH 08/45] minor fixes --- .../login/presentation/LoginPresenter.kt | 11 +++++--- .../authentication/login/ui/LoginFragment.kt | 28 +++++-------------- .../webview/oauth/ui/OauthWebViewActivity.kt | 1 - 3 files changed, 14 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginPresenter.kt b/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginPresenter.kt index 1205da28f4..dc564c33c7 100644 --- a/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginPresenter.kt +++ b/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginPresenter.kt @@ -55,6 +55,7 @@ class LoginPresenter @Inject constructor( private lateinit var credentialSecret: String private lateinit var deepLinkUserId: String private lateinit var deepLinkToken: String + private var loginCredentials: Credential? = null fun setupView() { setupConnectionInfo(currentServer) @@ -304,10 +305,12 @@ class LoginPresenter @Inject constructor( saveAccount(username) saveToken(token) registerPushToken() - var loginCredentials: Credential = Credential.Builder(usernameOrEmail) - .setPassword(password) - .build() - view.saveSmartLockCredentials(loginCredentials) + if (loginType == TYPE_LOGIN_USER_EMAIL) { + loginCredentials = Credential.Builder(usernameOrEmail) + .setPassword(password) + .build() + view.saveSmartLockCredentials(loginCredentials) + } navigator.toChatList() } else if (loginType == TYPE_LOGIN_OAUTH) { navigator.toRegisterUsername(token.userId, token.authToken) diff --git a/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt b/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt index bb5c3156e2..fe645356a7 100644 --- a/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt +++ b/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt @@ -57,6 +57,7 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener { areLoginOptionsNeeded() } + private var isOauthSuccessful = false private var isGlobalLayoutListenerSetUp = false private var deepLinkInfo: LoginDeepLinkInfo? = null private var credentialsToBeSaved: Credential? = null @@ -122,6 +123,7 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks presenter.authenticateWithCas(getStringExtra(INTENT_CAS_TOKEN)) } } else if (requestCode == REQUEST_CODE_FOR_OAUTH) { + isOauthSuccessful = true data?.apply { presenter.authenticateWithOauth( getStringExtra(INTENT_OAUTH_CREDENTIAL_TOKEN), @@ -147,20 +149,12 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks } else if (requestCode == MULTIPLE_CREDENTIALS_READ) { Log.d("STATUS", "failed ") } - //cancel button pressed by the user in case of reading from smart lock - else if (resultCode == Activity.RESULT_CANCELED) { - //add shared preference so that the dialog is not shown always - } - - //create new account to use it as login account (deal with this case carefully, many edge cases) - else if (resultCode == CredentialsApi.ACTIVITY_RESULT_ADD_ACCOUNT) { + else if (resultCode == Activity.RESULT_CANCELED && requestCode == REQUEST_CODE_FOR_OAUTH) { + Log.d("returned", "from oauth") } - //no hints for user id's exist else if (resultCode == CredentialsApi.ACTIVITY_RESULT_NO_HINTS_AVAILABLE) { - } else { - Log.d("Status", "nothing happening") } } @@ -182,14 +176,14 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks override fun onStart() { super.onStart() - requestCredentials() + if (!isOauthSuccessful) { + requestCredentials() + } } private fun requestCredentials() { var request: CredentialRequest = CredentialRequest.Builder() .setPasswordLoginSupported(true) - //add account types for custom login methods - /*.setAccountTypes(IdentityProviders.GOOGLE,IdentityProviders.FACEBOOK)*/ .build() Auth.CredentialsApi.request(googleApiClient, request).setResultCallback { credentialRequestResult -> @@ -224,14 +218,6 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks private fun handleCredential(loginCredentials: Credential) { if (loginCredentials.accountType == null) { presenter.authenticateWithUserAndPassword(loginCredentials.id, loginCredentials.password!!) - } else if (loginCredentials.accountType == IdentityProviders.GOOGLE) { - //TODO add SL code for google as custom login method - } else if (loginCredentials.accountType == IdentityProviders.FACEBOOK) { - //TODO add SL code for facebook as custom login method - } else if (loginCredentials.accountType == IdentityProviders.TWITTER) { - //TODO add SL code for twitter as custom login method - } else if (loginCredentials.accountType == IdentityProviders.LINKEDIN) { - //TODO add SL code for linkedin as custom login method } } diff --git a/app/src/main/java/chat/rocket/android/webview/oauth/ui/OauthWebViewActivity.kt b/app/src/main/java/chat/rocket/android/webview/oauth/ui/OauthWebViewActivity.kt index 1533f82476..b55e2385ba 100644 --- a/app/src/main/java/chat/rocket/android/webview/oauth/ui/OauthWebViewActivity.kt +++ b/app/src/main/java/chat/rocket/android/webview/oauth/ui/OauthWebViewActivity.kt @@ -40,7 +40,6 @@ class OauthWebViewActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_web_view) - webPageUrl = intent.getStringExtra(INTENT_WEB_PAGE_URL) requireNotNull(webPageUrl) { "no web_page_url provided in Intent extras" } From 9ba67a566ab434a7db7fa6a684fe6f06d6ca6254 Mon Sep 17 00:00:00 2001 From: aniket Date: Tue, 22 May 2018 03:41:17 +0530 Subject: [PATCH 09/45] set of changes #1 --- .../login/presentation/LoginPresenter.kt | 40 ++- .../authentication/login/ui/LoginFragment.kt | 246 ++++++++++-------- .../signup/presentation/SignupPresenter.kt | 26 +- .../signup/presentation/SignupView.kt | 2 +- .../signup/ui/SignupFragment.kt | 63 +++-- .../ui/AuthenticationActivity.kt | 8 +- .../rocket/android/main/ui/MainActivity.kt | 85 +++--- 7 files changed, 274 insertions(+), 196 deletions(-) diff --git a/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginPresenter.kt b/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginPresenter.kt index dc564c33c7..1a02c13934 100644 --- a/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginPresenter.kt +++ b/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginPresenter.kt @@ -151,13 +151,20 @@ class LoginPresenter @Inject constructor( client.settingsOauth().services } if (services.isNotEmpty()) { - val state = "{\"loginStyle\":\"popup\",\"credentialToken\":\"${generateRandomString(40)}\",\"isCordova\":true}".encodeToBase64() + val state = + "{\"loginStyle\":\"popup\",\"credentialToken\":\"${generateRandomString(40)}\",\"isCordova\":true}".encodeToBase64() var totalSocialAccountsEnabled = 0 if (settings.isFacebookAuthenticationEnabled()) { val clientId = getOauthClientId(services, SERVICE_NAME_FACEBOOK) if (clientId != null) { - view.setupFacebookButtonListener(OauthHelper.getFacebookOauthUrl(clientId, currentServer, state), state) + view.setupFacebookButtonListener( + OauthHelper.getFacebookOauthUrl( + clientId, + currentServer, + state + ), state + ) view.enableLoginByFacebook() totalSocialAccountsEnabled++ } @@ -165,7 +172,12 @@ class LoginPresenter @Inject constructor( if (settings.isGithubAuthenticationEnabled()) { val clientId = getOauthClientId(services, SERVICE_NAME_GITHUB) if (clientId != null) { - view.setupGithubButtonListener(OauthHelper.getGithubOauthUrl(clientId, state), state) + view.setupGithubButtonListener( + OauthHelper.getGithubOauthUrl( + clientId, + state + ), state + ) view.enableLoginByGithub() totalSocialAccountsEnabled++ } @@ -173,7 +185,13 @@ class LoginPresenter @Inject constructor( if (settings.isGoogleAuthenticationEnabled()) { val clientId = getOauthClientId(services, SERVICE_NAME_GOOGLE) if (clientId != null) { - view.setupGoogleButtonListener(OauthHelper.getGoogleOauthUrl(clientId, currentServer, state), state) + view.setupGoogleButtonListener( + OauthHelper.getGoogleOauthUrl( + clientId, + currentServer, + state + ), state + ) view.enableLoginByGoogle() totalSocialAccountsEnabled++ } @@ -181,7 +199,13 @@ class LoginPresenter @Inject constructor( if (settings.isLinkedinAuthenticationEnabled()) { val clientId = getOauthClientId(services, SERVICE_NAME_LINKEDIN) if (clientId != null) { - view.setupLinkedinButtonListener(OauthHelper.getLinkedinOauthUrl(clientId, currentServer, state), state) + view.setupLinkedinButtonListener( + OauthHelper.getLinkedinOauthUrl( + clientId, + currentServer, + state + ), state + ) view.enableLoginByLinkedin() totalSocialAccountsEnabled++ } @@ -307,8 +331,8 @@ class LoginPresenter @Inject constructor( registerPushToken() if (loginType == TYPE_LOGIN_USER_EMAIL) { loginCredentials = Credential.Builder(usernameOrEmail) - .setPassword(password) - .build() + .setPassword(password) + .build() view.saveSmartLockCredentials(loginCredentials) } navigator.toChatList() @@ -341,7 +365,7 @@ class LoginPresenter @Inject constructor( }.toString() } - private fun getCustomOauthServices(listMap: List>): List> { + private fun getCustomOauthServices(listMap: List>): List> { return listMap.filter { map -> map["custom"] == true } } diff --git a/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt b/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt index fe645356a7..adf788e185 100644 --- a/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt +++ b/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt @@ -10,7 +10,6 @@ import android.os.Build import android.os.Bundle import android.support.v4.app.Fragment import android.text.style.ClickableSpan -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -39,6 +38,7 @@ import com.google.android.gms.common.api.ResolvingResultCallbacks import com.google.android.gms.common.api.Status import dagger.android.support.AndroidSupportInjection import kotlinx.android.synthetic.main.fragment_authentication_log_in.* +import timber.log.Timber import javax.inject.Inject @@ -72,12 +72,11 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks } } - override fun onConnected(p0: Bundle?) { + override fun onConnected(bundle: Bundle?) { saveSmartLockCredentials(credentialsToBeSaved) } - override fun onConnectionSuspended(p0: Int) { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + override fun onConnectionSuspended(errorCode: Int) { } override fun onCreate(savedInstanceState: Bundle?) { @@ -88,11 +87,11 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks } override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? ): View? = - container?.inflate(R.layout.fragment_authentication_log_in) + container?.inflate(R.layout.fragment_authentication_log_in) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -118,40 +117,42 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { if (resultCode == Activity.RESULT_OK) { - if (requestCode == REQUEST_CODE_FOR_CAS) { - data?.apply { + when (requestCode) { + REQUEST_CODE_FOR_CAS -> data?.apply { presenter.authenticateWithCas(getStringExtra(INTENT_CAS_TOKEN)) } - } else if (requestCode == REQUEST_CODE_FOR_OAUTH) { - isOauthSuccessful = true - data?.apply { - presenter.authenticateWithOauth( + REQUEST_CODE_FOR_OAUTH -> { + isOauthSuccessful = true + data?.apply { + presenter.authenticateWithOauth( getStringExtra(INTENT_OAUTH_CREDENTIAL_TOKEN), getStringExtra(INTENT_OAUTH_CREDENTIAL_SECRET) - ) + ) + } + } + MULTIPLE_CREDENTIALS_READ -> { + val loginCredentials: Credential = data!!.getParcelableExtra(Credential.EXTRA_KEY) + handleCredential(loginCredentials) + } + NO_CREDENTIALS_EXIST -> { + //use the hints to autofill sign in forms to reduce the info to be filled + val loginCredentials: Credential = data!!.getParcelableExtra(Credential.EXTRA_KEY) + val email = loginCredentials.id + val password = loginCredentials.password + + text_username_or_email.setText(email) + text_password.setText(password) } - } else if (requestCode == MULTIPLE_CREDENTIALS_READ) { - var loginCredentials: Credential = data!!.getParcelableExtra(Credential.EXTRA_KEY) - handleCredential(loginCredentials) - } else if (requestCode == NO_CREDENTIALS_EXIST) { - //use the hints to autofill sign in forms to reduce the info to be filled - var loginCredentials: Credential = data!!.getParcelableExtra(Credential.EXTRA_KEY) - var email = loginCredentials.id - var password = loginCredentials.password - - text_username_or_email.setText(email) - text_password.setText(password) - } else if (requestCode == SAVE_CREDENTIALS) { - Toast.makeText(context, "Credentials saved successfully", Toast.LENGTH_SHORT).show() + SAVE_CREDENTIALS -> Toast.makeText(context, "Credentials saved successfully", Toast.LENGTH_SHORT).show() } } else if (requestCode == SAVE_CREDENTIALS) { - Log.e("STATUS", "ERROR: Cancelled by user") + Timber.e("ERROR: Cancelled by user") } else if (requestCode == MULTIPLE_CREDENTIALS_READ) { - Log.d("STATUS", "failed ") + Timber.e("ERROR: Failed reading credentials") } //cancel button pressed by the user in case of reading from smart lock else if (resultCode == Activity.RESULT_CANCELED && requestCode == REQUEST_CODE_FOR_OAUTH) { - Log.d("returned", "from oauth") + Timber.d("Returned from oauth") } //no hints for user id's exist else if (resultCode == CredentialsApi.ACTIVITY_RESULT_NO_HINTS_AVAILABLE) { @@ -166,12 +167,12 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks private fun buildGoogleApiClient() { googleApiClient = GoogleApiClient.Builder(context!!) - .enableAutoManage(activity!!, { - Log.d("STATUS", "ERROR: connection to client failed") - }) - .addConnectionCallbacks(this) - .addApi(Auth.CREDENTIALS_API) - .build() + .enableAutoManage(activity!!, { + Timber.e("ERROR: Connection to client failed") + }) + .addConnectionCallbacks(this) + .addApi(Auth.CREDENTIALS_API) + .build() } override fun onStart() { @@ -182,42 +183,57 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks } private fun requestCredentials() { - var request: CredentialRequest = CredentialRequest.Builder() - .setPasswordLoginSupported(true) - .build() - - Auth.CredentialsApi.request(googleApiClient, request).setResultCallback { credentialRequestResult -> - val status = credentialRequestResult.status - if (status.isSuccess) { - // Auto sign-in success - handleCredential(credentialRequestResult.credential) - } else if (status.statusCode == CommonStatusCodes.RESOLUTION_REQUIRED) { - resolveResult(status, MULTIPLE_CREDENTIALS_READ) - } else if (status.statusCode == CommonStatusCodes.SIGN_IN_REQUIRED) { - - //build a dialog for possible account hints - var hintRequest: HintRequest = HintRequest.Builder() - .setHintPickerConfig(CredentialPickerConfig.Builder() + val request: CredentialRequest = CredentialRequest.Builder() + .setPasswordLoginSupported(true) + .build() + + Auth.CredentialsApi.request(googleApiClient, request) + .setResultCallback { credentialRequestResult -> + val status = credentialRequestResult.status + if (status.isSuccess) { + // Auto sign-in success + handleCredential(credentialRequestResult.credential) + } else if (status.statusCode == CommonStatusCodes.RESOLUTION_REQUIRED) { + resolveResult(status, MULTIPLE_CREDENTIALS_READ) + } else if (status.statusCode == CommonStatusCodes.SIGN_IN_REQUIRED) { + + //build a dialog for possible account hints + val hintRequest: HintRequest = HintRequest.Builder() + .setHintPickerConfig( + CredentialPickerConfig.Builder() .setShowCancelButton(true) - .build()) + .build() + ) .setEmailAddressIdentifierSupported(true) .setAccountTypes(IdentityProviders.GOOGLE) .build() - var intent: PendingIntent = Auth.CredentialsApi.getHintPickerIntent(googleApiClient, hintRequest) - try { - startIntentSenderForResult(intent.intentSender, NO_CREDENTIALS_EXIST, null, 0, 0, 0, null) - } catch (e: IntentSender.SendIntentException) { - Log.e("STATUS", "ERROR: Could not start hint picker Intent", e); + val intent: PendingIntent = + Auth.CredentialsApi.getHintPickerIntent(googleApiClient, hintRequest) + try { + startIntentSenderForResult( + intent.intentSender, + NO_CREDENTIALS_EXIST, + null, + 0, + 0, + 0, + null + ) + } catch (e: IntentSender.SendIntentException) { + Timber.e("STATUS", "ERROR: Could not start hint picker Intent", e); + } + } else { + Timber.d("STATUS", "ERROR: nothing happening") } - } else { - Log.d("STATUS", "ERROR: nothing happening") } - } } private fun handleCredential(loginCredentials: Credential) { if (loginCredentials.accountType == null) { - presenter.authenticateWithUserAndPassword(loginCredentials.id, loginCredentials.password!!) + presenter.authenticateWithUserAndPassword( + loginCredentials.id, + loginCredentials.password!! + ) } } @@ -225,7 +241,7 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks try { status.startResolutionForResult(activity, requestCode) } catch (e: IntentSender.SendIntentException) { - Log.e("STATUS", "Failed to send Credentials intent.", e) + Timber.e("STATUS", "Failed to send Credentials intent.", e) } } @@ -236,31 +252,31 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks } Auth.CredentialsApi.save(googleApiClient, credentialsToBeSaved).setResultCallback( - object : ResolvingResultCallbacks(activity!!, SAVE_CREDENTIALS) { - override fun onSuccess(status: Status) { - Log.d("STATUS", "save:SUCCESS:$status") - credentialsToBeSaved = null - } + object : ResolvingResultCallbacks(activity!!, SAVE_CREDENTIALS) { + override fun onSuccess(status: Status) { + Timber.d("STATUS", "save:SUCCESS:$status") + credentialsToBeSaved = null + } - override fun onUnresolvableFailure(status: Status) { - Log.w("STATUS", "save:FAILURE:$status") - credentialsToBeSaved = null - } - }) + override fun onUnresolvableFailure(status: Status) { + Timber.w("STATUS", "save:FAILURE:$status") + credentialsToBeSaved = null + } + }) } private fun tintEditTextDrawableStart() { ui { val personDrawable = - DrawableHelper.getDrawableFromId(R.drawable.ic_assignment_ind_black_24dp, it) + DrawableHelper.getDrawableFromId(R.drawable.ic_assignment_ind_black_24dp, it) val lockDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_lock_black_24dp, it) val drawables = arrayOf(personDrawable, lockDrawable) DrawableHelper.wrapDrawables(drawables) DrawableHelper.tintDrawables(drawables, it, R.color.colorDrawableTintGrey) DrawableHelper.compoundDrawables( - arrayOf(text_username_or_email, text_password), - drawables + arrayOf(text_username_or_email, text_password), + drawables ) } } @@ -311,8 +327,8 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks ui { button_log_in.setOnClickListener { presenter.authenticateWithUserAndPassword( - text_username_or_email.textContent, - text_password.textContent + text_username_or_email.textContent, + text_password.textContent ) } } @@ -350,8 +366,8 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks ui { activity -> button_cas.setOnClickListener { startActivityForResult( - activity.casWebViewIntent(casUrl, casToken), - REQUEST_CODE_FOR_CAS + activity.casWebViewIntent(casUrl, casToken), + REQUEST_CODE_FOR_CAS ) activity.overridePendingTransition(R.anim.slide_up, R.anim.hold) } @@ -443,8 +459,8 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks ui { activity -> button_facebook.setOnClickListener { startActivityForResult( - activity.oauthWebViewIntent(facebookOauthUrl, state), - REQUEST_CODE_FOR_OAUTH + activity.oauthWebViewIntent(facebookOauthUrl, state), + REQUEST_CODE_FOR_OAUTH ) activity.overridePendingTransition(R.anim.slide_up, R.anim.hold) } @@ -461,8 +477,8 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks ui { activity -> button_github.setOnClickListener { startActivityForResult( - activity.oauthWebViewIntent(githubUrl, state), - REQUEST_CODE_FOR_OAUTH + activity.oauthWebViewIntent(githubUrl, state), + REQUEST_CODE_FOR_OAUTH ) activity.overridePendingTransition(R.anim.slide_up, R.anim.hold) } @@ -481,8 +497,8 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks ui { activity -> button_google.setOnClickListener { startActivityForResult( - activity.oauthWebViewIntent(googleUrl, state), - REQUEST_CODE_FOR_OAUTH + activity.oauthWebViewIntent(googleUrl, state), + REQUEST_CODE_FOR_OAUTH ) activity.overridePendingTransition(R.anim.slide_up, R.anim.hold) } @@ -499,8 +515,8 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks ui { activity -> button_linkedin.setOnClickListener { startActivityForResult( - activity.oauthWebViewIntent(linkedinUrl, state), - REQUEST_CODE_FOR_OAUTH + activity.oauthWebViewIntent(linkedinUrl, state), + REQUEST_CODE_FOR_OAUTH ) activity.overridePendingTransition(R.anim.slide_up, R.anim.hold) } @@ -529,8 +545,8 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks ui { activity -> button_gitlab.setOnClickListener { startActivityForResult( - activity.oauthWebViewIntent(gitlabUrl, state), - REQUEST_CODE_FOR_OAUTH + activity.oauthWebViewIntent(gitlabUrl, state), + REQUEST_CODE_FOR_OAUTH ) activity.overridePendingTransition(R.anim.slide_up, R.anim.hold) } @@ -538,11 +554,11 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks } override fun addCustomOauthServiceButton( - customOauthUrl: String, - state: String, - serviceName: String, - serviceNameColor: Int, - buttonColor: Int + customOauthUrl: String, + state: String, + serviceName: String, + serviceNameColor: Int, + buttonColor: Int ) { ui { activity -> val button = getCustomOauthButton(serviceName, serviceNameColor, buttonColor) @@ -550,8 +566,8 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks button.setOnClickListener { startActivityForResult( - activity.oauthWebViewIntent(customOauthUrl, state), - REQUEST_CODE_FOR_OAUTH + activity.oauthWebViewIntent(customOauthUrl, state), + REQUEST_CODE_FOR_OAUTH ) activity.overridePendingTransition(R.anim.slide_up, R.anim.hold) } @@ -599,9 +615,9 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks social_accounts_container.postDelayed(300) { ui { (0..social_accounts_container.childCount) - .mapNotNull { social_accounts_container.getChildAt(it) as? ImageButton } - .filter { it.isClickable } - .forEach { it.isVisible = true } + .mapNotNull { social_accounts_container.getChildAt(it) as? ImageButton } + .filter { it.isClickable } + .forEach { it.isVisible = true } } } } @@ -635,10 +651,10 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks private fun showThreeSocialAccountsMethods() { (0..social_accounts_container.childCount) - .mapNotNull { social_accounts_container.getChildAt(it) as? ImageButton } - .filter { it.isClickable } - .take(3) - .forEach { it.isVisible = true } + .mapNotNull { social_accounts_container.getChildAt(it) as? ImageButton } + .filter { it.isClickable } + .take(3) + .forEach { it.isVisible = true } } private fun showOauthView() { @@ -663,28 +679,28 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks private fun enabledOauthAccountsImageButtons(): Int { return (0..social_accounts_container.childCount) - .mapNotNull { social_accounts_container.getChildAt(it) as? ImageButton } - .filter { it.isClickable } - .size + .mapNotNull { social_accounts_container.getChildAt(it) as? ImageButton } + .filter { it.isClickable } + .size } private fun enabledServicesAccountsButtons(): Int { return (0..social_accounts_container.childCount) - .mapNotNull { social_accounts_container.getChildAt(it) as? Button } - .size + .mapNotNull { social_accounts_container.getChildAt(it) as? Button } + .size } /** * Gets a stylized custom OAuth button. */ private fun getCustomOauthButton( - buttonText: String, - buttonTextColor: Int, - buttonBgColor: Int + buttonText: String, + buttonTextColor: Int, + buttonBgColor: Int ): Button { val params: LinearLayout.LayoutParams = LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, - LinearLayout.LayoutParams.WRAP_CONTENT + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT ) val margin = resources.getDimensionPixelSize(R.dimen.screen_edge_left_and_right_margins) diff --git a/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupPresenter.kt b/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupPresenter.kt index 4890999dc2..1299dcb56f 100644 --- a/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupPresenter.kt +++ b/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupPresenter.kt @@ -23,15 +23,17 @@ import chat.rocket.core.model.Myself import com.google.android.gms.auth.api.credentials.Credential import javax.inject.Inject -class SignupPresenter @Inject constructor(private val view: SignupView, - private val strategy: CancelStrategy, - private val navigator: AuthenticationNavigator, - private val localRepository: LocalRepository, - private val serverInteractor: GetCurrentServerInteractor, - private val factory: RocketChatClientFactory, - private val saveAccountInteractor: SaveAccountInteractor, - private val getAccountsInteractor: GetAccountsInteractor, - settingsInteractor: GetSettingsInteractor) { +class SignupPresenter @Inject constructor( + private val view: SignupView, + private val strategy: CancelStrategy, + private val navigator: AuthenticationNavigator, + private val localRepository: LocalRepository, + private val serverInteractor: GetCurrentServerInteractor, + private val factory: RocketChatClientFactory, + private val saveAccountInteractor: SaveAccountInteractor, + private val getAccountsInteractor: GetAccountsInteractor, + settingsInteractor: GetSettingsInteractor +) { private val currentServer = serverInteractor.get()!! private val client: RocketChatClient = factory.create(currentServer) private var settings: PublicSettings = settingsInteractor.get(serverInteractor.get()!!) @@ -67,9 +69,9 @@ class SignupPresenter @Inject constructor(private val view: SignupView, localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, me.username) saveAccount(me) registerPushToken() - var loginCredentials: Credential = Credential.Builder(email) - .setPassword(password) - .build() + val loginCredentials = Credential.Builder(email) + .setPassword(password) + .build() view.saveSmartLockCredentials(loginCredentials) navigator.toChatList() } catch (exception: RocketChatException) { diff --git a/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupView.kt b/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupView.kt index caf2d54ce7..8730e84b1b 100644 --- a/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupView.kt +++ b/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupView.kt @@ -29,5 +29,5 @@ interface SignupView : LoadingView, MessageView { /** * Save credentials via google smart lock */ - fun saveSmartLockCredentials(loginCredential: Credential?) + fun saveSmartLockCredentials(loginCredential: Credential) } \ No newline at end of file diff --git a/app/src/main/java/chat/rocket/android/authentication/signup/ui/SignupFragment.kt b/app/src/main/java/chat/rocket/android/authentication/signup/ui/SignupFragment.kt index 09f2e19874..3bd67ddcd2 100644 --- a/app/src/main/java/chat/rocket/android/authentication/signup/ui/SignupFragment.kt +++ b/app/src/main/java/chat/rocket/android/authentication/signup/ui/SignupFragment.kt @@ -7,7 +7,6 @@ import android.os.Build import android.os.Bundle import android.support.v4.app.Fragment import android.text.style.ClickableSpan -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -26,6 +25,7 @@ import com.google.android.gms.common.api.ResolvingResultCallbacks import com.google.android.gms.common.api.Status import dagger.android.support.AndroidSupportInjection import kotlinx.android.synthetic.main.fragment_authentication_sign_up.* +import timber.log.Timber import javax.inject.Inject internal const val SAVE_CREDENTIALS = 1 @@ -34,7 +34,7 @@ class SignupFragment : Fragment(), SignupView { @Inject lateinit var presenter: SignupPresenter - private var credentialsToBeSaved: Credential? = null + private lateinit var credentialsToBeSaved: Credential private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener { if (KeyboardHelper.isSoftKeyboardShown(relative_layout.rootView)) { bottom_container.setVisible(false) @@ -56,7 +56,11 @@ class SignupFragment : Fragment(), SignupView { AndroidSupportInjection.inject(this) } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = inflater.inflate(R.layout.fragment_authentication_sign_up, container, false) + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? = inflater.inflate(R.layout.fragment_authentication_sign_up, container, false) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -70,7 +74,12 @@ class SignupFragment : Fragment(), SignupView { setUpNewUserAgreementListener() button_sign_up.setOnClickListener { - presenter.signup(text_username.textContent, text_username.textContent, text_password.textContent, text_email.textContent) + presenter.signup( + text_username.textContent, + text_username.textContent, + text_password.textContent, + text_email.textContent + ) } } @@ -111,7 +120,7 @@ class SignupFragment : Fragment(), SignupView { } } - override fun saveSmartLockCredentials(loginCredential: Credential?) { + override fun saveSmartLockCredentials(loginCredential: Credential) { credentialsToBeSaved = loginCredential if (googleApiClient!!.isConnected) { saveCredentials() @@ -123,27 +132,22 @@ class SignupFragment : Fragment(), SignupView { if (resultCode == RESULT_OK) { Toast.makeText(context, "Credentials saved successfully", Toast.LENGTH_SHORT).show() } else { - Log.e("STATUS", "ERROR: Cancelled by user") + Timber.e("ERROR: Cancelled by user") } } } private fun saveCredentials() { - if (credentialsToBeSaved == null) { - return - } Auth.CredentialsApi.save(googleApiClient, credentialsToBeSaved).setResultCallback( - object : ResolvingResultCallbacks(activity!!, SAVE_CREDENTIALS) { - override fun onSuccess(status: Status) { - Log.d("STATUS", "save:SUCCESS:$status") - credentialsToBeSaved = null - } + object : ResolvingResultCallbacks(activity!!, SAVE_CREDENTIALS) { + override fun onSuccess(status: Status) { + Timber.d("save:SUCCESS:$status") + } - override fun onUnresolvableFailure(status: Status) { - Log.w("STATUS", "save:FAILURE:$status") - credentialsToBeSaved = null - } - }) + override fun onUnresolvableFailure(status: Status) { + Timber.w("save:FAILURE:$status") + } + }) } override fun showLoading() { @@ -178,7 +182,8 @@ class SignupFragment : Fragment(), SignupView { private fun tintEditTextDrawableStart() { ui { - val personDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_person_black_24dp, it) + val personDrawable = + DrawableHelper.getDrawableFromId(R.drawable.ic_person_black_24dp, it) val atDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_at_black_24dp, it) val lockDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_lock_black_24dp, it) val emailDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_email_black_24dp, it) @@ -186,14 +191,22 @@ class SignupFragment : Fragment(), SignupView { val drawables = arrayOf(personDrawable, atDrawable, lockDrawable, emailDrawable) DrawableHelper.wrapDrawables(drawables) DrawableHelper.tintDrawables(drawables, it, R.color.colorDrawableTintGrey) - DrawableHelper.compoundDrawables(arrayOf(text_name, text_username, text_password, text_email), drawables) + DrawableHelper.compoundDrawables( + arrayOf( + text_name, + text_username, + text_password, + text_email + ), drawables + ) } } private fun setUpNewUserAgreementListener() { val termsOfService = getString(R.string.action_terms_of_service) val privacyPolicy = getString(R.string.action_privacy_policy) - val newUserAgreement = String.format(getString(R.string.msg_new_user_agreement), termsOfService, privacyPolicy) + val newUserAgreement = + String.format(getString(R.string.msg_new_user_agreement), termsOfService, privacyPolicy) text_new_user_agreement.text = newUserAgreement @@ -209,7 +222,11 @@ class SignupFragment : Fragment(), SignupView { } } - TextHelper.addLink(text_new_user_agreement, arrayOf(termsOfService, privacyPolicy), arrayOf(termsOfServiceListener, privacyPolicyListener)) + TextHelper.addLink( + text_new_user_agreement, + arrayOf(termsOfService, privacyPolicy), + arrayOf(termsOfServiceListener, privacyPolicyListener) + ) } private fun enableUserInput(value: Boolean) { diff --git a/app/src/main/java/chat/rocket/android/authentication/ui/AuthenticationActivity.kt b/app/src/main/java/chat/rocket/android/authentication/ui/AuthenticationActivity.kt index f02c73bb58..b4290d5070 100644 --- a/app/src/main/java/chat/rocket/android/authentication/ui/AuthenticationActivity.kt +++ b/app/src/main/java/chat/rocket/android/authentication/ui/AuthenticationActivity.kt @@ -21,8 +21,10 @@ import kotlinx.coroutines.experimental.launch import javax.inject.Inject class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector { - @Inject lateinit var fragmentDispatchingAndroidInjector: DispatchingAndroidInjector - @Inject lateinit var presenter: AuthenticationPresenter + @Inject + lateinit var fragmentDispatchingAndroidInjector: DispatchingAndroidInjector + @Inject + lateinit var presenter: AuthenticationPresenter val job = Job() override fun onCreate(savedInstanceState: Bundle?) { @@ -46,7 +48,7 @@ class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) val currentFragment = supportFragmentManager.findFragmentById(R.id.fragment_container) - if (currentFragment!=null){ + if (currentFragment != null) { currentFragment.onActivityResult(requestCode, resultCode, data) } } diff --git a/app/src/main/java/chat/rocket/android/main/ui/MainActivity.kt b/app/src/main/java/chat/rocket/android/main/ui/MainActivity.kt index 696a98f120..168c349f0a 100644 --- a/app/src/main/java/chat/rocket/android/main/ui/MainActivity.kt +++ b/app/src/main/java/chat/rocket/android/main/ui/MainActivity.kt @@ -6,7 +6,6 @@ import android.os.Bundle import android.support.v4.app.Fragment import android.support.v7.app.AppCompatActivity import android.support.v7.widget.LinearLayoutManager -import android.util.Log import android.view.Gravity import android.view.MenuItem import android.view.View @@ -40,13 +39,8 @@ import kotlinx.coroutines.experimental.launch import timber.log.Timber import javax.inject.Inject -class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupportFragmentInjector, GoogleApiClient.ConnectionCallbacks { - override fun onConnected(p0: Bundle?) { - } - - override fun onConnectionSuspended(p0: Int) { - } - +class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupportFragmentInjector, + GoogleApiClient.ConnectionCallbacks { @Inject lateinit var activityDispatchingAndroidInjector: DispatchingAndroidInjector @Inject @@ -55,7 +49,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp lateinit var presenter: MainPresenter private var isFragmentAdded: Boolean = false private var expanded = false - private var googleApiClient: GoogleApiClient? = null + private lateinit var googleApiClient: GoogleApiClient private val headerLayout by lazy { view_navigation.getHeaderView(0) } override fun onCreate(savedInstanceState: Bundle?) { @@ -66,7 +60,11 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp launch(CommonPool) { try { - val token = InstanceID.getInstance(this@MainActivity).getToken(getString(R.string.gcm_sender_id), GoogleCloudMessaging.INSTANCE_ID_SCOPE, null) + val token = InstanceID.getInstance(this@MainActivity).getToken( + getString(R.string.gcm_sender_id), + GoogleCloudMessaging.INSTANCE_ID_SCOPE, + null + ) Timber.d("GCM token: $token") presenter.refreshToken(token) } catch (ex: Exception) { @@ -80,28 +78,36 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp setupNavigationView() } + override fun onConnected(bundle: Bundle?) { + } + + override fun onConnectionSuspended(errorCode: Int) { + } + private fun buildGoogleApiClient() { googleApiClient = GoogleApiClient.Builder(this) - .enableAutoManage(this, { - Log.d("STATUS", "ERROR: connection to client failed") - }) - .addConnectionCallbacks(this) - .addApi(Auth.CREDENTIALS_API) - .build() + .enableAutoManage(this, { + Timber.d("ERROR: connection to client failed") + }) + .addConnectionCallbacks(this) + .addApi(Auth.CREDENTIALS_API) + .build() } override fun onStart() { super.onStart() - if (googleApiClient!!.isConnected) { - Log.d("STATUS", "google api client connected successfully") + googleApiClient.let { + if (it.isConnected) { + Timber.d("Google api client connected successfully") + } } } override fun disableAutoSignIn() { - if (googleApiClient!!.isConnected) { - Auth.CredentialsApi.disableAutoSignIn(googleApiClient) - } else { - Log.e("STATUS", "Failed to disable auto sign in") + googleApiClient.let { + if (it.isConnected) { + Auth.CredentialsApi.disableAutoSignIn(googleApiClient) + } } } @@ -123,7 +129,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp override fun showUserStatus(userStatus: UserStatus) { headerLayout.apply { image_user_status.setImageDrawable( - DrawableHelper.getUserStatusDrawable(userStatus, this.context) + DrawableHelper.getUserStatusDrawable(userStatus, this.context) ) } } @@ -134,7 +140,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp with(viewModel) { if (userStatus != null) { image_user_status.setImageDrawable( - DrawableHelper.getUserStatusDrawable(userStatus, context) + DrawableHelper.getUserStatusDrawable(userStatus, context) ) } if (userDisplayName != null) { @@ -158,19 +164,29 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp override fun alertNotRecommendedVersion() { AlertDialog.Builder(this) - .setMessage(getString(R.string.msg_ver_not_recommended, BuildConfig.RECOMMENDED_SERVER_VERSION)) - .setPositiveButton(R.string.msg_ok, null) - .create() - .show() + .setMessage( + getString( + R.string.msg_ver_not_recommended, + BuildConfig.RECOMMENDED_SERVER_VERSION + ) + ) + .setPositiveButton(R.string.msg_ok, null) + .create() + .show() } override fun blockAndAlertNotRequiredVersion() { AlertDialog.Builder(this) - .setMessage(getString(R.string.msg_ver_not_minimum, BuildConfig.REQUIRED_SERVER_VERSION)) - .setOnDismissListener { presenter.logout() } - .setPositiveButton(R.string.msg_ok, null) - .create() - .show() + .setMessage( + getString( + R.string.msg_ver_not_minimum, + BuildConfig.REQUIRED_SERVER_VERSION + ) + ) + .setOnDismissListener { presenter.logout() } + .setPositiveButton(R.string.msg_ok, null) + .create() + .show() } private fun setupAccountsList(header: View, accounts: List) { @@ -215,7 +231,8 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp override fun activityInjector(): AndroidInjector = activityDispatchingAndroidInjector - override fun supportFragmentInjector(): AndroidInjector = fragmentDispatchingAndroidInjector + override fun supportFragmentInjector(): AndroidInjector = + fragmentDispatchingAndroidInjector private fun setupToolbar() { setSupportActionBar(toolbar) From 7db2410923af0bb7cb5257a216968c379b6c7a9d Mon Sep 17 00:00:00 2001 From: aniket Date: Tue, 22 May 2018 04:23:15 +0530 Subject: [PATCH 10/45] changes #2 --- .../authentication/login/ui/LoginFragment.kt | 20 +++++++++++++------ .../signup/presentation/SignupPresenter.kt | 7 +------ .../signup/ui/SignupFragment.kt | 14 +++++++++---- .../rocket/android/main/ui/MainActivity.kt | 3 ++- .../webview/oauth/ui/OauthWebViewActivity.kt | 17 +++++++++++++--- app/src/main/res/values-es/strings.xml | 3 +++ app/src/main/res/values-fr/strings.xml | 3 +++ app/src/main/res/values-hi-rIN/strings.xml | 3 +++ app/src/main/res/values-pt-rBR/strings.xml | 3 +++ app/src/main/res/values-uk-rUA/strings.xml | 15 ++++++++++++++ app/src/main/res/values/strings.xml | 1 + 11 files changed, 69 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt b/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt index adf788e185..d6a21b9bb3 100644 --- a/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt +++ b/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt @@ -48,7 +48,7 @@ internal const val MULTIPLE_CREDENTIALS_READ = 3 internal const val NO_CREDENTIALS_EXIST = 4 internal const val SAVE_CREDENTIALS = 5 -var googleApiClient: GoogleApiClient? = null +lateinit var googleApiClient: GoogleApiClient class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks { @Inject @@ -131,19 +131,25 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks } } MULTIPLE_CREDENTIALS_READ -> { - val loginCredentials: Credential = data!!.getParcelableExtra(Credential.EXTRA_KEY) + val loginCredentials: Credential = + data!!.getParcelableExtra(Credential.EXTRA_KEY) handleCredential(loginCredentials) } NO_CREDENTIALS_EXIST -> { //use the hints to autofill sign in forms to reduce the info to be filled - val loginCredentials: Credential = data!!.getParcelableExtra(Credential.EXTRA_KEY) + val loginCredentials: Credential = + data!!.getParcelableExtra(Credential.EXTRA_KEY) val email = loginCredentials.id val password = loginCredentials.password text_username_or_email.setText(email) text_password.setText(password) } - SAVE_CREDENTIALS -> Toast.makeText(context, "Credentials saved successfully", Toast.LENGTH_SHORT).show() + SAVE_CREDENTIALS -> Toast.makeText( + context, + getString(R.string.message_credentials_saved_successfully), + Toast.LENGTH_SHORT + ).show() } } else if (requestCode == SAVE_CREDENTIALS) { Timber.e("ERROR: Cancelled by user") @@ -161,8 +167,10 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks override fun onDestroy() { super.onDestroy() - googleApiClient!!.stopAutoManage(activity!!) - googleApiClient!!.disconnect() + googleApiClient.let { + it.stopAutoManage(activity!!) + it.disconnect() + } } private fun buildGoogleApiClient() { diff --git a/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupPresenter.kt b/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupPresenter.kt index 1299dcb56f..da2c69156e 100644 --- a/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupPresenter.kt +++ b/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupPresenter.kt @@ -6,12 +6,7 @@ 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.util.extensions.avatarUrl -import chat.rocket.android.util.extensions.launchUI -import chat.rocket.android.util.extensions.privacyPolicyUrl -import chat.rocket.android.util.extensions.registerPushToken -import chat.rocket.android.util.extensions.serverLogoUrl -import chat.rocket.android.util.extensions.termsOfServiceUrl +import chat.rocket.android.util.extensions.* import chat.rocket.android.util.retryIO import chat.rocket.common.RocketChatException import chat.rocket.common.util.ifNull diff --git a/app/src/main/java/chat/rocket/android/authentication/signup/ui/SignupFragment.kt b/app/src/main/java/chat/rocket/android/authentication/signup/ui/SignupFragment.kt index 3bd67ddcd2..74179d6162 100644 --- a/app/src/main/java/chat/rocket/android/authentication/signup/ui/SignupFragment.kt +++ b/app/src/main/java/chat/rocket/android/authentication/signup/ui/SignupFragment.kt @@ -122,15 +122,21 @@ class SignupFragment : Fragment(), SignupView { override fun saveSmartLockCredentials(loginCredential: Credential) { credentialsToBeSaved = loginCredential - if (googleApiClient!!.isConnected) { - saveCredentials() + googleApiClient.let { + if (it.isConnected) { + saveCredentials() + } } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { if (requestCode == SAVE_CREDENTIALS) { if (resultCode == RESULT_OK) { - Toast.makeText(context, "Credentials saved successfully", Toast.LENGTH_SHORT).show() + Toast.makeText( + context, + getString(R.string.message_credentials_saved_successfully), + Toast.LENGTH_SHORT + ).show() } else { Timber.e("ERROR: Cancelled by user") } @@ -145,7 +151,7 @@ class SignupFragment : Fragment(), SignupView { } override fun onUnresolvableFailure(status: Status) { - Timber.w("save:FAILURE:$status") + Timber.e("save:FAILURE:$status") } }) } diff --git a/app/src/main/java/chat/rocket/android/main/ui/MainActivity.kt b/app/src/main/java/chat/rocket/android/main/ui/MainActivity.kt index 168c349f0a..7966db664f 100644 --- a/app/src/main/java/chat/rocket/android/main/ui/MainActivity.kt +++ b/app/src/main/java/chat/rocket/android/main/ui/MainActivity.kt @@ -1,5 +1,6 @@ package chat.rocket.android.main.ui +import DrawableHelper import android.app.Activity import android.app.AlertDialog import android.os.Bundle @@ -11,8 +12,8 @@ import android.view.MenuItem import android.view.View import chat.rocket.android.BuildConfig import chat.rocket.android.R -import chat.rocket.android.main.adapter.Selector import chat.rocket.android.main.adapter.AccountsAdapter +import chat.rocket.android.main.adapter.Selector import chat.rocket.android.main.presentation.MainPresenter import chat.rocket.android.main.presentation.MainView import chat.rocket.android.main.viewmodel.NavHeaderViewModel diff --git a/app/src/main/java/chat/rocket/android/webview/oauth/ui/OauthWebViewActivity.kt b/app/src/main/java/chat/rocket/android/webview/oauth/ui/OauthWebViewActivity.kt index b55e2385ba..0a7a66a321 100644 --- a/app/src/main/java/chat/rocket/android/webview/oauth/ui/OauthWebViewActivity.kt +++ b/app/src/main/java/chat/rocket/android/webview/oauth/ui/OauthWebViewActivity.kt @@ -80,7 +80,8 @@ class OauthWebViewActivity : AppCompatActivity() { domStorageEnabled = true // TODO Remove this workaround that is required to make Google OAuth to work. We should use Custom Tabs instead. See https://github.com/RocketChat/Rocket.Chat.Android/issues/968 if (webPageUrl.contains("google")) { - userAgentString = "Mozilla/5.0 (Linux; Android 4.1.1; Galaxy Nexus Build/JRO03C) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/43.0.2357.65 Mobile Safari/535.19" + userAgentString = + "Mozilla/5.0 (Linux; Android 4.1.1; Galaxy Nexus Build/JRO03C) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/43.0.2357.65 Mobile Safari/535.19" } } web_view.webViewClient = object : WebViewClient() { @@ -113,8 +114,18 @@ class OauthWebViewActivity : AppCompatActivity() { private fun getCredentialSecret(json: JSONObject): String = json.optString(JSON_CREDENTIAL_SECRET) - private fun closeView(activityResult: Int = Activity.RESULT_CANCELED, credentialToken: String? = null, credentialSecret: String? = null) { - setResult(activityResult, Intent().putExtra(INTENT_OAUTH_CREDENTIAL_TOKEN, credentialToken).putExtra(INTENT_OAUTH_CREDENTIAL_SECRET, credentialSecret)) + private fun closeView( + activityResult: Int = Activity.RESULT_CANCELED, + credentialToken: String? = null, + credentialSecret: String? = null + ) { + setResult( + activityResult, + Intent().putExtra(INTENT_OAUTH_CREDENTIAL_TOKEN, credentialToken).putExtra( + INTENT_OAUTH_CREDENTIAL_SECRET, + credentialSecret + ) + ) finish() overridePendingTransition(R.anim.hold, R.anim.slide_down) } diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 5a45840cba..e518d0750f 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -134,6 +134,9 @@ Usuario %1$s no silenciado por %2$s %1$s fue establecido %2$s por %3$s %1$s ya no es %2$s por %3$s + // TODO:Add proper translation. + Credentials saved successfully + Respuesta diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 6cba4c717e..d45d2c41e0 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -134,6 +134,9 @@ Utilisateur %1$s non muté par %2$s %1$s a été défini %2$s par %3$s %1$s is no longer %2$s par %3$s + // TODO:Add proper translation. + Credentials saved successfully + Répondre diff --git a/app/src/main/res/values-hi-rIN/strings.xml b/app/src/main/res/values-hi-rIN/strings.xml index f89fa2cdf3..e8f9abd3ca 100644 --- a/app/src/main/res/values-hi-rIN/strings.xml +++ b/app/src/main/res/values-hi-rIN/strings.xml @@ -136,6 +136,9 @@ उपयोगकर्ता %1$s %2$s द्वारा अनम्यूट किया गया %1$s %3$s द्वारा %2$s सेट किया गया था %1$s अब %3$s द्वारा %2$s नहीं है + // TODO:Add proper translation. + Credentials saved successfully + जवाब दें diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index a980b6ab63..1a330b4601 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -126,6 +126,9 @@ Usuário %1$s saiu do modo mudo por %2$s %1$s foi definido %2$s por %3$s %1$s não é mais %2$s por %3$s + // TODO:Add proper translation. + Credentials saved successfully + Responder diff --git a/app/src/main/res/values-uk-rUA/strings.xml b/app/src/main/res/values-uk-rUA/strings.xml index 4f451d917a..af393e012a 100644 --- a/app/src/main/res/values-uk-rUA/strings.xml +++ b/app/src/main/res/values-uk-rUA/strings.xml @@ -108,6 +108,14 @@ Image has been saved to gallery Failed to save image (edited) + // TODO: Add proper translation. + \u0020and\u0020 + // TODO: Add proper translation. + \u0020is typing… + // TODO: Add proper translation. + \u0020are typing… + // TODO: Add proper translation. + Several users are typing… No result found @@ -121,6 +129,13 @@ Pinned a message: User %1$s muted by %2$s User %1$s unmuted by %2$s + // TODO: Add proper translation. + %1$s was set %2$s by %3$s + // TODO: Add proper translation. + %1$s is no longer %2$s by %3$s + // TODO: Add proper translation. + Credentials saved successfully + Reply diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ca15ce9cba..16bfc22acc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -127,6 +127,7 @@ User %1$s unmuted by %2$s %1$s was set %2$s by %3$s %1$s is no longer %2$s by %3$s + Credentials saved successfully Reply From b2cc4390e2ac44fc3e5f33aa4f84d82bb3e84b6e Mon Sep 17 00:00:00 2001 From: aniket Date: Tue, 22 May 2018 15:34:52 +0530 Subject: [PATCH 11/45] fixes failing test --- .../android/authentication/login/ui/LoginFragment.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt b/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt index d6a21b9bb3..665da286b3 100644 --- a/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt +++ b/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt @@ -228,10 +228,10 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks null ) } catch (e: IntentSender.SendIntentException) { - Timber.e("STATUS", "ERROR: Could not start hint picker Intent", e); + Timber.e("ERROR: Could not start hint picker Intent") } } else { - Timber.d("STATUS", "ERROR: nothing happening") + Timber.d("ERROR: nothing happening") } } } @@ -249,7 +249,7 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks try { status.startResolutionForResult(activity, requestCode) } catch (e: IntentSender.SendIntentException) { - Timber.e("STATUS", "Failed to send Credentials intent.", e) + Timber.e("Failed to send Credentials intent") } } @@ -262,12 +262,12 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks Auth.CredentialsApi.save(googleApiClient, credentialsToBeSaved).setResultCallback( object : ResolvingResultCallbacks(activity!!, SAVE_CREDENTIALS) { override fun onSuccess(status: Status) { - Timber.d("STATUS", "save:SUCCESS:$status") + Timber.d("credentials save:SUCCESS:$status") credentialsToBeSaved = null } override fun onUnresolvableFailure(status: Status) { - Timber.w("STATUS", "save:FAILURE:$status") + Timber.e("credentials save:FAILURE:$status") credentialsToBeSaved = null } }) From 2fa3abb1c08bd96ff23e127339793ba4439198c3 Mon Sep 17 00:00:00 2001 From: aniket Date: Tue, 22 May 2018 17:19:21 +0530 Subject: [PATCH 12/45] resolves minor issues --- dependencies.gradle | 15 ++++++++++++++- wear/build.gradle | 15 +++++++-------- wear/src/main/res/mipmap-hdpi/ic_launcher.png | Bin 3418 -> 3965 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 3965 bytes wear/src/main/res/mipmap-mdpi/ic_launcher.png | Bin 2206 -> 2596 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 2596 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 4842 -> 5744 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 5744 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 7718 -> 9110 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 9110 bytes wear/src/main/res/values/strings.xml | 2 +- 11 files changed, 22 insertions(+), 10 deletions(-) create mode 100644 wear/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 wear/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 wear/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 wear/src/main/res/mipmap-xxhdpi/ic_launcher_round.png diff --git a/dependencies.gradle b/dependencies.gradle index 942e23a34c..2e838aebf6 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -36,7 +36,12 @@ ext { junit : '4.12', truth : '0.36', espresso : '3.0.2', - mockito : '2.10.0' + mockito : '2.10.0', + + //For wearable + wear : '2.3.0', + playServicesWearable : '15.0.1', + supportWearable : '26.1.0' ] libraries = [ kotlin : "org.jetbrains.kotlin:kotlin-stdlib-jre8:${versions.kotlin}", @@ -102,5 +107,13 @@ ext { espressoIntents : "com.android.support.test.espresso:espresso-intents:${versions.espresso}", roomTest : "android.arch.persistence.room:testing:${versions.room}", truth : "com.google.truth:truth:$versions.truth", + + //For the wear app + wearable : "com.google.android.support:wearable:${versions.wear}", + playServicesWearable : "com.google.android.gms:play-services-wearable:${versions.playServicesWearable}", + percentLayout : "com.android.support:percent:${versions.supportWearable}", + supportWearable : "com.android.support:support-v4:${versions.supportWearable}", + wearableRecyclerView : "com.android.support:recyclerview-v7:${versions.supportWearable}", + wearSupport : "com.android.support:wear:${versions.supportWearable}" ] } diff --git a/wear/build.gradle b/wear/build.gradle index f7754ff312..c814dfacc5 100644 --- a/wear/build.gradle +++ b/wear/build.gradle @@ -4,13 +4,12 @@ apply plugin: 'kotlin-android-extensions' android { compileSdkVersion 26 - defaultConfig { applicationId "chat.rocket.android.wear" minSdkVersion 23 targetSdkVersion versions.targetSdk versionCode 1 - versionName "1.0" + versionName "1.0.0" } buildTypes { release { @@ -28,11 +27,11 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation libraries.kotlin - implementation 'com.google.android.support:wearable:2.3.0' - implementation 'com.google.android.gms:play-services-wearable:15.0.1' - implementation 'com.android.support:percent:26.1.0' - implementation 'com.android.support:support-v4:26.1.0' - implementation 'com.android.support:recyclerview-v7:26.1.0' - implementation 'com.android.support:wear:26.1.0' + implementation libraries.wearable + implementation libraries.playServicesWearable + implementation libraries.percentLayout + implementation libraries.supportWearable + implementation libraries.wearableRecyclerView + implementation libraries.wearSupport compileOnly 'com.google.android.wearable:wearable:2.3.0' } diff --git a/wear/src/main/res/mipmap-hdpi/ic_launcher.png b/wear/src/main/res/mipmap-hdpi/ic_launcher.png index cde69bcccec65160d92116f20ffce4fce0b5245c..ef465d63b6aa3c0cb809e9da30dce6fb84cb18d8 100644 GIT binary patch literal 3965 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 literal 3418 zcmZ{nX*|@A^T0p5j$I+^%FVhdvMbgt%d+mG98ubwNv_tpITppba^GiieBBZGI>I89 zGgm8TA>_)DlEu&W;s3#ZUNiH4&CF{a%siTjzG;eOzQB6{003qKeT?}z_5U*{{kgZ; zdV@U&tqa-&4FGisjMN8o=P}$t-`oTM2oeB5d9mHPgTYJx4jup)+5a;Tke$m708DocFzDL>U$$}s6FGiy_I1?O zHXq`q884|^O4Q*%V#vwxqCz-#8i`Gu)2LeB0{%%VKunOF%9~JcFB9MM>N00M`E~;o zBU%)O5u-D6NF~OQV7TV#JAN;=Lylgxy0kncoQpGq<<_gxw`FC=C-cV#$L|(47Hatl ztq3Jngq00x#}HGW@_tj{&A?lwOwrVX4@d66vLVyj1H@i}VD2YXd)n03?U5?cKtFz4 zW#@+MLeDVP>fY0F2IzT;r5*MAJ2}P8Z{g3utX0<+ZdAC)Tvm-4uN!I7|BTw&G%RQn zR+A5VFx(}r<1q9^N40XzP=Jp?i=jlS7}T~tB4CsWx!XbiHSm zLu}yar%t>-3jlutK=wdZhES->*1X({YI;DN?6R=C*{1U6%wG`0>^?u}h0hhqns|SeTmV=s;Gxx5F9DtK>{>{f-`SpJ`dO26Ujk?^%ucsuCPe zIUk1(@I3D^7{@jmXO2@<84|}`tDjB}?S#k$ik;jC))BH8>8mQWmZ zF#V|$gW|Xc_wmmkoI-b5;4AWxkA>>0t4&&-eC-J_iP(tLT~c6*(ZnSFlhw%}0IbiJ ztgnrZwP{RBd(6Ds`dM~k;rNFgkbU&Yo$KR#q&%Kno^YXF5ONJwGwZ*wEr4wYkGiXs z$&?qX!H5sV*m%5t@3_>ijaS5hp#^Pu>N_9Q?2grdNp({IZnt|P9Xyh);q|BuoqeUJ zfk(AGX4odIVADHEmozF|I{9j>Vj^jCU}K)r>^%9#E#Y6B0i#f^iYsNA!b|kVS$*zE zx7+P?0{oudeZ2(ke=YEjn#+_cdu_``g9R95qet28SG>}@Me!D6&}un*e#CyvlURrg8d;i$&-0B?4{eYEgzwotp*DOQ_<=Ai21Kzb0u zegCN%3bdwxj!ZTLvBvexHmpTw{Z3GRGtvkwEoKB1?!#+6h1i2JR%4>vOkPN_6`J}N zk}zeyY3dPV+IAyn;zRtFH5e$Mx}V(|k+Ey#=nMg-4F#%h(*nDZDK=k1snlh~Pd3dA zV!$BoX_JfEGw^R6Q2kpdKD_e0m*NX?M5;)C zb3x+v?J1d#jRGr=*?(7Habkk1F_#72_iT7{IQFl<;hkqK83fA8Q8@(oS?WYuQd4z^ z)7eB?N01v=oS47`bBcBnKvI&)yS8`W8qHi(h2na?c6%t4mU(}H(n4MO zHIpFdsWql()UNTE8b=|ZzY*>$Z@O5m9QCnhOiM%)+P0S06prr6!VET%*HTeL4iu~!y$pN!mOo5t@1 z?$$q-!uP(+O-%7<+Zn5i=)2OftC+wOV;zAU8b`M5f))CrM6xu94e2s78i&zck@}%= zZq2l!$N8~@63!^|`{<=A&*fg;XN*7CndL&;zE(y+GZVs-IkK~}+5F`?ergDp=9x1w z0hkii!N(o!iiQr`k`^P2LvljczPcM`%7~2n#|K7nJq_e0Ew;UsXV_~3)<;L?K9$&D zUzgUOr{C6VLl{Aon}zp`+fH3>$*~swkjCw|e>_31G<=U0@B*~hIE)|WSb_MaE41Prxp-2eEg!gcon$fN6Ctl7A_lV8^@B9B+G~0=IYgc%VsprfC`e zoBn&O3O)3MraW#z{h3bWm;*HPbp*h+I*DoB%Y~(Fqp9+x;c>K2+niydO5&@E?SoiX_zf+cI09%%m$y=YMA~rg!xP*>k zmYxKS-|3r*n0J4y`Nt1eO@oyT0Xvj*E3ssVNZAqQnj-Uq{N_&3e45Gg5pna+r~Z6^ z>4PJ7r(gO~D0TctJQyMVyMIwmzw3rbM!};>C@8JA<&6j3+Y9zHUw?tT_-uNh^u@np zM?4qmcc4MZjY1mWLK!>1>7uZ*%Pe%=DV|skj)@OLYvwGXuYBoZvbB{@l}cHK!~UHm z4jV&m&uQAOLsZUYxORkW4|>9t3L@*ieU&b0$sAMH&tKidc%;nb4Z=)D7H<-`#%$^# zi`>amtzJ^^#zB2e%o*wF!gZBqML9>Hq9jqsl-|a}yD&JKsX{Op$7)_=CiZvqj;xN& zqb@L;#4xW$+icPN?@MB|{I!>6U(h!Wxa}14Z0S&y|A5$zbH(DXuE?~WrqNv^;x}vI z0PWfSUuL7Yy``H~*?|%z zT~ZWYq}{X;q*u-}CT;zc_NM|2MKT8)cMy|d>?i^^k)O*}hbEcCrU5Bk{Tjf1>$Q=@ zJ9=R}%vW$~GFV_PuXqE4!6AIuC?Tn~Z=m#Kbj3bUfpb82bxsJ=?2wL>EGp=wsj zAPVwM=CffcycEF; z@kPngVDwPM>T-Bj4##H9VONhbq%=SG;$AjQlV^HOH7!_vZk=}TMt*8qFI}bI=K9g$fgD9$! zO%cK1_+Wbk0Ph}E$BR2}4wO<_b0{qtIA1ll>s*2^!7d2e`Y>$!z54Z4FmZ*vyO}EP z@p&MG_C_?XiKBaP#_XrmRYszF;Hyz#2xqG%yr991pez^qN!~gT_Jc=PPCq^8V(Y9K zz33S+Mzi#$R}ncqe!oJ3>{gacj44kx(SOuC%^9~vT}%7itrC3b;ZPfX;R`D2AlGgN zw$o4-F77!eWU0$?^MhG9zxO@&zDcF;@w2beXEa3SL^htWYY{5k?ywyq7u&)~Nys;@ z8ZNIzUw$#ci&^bZ9mp@A;7y^*XpdWlzy%auO1hU=UfNvfHtiPM@+99# z!uo2`>!*MzphecTjN4x6H)xLeeDVEO#@1oDp`*QsBvmky=JpY@fC0$yIexO%f>c-O zAzUA{ch#N&l;RClb~;`@dqeLPh?e-Mr)T-*?Sr{32|n(}m>4}4c3_H3*U&Yj)grth z{%F0z7YPyjux9hfqa+J|`Y%4gwrZ_TZCQq~0wUR8}9@Jj4lh( z#~%AcbKZ++&f1e^G8LPQ)*Yy?lp5^z4pDTI@b^hlv06?GC%{ZywJcy}3U@zS3|M{M zGPp|cq4Zu~9o_cEZiiNyU*tc73=#Mf>7uzue|6Qo_e!U;oJ)Z$DP~(hOcRy&hR{`J zP7cNIgc)F%E2?p%{%&sxXGDb0yF#zac5fr2x>b)NZz8prv~HBhw^q=R$nZ~@&zdBi z)cEDu+cc1?-;ZLm?^x5Ov#XRhw9{zr;Q#0*wglhWD={Pn$Qm$;z?Vx)_f>igNB!id zmTlMmkp@8kP212#@jq=m%g4ZEl$*a_T;5nHrbt-6D0@eqFP7u+P`;X_Qk68bzwA0h zf{EW5xAV5fD)il-cV&zFmPG|KV4^Z{YJe-g^>uL2l7Ep|NeA2#;k$yerpffdlXY<2 znDODl8(v(24^8Cs3wr(UajK*lY*9yAqcS>92eFHOn@Z(=2H$MfLUdl`L02Ch_5UcAZ&*s~K(SiHCU&i2 ztG5%YIA&3`uU7+xYm0k~vNOLf9;Y>`eSemt=)DRQiwX&9p-5CMc3|P2{*CpiqY2ns z>vz)VBjLO#!9cleZf5sj(f3 zMh4}eN@MEU5tK$n_CfI8A@s3bK~jM;ge4Ol5uLJjG&puVC4tJkdiCo4?E;b{zPh@) z$xvezIOK_<>RU}kMa5%nO5PHEzUd4Yxr&6zlSTHC|4?aZ>8v)~WIG1u)O0p*>_Osj)MR92{JmAoO%>z#f$Rk5bUJYCL8`2*%!-SP zyIDQP76HW9J?Iu8vkF^kYp)6Kdopns7Z)Gqp?NhMAgO+}wYAHffpfL6kgh~UQSNu& zQTC^wQu^C(Q^tGmQRe#fl)G~$m7F?7)w#LEe%F(Ls$)~5?Ck7Un`LS;*{iOu?$6*< z$=E`Dld6jsDdU}YDDKH8$?(V{q#Hh*&JG)9Blr!+Oiw&P86hEb<;)qXEi3bgI0??C zyu7?BCMM?3>M?5qh_z-{Q`%FNnMnz=XFGu6O!T8iQ_AYqRFRzQ5vB&$pO=^SqcF8D znoNteXPj{9P3BYpDe<}IG=T&$`uo%6&6~+mQ`4a!A+N5gs-lF1gvoB3nj|fb86sSI zEq(&!iHQ_Db*g5NpiSVZyxqGgD>Rf+-*|&Ay!G$80e$*(ueqoqJ8^$|`($Y?Zx=G4rjda=6o;uDJ+^IXoPDjS#t6umHze{mFe|V8XZl=$Bt3Kfdh2;n{O1YiF@iPGL9clx`!Te zFxT^o7Afr&^J|7jD=8@{-oAbNT}=j$5;?QkJYQ&!rt(D%$2v~E+Jg_0>G8*v7^(R9 zaizWHr_BLqRaO?||M(-Nu34jyy$w*TeaS0VQdM@gW{}t^5xe|pL!_Pp42nJfP|?w&R8vqu77ox_!voc^_o=>oS@DMPvt}s|K;9FYR{YB^3ZPoZ znsc-;#=x=YFQ;kFoH=s~x6RdRUP?q=RHUf6T4Um#ewy<3?4hg0#ck)VnLnU0@N_Ug zdH8yyRL9ofMzN%mRI4W(WoS5K_*?m!m zl6|~#`m{R{wfU1zJ{jx)(4j+z1~EWYZY8KfP}@=5I%`q+`SVV5HJh>s_=+=k8)D^w z0vvUl^mXfO%mU;qc|}Ub`|mpq2y?b=YiOIlcdyIDl>w?^fQC8%w15Bp!CC>52`Yjz zXW@NiN(vbR0vz%&b(=DSfs7L-*t9|FqK>6-9s5ZA-1E;n4JkkaGRj6-*d2gA`skwv z901y}W5>X{x;nEK6O$SQv#Cr^cbbO8&zxza9WXZ=!e@ZvpLxcnP1@SEHhffl>5@b3 z30men9{=o;DI0z)YWXY2L zYA>b$)U#*L9vla0a`On|r58g1-&II&;fxtP;w)BDl2G)ksEQ4 z5-27oC;!G+_}Q${7Gd=b`T6-fv_{gXCo3wsZ$4uY~J>j2Z7j)d3kw1H)n~9eMJ!wY+HhY+D$3vTA6SZXU|g1S6`7H zrY?d)1h0jM54%-Sl>DRD>mwM0n{2elj)%HOM@KKy;_4f#Nn=U|6M{{8KQ=#(SP3?d zfx_c}J1nXvjeQ7y$8og7dBw%Ws2$TeHl-NWnAjaV$T)w#%}j?efehyVD_nlrvUW~QFiP*fYG&c@OXcD0F#iFB5qx=yDflgUKq zIgd+aVudzv9HK}T2Y~4uo#S_Q9;oyw?F}q3oKqP&f;J+=P%WBh&a8$sFE-JlNs}gF zVU=%_Zxrp`y}K*_xg{kfMRR5GN_2F?ycoP1VkCPa1PR77VH&^{91@sl+QZp9ty{P5IgI(sFTZqek%kNNrK-ofO^v31ONyp@S zR)~QmcGs?J+@bO^9%0vGjo_St90q68_l$aV=Hdl~gyJk&oX zD2U>id?BVKOB{H{Q&yNs&&4!io;Gb-0LB^|9IS1fBA)<6@7T9*Uog0YL9*DFNKx;N zXIG1AVP{#R2#dg25M+M)?YHk?j4M{GxS=@ zJ;=Y5Rj!-|HLP;w-#lrRs{ltat&=BDM)KJ15o2uLo76(>Ztl~k&jTF2rn!9O$-~!4 zqwk;#j>Xb@_wGFmV_URnQOjOQ;0tu;Kex}FJ2#N?G&5|sv%ZRezH@Y5&e_60F(xs# zmT$9X&-UX#cZGz6%r7h~w01U-BsiQ-SUDhE%wy>*#?)#bDe#&;efmxOw|{78=u!^; ztYVR8oyD_O&^vGr%u0g84|oN}5EvMEb8CVlH}A1y$Kt{RnVVkVgtAOz!X62dBxG<7 zo0WOZnl;dzfulx^LP5UGUi9^zFku3|d@x|noH^4t3r`o(wpD9+q^;u|^a&*rlgX4m zbLPw^(Pz>3Hh&A+d-CMTcnbae{QQQWI&~`I%9SgmGP(K=1P1q3ObUI9jEww+`#1u9 z6@6~ESH!(0PMnChiTkh&*|cd>NM>ecDV|tI0Hp>7ec+I!bmPX2?{nYqGH)Nz*LMGE zjMuPX!@BqD*AI&x2L%KKOg?(_=)r=50;RkmfU>lu>B&!52(OA+QOrsl^7r@03%rBS zH}tV1z)^2LBSwsXZQspuABXMFM@2=Qgr@^PiLK#_5-rgzoGY#YP^?m8a4+1G@4Wzh zKwm_kJmzC3iZ%@xFaTd;8OSnp@#4jE!otFKr=_Lkp^_-IN_@J)qW;DSAG3;qH;C#t zr7g~p=i^#tPH}K;++*Rwg(z2G$;v?V!4tHmsjbV%kt6+t*#W5gcnjFDVZ(n;oH!Al zl9F->)jJf(MQ%r~Hn?fDMJ*eJ{?ydeOPog?!8LJhp)mt-Puv@Q=nS6`>VhP2$dDn> zsJ<*%v2{N`-G^7MT=~N0&70RpL`3X07!1)IDkL+h=3KsfxtL9*41#?o{hXwvq-3;V zZri(M%a-*x7uOKlf@|K5dx(Uy^L%7U9TMDo@4ej+U<&iY+P`~P{sQmk>+Aaumf;xm z=+UDe9Y22j6bSb58y|ZRZO~SngLCh~wQx;byFFcZn}fwB(G^b{(d^KnLvQQVtJfW< z1qzR4tnw00000NkvXXu0mjfwl1l2 literal 0 HcmV?d00001 diff --git a/wear/src/main/res/mipmap-mdpi/ic_launcher.png b/wear/src/main/res/mipmap-mdpi/ic_launcher.png index c133a0cbd379f5af6dbf1a899a0293ca5eccfad0..ae9bf375a4fa1b0047833e5b7452d9b4bdb9aa83 100644 GIT binary patch delta 2588 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 VFwL6zQVak9002ovPDHLkV1jWP3EKbw diff --git a/wear/src/main/res/mipmap-mdpi/ic_launcher_round.png b/wear/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..ae9bf375a4fa1b0047833e5b7452d9b4bdb9aa83 GIT binary patch literal 2596 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 diff --git a/wear/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/wear/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..c8ec156e4e6d99d39794a264b3ccd96333187791 GIT binary patch literal 5744 zcmV-$7LVzPP)*jJqmHUO1G(pIWq7DH%_0sk-SB+gCvxsgoM@B*4~qvni{i3JdU4B&CiW%bTeiM zEydzpgwKzVyg~9KNwx^M0A@9bR*-5g`dhV$eGQbtuk|Cp_9Ld+cRvH)hs2X4)(Ge# zz_pfGy$Pw+Q;@zgVtHHo+;@tQc~fH&agW+Jjd$cjLl z-xlt}wb}OuIaB5DyO8(@z{@nlnz_jI#Q27L=py(A=Vt3@h4(M>cwN3JtpfnBFJf;% ze(|!gdu?`nPmNxyXZ5uX2)^hes^o}CtJQu&x9HTY{DRyNSOqn9zYKnLb+y)kf!7iF zUT|MkRaHM+ym+xyvrvNUY>~`R<9^|kQ{y09Z;fxL;Yv7{kdV+p#GolH|1oM*5C>oD z;FM!l*a{_7R#pb5rKNRhlAa)6FfT6;mLCZ4?v-4YbSrXmS?kAGy!7hb3rcw?ga-S1fRyYI4$ zefy-IQA5tn(fk4MDWe`88yh>URuf*rgUw6eoXJwZ_=0Hy1EpToP`v<{<>%)YkS}-!SSpoDSBts7yu5s^knvj9 z5Q<`Qjvu#+KWGR+)CFgsxqm+^&&ZH+7kerJupv2%j*kA^B3~faGKja@(}|+%ihRtW zqG+NHds%VVEP2x=miXCcEPllb7Q1jEyYc?}EMmqC zv;7dxo4R$Y?1{D(RzH{Yix6*hrGwM0alZO;Cnbf2zxG<)6!7Y+EPdxrR+gC9Xc=&2Q4z~Kd6M1u z)1R1P#0WzlF>V~o{NV?trCw%hNSd9Uo%!vz-}VBwhYlTbsjuScL^14}_Mjh00ZBPm{R z2zW1Eq;sj0eufSm>Poz&uvr0QUC36!wff;-)`0`|<##)ZCMbwSzV()Y5|GSU6ROG> zCMPF{cI(#7%_I%U(N9cFj0YW1Bpz%v_frO|lhG0tp?qNPnA_g8gp4`n&wtkO4G1KV zOfisK6<)mPFh#ZUtSol( zgAa5Kh=K(emZcN~Ja1EI?Ci{_}*MW zRHjcyiJqQP{$b%bfs_?16pD35{eTdkwjz(c$Cm6DVH@J;dvP^&}rq?2q|)Fih$19qJY9HSETSwZg9lwud_R8X%0Q+ z3#%7lB=J)cJ1myA!wqt3aFy&GHUkh zUo%`vv=C94eLOiTO>nT}FTlTbCTFD7DsuMdaj5s8%upZAq5NEQ?HVia_h%L4WX&C@ z0q5UZwMxgQSwcgS`D4LET3X7IB}<+%sE*1CIDY)NJMEioZ3v38uIPMY7cVwDi!6zZ z)KMO23<;1aB>(`?L4v1I_%rGt>HcBGFbFX!%QoLLGWKGwJXdvQ12sDU+xgfR6>FB#n*v}H^IA;uIy z-=Y3*mIBh#nL-%gu~q@ww{L%*m!=IBaOB950d}Jx0u&`?vvUVzoq9@u#TlC)R9RSP zlv32=#<8NnK%>?hIct_t%7SvtGA^Pv@mP$YSwle^H*OqeQUTw6_ubQ4t=4W%8083a z>*=?ZN){oE8=y3t{O^BDBV9&0nSd6{+)!4=o;}8kd=%|q>LY6WqSKbF-^>s>>*!IN zop5GmX70+BE1xx~fE6oN^q@Q-$?7mvqhZl==KPw3&6vT;Gc(Pv%~P0&VjZXpBWZOO z?RYI81u{f4r6_IJ6Sl%Y@=l$S<`P*l22n{y6!))x*&K!@B_*X$uGJS<>&zA6iJ0?% z0RuWwCTvq82CJ2+1RRT)-qEL^=SD>_^|Wa^gZLJX4@g+X$HzzY>eZ_&u+~q+q;X)9 zM>QJFd0R>mQdbuSH4rW$L332_F~RD_=FK|%k69Sjlp-P`B2bDzChY9#>8VqSaH22J zCEAmt*lX8f9C3O2u3ZiZJ}Q{EDE?Op5tpUKH~=pfMdP-K@C>SCnN2&>xqx?WXtM#HY%s;ZE9L>jtYfDQ@8Rc1+uq znE1c7sS&Z(+qP{RN}*_htwWNS$&6C8cmtT1iTZkT?Fpa>3KdxdC6r+wf2=c00=+oz z^l6J{TFW75dU|@%(xpqs0%KD{lHer#@OW`?vBFjbNQ3fMt{9XIqzb7WrvTb%8lf~~ z7#kbQ!uoSf_}J`=&F$+<#e2FHf-bA8DVg~NA&A|a>6)M~Y4v2-&S+G80R8IpoPA-Qxu zyeCH|SvIBo(tecqNXJ5O9u>v(s$<7kI9audj6z{#`a0T&!XRd2+p@HY$NyT;Pe4F` zFJan+V_Ww|8bXNln6IzzT*2zBpPo)uq!S6vnpG#C!eZeJCEzKmjsQKJJI53O0Zc)k z6$B!zH^hi2WWhcZ$>5k!IyU^rADMdfYNnh$n<*zuFgZILREb%8^%;mI^aBIgx^?SE zz_d>O*P|h~C!c)MowzI7+PaX@1saslq-@=4a_kmV5gi>ZfvF@v5xRW&-6V)aHE9wH zTd;tI{o@}@{pqJni97@A(752Unk01XS{Al)B@6rOUzti6f~<258UdgB<(E?EZ0Dyi zvheWm*e9NN0*m$B>ZKof`|!gLF?FId0kMJi0?Q|98Cjwv4H+UMHbOa|lu%040KN)y zN*6C?Ax7r!+egX1=gyrwf^vdv zs~73%C1RLaAeWe-SkMjl`{vD?66FB=(5qLOYTi7fizjSeq&H>^i+blBmP%nIW?xh` zP?1n@zPZz;O?weoE?TtcVM`WDN^^whwVO(%+M&Hm88wzrkN(_H)%58sL#2|U8f2RZ z2?_TsUZNa;zk4@Rz4D3yfKVg~e4q-I{`7Gpg28+wd>A#0|NGxrDkV{grN^+R$ z6DL?GQI0~GQ71b_tnI>cD5N!C1Yj9OI0$1=_Qw1kQHrSdV2BZeE#PykDMMYVF-sq^ z^-~~`N7nxmFq}7UUK?u`%R7x8JsOX8?{VeIl`R!_IafhO@(SBBU7(Cg%ExStk-REVjnX zv*x;N$=p#d7^oj@z=~sLm#1bqO`SRwF>Kc%Lxv1bPEJnbxk0^;EJGa#C22fI2#y&7 z3&%QOOs;~$9Q~1HG8TAMtJTSa1`Qeotj3KS*WMo1FK^Cc#*D!q8a%aO!-jd3UTf`D zuD%bRA1YB#Vq&6p<;s-{fDy;a?oUs29Xxn&XL{{>?AWnQ@bp$c$m2c(AB8|KFR!h@ zhGWzq&y#i{UTfQ~T|3Ncc_ttrzz4|&bb!>m1%fY?8&K;%a^%QSU;=FTQq%_frw!-6 zefvJjht>uK1_qu(tsV}jB`M+Vqv2|+Q9L|*_G|!QF&LO|Y#RI@HeATjNdCap)pdwM zp}5|XlpuqT-tX`4A50hw1r{8WM)_wAX*K0@&pr1zz4mf*a~m2I6ci}a(f#ZbIOo^! zHNPQk+qNA63^*388j^a9DM5D1y-A+Cbm`K0*)gM`)egQePe0TY9>&K`ojL{D!8G&U zg41pEe^|l=TMryK5dScQf1!K!^y$-wF!m}xRsDWYf*j}bHD0Li!{OqQ1AX|tw{8mX zb(BEP=W*T>2JPFoZ!;xpRTxy~8M8z72HEPlZ^IK1t9IwjlY*~9N8P)3 z|DO&WIzR~nR;^m~0cC}W;7UjA4WiZK9)g2|la?)8h9qkM?h*IeRJ2p45;}J5*r69~ z^uGVoPd`28o`l^Cs~TGRLD0 zXp8hOo*E4%Ze@FUd3}R6qwTl{+zalhF_J91QW6#;^y$;*Dc&0fjU797l8=wiUQ$FB ztPEq#{NFgVoSteMQW+fdT23=s9x_XzCEDce?Y(dG=+RTSqwUAr{z!AB9CInDP1mkn zAMe?-Cmgf~y7b7ABPWuTZKG};4IjZXX;~RHJaUp>YdLV10?jzh-8+{|G4gZ%5murN zd-m+vK79D_$((LH&{njWTfK`Tf^U#HgFmS|cOmE8hbv+brST)ytXZ?f*Vp%WczAdU z0KuS!+%x12!10i_T5ps6$**6~KG;_TTefs48hHv`D;d|swW$pwxy5^+ZD=Fs`c{q( zyfNQ^uG|is%A+Mv(*OeDSq~47m)5RbyNnW@1J|!#S7BB`c6PQjzzu7~93npQ#7u2` zj6#aKu)YhNo|~In6CWR+8yFaRT}}F&PO72{|b#Dftk*CxvC; zH|%ri(xrX-*SvrL0000j)A)QUxEdH>gn_l< zvmVsEsCB1yAGN{M?&obV|66zd84pV!kpN2tR6PStQ&WJUzjvTEoZ1X(3#o0Tc9L2s zwRmdT)QYL;sg<+;ALaaS#r!kz{5vQ4ITrGB;@lm~0II19rg{Pz{^Y}Iyi9F9wF}fT zn9Na8QBmtAbKp15Q(MP>8`sc81(WjuM{rL@e}ijzg4$|oLEK`<0i~P{J#Ply#G}GO z6A$rw;(5wV0IQf^%W8gY9!A&fY78*-aG#xv#z4Y7k22zVDnGIS9w~5qxLKFd8+`);jj5(LB1-53 zAGtnK4;ib2_tc61pw~PJatw_CIMv*n1V?$iPh3=MB*S$i??>^NNkes2wz{@pBe&MH zv4h7=@_wQf2M!uN~}(W3KGk1O=695OG`^%kn7vVz;~9bR>wZpNUztgpe}W3(7Z^ZvfSy$ z_erF=%5`j0YJ%!7DJj{1_3G7T4N~fhL@zHd&y%NBs4hzZmsVC*b}>3Sx_yHp*W%Mq zTwIL6U?c}mnL6-xZ)NCM;zUu2iHW@$ge4MhfNrWky-!ua<1i4VCM6{es@EVAO(@+= zKYD+I6CM>j4gwKb{H(aRxWV<(ok`pla&}Tw@YD^63JVKU!@|Pqmpc<>WNvQmo%B9J z1y9|927ax`k5*8zrH17rO90 zDtMd+M0&lRWn^R=q_JsYG*(XMx{!+Txg^e`4x)2_NMBM?!ctRHS7MBu&XtKQ-(WC2 zcY7tC8YFb8U?axl)~#DCCMIT@Qz<}m&c0Y<5^xv2+KNol4YGZ!EXBbvFx*FSzcfu%h%~xX?C^>h+4X`va&Lkl9Fi%NoQ2Pr!L*M*&O-k9M;83ZBP?k2=*l)` zjL?D~epvWBJ`+Ko#dk9H?qzuzO(m!d1{Ek48pf!gpa8Dy3(kjJ>y$JX6%{>L6W(K> zC!i!PjivA2&7$6UhiRX9f(8EJ4=nJ32Ws7dA9{#IzWF9g{ow~zoRY!}rKKuZEbyV5 zH*YeHMl%*;c>45d*Sd0DO~@=>wAyu%g^2(1OTog}0vK}*7VGLq9|=~iEH_sLj2Ty! zoSYm?W9a5^S0=hHx`;V7bzPbN{VxlB@=3)2bQ?(I@~*u4D$D-;ci3zdI5Ms*KR=&E zL_~bzP*=tynJ%fRsoe<4+1zzmpA@Fo3#3^4*kg)=NN{h&o8lHPW~F4ZRp3-h)Mx4G z={biFAHIi!sEMPvPZNW|u({T%i(FYwKmZGQ^ihX^2u(L>lA!8TepJh&~7baeDF#D)}eW1{uR$;lZfCdAw02K0r6LOe$^ ze7NI4G;A0PpEiv}%$g-wB_Ylavh<8 zAK97k?`~?ZsB@Eof`a#zPSt=SivQvZ zhpIBTm9Q6IWYM!{v#bC32aEmJzgYZ=6)bV>T9!fzF=NjjcH`(#fy1Qv`m*HBn*}v? zed$tmZNUP;ibTHk7Slcbw9P^Q5&D!ZTLiA;XaEtn#qb(PB(Vz@E-b{D2L%PW*<;QN z%~h}02Px&gq%AivP!TQ!wFFWd_QDG+X5KtOre~i%Em*x0QXt3-fFgt|wZ@$Q?E2zj zLB5w#r(@8ex-t(O5W)mkUVDvQe)ieQd#S+#0gH6``RCbN1Ua za~_>`v6=g1W@e5NtD!2LX^08X%abSD&%%&PjCuck!HV39h;Udg5r9~fl*Do`US!D| zH?r{Q(`)A12=D+p(p?-)rqhX&9XWDjJjUK8GR;j`)1soHm5$6bj9a+SHdioVf)J0v zQw+=E6iSDzT0vBlVEJI35O=Z`NJvw0RAvF(7*by~8qEfbeXZS?=)S)F_S@EU;s_RYNS5e~sB#eVh~3$mm>31r}J;8_l^ zLPAXF;K74W*V>KoFS(NX`nMyka!U02=bxJ;+K6Q5T)3d98jFvQzeOB)2(VD2prWY0NY1^; z`H$k{SzlDdlD_@cWH1h(iXzJdg4`HZU!XrS_U|{F{(E`K6d?^op`c=UW@aWkcI?<& zzyblty{7!fjr`zhLD$itbcZ1c-jx3bc;Z*C6as#YDd7vj$~>)>U4HI4lS=K%>#r*k zs(>4VoIiK&+;_l&)jSBkZtB#jFjvh=N=i;S0zd^ZF_m)G#3~CUof#Ui%d*t4R0+$L zOyxFV5*ilE-YFj7K}2KDfd>H-R`DQ_|EyfOvOVD@k@FwLT$r9ves%tQ*(Xe}Jj!%V zy=%83BU#wgsV1ZCSh=gjh^= zG=YZMmFozkL#Txk%#iA&B$J{8sloQnQuO!t{{fh=3P1}NE(8Z^L0>$mB!G}w0flAU z0op(PNl;}fP|VAFSRuJ+^|envv8U)Tl$MsVz`#Hx+qdM{Fn^34I~E^mSzKIv2?SaZ z7e>|vGw#(o0Bg3EEfdVN3KYxfzbJE4EmHsNv#cl~!Cr1mn3)fygD~O2vEgFQ`aq_+ z_U_%gO<7r4q*4Habce!{lcNRE(RPdEB5IA!w1pQS;}B^S#mUJwxm5^)!r0i#V5ptk zGlHvf3H$bv^cZ_!AUr($nzy$%N(H-ejF{v9+SEG@Q-Xm$vUa=t1nCR%})R{A9_TboXwFDryb?eskB=lt|1)zef zSA_&H;~Pg_j7{LVaih|*$#4T?_F)DeGu{@#lbBdZ@MVIVsP-X5#;q9qLb^|3!KBQ2 z04B_J+48{PI^`B*#(empT>%sq7nl9sd+!YZHmm`VyRWbBee~U1N(B+26yy{{>b7k* z@hUl!Zagg_44Q@992BUsl>LaHo~5*usK5Tz?0Hb)CjatfoAR!s=gg^g-td=RvWuG( zgNgC+@dclL`so8!1BhM+5_pQ00uYK!$xU0_Jaww}Pb0Y;xGhVGXc4czYVoPICSN&r z!2+}Mb2;bE3D(L|Qe47+{$tZTAFOt@co6l2-VVhcL;xiwCYCH-ym%zAVHQ9G=LH4T`kdetauHg&h^@KvphAZ=X|&*L=2B?WcI~q2JW+r98#CCT zIull}uCBzMc}B2+_5x5+Qj(qkG#c0-0J&QP(5_v(hB_KR$g;B~-&c6zEu@2rD#a{- zPM)+5Kr?5Wou^CNxzjoTk(;vxK-X8SP#i#Hfy%4`5I0ZFwr}4)h`>{%6o9bYMlMa& z77GNmBd6@D@q_8p&ATv??B+5e#fDX^295zu}@xDKMnoS`#gYOEzT{;cioZ{L!X? z&X130AvW?Prf~} z_W%?G?l4Fo))+)goz!IA7$V}a3l|DfTRab9E=ZZM+C7j}#GK#N4?YkAh&T_dPWX#2 zvaH{Jvw6-c^U@`zkr58bn>N`MKv%9@NgY3ad>>$gPiL415VgBXN=mLMHkDCOAgr{M zdn)af_Y4=3uhj}xz?NmfsGf$rWUSk=CJ1Q?NQk~80Up-Z9w`?{H;vl3QC1{Ck=k=A zL#NYSBTU>4Y;cU2Jev6U_@F9-XJKLC1;r*)si7W@@nkA)9_ppM5$8W!o2!$KT1pcq zRwvMtCs(eQvy%lvwGNF&qa{pq;Mg#m*6Kng3Lm;NJ3IS;lIa|n=#*0_D7J(t2^A=2 z+%&nq@b&9u)gl6mcFblFYA62w`|n8S@Z{Jq4!lJiYHzbGG&S^IY-e3J7y#bOM8wUJ&Lk-?Kn2fuFPSPS!@DV`F1W=Fgx1 z1TgXHtFJb<2q3QWTa6kusyh*=>q;syu_A>~kxV9s2eKY3HyZN>Dac7qNJwCzets+j zQD?avCF&=8aUWv|9~v5(JZR7$#DH2o@W2CZmH>pKsh5{mD|+?H$;tVR14z-O%}B-2 zJ@u5yQZ85v)fnAbUS1xm$FpFv(xEabz(VMQ9z0}V+G{zFi~_^b=DRy}>eN!)XVuct zWa`wZt?9M>_3PKaa#V2z)VndY{Sy+wP)Mq=?6L!^lSE`WBqW3dpE$v^SQjr>FNmxW zT$jE3MO0uY`MYo5zVCqrjtQ%_Kxp#RQ%?bHo_>CQFFR@vcsxNUCTVIW90~V{Yt}S) zu7g1%1yD(_MO-nC}Un)iSOjtQ#(==>iMVbW#)=N79$6Lfj1%IFQQ#jz*(lLBwZ5zy6wO%;YyA*X z88c=)1T63%qE%JOTtJpQJw4md`_A-4oKc!cTeIXxOgd{wWnPL&VPfrhcz8Gq3JPKx z6j?uVgoVtVYw{rytF#HESvg)<4*qlc^l6m1>jo@XOQ3HP>CvM{d(f-*l`B`4I}$+V zifm#bojs*$UBq$_Nd`HNkB=9C1B(+#USIA~l*>dLHHLguByAQ$34*REYNELgB$Ixs-)%W49B+l}?^-5Wfp z9W@_vV>(A#p_K2xuT?s#+1cy#bQDWMelNq*g)+7nSSTL79o!;*1Ls090-PJJ3Go}q zaDWCbN(2o!P7t}OP_nc^&pacqu#C1a$Kpf>ZNgFxE9vj=A4Ox2-JIKTEYzsjxUj*> zm@#AS1P>xpyV{WeLcL^D1*d8?=SF}D4i2vJXz=6p{{69!9K!WK7NN##!g>&JYf?JaO)@^HZ@gfVQha+V6Y|9o$5+uYFh28;s zvP#|8jvYI;VC+YZ9Eoc6t~CXalm~VE>Z`9N&{JCK2sef*Tc*}`A*-;a9Rg5DpMFQS zU`X*(r&!2_4NUjphc(&`3RWlf(@$9u0o#Ebt%5IQE+Ym00>-{J{O2|gLI#?LhsRxT zV;LD47aU=QQ2RJUCQTE;#F}Ki2m3@|`y^{4x!18gN8(BZm1DF@ zB52$=rhVlVrkyi~1<#+)Gz%86kTq*qsIM>69X!Z%Cr&UOSrDP&{ocJSbo+J|f*1{1 z2Mv7(-_w5Z0Sl#b2U)SO(d^a@Q>U`@pMS2Cx{zXRXao<}ty{MoV?KQNZ~(}yHty@T z8|&Y{KNxsNV&X%{_opctR74`XT+t~sRLZav_dq9rpFkd0aV36!mEf^v<2P(^iS571 z5@@g$xU}JKh?LuL>r32k)Z1?hlg5I@sk=EpVNB_v%N#Lc#Df^~K7IOl)Yg4fabrV= z4n=L4?xZHRaK&QxxiO?|U^RuQPPSAx2theKJzA}{DtO>lkUvDo-kN5@_@X2m(M5aE96b+;}8Uk|&28M-&Rg3Egjnq&Gk!4{n$;`TR{1&zj zh#~TfkO3?o+$0o`z<f^%Xw=d>L=SPr=+Vduo! zt|fC^6RZ#QsbTi)*@!%Mx$nOFY$ngf00I(QcJJODi_3Zw&)8R5=~8U6La0iCRg|Wz zgm&>_rXh)KRm=reho1Bx3jsO|J`%8`b85(^!4v_CJHVA9aEEnk$pqdiZS!U!F9}Ks zo5LXF0B#1$QriLv&R~5I6y3dh_i>CZA7e3j%q{>D$+UZyE?r=Sy1o1EyA!gqvI=dn zLYP&@GGSTXLr;3}%P+H_fB=hem|&9Fpk|?ZQZ!@6 zj42q~&Ye5M`ncOlo=sVyR`=d}Z)YIY-{1fHqDp^Wb1Ov1MO#%nW zR=SfXS;(SAEEpS*Siy4;V?hleb=7NC>w`JM?c29Qd68D?jZIZr*^4~ON_vjaZo}wE zOiWC{h14WE4O1`d_h2SjN;_>D)BXG3OpAcsnl(a0@J_dC71JT$XGI|?X$cin@v6^h zAWFr3AV>lN0@8;J8G_x_Be+!;4t}Avaw1Wb zzcp4;^mzaj)xS^*S9MwPQD;m5uUxtEbBv|Z)~5;=!W4gt7A?Bc>wP4n&vI9$x5jmW zjD}iBnzE(A;#?=|g<@6pKmpa2>(b-FKXm9&AdO*PK9=p2;65f5nU9Z;H(~~FzWL^) zq@<)GLuClwvLzz3i5d4B$B(m^4?Yk|^IKC=&Rn8Z$YYNQl05Uk0aluoRWB8hqo*v0 zOBPR?HVwqNr&7wxoE1Whpbde@hhF>a*|TRE!r(QXXce|2h>aDjMbd^1!V(o&Clq7C zdN8cS5Zc2JR~Ba)J65nTx=E9S&!QqbWcRJGFo)#JG!W}kj#%0^-+c2Qj2$1tHhfH* z+~I&jv_e81p|)+?q6B`w^XJc>yj3}f23u$3VMS184(S_EPI;uduzwh~eMG!Q-2&6L z`L4Zt_xfYZ_}F>!v2=*_sYa;Vw{H*g*K6Rwfg>X$Ba=%iS4bMvL32LvU<&B;>C>rw z`}TbpV@6}w$q~fa5}|G@y0S%!7R@4QUv4!YQk%ng4`f$pXlVJ|xpV)Du_E3B>(kZ| z#MzQ7!=&3?UAlDX4YRm&=gu!NZ^idotxyNgDQ4O$qN1X(%JdtI5g)6&Bon4Cchu2w z+m#^!sZEf=A4uX&$)@iB07HqvaZGIQrQ;N8A`d)!bzy1^kND}O;Q3Sa4JP(f6CmxZ)q6m{6T zb?biW+o9-V-q+qna=qSw#}tUXco3^E-RR&A8#b)MN&r;BPAFsdOv8+oH=j!`}-#%YK;sz)umbOvdY0FPn#zXR}~PpNNva>Cr&nF59OXa_fPjF4FTVIX7(2KXKy3$>=Ne8Tt-=$osMd@7pGK zH1I>3(b$F)J|8bHFPLEfY3Pa-EBsu0WE46Jh(vM?^V@}Jah@TKb0S z+9Vc74<8O;aq;5C{EZto?(EW~%OmI$^bPgVedsIRXChf{RN#^0MJ@O&vA2;m8Zu$R zglBi|+<7P@B%~yrmqg(fshlrrtkg)46XpO&6gE9C78Iiso>T|$WcZR6;lwnmra{C?RfIZC#P`W48}ck zch#NuMO)SC)QlUo;_3n>yL;=_t@}cGh!%-Dc=WPm%a)RA3ncd@SS8eP=4Po};^yiE zP)2|;h@ipB2v{4fR$GW`U%GVZ=N&q97$aI7+#~Ll1IL^9L952hb!uykT)C-k%@d)p ze8`7eB+<1Eq4vO(DO0AcUAuPUv17+{p`oG0P;dFXn1V5&q@c2uLrKrU&slB^7+6PG z8i3~V<;%smMqKOU$&+8gwext6;HtRA@!|LC$?v_HM4qeQu>+6fRAf9aNs}yu8DZuD|C&+__ab+4aPlijw4ze5jf3N;MgBX&3WhE$@5%5 zEPIm4?}sPY-Q8WZN*JK~2MieS_`G@Z-dn$Z{iZ#8_8`yy8o9eX3@lt86c2y}w<7MB zVd;QH!Dq3665mC2FIX48kxCwZ1HXmeoI7{!-~0FP{{*)*xTYbvMqDed8Q0G5p(DR1 z)#5k^NbWpr--aubyZKyDFA+=<%XAx1)J8n|=%dfhnKNhh^5x4{Y}vA9yRWbBkwb?L zT{v~>RK(e{XRlwla3PswbS8n}Cb^uPu&}TkolbX?fR}mc(xqg7fB!f?Kfj2>hYw%) z>8GEL;2bMftXPh7;@qyTt|R$x4duTrSQ-gvdhu($n_qidelPASa2yMyrd*MTR;jH7 zOqd($L9U?>kozjo*t3oH_uKu~m|S=`JpRJMkwTfVzjPIRR7z zh#`>65^E)GLyTIL|L0a^Fh7R?7MvTu!2#2a|F##u7Aa_qt&IvCCk0bW9(ZfV$!P~p zR6BEv(47x9xJWMyJ}d@+z*|4w`f_me;Q;Byzte-CqbonBH~$S!{+k~B8dT8KQ!q8< zgXYG`X$u}xY0deQ2&}ufYw5rVtrrJ_w0ZIW>%c#Imjozn_&Hng-)P2v)78j5si3Ln zfRchr1eQA=@aBB*TX3r(*TnyC&cEX>0g4nXDqtE3sEonY#0Y58CifZR^EA5Se>2i^ UL+aP$zyJUM07*qoM6N<$g5v{O=>Px# literal 7718 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 diff --git a/wear/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/wear/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..9970812ebb7d05ffeb7d583657ffc67ec1658497 GIT binary patch literal 9110 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 UL+aP$zyJUM07*qoM6N<$g5v{O=>Px# literal 0 HcmV?d00001 diff --git a/wear/src/main/res/values/strings.xml b/wear/src/main/res/values/strings.xml index e0468e0514..b7c1ab19d5 100644 --- a/wear/src/main/res/values/strings.xml +++ b/wear/src/main/res/values/strings.xml @@ -1,5 +1,5 @@ - Wear + Rocket.Chat - Conectar"' + Conectar Usa este nombre de usuario Toca en este botón para iniciar sesión o crear una cuenta Términos de Servicio @@ -34,6 +34,9 @@ Ausente Ocupado Invisible + // TODO: Add proper translation. + Save to gallery + @@ -158,7 +161,7 @@ Starring is not allowed - Lista de miembros + Miembros Mensajes fijados @@ -171,6 +174,13 @@ No favorite messages All the favorite messages\nappear here + + + Files + Files (%d) + No files + All the files appear here + Tamaño del archivo (%1$d bytes) excedió el tamaño máximo de carga de %2$d bytes diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 0fb7189f32..c26b34709f 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -34,6 +34,8 @@ Loin Occupé Invisible + // TODO: Add proper translation. + Save to gallery @@ -159,7 +161,7 @@ Starring is not allowed - Liste des membres + Membres Messages épinglés @@ -172,6 +174,13 @@ No favorite messages All the favorite messages\nappear here + + + Files + Files (%d) + No files + All the files appear here + Taille du fichier (%1$d bytes) dépassé la taille de téléchargement maximale de %2$d bytes diff --git a/app/src/main/res/values-hi-rIN/strings.xml b/app/src/main/res/values-hi-rIN/strings.xml index a107cd5fb1..a26bb708cf 100644 --- a/app/src/main/res/values-hi-rIN/strings.xml +++ b/app/src/main/res/values-hi-rIN/strings.xml @@ -35,6 +35,8 @@ दूर व्यस्त अदृश्य + // TODO: Add proper translation. + Save to gallery @@ -160,7 +162,7 @@ Starring is not allowed - सदस्यों की सूची + सदस्य पिन किए गए संदेश @@ -173,6 +175,13 @@ No favorite messages All the favorite messages\nappear here + + + Files + Files (%d) + No files + All the files appear here + फ़ाइल का आकार %1$d बाइट्स ने %2$d बाइट्स के अधिकतम अपलोड आकार को पार कर लिया है diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 0256faa6c4..0abef6ea8c 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -34,6 +34,7 @@ Ausente Ocupado Invisível + Salvar na galeria @@ -148,7 +149,7 @@ Favoritar não permitido - Lista de Membros + Membros Mensagens Pinadas @@ -156,11 +157,16 @@ Todas as mensagens pinadas\naparecerão aqui - Messagens Favoritas Nenhuma messagem favorita Todas as mensagens favoritas\naparecerão aqui + + Arquivos + Arquivos (%d) + Nenhum arquivo + Todos os arquivos aparecerão aqui + Tamanho de arquivo (%1$d bytes) excedeu tamanho máximo de upload (%2$d bytes) diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 8fd3487287..a3bac4be4a 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -3,13 +3,13 @@ #FF303030 - #ff212121 + #FF212121 #FF1976D2 #DE000000 - #787878 - #c1c1c1 + #FF787878 + #FFC1C1C1 #2FE1A8 @@ -17,7 +17,16 @@ #FDD236 #d9d9d9 - #FFFFFF + + #FFFFFFFF + #FF000000 + #FFFF0000 + + #FFa0a0a0 + #FF727272 + #FFf1f1f1 + + @color/colorWhite #9FA2A8 @@ -29,13 +38,6 @@ #4D000000 - #FFFFFFFF - #FF000000 - #FFFF0000 - #FFa0a0a0 - #FF727272 - #FFf1f1f1 - #70F1F1F1 #FF767676 @@ -43,7 +45,7 @@ #A0A0A0 - @android:color/white + @color/colorWhite #AFADAF diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2431348828..3551001c3b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -35,6 +35,7 @@ Away Busy Invisible + Save to gallery @@ -149,7 +150,7 @@ Starring is not allowed - Members List + Members Pinned Messages @@ -161,6 +162,12 @@ No favorite messages All the favorite messages\nappear here + + Files + Files (%d) + No files + All the files appear here + File size %1$d bytes exceeded max upload size of %2$d bytes diff --git a/dependencies.gradle b/dependencies.gradle index 942e23a34c..cf69557530 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -24,7 +24,7 @@ ext { timber : '4.7.0', threeTenABP : '1.0.5', rxBinding : '2.0.0', - fresco : '1.8.1', + fresco : '1.9.0', kotshi : '1.0.2', frescoImageViewer : '0.5.1', markwon : '1.0.3', diff --git a/player/src/main/java/chat/rocket/android/player/PlayerActivity.kt b/player/src/main/java/chat/rocket/android/player/PlayerActivity.kt index 525c17d58f..87ab95545d 100644 --- a/player/src/main/java/chat/rocket/android/player/PlayerActivity.kt +++ b/player/src/main/java/chat/rocket/android/player/PlayerActivity.kt @@ -5,7 +5,6 @@ import android.content.Intent import android.net.Uri import android.os.Bundle import android.support.v7.app.AppCompatActivity -import android.util.Log import android.view.View import com.google.android.exoplayer2.DefaultLoadControl import com.google.android.exoplayer2.DefaultRenderersFactory @@ -72,7 +71,6 @@ class PlayerActivity : AppCompatActivity() { } val uri = Uri.parse(videoUrl) val mediaSource = buildMediaSource(uri) - Log.d("PlayerActivity", "Player with: " + videoUrl) player.prepare(mediaSource, true, false) } @@ -94,7 +92,7 @@ class PlayerActivity : AppCompatActivity() { } companion object { - const private val URL_KEY = "URL_KEY" + private const val URL_KEY = "URL_KEY" fun play(context: Context, url: String) { context.startActivity(Intent(context, PlayerActivity::class.java).apply { putExtra(URL_KEY, url) From 6b599ca07a99db63a6be899bbf289dc3f34b466d Mon Sep 17 00:00:00 2001 From: Leonardo Aramaki Date: Thu, 24 May 2018 23:23:29 -0300 Subject: [PATCH 21/45] Farewell GCM --- app/build.gradle | 10 ++++++- app/src/main/AndroidManifest.xml | 28 +++--------------- .../android/dagger/module/ServiceBuilder.kt | 8 ++--- .../rocket/android/main/ui/MainActivity.kt | 13 ++++---- .../android/push/FirebaseMessagingService.kt | 24 +++++++++++++++ .../android/push/FirebaseTokenService.kt | 19 +++++------- .../rocket/android/push/GcmListenerService.kt | 23 -------------- ...kt => FirebaseMessagingServiceProvider.kt} | 6 ++-- app/src/main/res/values/api_keys.xml | 5 ---- debug.keystore | Bin 0 -> 2252 bytes dependencies.gradle | 3 +- 11 files changed, 60 insertions(+), 79 deletions(-) create mode 100644 app/src/main/java/chat/rocket/android/push/FirebaseMessagingService.kt delete mode 100644 app/src/main/java/chat/rocket/android/push/GcmListenerService.kt rename app/src/main/java/chat/rocket/android/push/di/{GcmListenerServiceProvider.kt => FirebaseMessagingServiceProvider.kt} (53%) delete mode 100644 app/src/main/res/values/api_keys.xml create mode 100644 debug.keystore diff --git a/app/build.gradle b/app/build.gradle index bf152facd5..26ec10e234 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -26,6 +26,13 @@ android { keyAlias System.getenv("KEY_ALIAS") keyPassword System.getenv("KEY_PASSWORD") } + + debug { + storeFile project.rootProject.file('debug.keystore').getCanonicalFile() + storePassword "android" + keyAlias "androiddebugkey" + keyPassword "android" + } } buildTypes { @@ -40,6 +47,7 @@ android { debug { buildConfigField "String", "REQUIRED_SERVER_VERSION", '"0.62.0"' buildConfigField "String", "RECOMMENDED_SERVER_VERSION", '"0.63.0"' + signingConfig signingConfigs.debug applicationIdSuffix ".dev" } } @@ -73,7 +81,7 @@ dependencies { kapt libraries.daggerProcessor kapt libraries.daggerAndroidApt - implementation libraries.playServicesGcm + implementation libraries.fcm implementation libraries.room kapt libraries.roomProcessor diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 11306f2ce1..976221a998 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,17 +3,9 @@ package="chat.rocket.android"> - - - - - - - - - - - - - - - - + + - + + ) { diff --git a/app/src/main/java/chat/rocket/android/push/FirebaseMessagingService.kt b/app/src/main/java/chat/rocket/android/push/FirebaseMessagingService.kt new file mode 100644 index 0000000000..ef4b7cb942 --- /dev/null +++ b/app/src/main/java/chat/rocket/android/push/FirebaseMessagingService.kt @@ -0,0 +1,24 @@ +package chat.rocket.android.push + +import androidx.core.os.bundleOf +import com.google.firebase.messaging.FirebaseMessagingService +import com.google.firebase.messaging.RemoteMessage +import dagger.android.AndroidInjection +import javax.inject.Inject + +class FirebaseMessagingService : FirebaseMessagingService() { + + @Inject + lateinit var pushManager: PushManager + + override fun onCreate() { + super.onCreate() + AndroidInjection.inject(this) + } + + override fun onMessageReceived(message: RemoteMessage) { + message.data?.let { + pushManager.handle(bundleOf(*(it.map { Pair(it.key, it.value) }).toTypedArray())) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/chat/rocket/android/push/FirebaseTokenService.kt b/app/src/main/java/chat/rocket/android/push/FirebaseTokenService.kt index 247bd0ef4a..3c57cc15f6 100644 --- a/app/src/main/java/chat/rocket/android/push/FirebaseTokenService.kt +++ b/app/src/main/java/chat/rocket/android/push/FirebaseTokenService.kt @@ -1,14 +1,12 @@ package chat.rocket.android.push -import chat.rocket.android.R import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.util.retryIO import chat.rocket.common.RocketChatException import chat.rocket.core.internal.rest.registerPushToken -import com.google.android.gms.gcm.GoogleCloudMessaging -import com.google.android.gms.iid.InstanceID +import com.google.firebase.iid.FirebaseInstanceId import com.google.firebase.iid.FirebaseInstanceIdService import dagger.android.AndroidInjection import kotlinx.coroutines.experimental.launch @@ -31,21 +29,18 @@ class FirebaseTokenService : FirebaseInstanceIdService() { } override fun onTokenRefresh() { - //TODO: We need to use the Cordova Project gcm_sender_id since it's the one configured on RC - // default push gateway. We should register this project's own project sender id into it. try { - val gcmToken = InstanceID.getInstance(this) - .getToken(getString(R.string.gcm_sender_id), GoogleCloudMessaging.INSTANCE_ID_SCOPE, null) + val fcmToken = FirebaseInstanceId.getInstance().token val currentServer = getCurrentServerInteractor.get() val client = currentServer?.let { factory.create(currentServer) } - gcmToken?.let { - localRepository.save(LocalRepository.KEY_PUSH_TOKEN, gcmToken) + fcmToken?.let { + localRepository.save(LocalRepository.KEY_PUSH_TOKEN, fcmToken) client?.let { launch { try { - Timber.d("Registering push token: $gcmToken for ${client.url}") - retryIO("register push token") { client.registerPushToken(gcmToken) } + Timber.d("Registering push token: $fcmToken for ${client.url}") + retryIO("register push token") { client.registerPushToken(fcmToken) } } catch (ex: RocketChatException) { Timber.e(ex, "Error registering push token") } @@ -53,7 +48,7 @@ class FirebaseTokenService : FirebaseInstanceIdService() { } } } catch (ex: Exception) { - Timber.d(ex, "Error refreshing Firebase TOKEN") + Timber.e(ex, "Error refreshing Firebase TOKEN") } } } \ No newline at end of file diff --git a/app/src/main/java/chat/rocket/android/push/GcmListenerService.kt b/app/src/main/java/chat/rocket/android/push/GcmListenerService.kt deleted file mode 100644 index ea910521fc..0000000000 --- a/app/src/main/java/chat/rocket/android/push/GcmListenerService.kt +++ /dev/null @@ -1,23 +0,0 @@ -package chat.rocket.android.push - -import android.os.Bundle -import com.google.android.gms.gcm.GcmListenerService -import dagger.android.AndroidInjection -import javax.inject.Inject - -class GcmListenerService : GcmListenerService() { - - @Inject - lateinit var pushManager: PushManager - - override fun onCreate() { - super.onCreate() - AndroidInjection.inject(this) - } - - override fun onMessageReceived(from: String?, data: Bundle?) { - data?.let { - pushManager.handle(data) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/chat/rocket/android/push/di/GcmListenerServiceProvider.kt b/app/src/main/java/chat/rocket/android/push/di/FirebaseMessagingServiceProvider.kt similarity index 53% rename from app/src/main/java/chat/rocket/android/push/di/GcmListenerServiceProvider.kt rename to app/src/main/java/chat/rocket/android/push/di/FirebaseMessagingServiceProvider.kt index 52073b343f..aa3d0a0049 100644 --- a/app/src/main/java/chat/rocket/android/push/di/GcmListenerServiceProvider.kt +++ b/app/src/main/java/chat/rocket/android/push/di/FirebaseMessagingServiceProvider.kt @@ -1,11 +1,11 @@ package chat.rocket.android.push.di import chat.rocket.android.dagger.module.AppModule -import chat.rocket.android.push.GcmListenerService +import chat.rocket.android.push.FirebaseMessagingService import dagger.Module import dagger.android.ContributesAndroidInjector -@Module abstract class GcmListenerServiceProvider { +@Module abstract class FirebaseMessagingServiceProvider { @ContributesAndroidInjector(modules = [AppModule::class]) - abstract fun provideGcmListenerService(): GcmListenerService + abstract fun provideFirebaseMessagingService(): FirebaseMessagingService } \ No newline at end of file diff --git a/app/src/main/res/values/api_keys.xml b/app/src/main/res/values/api_keys.xml deleted file mode 100644 index 99ca89644b..0000000000 --- a/app/src/main/res/values/api_keys.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - 673693445664 - \ No newline at end of file diff --git a/debug.keystore b/debug.keystore new file mode 100644 index 0000000000000000000000000000000000000000..30d607ced9d6ef1066c6a612832198223cd65847 GIT binary patch literal 2252 zcmchYc{J1uAI9f5V`h+j8%xH{y@VOAP{JDKYxACbDr}&2df9GAP@+AB;arHdj|W2 zhXnZe_4_+FfBbd|iHKgqd9CG!7bcaCQYz9D(+`RJa;E>Wc? z`&C&Xl->J5SPxx{DQg535fFlp4~;^ZE`iS#=9k_#7O$Uuaz~QQG%FB)DaAi7KU-hB z+xG3%A6I)hRRSwqrJU!P5!#I{QT`_%s-c$I2rpx8>niV$i&6Km*SWPM$ak(MyUGR3 zHAKQ^4ZM$+&jw_M{d*yL5Qpm;5V{xNXXqE!udMbNDp3ExV$9m)ypaRD^^=b6i0CwB zr&P*?9prRos1Au_dSUe}8KL&|RENqvT>K49)9I!yOq1wH`h>I%?N<8SSE=g9f1W=^ z$5Zl@h6l4*1pTj{voV`3&1npAxRM}P@JfPFNhGoQR~qHUhOTqsQjGn9xWt@UkD!^W zM8XBT`T1;4CFN)2gC31p8s{3Dopqp(n+`D};aT&%|@M z!LvchQ)WKd*u9Jdwt$Qf3@ni2lkHO2uSgbfQVL#yZ$Tl#B|~zb#S6NS$>wbbV)v~`oUFJlKd~W z#teMQsoUE9*%8C`FbX66W%6)eeNB)dNw!iGLtLVs*Q`@icxSJGKzkh`np%IoNE)w6 z$aAm^vYt0b%9W9k*F2rQ?Ym|lxlB;! z4@VE0_7%A~Ya!ldTrAn?W!_4pjya2x7K=J)jVt(38V1Sa9%vwhc3Zxr6xLaf?bi)a zkKOF~Ive0F@IKFNC~PbC+0zPa?epm#qe+*wIf^yXH&wMd>W92f#xg!Sq)WmFNbYADcfaS8`2GAY2QYKm+>;}Px=!) z!z!w{2dy4ii0+UCIK?RVsM-r1-K`8W4cPrJldJK;Z%QMP#YhJ6^Hir+@AP%{OPyRY zIks{lohFcJ=r3_%j8)6`At9$6W^!@`+umhE)8&r|G{G{gDLEJ7;&SG<_gc+|dm>x- zgd130!F$24%T3y4oZ~hkO;_Bepru1&xBD0J7MjSJsR{uj5TpcMLN<1mx5cwALw z0JEhu#c2?}M7_U&FS1T#rf>55c}%HOC$nw(L;?`Gzq{(43m?YJSOM|(5SCs&J#(%s zv3|XPT?H-RL4IsxhwFV)dS5N$@~wp}1OFw*IFkO@yhf)e59e}2+_m851oSjjFn2BD zIp@v%Sjhr}Upuj&&%Sg0OS6pJt?fGvuT~SoP`JZ6#`_ZTm22 zLSo??-*Bqs_6_R+5hOaz`=ZIRjH#ST^=9!zKXx;SYNh`6GVOlMAM~MI0^{{T_YY`X zfO}eUa)l^Z?bPvL7s_(a{Ag%X_!Dz<#5RyTm~!>UgvBO$&?79R~~r(-H=ch z4IPv|2n4x_!b74_c(6w)1Pp+|9ERzV;wWAimuiZ2$`4)uI0glwg3tm-jR|q&fCp zO@nrQkBd#Lv?QWzMkNatUs+gltCrSyP%@6W5>jF9Q*JlaXPTVcp+7<_$n1^6ikX{0 zVC9LVogWG-i~HGW>7${#=q}|}ij|+qE+HO%$<$D$g;Pv2FLv^Y%XfOD+j84B;dJ2; zu8m$Y8p9*6%aR$}VAV|~kZoeuBRckrb3Go`gvrVxyNJR?;>yaYCv2tnckx1EDb#1P zsUjCuk_H~TJ)P6_{LTf203e`D4kd$T`dSC8h|;BHg;lGRnM^| z|7qe;rjASmK$$!rE1h5>QtYqzx&yCxasOBLI#~6a47)`GobAGaQ=2|0Vim!QdnDyH z<;~sTWW95tp6EJ~TA=L6RM?!zT`ri4g3F1?VMIUf@Q|Zc1!S4pBg=L0URNbT5xgX_ zn^)_B@fmVx)!-LKo{{h20WLks+c8-zJp#?u4K0w38#I~L)ixxrBP&-UQr=Y!?Qg`o zH%|Qy7#rn4@)SM3?Uv6x)Z{mHUQW|m0+~i(NPBVPl38<{bL5dq&vdGts&}(xot0gy z#qI!Xgn3Wtgl6BTEpddUM?^F0Pfh^E@X%krj7gUVo1uTF7vI8OAF{1d57@) b8oRZ37X& literal 0 HcmV?d00001 diff --git a/dependencies.gradle b/dependencies.gradle index 942e23a34c..dede793aa2 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -15,6 +15,7 @@ ext { dagger : '2.14.1', exoPlayer : '2.6.0', playServices : '11.8.0', + firebase : '15.0.0', room : '1.0.0', lifecycle : '1.1.1', rxKotlin : '2.2.0', @@ -58,7 +59,7 @@ ext { daggerSupport : "com.google.dagger:dagger-android-support:${versions.dagger}", daggerProcessor : "com.google.dagger:dagger-compiler:${versions.dagger}", daggerAndroidApt : "com.google.dagger:dagger-android-processor:${versions.dagger}", - playServicesGcm : "com.google.android.gms:play-services-gcm:${versions.playServices}", + fcm : "com.google.firebase:firebase-messaging:${versions.firebase}", exoPlayer : "com.google.android.exoplayer:exoplayer:${versions.exoPlayer}", room : "android.arch.persistence.room:runtime:${versions.room}", From 0815edac3432c04e70d2b61993c079a5943ac1fa Mon Sep 17 00:00:00 2001 From: Leonardo Aramaki Date: Fri, 25 May 2018 10:30:34 -0300 Subject: [PATCH 22/45] Update google-services.json --- app/google-services.json | 260 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 247 insertions(+), 13 deletions(-) diff --git a/app/google-services.json b/app/google-services.json index 7a936ea18e..a818e52b98 100644 --- a/app/google-services.json +++ b/app/google-services.json @@ -1,27 +1,51 @@ { "project_info": { - "project_number": "1020987621558", - "firebase_url": "https://rocketchatnative.firebaseio.com", - "project_id": "rocketchatnative", - "storage_bucket": "rocketchatnative.appspot.com" + "project_number": "673693445664", + "firebase_url": "https://rocketchat-9e9be.firebaseio.com", + "project_id": "rocketchat-9e9be", + "storage_bucket": "rocketchat-9e9be.appspot.com" }, "client": [ { "client_info": { - "mobilesdk_app_id": "1:1020987621558:android:16da2e50aff9f0c9", + "mobilesdk_app_id": "1:673693445664:android:6ef4638e500ec958", "android_client_info": { - "package_name": "chat.rocket.android" + "package_name": "RocketChat" } }, "oauth_client": [ { - "client_id": "1020987621558-trk61fjrahho0ujtjap095p1jmi48pfq.apps.googleusercontent.com", + "client_id": "673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "673693445664-vtcvuvso7k88gpodlpshod55g1ehs03s.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "673693445664-sf7lqf11kk6vplg9ljh7pi491gvb08f3.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "673693445664-fb71j3aqmafmm20jkj8gvpusv04fdnq8.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "673693445664-ejd5lkbsdjoo5onc052dotsjacdh1kcc.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "673693445664-ssfpeb0are3svvg0etbttog789s0n3ua.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com", "client_type": 3 } ], "api_key": [ { - "current_key": "AIzaSyDc7VYUdU6kRkoRTToiCn1rh-W0wJvhLWk" + "current_key": "AIzaSyDIkZj1TRz8TmhnMswDwVY5OnWuzFK3rxg" } ], "services": { @@ -39,20 +63,218 @@ }, { "client_info": { - "mobilesdk_app_id": "1:1020987621558:android:1551054db195f705", + "mobilesdk_app_id": "1:673693445664:android:16da2e50aff9f0c9", + "android_client_info": { + "package_name": "chat.rocket.android" + } + }, + "oauth_client": [ + { + "client_id": "673693445664-k0mvosdjoe5dbvqce3b377ckabb5dgu8.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "chat.rocket.android", + "certificate_hash": "33fa8582794176014a59054192e261bfad0e5273" + } + }, + { + "client_id": "673693445664-hrjftksij02vqtd467ln2cubvu48ft5j.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "chat.rocket.android", + "certificate_hash": "41cf750df786a6d9da712a98a629d0c8391876d6" + } + }, + { + "client_id": "673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "673693445664-vtcvuvso7k88gpodlpshod55g1ehs03s.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "673693445664-sf7lqf11kk6vplg9ljh7pi491gvb08f3.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "673693445664-fb71j3aqmafmm20jkj8gvpusv04fdnq8.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "673693445664-ejd5lkbsdjoo5onc052dotsjacdh1kcc.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "673693445664-ssfpeb0are3svvg0etbttog789s0n3ua.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyDIkZj1TRz8TmhnMswDwVY5OnWuzFK3rxg" + } + ], + "services": { + "analytics_service": { + "status": 1 + }, + "appinvite_service": { + "status": 2, + "other_platform_oauth_client": [ + { + "client_id": "673693445664-pa3k48sg81r89rn65e9rlnu4gpmm5vem.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "com.konecty.rocket.chat" + } + }, + { + "client_id": "673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com", + "client_type": 3 + } + ] + }, + "ads_service": { + "status": 2 + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:673693445664:android:1551054db195f705", "android_client_info": { "package_name": "chat.rocket.android.dev" } }, "oauth_client": [ { - "client_id": "1020987621558-trk61fjrahho0ujtjap095p1jmi48pfq.apps.googleusercontent.com", + "client_id": "673693445664-t5aeku0oie010npd40a0tgn27c418vk7.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "chat.rocket.android.dev", + "certificate_hash": "41cf750df786a6d9da712a98a629d0c8391876d6" + } + }, + { + "client_id": "673693445664-iml14ln4vccuu7liclrpt2k671fkjs38.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "chat.rocket.android.dev", + "certificate_hash": "33fa8582794176014a59054192e261bfad0e5273" + } + }, + { + "client_id": "673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "673693445664-vtcvuvso7k88gpodlpshod55g1ehs03s.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "673693445664-sf7lqf11kk6vplg9ljh7pi491gvb08f3.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "673693445664-fb71j3aqmafmm20jkj8gvpusv04fdnq8.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "673693445664-ejd5lkbsdjoo5onc052dotsjacdh1kcc.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "673693445664-ssfpeb0are3svvg0etbttog789s0n3ua.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyDIkZj1TRz8TmhnMswDwVY5OnWuzFK3rxg" + } + ], + "services": { + "analytics_service": { + "status": 1 + }, + "appinvite_service": { + "status": 2, + "other_platform_oauth_client": [ + { + "client_id": "673693445664-pa3k48sg81r89rn65e9rlnu4gpmm5vem.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "com.konecty.rocket.chat" + } + }, + { + "client_id": "673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com", + "client_type": 3 + } + ] + }, + "ads_service": { + "status": 2 + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:673693445664:android:64932c99863e2838", + "android_client_info": { + "package_name": "com.konecty.rocket.chat" + } + }, + "oauth_client": [ + { + "client_id": "673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "673693445664-vtcvuvso7k88gpodlpshod55g1ehs03s.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "673693445664-sf7lqf11kk6vplg9ljh7pi491gvb08f3.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "673693445664-fb71j3aqmafmm20jkj8gvpusv04fdnq8.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "673693445664-ejd5lkbsdjoo5onc052dotsjacdh1kcc.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "673693445664-3ajben08beuco6eout3kpod2gbbm8fij.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "com.konecty.rocket.chat", + "certificate_hash": "cd5806ba3f0141d0f2e47acfe64a485f575108ab" + } + }, + { + "client_id": "673693445664-ssfpeb0are3svvg0etbttog789s0n3ua.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com", "client_type": 3 } ], "api_key": [ { - "current_key": "AIzaSyDc7VYUdU6kRkoRTToiCn1rh-W0wJvhLWk" + "current_key": "AIzaSyDIkZj1TRz8TmhnMswDwVY5OnWuzFK3rxg" } ], "services": { @@ -60,8 +282,20 @@ "status": 1 }, "appinvite_service": { - "status": 1, - "other_platform_oauth_client": [] + "status": 2, + "other_platform_oauth_client": [ + { + "client_id": "673693445664-pa3k48sg81r89rn65e9rlnu4gpmm5vem.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "com.konecty.rocket.chat" + } + }, + { + "client_id": "673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com", + "client_type": 3 + } + ] }, "ads_service": { "status": 2 From 0a26da2b59c3e7e545ebfcc247252f0f180c0724 Mon Sep 17 00:00:00 2001 From: aniket Date: Fri, 25 May 2018 20:47:52 +0530 Subject: [PATCH 23/45] adds build tools version to build.gradle --- wear/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/wear/build.gradle b/wear/build.gradle index c814dfacc5..5a8729683e 100644 --- a/wear/build.gradle +++ b/wear/build.gradle @@ -3,6 +3,7 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' android { compileSdkVersion 26 + buildToolsVersion versions.buildTools defaultConfig { applicationId "chat.rocket.android.wear" From 90cf5125108915d46b7d1f3942a989c91407c24f Mon Sep 17 00:00:00 2001 From: Filipe de Lima Brito Date: Fri, 25 May 2018 12:19:40 -0300 Subject: [PATCH 24/45] Shows only the chat icon on the chat room view. --- app/build.gradle | 4 +- .../android/chatroom/ui/ChatRoomActivity.kt | 86 ++++++++----------- .../android/chatroom/ui/ChatRoomFragment.kt | 61 ++++--------- .../android/chatrooms/ui/ChatRoomsAdapter.kt | 4 +- .../ui/FavoriteMessagesFragment.kt | 5 +- .../rocket/android/files/ui/FilesFragment.kt | 7 +- .../android/members/ui/MembersFragment.kt | 7 +- .../ui/PinnedMessagesFragment.kt | 5 +- app/src/main/res/drawable/ic_hashtag_12dp.xml | 30 ------- .../res/drawable/ic_hashtag_black_12dp.xml | 22 +++++ app/src/main/res/drawable/ic_lock_12_dp.xml | 18 ---- .../main/res/drawable/ic_lock_black_12_dp.xml | 16 ++++ app/src/main/res/drawable/ic_room_channel.xml | 9 -- app/src/main/res/drawable/ic_room_dm.xml | 9 -- app/src/main/res/drawable/ic_room_lock.xml | 9 -- app/src/main/res/layout/app_bar_chat_room.xml | 36 ++------ app/src/main/res/layout/item_chat.xml | 4 +- app/src/main/res/values/dimens.xml | 2 +- 18 files changed, 125 insertions(+), 209 deletions(-) delete mode 100644 app/src/main/res/drawable/ic_hashtag_12dp.xml create mode 100644 app/src/main/res/drawable/ic_hashtag_black_12dp.xml delete mode 100644 app/src/main/res/drawable/ic_lock_12_dp.xml create mode 100644 app/src/main/res/drawable/ic_lock_black_12_dp.xml delete mode 100644 app/src/main/res/drawable/ic_room_channel.xml delete mode 100644 app/src/main/res/drawable/ic_room_dm.xml delete mode 100644 app/src/main/res/drawable/ic_room_lock.xml diff --git a/app/build.gradle b/app/build.gradle index 15a375de97..df2b2a2f6d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,8 +13,8 @@ android { applicationId "chat.rocket.android" minSdkVersion 21 targetSdkVersion versions.targetSdk - versionCode 2021 - versionName "2.2.0" + versionCode 2022 + versionName "2.3.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" multiDexEnabled true } diff --git a/app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomActivity.kt b/app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomActivity.kt index 760bfb7b0c..ce72a45ea6 100644 --- a/app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomActivity.kt +++ b/app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomActivity.kt @@ -1,10 +1,13 @@ package chat.rocket.android.chatroom.ui +import DrawableHelper import android.content.Context import android.content.Intent import android.os.Bundle import android.support.v4.app.Fragment import android.support.v7.app.AppCompatActivity +import android.text.SpannableStringBuilder +import androidx.core.view.isVisible import chat.rocket.android.R import chat.rocket.android.chatroom.presentation.ChatRoomNavigator import chat.rocket.android.server.domain.GetCurrentServerInteractor @@ -63,14 +66,6 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector { @Inject lateinit var managerFactory: ConnectionManagerFactory - private lateinit var chatRoomId: String - private lateinit var chatRoomName: String - private lateinit var chatRoomType: String - private var isChatRoomReadOnly: Boolean = false - private var isChatRoomSubscribed: Boolean = true - private var isChatRoomCreator: Boolean = false - private var chatRoomLastSeen: Long = -1L - override fun onCreate(savedInstanceState: Bundle?) { AndroidInjection.inject(this) super.onCreate(savedInstanceState) @@ -85,33 +80,33 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector { return } - chatRoomId = intent.getStringExtra(INTENT_CHAT_ROOM_ID) + val chatRoomId = intent.getStringExtra(INTENT_CHAT_ROOM_ID) requireNotNull(chatRoomId) { "no chat_room_id provided in Intent extras" } - chatRoomName = intent.getStringExtra(INTENT_CHAT_ROOM_NAME) + val chatRoomName = intent.getStringExtra(INTENT_CHAT_ROOM_NAME) requireNotNull(chatRoomName) { "no chat_room_name provided in Intent extras" } - chatRoomType = intent.getStringExtra(INTENT_CHAT_ROOM_TYPE) + val chatRoomType = intent.getStringExtra(INTENT_CHAT_ROOM_TYPE) requireNotNull(chatRoomType) { "no chat_room_type provided in Intent extras" } - isChatRoomReadOnly = intent.getBooleanExtra(INTENT_CHAT_ROOM_IS_READ_ONLY, true) - requireNotNull(isChatRoomReadOnly) { "no chat_room_is_read_only provided in Intent extras" } + val isChatRoomReadOnly = intent.getBooleanExtra(INTENT_CHAT_ROOM_IS_READ_ONLY, true) - isChatRoomCreator = intent.getBooleanExtra(INTENT_CHAT_ROOM_IS_CREATOR, false) - requireNotNull(isChatRoomCreator) { "no chat_room_is_creator provided in Intent extras" } + val isChatRoomCreator = intent.getBooleanExtra(INTENT_CHAT_ROOM_IS_CREATOR, false) - val chatRoomMessage = intent.getStringExtra(INTENT_CHAT_ROOM_MESSAGE) + val chatRoomLastSeen = intent.getLongExtra(INTENT_CHAT_ROOM_LAST_SEEN, -1) - setupToolbar() + val isChatRoomSubscribed = intent.getBooleanExtra(INTENT_CHAT_IS_SUBSCRIBED, true) - chatRoomLastSeen = intent.getLongExtra(INTENT_CHAT_ROOM_LAST_SEEN, -1) + val chatRoomMessage = intent.getStringExtra(INTENT_CHAT_ROOM_MESSAGE) - isChatRoomSubscribed = intent.getBooleanExtra(INTENT_CHAT_IS_SUBSCRIBED, true) + setupToolbar() if (supportFragmentManager.findFragmentByTag(TAG_CHAT_ROOM_FRAGMENT) == null) { addFragment(TAG_CHAT_ROOM_FRAGMENT, R.id.fragment_container) { - newInstance(chatRoomId, chatRoomName, chatRoomType, isChatRoomReadOnly, chatRoomLastSeen, - isChatRoomSubscribed, isChatRoomCreator, chatRoomMessage) + newInstance( + chatRoomId, chatRoomName, chatRoomType, isChatRoomReadOnly, chatRoomLastSeen, + isChatRoomSubscribed, isChatRoomCreator, chatRoomMessage + ) } } } @@ -128,43 +123,34 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector { setSupportActionBar(toolbar) supportActionBar?.setDisplayShowTitleEnabled(false) toolbar.setNavigationIcon(R.drawable.ic_arrow_back_white_24dp) - text_room_name.textContent = chatRoomName - - showRoomTypeIcon(true) - toolbar.setNavigationOnClickListener { finishActivity() } } - fun showRoomTypeIcon(showRoomTypeIcon: Boolean) { - if (showRoomTypeIcon) { - val roomType = roomTypeOf(chatRoomType) - val drawable = when (roomType) { - is RoomType.Channel -> { - DrawableHelper.getDrawableFromId(R.drawable.ic_room_channel, this) - } - is RoomType.PrivateGroup -> { - DrawableHelper.getDrawableFromId(R.drawable.ic_room_lock, this) - } - is RoomType.DirectMessage -> { - DrawableHelper.getDrawableFromId(R.drawable.ic_room_dm, this) - } - else -> null - } + fun showToolbarTitle(title: String) { + text_room_name.textContent = title + } - drawable?.let { - val wrappedDrawable = DrawableHelper.wrapDrawable(it) - val mutableDrawable = wrappedDrawable.mutate() - DrawableHelper.tintDrawable(mutableDrawable, this, R.color.colorWhite) - DrawableHelper.compoundDrawable(text_room_name, mutableDrawable) + fun showToolbarChatRoomIcon(chatRoomType: String) { + val drawable = when (roomTypeOf(chatRoomType)) { + is RoomType.Channel -> { + DrawableHelper.getDrawableFromId(R.drawable.ic_hashtag_black_12dp, this) } - } else { - text_room_name.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0) + is RoomType.PrivateGroup -> { + DrawableHelper.getDrawableFromId(R.drawable.ic_lock_black_12_dp, this) + } + else -> null } - } + drawable?.let { + val wrappedDrawable = DrawableHelper.wrapDrawable(it) + val mutableDrawable = wrappedDrawable.mutate() + DrawableHelper.tintDrawable(mutableDrawable, this, R.color.colorWhite) + DrawableHelper.compoundDrawable(text_room_name, mutableDrawable) + } + } - fun setupToolbarTitle(toolbarTitle: String) { - text_room_name.textContent = toolbarTitle + fun hideToolbarChatRoomIcon() { + text_room_name.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0) } private fun finishActivity() { diff --git a/app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt b/app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt index a53c367dae..14e359e517 100644 --- a/app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt +++ b/app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt @@ -13,22 +13,12 @@ import android.support.v4.app.Fragment import android.support.v7.widget.DefaultItemAnimator import android.support.v7.widget.LinearLayoutManager import android.support.v7.widget.RecyclerView -import android.view.KeyEvent -import android.view.LayoutInflater -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem -import android.view.View -import android.view.ViewGroup import android.text.SpannableStringBuilder +import android.view.* import androidx.core.text.bold import androidx.core.view.isVisible import chat.rocket.android.R -import chat.rocket.android.chatroom.adapter.ChatRoomAdapter -import chat.rocket.android.chatroom.adapter.CommandSuggestionsAdapter -import chat.rocket.android.chatroom.adapter.PEOPLE -import chat.rocket.android.chatroom.adapter.PeopleSuggestionsAdapter -import chat.rocket.android.chatroom.adapter.RoomSuggestionsAdapter +import chat.rocket.android.chatroom.adapter.* import chat.rocket.android.chatroom.presentation.ChatRoomPresenter import chat.rocket.android.chatroom.presentation.ChatRoomView import chat.rocket.android.chatroom.viewmodel.BaseViewModel @@ -39,26 +29,8 @@ import chat.rocket.android.chatroom.viewmodel.suggestion.PeopleSuggestionViewMod import chat.rocket.android.helper.EndlessRecyclerViewScrollListener import chat.rocket.android.helper.KeyboardHelper import chat.rocket.android.helper.MessageParser -import chat.rocket.android.util.extensions.asObservable -import chat.rocket.android.util.extensions.circularRevealOrUnreveal -import chat.rocket.android.util.extensions.fadeIn -import chat.rocket.android.util.extensions.fadeOut -import chat.rocket.android.util.extensions.hideKeyboard -import chat.rocket.android.util.extensions.inflate -import chat.rocket.android.util.extensions.isAtBottom -import chat.rocket.android.util.extensions.rotateBy -import chat.rocket.android.util.extensions.setVisible -import chat.rocket.android.util.extensions.showToast -import chat.rocket.android.util.extensions.textContent -import chat.rocket.android.util.extensions.ui -import chat.rocket.android.widget.emoji.ComposerEditText -import chat.rocket.android.widget.emoji.Emoji -import chat.rocket.android.widget.emoji.EmojiKeyboardListener -import chat.rocket.android.widget.emoji.EmojiKeyboardPopup -import chat.rocket.android.widget.emoji.EmojiListenerAdapter -import chat.rocket.android.widget.emoji.EmojiParser -import chat.rocket.android.widget.emoji.EmojiPickerPopup -import chat.rocket.android.widget.emoji.EmojiReactionListener +import chat.rocket.android.util.extensions.* +import chat.rocket.android.widget.emoji.* import chat.rocket.core.internal.realtime.socket.model.State import chat.rocket.core.model.ChatRoom import dagger.android.support.AndroidSupportInjection @@ -187,8 +159,9 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR setupFab() setupSuggestionsView() setupActionSnackbar() - activity?.apply { - (this as? ChatRoomActivity)?.showRoomTypeIcon(true) + (activity as ChatRoomActivity).let { + it.showToolbarTitle(chatRoomName) + it.showToolbarChatRoomIcon(chatRoomType) } } @@ -292,7 +265,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR } } - override fun onRoomUpdated(userCanPost: Boolean, channelIsBroadcast: Boolean, userCanMod: Boolean) { + override fun onRoomUpdated( + userCanPost: Boolean, + channelIsBroadcast: Boolean, + userCanMod: Boolean + ) { // TODO: We should rely solely on the user being able to post, but we cannot guarantee // that the "(channels|groups).roles" endpoint is supported by the server in use. setupMessageComposer(userCanPost) @@ -616,15 +593,15 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR handler.postDelayed(dismissStatus, 2000) } is State.Disconnected -> connection_status_text.text = - getString(R.string.status_disconnected) + getString(R.string.status_disconnected) is State.Connecting -> connection_status_text.text = - getString(R.string.status_connecting) + getString(R.string.status_connecting) is State.Authenticating -> connection_status_text.text = - getString(R.string.status_authenticating) + getString(R.string.status_authenticating) is State.Disconnecting -> connection_status_text.text = - getString(R.string.status_disconnecting) + getString(R.string.status_disconnecting) is State.Waiting -> connection_status_text.text = - getString(R.string.status_waiting, state.seconds) + getString(R.string.status_waiting, state.seconds) } } } @@ -681,7 +658,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR subscribeComposeTextMessage() emojiKeyboardPopup = - EmojiKeyboardPopup(activity!!, activity!!.findViewById(R.id.fragment_container)) + EmojiKeyboardPopup(activity!!, activity!!.findViewById(R.id.fragment_container)) emojiKeyboardPopup.listener = this text_message.listener = object : ComposerEditText.ComposerEditTextListener { override fun onKeyboardOpened() { @@ -847,6 +824,6 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR } private fun setupToolbar(toolbarTitle: String) { - (activity as ChatRoomActivity).setupToolbarTitle(toolbarTitle) + (activity as ChatRoomActivity).showToolbarTitle(toolbarTitle) } } \ No newline at end of file diff --git a/app/src/main/java/chat/rocket/android/chatrooms/ui/ChatRoomsAdapter.kt b/app/src/main/java/chat/rocket/android/chatrooms/ui/ChatRoomsAdapter.kt index c29a5998c2..e57ec76b4d 100644 --- a/app/src/main/java/chat/rocket/android/chatrooms/ui/ChatRoomsAdapter.kt +++ b/app/src/main/java/chat/rocket/android/chatrooms/ui/ChatRoomsAdapter.kt @@ -92,11 +92,11 @@ class ChatRoomsAdapter( private fun bindIcon(chatRoom: ChatRoom, imageView: ImageView) { val drawable = when (chatRoom.type) { is RoomType.Channel -> DrawableHelper.getDrawableFromId( - R.drawable.ic_hashtag_12dp, + R.drawable.ic_hashtag_black_12dp, context ) is RoomType.PrivateGroup -> DrawableHelper.getDrawableFromId( - R.drawable.ic_lock_12_dp, + R.drawable.ic_lock_black_12_dp, context ) is RoomType.DirectMessage -> DrawableHelper.getUserStatusDrawable( diff --git a/app/src/main/java/chat/rocket/android/favoritemessages/ui/FavoriteMessagesFragment.kt b/app/src/main/java/chat/rocket/android/favoritemessages/ui/FavoriteMessagesFragment.kt index af28966206..899d087e00 100644 --- a/app/src/main/java/chat/rocket/android/favoritemessages/ui/FavoriteMessagesFragment.kt +++ b/app/src/main/java/chat/rocket/android/favoritemessages/ui/FavoriteMessagesFragment.kt @@ -110,6 +110,9 @@ class FavoriteMessagesFragment : Fragment(), FavoriteMessagesView { } private fun setupToolbar() { - (activity as ChatRoomActivity).setupToolbarTitle(getString(R.string.title_favorite_messages)) + (activity as ChatRoomActivity).let { + it.showToolbarTitle(getString(R.string.title_favorite_messages)) + it.hideToolbarChatRoomIcon() + } } } \ No newline at end of file diff --git a/app/src/main/java/chat/rocket/android/files/ui/FilesFragment.kt b/app/src/main/java/chat/rocket/android/files/ui/FilesFragment.kt index e59ffa074e..7920a0681e 100644 --- a/app/src/main/java/chat/rocket/android/files/ui/FilesFragment.kt +++ b/app/src/main/java/chat/rocket/android/files/ui/FilesFragment.kt @@ -147,8 +147,9 @@ class FilesFragment : Fragment(), FilesView { } private fun setupToolbar(totalFiles: Long) { - (activity as ChatRoomActivity?)?.setupToolbarTitle( - getString(R.string.title_files_total, totalFiles) - ) + (activity as ChatRoomActivity).let { + it.showToolbarTitle(getString(R.string.title_files_total, totalFiles)) + it.hideToolbarChatRoomIcon() + } } } \ No newline at end of file diff --git a/app/src/main/java/chat/rocket/android/members/ui/MembersFragment.kt b/app/src/main/java/chat/rocket/android/members/ui/MembersFragment.kt index 427e5ed7f0..168bb21ad1 100644 --- a/app/src/main/java/chat/rocket/android/members/ui/MembersFragment.kt +++ b/app/src/main/java/chat/rocket/android/members/ui/MembersFragment.kt @@ -120,8 +120,9 @@ class MembersFragment : Fragment(), MembersView { } private fun setupToolbar(totalMembers: Long) { - (activity as ChatRoomActivity?)?.setupToolbarTitle( - getString(R.string.title_members, totalMembers) - ) + (activity as ChatRoomActivity).let { + it.showToolbarTitle(getString(R.string.title_members, totalMembers)) + it.hideToolbarChatRoomIcon() + } } } \ No newline at end of file diff --git a/app/src/main/java/chat/rocket/android/pinnedmessages/ui/PinnedMessagesFragment.kt b/app/src/main/java/chat/rocket/android/pinnedmessages/ui/PinnedMessagesFragment.kt index ea362822ac..df9156850a 100644 --- a/app/src/main/java/chat/rocket/android/pinnedmessages/ui/PinnedMessagesFragment.kt +++ b/app/src/main/java/chat/rocket/android/pinnedmessages/ui/PinnedMessagesFragment.kt @@ -116,6 +116,9 @@ class PinnedMessagesFragment : Fragment(), PinnedMessagesView { } private fun setupToolbar() { - (activity as ChatRoomActivity).setupToolbarTitle(getString(R.string.title_pinned_messages)) + (activity as ChatRoomActivity).let { + it.showToolbarTitle(getString(R.string.title_pinned_messages)) + it.hideToolbarChatRoomIcon() + } } } \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_hashtag_12dp.xml b/app/src/main/res/drawable/ic_hashtag_12dp.xml deleted file mode 100644 index 9bc13ca446..0000000000 --- a/app/src/main/res/drawable/ic_hashtag_12dp.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_hashtag_black_12dp.xml b/app/src/main/res/drawable/ic_hashtag_black_12dp.xml new file mode 100644 index 0000000000..d7b7455e2f --- /dev/null +++ b/app/src/main/res/drawable/ic_hashtag_black_12dp.xml @@ -0,0 +1,22 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_lock_12_dp.xml b/app/src/main/res/drawable/ic_lock_12_dp.xml deleted file mode 100644 index 346fb8ac9f..0000000000 --- a/app/src/main/res/drawable/ic_lock_12_dp.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_lock_black_12_dp.xml b/app/src/main/res/drawable/ic_lock_black_12_dp.xml new file mode 100644 index 0000000000..ec2a0d9f99 --- /dev/null +++ b/app/src/main/res/drawable/ic_lock_black_12_dp.xml @@ -0,0 +1,16 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_room_channel.xml b/app/src/main/res/drawable/ic_room_channel.xml deleted file mode 100644 index 93c36d9fbe..0000000000 --- a/app/src/main/res/drawable/ic_room_channel.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_room_dm.xml b/app/src/main/res/drawable/ic_room_dm.xml deleted file mode 100644 index 1556b80fe5..0000000000 --- a/app/src/main/res/drawable/ic_room_dm.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_room_lock.xml b/app/src/main/res/drawable/ic_room_lock.xml deleted file mode 100644 index 251ffbd028..0000000000 --- a/app/src/main/res/drawable/ic_room_lock.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - \ No newline at end of file diff --git a/app/src/main/res/layout/app_bar_chat_room.xml b/app/src/main/res/layout/app_bar_chat_room.xml index a510e61601..7059110d42 100644 --- a/app/src/main/res/layout/app_bar_chat_room.xml +++ b/app/src/main/res/layout/app_bar_chat_room.xml @@ -16,35 +16,17 @@ app:popupTheme="@style/ThemeOverlay.AppCompat.Light" app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"> - - - - - - - + android:drawablePadding="@dimen/text_view_drawable_padding" + android:ellipsize="end" + android:maxLines="1" + android:textColor="@color/colorWhite" + android:textSize="18sp" + android:textStyle="bold" + tools:text="general" /> \ No newline at end of file diff --git a/app/src/main/res/layout/item_chat.xml b/app/src/main/res/layout/item_chat.xml index e71086a000..e93141b776 100644 --- a/app/src/main/res/layout/item_chat.xml +++ b/app/src/main/res/layout/item_chat.xml @@ -27,7 +27,7 @@ app:layout_constraintBottom_toBottomOf="@+id/text_chat_name" app:layout_constraintStart_toEndOf="@+id/image_avatar" app:layout_constraintTop_toTopOf="@+id/text_chat_name" - tools:src="@drawable/ic_hashtag_12dp" /> + tools:src="@drawable/ic_hashtag_black_12dp" /> 10dp 16dp - 4dp + 8dp 6dp From af2efc9fef4bbaeea6aa4c0acebe01c300867d35 Mon Sep 17 00:00:00 2001 From: Rafael Kellermann Streit Date: Fri, 25 May 2018 14:40:35 -0300 Subject: [PATCH 25/45] Revert "[NEW] Wear Module" --- app/src/main/AndroidManifest.xml | 19 ++++++--- dependencies.gradle | 15 +------ settings.gradle | 2 +- wear/.gitignore | 1 - wear/build.gradle | 38 ------------------ wear/proguard-rules.pro | 21 ---------- wear/src/main/AndroidManifest.xml | 37 ----------------- .../chat/rocket/android/wear/MainActivity.kt | 15 ------- wear/src/main/res/layout/activity_main.xml | 27 ------------- wear/src/main/res/mipmap-hdpi/ic_launcher.png | Bin 3965 -> 0 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 3965 -> 0 bytes wear/src/main/res/mipmap-mdpi/ic_launcher.png | Bin 2596 -> 0 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 2596 -> 0 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 5744 -> 0 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 5744 -> 0 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 9110 -> 0 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 9110 -> 0 bytes wear/src/main/res/values-round/strings.xml | 3 -- wear/src/main/res/values/dimens.xml | 15 ------- wear/src/main/res/values/strings.xml | 8 ---- 20 files changed, 15 insertions(+), 186 deletions(-) delete mode 100644 wear/.gitignore delete mode 100644 wear/build.gradle delete mode 100644 wear/proguard-rules.pro delete mode 100644 wear/src/main/AndroidManifest.xml delete mode 100644 wear/src/main/java/chat/rocket/android/wear/MainActivity.kt delete mode 100644 wear/src/main/res/layout/activity_main.xml delete mode 100644 wear/src/main/res/mipmap-hdpi/ic_launcher.png delete mode 100644 wear/src/main/res/mipmap-hdpi/ic_launcher_round.png delete mode 100644 wear/src/main/res/mipmap-mdpi/ic_launcher.png delete mode 100644 wear/src/main/res/mipmap-mdpi/ic_launcher_round.png delete mode 100644 wear/src/main/res/mipmap-xhdpi/ic_launcher.png delete mode 100644 wear/src/main/res/mipmap-xhdpi/ic_launcher_round.png delete mode 100644 wear/src/main/res/mipmap-xxhdpi/ic_launcher.png delete mode 100644 wear/src/main/res/mipmap-xxhdpi/ic_launcher_round.png delete mode 100644 wear/src/main/res/values-round/strings.xml delete mode 100644 wear/src/main/res/values/dimens.xml delete mode 100644 wear/src/main/res/values/strings.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5a56e49193..11306f2ce1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -20,9 +20,10 @@ android:fullBackupContent="@xml/backup_config" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" - android:networkSecurityConfig="@xml/network_security_config" android:roundIcon="@mipmap/ic_launcher_round" + android:networkSecurityConfig="@xml/network_security_config" android:supportsRtl="true"> + + - - @@ -50,36 +50,42 @@ android:scheme="https" /> + + + + + + - + - + @@ -95,6 +101,7 @@ + - \ No newline at end of file + diff --git a/dependencies.gradle b/dependencies.gradle index 063919915f..33cc29b8f3 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -36,12 +36,7 @@ ext { junit : '4.12', truth : '0.36', espresso : '3.0.2', - mockito : '2.10.0', - - //For wearable - wear : '2.3.0', - playServicesWearable : '15.0.1', - supportWearable : '26.1.0' + mockito : '2.10.0' ] libraries = [ kotlin : "org.jetbrains.kotlin:kotlin-stdlib-jre8:${versions.kotlin}", @@ -108,13 +103,5 @@ ext { espressoIntents : "com.android.support.test.espresso:espresso-intents:${versions.espresso}", roomTest : "android.arch.persistence.room:testing:${versions.room}", truth : "com.google.truth:truth:$versions.truth", - - //For the wear app - wearable : "com.google.android.support:wearable:${versions.wear}", - playServicesWearable : "com.google.android.gms:play-services-wearable:${versions.playServicesWearable}", - percentLayout : "com.android.support:percent:${versions.supportWearable}", - supportWearable : "com.android.support:support-v4:${versions.supportWearable}", - wearableRecyclerView : "com.android.support:recyclerview-v7:${versions.supportWearable}", - wearSupport : "com.android.support:wear:${versions.supportWearable}" ] } diff --git a/settings.gradle b/settings.gradle index 700dcc220e..4457826772 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':app', ':player', ':wear' \ No newline at end of file +include ':app', ':player' \ No newline at end of file diff --git a/wear/.gitignore b/wear/.gitignore deleted file mode 100644 index 796b96d1c4..0000000000 --- a/wear/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build diff --git a/wear/build.gradle b/wear/build.gradle deleted file mode 100644 index 5a8729683e..0000000000 --- a/wear/build.gradle +++ /dev/null @@ -1,38 +0,0 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' -android { - compileSdkVersion 26 - buildToolsVersion versions.buildTools - - defaultConfig { - applicationId "chat.rocket.android.wear" - minSdkVersion 23 - targetSdkVersion versions.targetSdk - versionCode 1 - versionName "1.0.0" - } - buildTypes { - release { - postprocessing { - removeUnusedCode false - removeUnusedResources false - obfuscate false - optimizeCode false - proguardFile 'proguard-rules.pro' - } - } - } -} - -dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation libraries.kotlin - implementation libraries.wearable - implementation libraries.playServicesWearable - implementation libraries.percentLayout - implementation libraries.supportWearable - implementation libraries.wearableRecyclerView - implementation libraries.wearSupport - compileOnly 'com.google.android.wearable:wearable:2.3.0' -} diff --git a/wear/proguard-rules.pro b/wear/proguard-rules.pro deleted file mode 100644 index f1b424510d..0000000000 --- a/wear/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile diff --git a/wear/src/main/AndroidManifest.xml b/wear/src/main/AndroidManifest.xml deleted file mode 100644 index ee1483864f..0000000000 --- a/wear/src/main/AndroidManifest.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/wear/src/main/java/chat/rocket/android/wear/MainActivity.kt b/wear/src/main/java/chat/rocket/android/wear/MainActivity.kt deleted file mode 100644 index 42985241ce..0000000000 --- a/wear/src/main/java/chat/rocket/android/wear/MainActivity.kt +++ /dev/null @@ -1,15 +0,0 @@ -package chat.rocket.android.wear - -import android.os.Bundle -import android.support.wearable.activity.WearableActivity - -class MainActivity : WearableActivity() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) - - // Enables Always-on - setAmbientEnabled() - } -} diff --git a/wear/src/main/res/layout/activity_main.xml b/wear/src/main/res/layout/activity_main.xml deleted file mode 100644 index 3bc9c6bf8c..0000000000 --- a/wear/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - diff --git a/wear/src/main/res/mipmap-hdpi/ic_launcher.png b/wear/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index ef465d63b6aa3c0cb809e9da30dce6fb84cb18d8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3965 zcmV-@4}$QCP)HOn@Z(=2H$MfLUdl`L02Ch_5UcAZ&*s~K(SiHCU&i2 ztG5%YIA&3`uU7+xYm0k~vNOLf9;Y>`eSemt=)DRQiwX&9p-5CMc3|P2{*CpiqY2ns z>vz)VBjLO#!9cleZf5sj(f3 zMh4}eN@MEU5tK$n_CfI8A@s3bK~jM;ge4Ol5uLJjG&puVC4tJkdiCo4?E;b{zPh@) z$xvezIOK_<>RU}kMa5%nO5PHEzUd4Yxr&6zlSTHC|4?aZ>8v)~WIG1u)O0p*>_Osj)MR92{JmAoO%>z#f$Rk5bUJYCL8`2*%!-SP zyIDQP76HW9J?Iu8vkF^kYp)6Kdopns7Z)Gqp?NhMAgO+}wYAHffpfL6kgh~UQSNu& zQTC^wQu^C(Q^tGmQRe#fl)G~$m7F?7)w#LEe%F(Ls$)~5?Ck7Un`LS;*{iOu?$6*< z$=E`Dld6jsDdU}YDDKH8$?(V{q#Hh*&JG)9Blr!+Oiw&P86hEb<;)qXEi3bgI0??C zyu7?BCMM?3>M?5qh_z-{Q`%FNnMnz=XFGu6O!T8iQ_AYqRFRzQ5vB&$pO=^SqcF8D znoNteXPj{9P3BYpDe<}IG=T&$`uo%6&6~+mQ`4a!A+N5gs-lF1gvoB3nj|fb86sSI zEq(&!iHQ_Db*g5NpiSVZyxqGgD>Rf+-*|&Ay!G$80e$*(ueqoqJ8^$|`($Y?Zx=G4rjda=6o;uDJ+^IXoPDjS#t6umHze{mFe|V8XZl=$Bt3Kfdh2;n{O1YiF@iPGL9clx`!Te zFxT^o7Afr&^J|7jD=8@{-oAbNT}=j$5;?QkJYQ&!rt(D%$2v~E+Jg_0>G8*v7^(R9 zaizWHr_BLqRaO?||M(-Nu34jyy$w*TeaS0VQdM@gW{}t^5xe|pL!_Pp42nJfP|?w&R8vqu77ox_!voc^_o=>oS@DMPvt}s|K;9FYR{YB^3ZPoZ znsc-;#=x=YFQ;kFoH=s~x6RdRUP?q=RHUf6T4Um#ewy<3?4hg0#ck)VnLnU0@N_Ug zdH8yyRL9ofMzN%mRI4W(WoS5K_*?m!m zl6|~#`m{R{wfU1zJ{jx)(4j+z1~EWYZY8KfP}@=5I%`q+`SVV5HJh>s_=+=k8)D^w z0vvUl^mXfO%mU;qc|}Ub`|mpq2y?b=YiOIlcdyIDl>w?^fQC8%w15Bp!CC>52`Yjz zXW@NiN(vbR0vz%&b(=DSfs7L-*t9|FqK>6-9s5ZA-1E;n4JkkaGRj6-*d2gA`skwv z901y}W5>X{x;nEK6O$SQv#Cr^cbbO8&zxza9WXZ=!e@ZvpLxcnP1@SEHhffl>5@b3 z30men9{=o;DI0z)YWXY2L zYA>b$)U#*L9vla0a`On|r58g1-&II&;fxtP;w)BDl2G)ksEQ4 z5-27oC;!G+_}Q${7Gd=b`T6-fv_{gXCo3wsZ$4uY~J>j2Z7j)d3kw1H)n~9eMJ!wY+HhY+D$3vTA6SZXU|g1S6`7H zrY?d)1h0jM54%-Sl>DRD>mwM0n{2elj)%HOM@KKy;_4f#Nn=U|6M{{8KQ=#(SP3?d zfx_c}J1nXvjeQ7y$8og7dBw%Ws2$TeHl-NWnAjaV$T)w#%}j?efehyVD_nlrvUW~QFiP*fYG&c@OXcD0F#iFB5qx=yDflgUKq zIgd+aVudzv9HK}T2Y~4uo#S_Q9;oyw?F}q3oKqP&f;J+=P%WBh&a8$sFE-JlNs}gF zVU=%_Zxrp`y}K*_xg{kfMRR5GN_2F?ycoP1VkCPa1PR77VH&^{91@sl+QZp9ty{P5IgI(sFTZqek%kNNrK-ofO^v31ONyp@S zR)~QmcGs?J+@bO^9%0vGjo_St90q68_l$aV=Hdl~gyJk&oX zD2U>id?BVKOB{H{Q&yNs&&4!io;Gb-0LB^|9IS1fBA)<6@7T9*Uog0YL9*DFNKx;N zXIG1AVP{#R2#dg25M+M)?YHk?j4M{GxS=@ zJ;=Y5Rj!-|HLP;w-#lrRs{ltat&=BDM)KJ15o2uLo76(>Ztl~k&jTF2rn!9O$-~!4 zqwk;#j>Xb@_wGFmV_URnQOjOQ;0tu;Kex}FJ2#N?G&5|sv%ZRezH@Y5&e_60F(xs# zmT$9X&-UX#cZGz6%r7h~w01U-BsiQ-SUDhE%wy>*#?)#bDe#&;efmxOw|{78=u!^; ztYVR8oyD_O&^vGr%u0g84|oN}5EvMEb8CVlH}A1y$Kt{RnVVkVgtAOz!X62dBxG<7 zo0WOZnl;dzfulx^LP5UGUi9^zFku3|d@x|noH^4t3r`o(wpD9+q^;u|^a&*rlgX4m zbLPw^(Pz>3Hh&A+d-CMTcnbae{QQQWI&~`I%9SgmGP(K=1P1q3ObUI9jEww+`#1u9 z6@6~ESH!(0PMnChiTkh&*|cd>NM>ecDV|tI0Hp>7ec+I!bmPX2?{nYqGH)Nz*LMGE zjMuPX!@BqD*AI&x2L%KKOg?(_=)r=50;RkmfU>lu>B&!52(OA+QOrsl^7r@03%rBS zH}tV1z)^2LBSwsXZQspuABXMFM@2=Qgr@^PiLK#_5-rgzoGY#YP^?m8a4+1G@4Wzh zKwm_kJmzC3iZ%@xFaTd;8OSnp@#4jE!otFKr=_Lkp^_-IN_@J)qW;DSAG3;qH;C#t zr7g~p=i^#tPH}K;++*Rwg(z2G$;v?V!4tHmsjbV%kt6+t*#W5gcnjFDVZ(n;oH!Al zl9F->)jJf(MQ%r~Hn?fDMJ*eJ{?ydeOPog?!8LJhp)mt-Puv@Q=nS6`>VhP2$dDn> zsJ<*%v2{N`-G^7MT=~N0&70RpL`3X07!1)IDkL+h=3KsfxtL9*41#?o{hXwvq-3;V zZri(M%a-*x7uOKlf@|K5dx(Uy^L%7U9TMDo@4ej+U<&iY+P`~P{sQmk>+Aaumf;xm z=+UDe9Y22j6bSb58y|ZRZO~SngLCh~wQx;byFFcZn}fwB(G^b{(d^KnLvQQVtJfW< z1qzR4tnw00000NkvXXu0mjfwl1l2 diff --git a/wear/src/main/res/mipmap-hdpi/ic_launcher_round.png b/wear/src/main/res/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index ef465d63b6aa3c0cb809e9da30dce6fb84cb18d8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3965 zcmV-@4}$QCP)HOn@Z(=2H$MfLUdl`L02Ch_5UcAZ&*s~K(SiHCU&i2 ztG5%YIA&3`uU7+xYm0k~vNOLf9;Y>`eSemt=)DRQiwX&9p-5CMc3|P2{*CpiqY2ns z>vz)VBjLO#!9cleZf5sj(f3 zMh4}eN@MEU5tK$n_CfI8A@s3bK~jM;ge4Ol5uLJjG&puVC4tJkdiCo4?E;b{zPh@) z$xvezIOK_<>RU}kMa5%nO5PHEzUd4Yxr&6zlSTHC|4?aZ>8v)~WIG1u)O0p*>_Osj)MR92{JmAoO%>z#f$Rk5bUJYCL8`2*%!-SP zyIDQP76HW9J?Iu8vkF^kYp)6Kdopns7Z)Gqp?NhMAgO+}wYAHffpfL6kgh~UQSNu& zQTC^wQu^C(Q^tGmQRe#fl)G~$m7F?7)w#LEe%F(Ls$)~5?Ck7Un`LS;*{iOu?$6*< z$=E`Dld6jsDdU}YDDKH8$?(V{q#Hh*&JG)9Blr!+Oiw&P86hEb<;)qXEi3bgI0??C zyu7?BCMM?3>M?5qh_z-{Q`%FNnMnz=XFGu6O!T8iQ_AYqRFRzQ5vB&$pO=^SqcF8D znoNteXPj{9P3BYpDe<}IG=T&$`uo%6&6~+mQ`4a!A+N5gs-lF1gvoB3nj|fb86sSI zEq(&!iHQ_Db*g5NpiSVZyxqGgD>Rf+-*|&Ay!G$80e$*(ueqoqJ8^$|`($Y?Zx=G4rjda=6o;uDJ+^IXoPDjS#t6umHze{mFe|V8XZl=$Bt3Kfdh2;n{O1YiF@iPGL9clx`!Te zFxT^o7Afr&^J|7jD=8@{-oAbNT}=j$5;?QkJYQ&!rt(D%$2v~E+Jg_0>G8*v7^(R9 zaizWHr_BLqRaO?||M(-Nu34jyy$w*TeaS0VQdM@gW{}t^5xe|pL!_Pp42nJfP|?w&R8vqu77ox_!voc^_o=>oS@DMPvt}s|K;9FYR{YB^3ZPoZ znsc-;#=x=YFQ;kFoH=s~x6RdRUP?q=RHUf6T4Um#ewy<3?4hg0#ck)VnLnU0@N_Ug zdH8yyRL9ofMzN%mRI4W(WoS5K_*?m!m zl6|~#`m{R{wfU1zJ{jx)(4j+z1~EWYZY8KfP}@=5I%`q+`SVV5HJh>s_=+=k8)D^w z0vvUl^mXfO%mU;qc|}Ub`|mpq2y?b=YiOIlcdyIDl>w?^fQC8%w15Bp!CC>52`Yjz zXW@NiN(vbR0vz%&b(=DSfs7L-*t9|FqK>6-9s5ZA-1E;n4JkkaGRj6-*d2gA`skwv z901y}W5>X{x;nEK6O$SQv#Cr^cbbO8&zxza9WXZ=!e@ZvpLxcnP1@SEHhffl>5@b3 z30men9{=o;DI0z)YWXY2L zYA>b$)U#*L9vla0a`On|r58g1-&II&;fxtP;w)BDl2G)ksEQ4 z5-27oC;!G+_}Q${7Gd=b`T6-fv_{gXCo3wsZ$4uY~J>j2Z7j)d3kw1H)n~9eMJ!wY+HhY+D$3vTA6SZXU|g1S6`7H zrY?d)1h0jM54%-Sl>DRD>mwM0n{2elj)%HOM@KKy;_4f#Nn=U|6M{{8KQ=#(SP3?d zfx_c}J1nXvjeQ7y$8og7dBw%Ws2$TeHl-NWnAjaV$T)w#%}j?efehyVD_nlrvUW~QFiP*fYG&c@OXcD0F#iFB5qx=yDflgUKq zIgd+aVudzv9HK}T2Y~4uo#S_Q9;oyw?F}q3oKqP&f;J+=P%WBh&a8$sFE-JlNs}gF zVU=%_Zxrp`y}K*_xg{kfMRR5GN_2F?ycoP1VkCPa1PR77VH&^{91@sl+QZp9ty{P5IgI(sFTZqek%kNNrK-ofO^v31ONyp@S zR)~QmcGs?J+@bO^9%0vGjo_St90q68_l$aV=Hdl~gyJk&oX zD2U>id?BVKOB{H{Q&yNs&&4!io;Gb-0LB^|9IS1fBA)<6@7T9*Uog0YL9*DFNKx;N zXIG1AVP{#R2#dg25M+M)?YHk?j4M{GxS=@ zJ;=Y5Rj!-|HLP;w-#lrRs{ltat&=BDM)KJ15o2uLo76(>Ztl~k&jTF2rn!9O$-~!4 zqwk;#j>Xb@_wGFmV_URnQOjOQ;0tu;Kex}FJ2#N?G&5|sv%ZRezH@Y5&e_60F(xs# zmT$9X&-UX#cZGz6%r7h~w01U-BsiQ-SUDhE%wy>*#?)#bDe#&;efmxOw|{78=u!^; ztYVR8oyD_O&^vGr%u0g84|oN}5EvMEb8CVlH}A1y$Kt{RnVVkVgtAOz!X62dBxG<7 zo0WOZnl;dzfulx^LP5UGUi9^zFku3|d@x|noH^4t3r`o(wpD9+q^;u|^a&*rlgX4m zbLPw^(Pz>3Hh&A+d-CMTcnbae{QQQWI&~`I%9SgmGP(K=1P1q3ObUI9jEww+`#1u9 z6@6~ESH!(0PMnChiTkh&*|cd>NM>ecDV|tI0Hp>7ec+I!bmPX2?{nYqGH)Nz*LMGE zjMuPX!@BqD*AI&x2L%KKOg?(_=)r=50;RkmfU>lu>B&!52(OA+QOrsl^7r@03%rBS zH}tV1z)^2LBSwsXZQspuABXMFM@2=Qgr@^PiLK#_5-rgzoGY#YP^?m8a4+1G@4Wzh zKwm_kJmzC3iZ%@xFaTd;8OSnp@#4jE!otFKr=_Lkp^_-IN_@J)qW;DSAG3;qH;C#t zr7g~p=i^#tPH}K;++*Rwg(z2G$;v?V!4tHmsjbV%kt6+t*#W5gcnjFDVZ(n;oH!Al zl9F->)jJf(MQ%r~Hn?fDMJ*eJ{?ydeOPog?!8LJhp)mt-Puv@Q=nS6`>VhP2$dDn> zsJ<*%v2{N`-G^7MT=~N0&70RpL`3X07!1)IDkL+h=3KsfxtL9*41#?o{hXwvq-3;V zZri(M%a-*x7uOKlf@|K5dx(Uy^L%7U9TMDo@4ej+U<&iY+P`~P{sQmk>+Aaumf;xm z=+UDe9Y22j6bSb58y|ZRZO~SngLCh~wQx;byFFcZn}fwB(G^b{(d^KnLvQQVtJfW< z1qzR4tnw00000NkvXXu0mjfwl1l2 diff --git a/wear/src/main/res/mipmap-mdpi/ic_launcher.png b/wear/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index ae9bf375a4fa1b0047833e5b7452d9b4bdb9aa83..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2596 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 UL+aP$zyJUM07*qoM6N<$g5v{O=>Px# diff --git a/wear/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/wear/src/main/res/mipmap-xxhdpi/ic_launcher_round.png deleted file mode 100644 index 9970812ebb7d05ffeb7d583657ffc67ec1658497..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9110 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 UL+aP$zyJUM07*qoM6N<$g5v{O=>Px# diff --git a/wear/src/main/res/values-round/strings.xml b/wear/src/main/res/values-round/strings.xml deleted file mode 100644 index 452e3358d9..0000000000 --- a/wear/src/main/res/values-round/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - Hello Round World! - diff --git a/wear/src/main/res/values/dimens.xml b/wear/src/main/res/values/dimens.xml deleted file mode 100644 index e865b412be..0000000000 --- a/wear/src/main/res/values/dimens.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - 0dp - - - 5dp - diff --git a/wear/src/main/res/values/strings.xml b/wear/src/main/res/values/strings.xml deleted file mode 100644 index b7c1ab19d5..0000000000 --- a/wear/src/main/res/values/strings.xml +++ /dev/null @@ -1,8 +0,0 @@ - - Rocket.Chat - - Hello Square World! - From 6fb08385c3f16b5c31fabdb75574c7c6b2cc4973 Mon Sep 17 00:00:00 2001 From: Leonardo Aramaki Date: Fri, 25 May 2018 15:41:03 -0300 Subject: [PATCH 26/45] Dismiss notification on tap if host is empty --- app/src/main/java/chat/rocket/android/push/PushManager.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/chat/rocket/android/push/PushManager.kt b/app/src/main/java/chat/rocket/android/push/PushManager.kt index 3836518e1e..48aadec394 100644 --- a/app/src/main/java/chat/rocket/android/push/PushManager.kt +++ b/app/src/main/java/chat/rocket/android/push/PushManager.kt @@ -250,11 +250,14 @@ class PushManager @Inject constructor( .setContentIntent(contentIntent) .setMessageNotification() + if (host.isEmpty()) { + builder.setContentIntent(deleteIntent) + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channelId: String val channelName: String if (host.isEmpty()) { - builder.setContentIntent(deleteIntent) channelName = "Test Notification" channelId = "test-channel" } else { From ae70fc750f73ef8045db2436776adf429782b1c0 Mon Sep 17 00:00:00 2001 From: Leonardo Aramaki Date: Fri, 25 May 2018 16:39:23 -0300 Subject: [PATCH 27/45] Make FirebaseMessagingService exported --- app/src/main/AndroidManifest.xml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 976221a998..37112f7dc6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -12,8 +12,8 @@ android:fullBackupContent="@xml/backup_config" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" - android:roundIcon="@mipmap/ic_launcher_round" android:networkSecurityConfig="@xml/network_security_config" + android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true"> + + @@ -99,7 +101,10 @@ - + From abe613c1a076af91ade86caa2875f1121b617344 Mon Sep 17 00:00:00 2001 From: Leonardo Aramaki Date: Fri, 25 May 2018 16:47:56 -0300 Subject: [PATCH 28/45] Revert "[FIX] Better multi-server support for push notifications" --- .../login/presentation/LoginPresenter.kt | 2 +- .../presentation/RegisterUsernamePresenter.kt | 2 +- .../signup/presentation/SignupPresenter.kt | 2 +- .../twofactor/presentation/TwoFAPresenter.kt | 2 +- .../rocket/android/infrastructure/LocalRepository.kt | 4 +--- .../SharedPreferencesLocalRepository.kt | 10 +--------- .../rocket/android/main/presentation/MainPresenter.kt | 11 ++++++----- .../chat/rocket/android/push/FirebaseTokenService.kt | 6 +++--- .../main/java/chat/rocket/android/push/PushManager.kt | 5 +---- 9 files changed, 16 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginPresenter.kt b/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginPresenter.kt index 543705cbf4..46452ff2bc 100644 --- a/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginPresenter.kt +++ b/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginPresenter.kt @@ -382,7 +382,7 @@ class LoginPresenter @Inject constructor( } private suspend fun registerPushToken() { - localRepository.getPushToken(currentServer)?.let { + localRepository.get(LocalRepository.KEY_PUSH_TOKEN)?.let { client.registerPushToken(it, getAccountsInteractor.get(), factory) } // TODO: When the push token is null, at some point we should receive it with diff --git a/app/src/main/java/chat/rocket/android/authentication/registerusername/presentation/RegisterUsernamePresenter.kt b/app/src/main/java/chat/rocket/android/authentication/registerusername/presentation/RegisterUsernamePresenter.kt index f6bd5a8afe..dcaa64c5a7 100644 --- a/app/src/main/java/chat/rocket/android/authentication/registerusername/presentation/RegisterUsernamePresenter.kt +++ b/app/src/main/java/chat/rocket/android/authentication/registerusername/presentation/RegisterUsernamePresenter.kt @@ -65,7 +65,7 @@ class RegisterUsernamePresenter @Inject constructor( } private suspend fun registerPushToken() { - localRepository.getPushToken(currentServer)?.let { + localRepository.get(LocalRepository.KEY_PUSH_TOKEN)?.let { client.registerPushToken(it, getAccountsInteractor.get(), factory) } // TODO: When the push token is null, at some point we should receive it with diff --git a/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupPresenter.kt b/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupPresenter.kt index 0db694d0fa..183bda5144 100644 --- a/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupPresenter.kt +++ b/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupPresenter.kt @@ -95,7 +95,7 @@ class SignupPresenter @Inject constructor(private val view: SignupView, } private suspend fun registerPushToken() { - localRepository.getPushToken(currentServer)?.let { + localRepository.get(LocalRepository.KEY_PUSH_TOKEN)?.let { client.registerPushToken(it, getAccountsInteractor.get(), factory) } // TODO: When the push token is null, at some point we should receive it with diff --git a/app/src/main/java/chat/rocket/android/authentication/twofactor/presentation/TwoFAPresenter.kt b/app/src/main/java/chat/rocket/android/authentication/twofactor/presentation/TwoFAPresenter.kt index d24e2f82cf..76330566ee 100644 --- a/app/src/main/java/chat/rocket/android/authentication/twofactor/presentation/TwoFAPresenter.kt +++ b/app/src/main/java/chat/rocket/android/authentication/twofactor/presentation/TwoFAPresenter.kt @@ -79,7 +79,7 @@ class TwoFAPresenter @Inject constructor(private val view: TwoFAView, fun signup() = navigator.toSignUp() private suspend fun registerPushToken() { - localRepository.getPushToken(currentServer)?.let { + localRepository.get(LocalRepository.KEY_PUSH_TOKEN)?.let { client.registerPushToken(it, getAccountsInteractor.get(), factory) } // TODO: When the push token is null, at some point we should receive it with diff --git a/app/src/main/java/chat/rocket/android/infrastructure/LocalRepository.kt b/app/src/main/java/chat/rocket/android/infrastructure/LocalRepository.kt index 0bb9a454d3..fa2a523bb7 100644 --- a/app/src/main/java/chat/rocket/android/infrastructure/LocalRepository.kt +++ b/app/src/main/java/chat/rocket/android/infrastructure/LocalRepository.kt @@ -18,11 +18,9 @@ interface LocalRepository { fun clearAllFromServer(server: String) fun getCurrentUser(url: String): User? fun saveCurrentUser(url: String, user: User) - fun savePushToken(url: String, token: String) - fun getPushToken(url: String): String? companion object { - const val PUSH_TOKEN_KEY = "push_token_" + const val KEY_PUSH_TOKEN = "KEY_PUSH_TOKEN" const val MIGRATION_FINISHED_KEY = "MIGRATION_FINISHED_KEY" const val TOKEN_KEY = "token_" const val SETTINGS_KEY = "settings_" diff --git a/app/src/main/java/chat/rocket/android/infrastructure/SharedPreferencesLocalRepository.kt b/app/src/main/java/chat/rocket/android/infrastructure/SharedPreferencesLocalRepository.kt index 0c92ee98ca..11df24c738 100644 --- a/app/src/main/java/chat/rocket/android/infrastructure/SharedPreferencesLocalRepository.kt +++ b/app/src/main/java/chat/rocket/android/infrastructure/SharedPreferencesLocalRepository.kt @@ -2,7 +2,6 @@ package chat.rocket.android.infrastructure import android.content.SharedPreferences import androidx.core.content.edit -import chat.rocket.android.infrastructure.LocalRepository.Companion.PUSH_TOKEN_KEY import chat.rocket.common.model.User import com.squareup.moshi.Moshi @@ -45,15 +44,8 @@ class SharedPreferencesLocalRepository( override fun clear(key: String) = preferences.edit { remove(key) } - override fun savePushToken(url: String, token: String) { - save(PUSH_TOKEN_KEY + url, token) - } - - override fun getPushToken(url: String): String? = preferences.getString(PUSH_TOKEN_KEY + url, null) - override fun clearAllFromServer(server: String) { - clear(LocalRepository.PUSH_TOKEN_KEY) //TODO: keep this for now to clear up for some version rolls. - clear(LocalRepository.PUSH_TOKEN_KEY + server) + clear(LocalRepository.KEY_PUSH_TOKEN) clear(LocalRepository.TOKEN_KEY + server) clear(LocalRepository.SETTINGS_KEY + server) clear(LocalRepository.CURRENT_USERNAME_KEY) diff --git a/app/src/main/java/chat/rocket/android/main/presentation/MainPresenter.kt b/app/src/main/java/chat/rocket/android/main/presentation/MainPresenter.kt index 05c1e864e7..a93e66423a 100644 --- a/app/src/main/java/chat/rocket/android/main/presentation/MainPresenter.kt +++ b/app/src/main/java/chat/rocket/android/main/presentation/MainPresenter.kt @@ -152,7 +152,7 @@ class MainPresenter @Inject constructor( suspend fun refreshToken(token: String?) { token?.let { - localRepository.savePushToken(currentServer, it) + localRepository.save(LocalRepository.KEY_PUSH_TOKEN, token) client.registerPushToken(token, getAccountsInteractor.get(), factory) } } @@ -172,15 +172,16 @@ class MainPresenter @Inject constructor( } private suspend fun clearTokens() { - localRepository.get(LocalRepository.PUSH_TOKEN_KEY + currentServer)?.let { + serverInteractor.clear() + val pushToken = localRepository.get(LocalRepository.KEY_PUSH_TOKEN) + if (pushToken != null) { try { - retryIO("unregisterPushToken") { client.unregisterPushToken(it) } - view.invalidateToken(it) + retryIO("unregisterPushToken") { client.unregisterPushToken(pushToken) } + view.invalidateToken(pushToken) } catch (ex: Exception) { Timber.d(ex, "Error unregistering push token") } } - serverInteractor.clear() localRepository.clearAllFromServer(currentServer) } diff --git a/app/src/main/java/chat/rocket/android/push/FirebaseTokenService.kt b/app/src/main/java/chat/rocket/android/push/FirebaseTokenService.kt index 091de30d2f..247bd0ef4a 100644 --- a/app/src/main/java/chat/rocket/android/push/FirebaseTokenService.kt +++ b/app/src/main/java/chat/rocket/android/push/FirebaseTokenService.kt @@ -39,8 +39,8 @@ class FirebaseTokenService : FirebaseInstanceIdService() { val currentServer = getCurrentServerInteractor.get() val client = currentServer?.let { factory.create(currentServer) } - if (gcmToken != null && currentServer != null) { - localRepository.savePushToken(currentServer, gcmToken) + gcmToken?.let { + localRepository.save(LocalRepository.KEY_PUSH_TOKEN, gcmToken) client?.let { launch { try { @@ -53,7 +53,7 @@ class FirebaseTokenService : FirebaseInstanceIdService() { } } } catch (ex: Exception) { - Timber.e(ex, "Error refreshing Firebase TOKEN") + Timber.d(ex, "Error refreshing Firebase TOKEN") } } } \ No newline at end of file diff --git a/app/src/main/java/chat/rocket/android/push/PushManager.kt b/app/src/main/java/chat/rocket/android/push/PushManager.kt index 48aadec394..3836518e1e 100644 --- a/app/src/main/java/chat/rocket/android/push/PushManager.kt +++ b/app/src/main/java/chat/rocket/android/push/PushManager.kt @@ -250,14 +250,11 @@ class PushManager @Inject constructor( .setContentIntent(contentIntent) .setMessageNotification() - if (host.isEmpty()) { - builder.setContentIntent(deleteIntent) - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channelId: String val channelName: String if (host.isEmpty()) { + builder.setContentIntent(deleteIntent) channelName = "Test Notification" channelId = "test-channel" } else { From e6c19fe5c3f17efa2cbc04e09f2860f2fd02d930 Mon Sep 17 00:00:00 2001 From: Leonardo Aramaki Date: Fri, 25 May 2018 22:58:41 -0300 Subject: [PATCH 29/45] On entering a chat, initialization call a suspendable function subscribeTypingStatus which is blocking the current execution thread from continuing and thus, from making a call to subscribeState function. --- .../presentation/ChatRoomPresenter.kt | 22 +++++++++---------- .../chatroom/service/MessageService.kt | 2 +- .../infraestructure/ConnectionManager.kt | 12 +++++----- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomPresenter.kt b/app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomPresenter.kt index c1f93ac3f3..52483d5861 100644 --- a/app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomPresenter.kt +++ b/app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomPresenter.kt @@ -181,9 +181,7 @@ class ChatRoomPresenter @Inject constructor( } subscribeTypingStatus() - if (offset == 0L) { - subscribeState() - } + subscribeState() } } @@ -229,10 +227,9 @@ class ChatRoomPresenter @Inject constructor( ) try { messagesRepository.save(newMessage) - val message = client.sendMessage(id, chatRoomId, text) view.showNewMessage(mapper.map(newMessage, RoomViewModel( roles = chatRoles, isBroadcast = chatIsBroadcast))) - message + client.sendMessage(id, chatRoomId, text) } catch (ex: Exception) { // Ok, not very beautiful, but the backend sends us a not valid response // When someone sends a message on a read-only channel, so we just ignore it @@ -324,7 +321,7 @@ class ChatRoomPresenter @Inject constructor( } } - private fun subscribeState() { + private suspend fun subscribeState() { Timber.d("Subscribing to Status changes") lastState = manager.state manager.addStatusChannel(stateChannel) @@ -790,12 +787,14 @@ class ChatRoomPresenter @Inject constructor( } private suspend fun subscribeTypingStatus() { - client.subscribeTypingStatus(chatRoomId.toString()) { _, id -> - typingStatusSubscriptionId = id - } + launch(CommonPool + strategy.jobs) { + client.subscribeTypingStatus(chatRoomId.toString()) { _, id -> + typingStatusSubscriptionId = id + } - for (typingStatus in client.typingStatusChannel) { - processTypingStatus(typingStatus) + for (typingStatus in client.typingStatusChannel) { + processTypingStatus(typingStatus) + } } } @@ -837,6 +836,7 @@ class ChatRoomPresenter @Inject constructor( launchUI(strategy) { val viewModelStreamedMessage = mapper.map(streamedMessage, RoomViewModel( roles = chatRoles, isBroadcast = chatIsBroadcast)) + val roomMessages = messagesRepository.getByRoomId(streamedMessage.roomId) val index = roomMessages.indexOfFirst { msg -> msg.id == streamedMessage.id } if (index > -1) { diff --git a/app/src/main/java/chat/rocket/android/chatroom/service/MessageService.kt b/app/src/main/java/chat/rocket/android/chatroom/service/MessageService.kt index 948302c5b7..ed43d7c82d 100644 --- a/app/src/main/java/chat/rocket/android/chatroom/service/MessageService.kt +++ b/app/src/main/java/chat/rocket/android/chatroom/service/MessageService.kt @@ -64,7 +64,7 @@ class MessageService : JobService() { Timber.e(ex) // TODO - remove the generic message when we implement :userId:/message subscription if (ex is IllegalStateException) { - Timber.d(ex, "Probably a read-only problem...") + Timber.e(ex, "Probably a read-only problem...") // TODO: For now we are only going to reschedule when api is fixed. messageRepository.removeById(message.id) jobFinished(params, false) diff --git a/app/src/main/java/chat/rocket/android/server/infraestructure/ConnectionManager.kt b/app/src/main/java/chat/rocket/android/server/infraestructure/ConnectionManager.kt index 3fc95aa4a1..35e6147543 100644 --- a/app/src/main/java/chat/rocket/android/server/infraestructure/ConnectionManager.kt +++ b/app/src/main/java/chat/rocket/android/server/infraestructure/ConnectionManager.kt @@ -3,16 +3,16 @@ package chat.rocket.android.server.infraestructure import chat.rocket.common.model.BaseRoom import chat.rocket.common.model.User 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.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.subscribeActiveUsers +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.subscribeUserData +import chat.rocket.core.internal.realtime.unsubscribe import chat.rocket.core.internal.rest.chatRooms import chat.rocket.core.model.Message import chat.rocket.core.model.Myself From 234f1a7cccbeb6f8ba4eb591b8f266d7910e2e62 Mon Sep 17 00:00:00 2001 From: Filipe de Lima Brito Date: Mon, 28 May 2018 12:22:48 -0300 Subject: [PATCH 30/45] Fix download button. --- .../android/chatroom/adapter/ImageAttachmentViewHolder.kt | 2 +- .../java/chat/rocket/android/files/ui/FilesFragment.kt | 8 ++------ .../main/java/chat/rocket/android/helper/ImageHelper.kt | 3 --- app/src/main/res/layout/fragment_files.xml | 1 + 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/chat/rocket/android/chatroom/adapter/ImageAttachmentViewHolder.kt b/app/src/main/java/chat/rocket/android/chatroom/adapter/ImageAttachmentViewHolder.kt index a8849d39e3..a7f9c67544 100644 --- a/app/src/main/java/chat/rocket/android/chatroom/adapter/ImageAttachmentViewHolder.kt +++ b/app/src/main/java/chat/rocket/android/chatroom/adapter/ImageAttachmentViewHolder.kt @@ -30,7 +30,7 @@ class ImageAttachmentViewHolder( file_name.text = data.attachmentTitle image_attachment.setOnClickListener { ImageHelper.openImage( - it.context, + context, data.attachmentUrl, data.attachmentTitle.toString() ) diff --git a/app/src/main/java/chat/rocket/android/files/ui/FilesFragment.kt b/app/src/main/java/chat/rocket/android/files/ui/FilesFragment.kt index 7920a0681e..2653640fe8 100644 --- a/app/src/main/java/chat/rocket/android/files/ui/FilesFragment.kt +++ b/app/src/main/java/chat/rocket/android/files/ui/FilesFragment.kt @@ -94,17 +94,13 @@ class FilesFragment : Fragment(), FilesView { override fun playMedia(url: String) { ui { - activity?.let { - PlayerActivity.play(it, url) - } + PlayerActivity.play(it, url) } } override fun openImage(url: String, name: String) { ui { - activity?.let { - ImageHelper.openImage(it, url, name) - } + ImageHelper.openImage(root_layout.context, url, name) } } diff --git a/app/src/main/java/chat/rocket/android/helper/ImageHelper.kt b/app/src/main/java/chat/rocket/android/helper/ImageHelper.kt index 5d51796156..fae2123e5d 100644 --- a/app/src/main/java/chat/rocket/android/helper/ImageHelper.kt +++ b/app/src/main/java/chat/rocket/android/helper/ImageHelper.kt @@ -53,7 +53,6 @@ object ImageHelper { ) val toolbar = Toolbar(context).also { it.inflateMenu(R.menu.image_actions) - it.overflowIcon?.setTint(Color.WHITE) it.setOnMenuItemClickListener { return@setOnMenuItemClickListener when (it.itemId) { R.id.action_save_image -> saveImage(context) @@ -109,7 +108,6 @@ object ImageHelper { .hideStatusBar(false) .setCustomDraweeControllerBuilder(builder) .show() - } private fun saveImage(context: Context): Boolean { @@ -166,5 +164,4 @@ object ImageHelper { ) } } - } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_files.xml b/app/src/main/res/layout/fragment_files.xml index 0f6fdeab6b..b0e48927a0 100644 --- a/app/src/main/res/layout/fragment_files.xml +++ b/app/src/main/res/layout/fragment_files.xml @@ -2,6 +2,7 @@ From 576b2a204e93aee7ecf1793ae2d6c97d11d96fde Mon Sep 17 00:00:00 2001 From: Filipe de Lima Brito Date: Mon, 28 May 2018 16:33:30 -0300 Subject: [PATCH 31/45] Fix pinned messages and favorite messages layouts. --- .../android/chatroom/ui/ChatRoomFragment.kt | 4 +- .../chatroom/viewmodel/ViewModelMapper.kt | 89 ++++++++++--------- .../presentation/FavoriteMessagesPresenter.kt | 4 +- .../ui/FavoriteMessagesFragment.kt | 5 +- .../presentation/PinnedMessagesPresenter.kt | 5 +- .../ui/PinnedMessagesFragment.kt | 5 +- 6 files changed, 60 insertions(+), 52 deletions(-) diff --git a/app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt b/app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt index 14e359e517..358a490324 100644 --- a/app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt +++ b/app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt @@ -243,7 +243,9 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR if (recycler_view.adapter == null) { adapter = ChatRoomAdapter( - chatRoomType, chatRoomName, presenter, + chatRoomType, + chatRoomName, + presenter, reactionListener = this@ChatRoomFragment ) recycler_view.adapter = adapter diff --git a/app/src/main/java/chat/rocket/android/chatroom/viewmodel/ViewModelMapper.kt b/app/src/main/java/chat/rocket/android/chatroom/viewmodel/ViewModelMapper.kt index 3586cf3948..cc2d4fa2d4 100644 --- a/app/src/main/java/chat/rocket/android/chatroom/viewmodel/ViewModelMapper.kt +++ b/app/src/main/java/chat/rocket/android/chatroom/viewmodel/ViewModelMapper.kt @@ -65,70 +65,75 @@ class ViewModelMapper @Inject constructor( private val currentUsername: String? = localRepository.get(LocalRepository.CURRENT_USERNAME_KEY) private val secondaryTextColor = ContextCompat.getColor(context, R.color.colorSecondaryText) - suspend fun map(message: Message, roomViewModel: RoomViewModel = RoomViewModel( - roles = emptyList(), isBroadcast = true)): List> { + suspend fun map( + message: Message, + roomViewModel: RoomViewModel = RoomViewModel(roles = emptyList(), isBroadcast = true) + ): List> { return translate(message, roomViewModel) } - suspend fun map(messages: List, roomViewModel: RoomViewModel = RoomViewModel( - roles = emptyList(), isBroadcast = true)): List> = withContext(CommonPool) { - val list = ArrayList>(messages.size) + suspend fun map( + messages: List, + roomViewModel: RoomViewModel = RoomViewModel(roles = emptyList(), isBroadcast = true) + ): List> = + withContext(CommonPool) { + val list = ArrayList>(messages.size) - messages.forEach { - list.addAll(translate(it, roomViewModel)) + messages.forEach { list.addAll(translate(it, roomViewModel)) } + return@withContext list } - return@withContext list - } - - private suspend fun translate(message: Message, roomViewModel: RoomViewModel) - : List> = withContext(CommonPool) { - val list = ArrayList>() + private suspend fun translate( + message: Message, + roomViewModel: RoomViewModel + ): List> = + withContext(CommonPool) { + val list = ArrayList>() - message.urls?.forEach { - val url = mapUrl(message, it) - url?.let { list.add(url) } - } + message.urls?.forEach { + val url = mapUrl(message, it) + url?.let { list.add(url) } + } - message.attachments?.forEach { - val attachment = mapAttachment(message, it) - attachment?.let { list.add(attachment) } - } + message.attachments?.forEach { + val attachment = mapAttachment(message, it) + attachment?.let { list.add(attachment) } + } - mapMessage(message).let { - if (list.isNotEmpty()) { - it.preview = list.first().preview + mapMessage(message).let { + if (list.isNotEmpty()) { + it.preview = list.first().preview + } + list.add(it) } - list.add(it) - } - for (i in list.size - 1 downTo 0) { - val next = if (i - 1 < 0) null else list[i - 1] - list[i].nextDownStreamMessage = next - } + for (i in list.size - 1 downTo 0) { + val next = if (i - 1 < 0) null else list[i - 1] + list[i].nextDownStreamMessage = next + } - if (isBroadcastReplyAvailable(roomViewModel, message)) { - roomsInteractor.getById(currentServer, message.roomId)?.let { chatRoom -> - val replyViewModel = mapMessageReply(message, chatRoom) - list.first().nextDownStreamMessage = replyViewModel - list.add(0, replyViewModel) + if (isBroadcastReplyAvailable(roomViewModel, message)) { + roomsInteractor.getById(currentServer, message.roomId)?.let { chatRoom -> + val replyViewModel = mapMessageReply(message, chatRoom) + list.first().nextDownStreamMessage = replyViewModel + list.add(0, replyViewModel) + } } - } - return@withContext list - } + return@withContext list + } private fun isBroadcastReplyAvailable(roomViewModel: RoomViewModel, message: Message): Boolean { val senderUsername = message.sender?.username return roomViewModel.isRoom && roomViewModel.isBroadcast && - !message.isSystemMessage() && - senderUsername != currentUsername + !message.isSystemMessage() && + senderUsername != currentUsername } private fun mapMessageReply(message: Message, chatRoom: ChatRoom): MessageReplyViewModel { val name = message.sender?.name - val roomName = if (settings.useRealName() && name != null) name else message.sender?.username - ?: "" + val roomName = + if (settings.useRealName() && name != null) name else message.sender?.username ?: "" val permalink = messageHelper.createPermalink(message, chatRoom) return MessageReplyViewModel( messageId = message.id, diff --git a/app/src/main/java/chat/rocket/android/favoritemessages/presentation/FavoriteMessagesPresenter.kt b/app/src/main/java/chat/rocket/android/favoritemessages/presentation/FavoriteMessagesPresenter.kt index 73c0a2a7cc..73aed83549 100644 --- a/app/src/main/java/chat/rocket/android/favoritemessages/presentation/FavoriteMessagesPresenter.kt +++ b/app/src/main/java/chat/rocket/android/favoritemessages/presentation/FavoriteMessagesPresenter.kt @@ -25,9 +25,9 @@ class FavoriteMessagesPresenter @Inject constructor( private var offset: Int = 0 /** - * Loads all favorite messages for room. the given room id. + * Loads all favorite messages for the given room id. * - * @param roomId The id of the room to get its favorite messages. + * @param roomId The id of the room to get favorite messages from. */ fun loadFavoriteMessages(roomId: String) { launchUI(strategy) { diff --git a/app/src/main/java/chat/rocket/android/favoritemessages/ui/FavoriteMessagesFragment.kt b/app/src/main/java/chat/rocket/android/favoritemessages/ui/FavoriteMessagesFragment.kt index 899d087e00..b6d28cfa53 100644 --- a/app/src/main/java/chat/rocket/android/favoritemessages/ui/FavoriteMessagesFragment.kt +++ b/app/src/main/java/chat/rocket/android/favoritemessages/ui/FavoriteMessagesFragment.kt @@ -69,10 +69,11 @@ class FavoriteMessagesFragment : Fragment(), FavoriteMessagesView { adapter = ChatRoomAdapter(enableActions = false) recycler_view.adapter = adapter val linearLayoutManager = - LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) + LinearLayoutManager(context, LinearLayoutManager.VERTICAL, true) + linearLayoutManager.stackFromEnd = true recycler_view.layoutManager = linearLayoutManager recycler_view.itemAnimator = DefaultItemAnimator() - if (favoriteMessages.size > 10) { + if (favoriteMessages.size >= 30) { recycler_view.addOnScrollListener(object : EndlessRecyclerViewScrollListener(linearLayoutManager) { override fun onLoadMore( diff --git a/app/src/main/java/chat/rocket/android/pinnedmessages/presentation/PinnedMessagesPresenter.kt b/app/src/main/java/chat/rocket/android/pinnedmessages/presentation/PinnedMessagesPresenter.kt index da5586871c..1a04bf3a9e 100644 --- a/app/src/main/java/chat/rocket/android/pinnedmessages/presentation/PinnedMessagesPresenter.kt +++ b/app/src/main/java/chat/rocket/android/pinnedmessages/presentation/PinnedMessagesPresenter.kt @@ -26,7 +26,7 @@ class PinnedMessagesPresenter @Inject constructor( private var offset: Int = 0 /** - * Load all pinned messages for the given room id. + * Loads all pinned messages for the given room id. * * @param roomId The id of the room to get pinned messages from. */ @@ -36,8 +36,7 @@ class PinnedMessagesPresenter @Inject constructor( view.showLoading() roomsInteractor.getById(serverUrl, roomId)?.let { val pinnedMessages = client.getPinnedMessages(roomId, it.type, offset) - val messageList = - mapper.map(pinnedMessages.result.filterNot { it.isSystemMessage() }) + val messageList = mapper.map(pinnedMessages.result) view.showPinnedMessages(messageList) offset += 1 * 30 }.ifNull { diff --git a/app/src/main/java/chat/rocket/android/pinnedmessages/ui/PinnedMessagesFragment.kt b/app/src/main/java/chat/rocket/android/pinnedmessages/ui/PinnedMessagesFragment.kt index df9156850a..118c6a5f27 100644 --- a/app/src/main/java/chat/rocket/android/pinnedmessages/ui/PinnedMessagesFragment.kt +++ b/app/src/main/java/chat/rocket/android/pinnedmessages/ui/PinnedMessagesFragment.kt @@ -71,10 +71,11 @@ class PinnedMessagesFragment : Fragment(), PinnedMessagesView { adapter = ChatRoomAdapter(enableActions = false) recycler_view_pinned.adapter = adapter val linearLayoutManager = - LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) + LinearLayoutManager(context, LinearLayoutManager.VERTICAL, true) + linearLayoutManager.stackFromEnd = true recycler_view_pinned.layoutManager = linearLayoutManager recycler_view_pinned.itemAnimator = DefaultItemAnimator() - if (pinnedMessages.size > 10) { + if (pinnedMessages.size >= 30) { recycler_view_pinned.addOnScrollListener(object : EndlessRecyclerViewScrollListener(linearLayoutManager) { override fun onLoadMore( From efce1574db3679f37edcdf9fdcd3e59cfc90ee9a Mon Sep 17 00:00:00 2001 From: Filipe de Lima Brito Date: Mon, 28 May 2018 19:05:14 -0300 Subject: [PATCH 32/45] Favorite/pinned messages aren't shown as the messages on the chatroom are. --- .../chatroom/viewmodel/MessageViewModel.kt | 26 ++++---- .../chatroom/viewmodel/ReactionViewModel.kt | 10 ++-- .../chatroom/viewmodel/ViewModelMapper.kt | 60 ++++++++++++++++++- .../presentation/FavoriteMessagesPresenter.kt | 2 +- .../ui/FavoriteMessagesFragment.kt | 3 +- .../presentation/PinnedMessagesPresenter.kt | 2 +- .../ui/PinnedMessagesFragment.kt | 3 +- 7 files changed, 80 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/chat/rocket/android/chatroom/viewmodel/MessageViewModel.kt b/app/src/main/java/chat/rocket/android/chatroom/viewmodel/MessageViewModel.kt index 030294e59e..e5df5ff1ac 100644 --- a/app/src/main/java/chat/rocket/android/chatroom/viewmodel/MessageViewModel.kt +++ b/app/src/main/java/chat/rocket/android/chatroom/viewmodel/MessageViewModel.kt @@ -4,19 +4,19 @@ import chat.rocket.android.R import chat.rocket.core.model.Message data class MessageViewModel( - override val message: Message, - override val rawData: Message, - override val messageId: String, - override val avatar: String, - override val time: CharSequence, - override val senderName: CharSequence, - override val content: CharSequence, - override val isPinned: Boolean, - override var reactions: List, - override var nextDownStreamMessage: BaseViewModel<*>? = null, - override var preview: Message? = null, - var isFirstUnread: Boolean, - override var isTemporary: Boolean = false + override val message: Message, + override val rawData: Message, + override val messageId: String, + override val avatar: String, + override val time: CharSequence, + override val senderName: CharSequence, + override val content: CharSequence, + override val isPinned: Boolean, + override var reactions: List, + override var nextDownStreamMessage: BaseViewModel<*>? = null, + override var preview: Message? = null, + var isFirstUnread: Boolean, + override var isTemporary: Boolean = false ) : BaseMessageViewModel { override val viewType: Int get() = BaseViewModel.ViewType.MESSAGE.viewType diff --git a/app/src/main/java/chat/rocket/android/chatroom/viewmodel/ReactionViewModel.kt b/app/src/main/java/chat/rocket/android/chatroom/viewmodel/ReactionViewModel.kt index 6a12b9f792..79b0540a08 100644 --- a/app/src/main/java/chat/rocket/android/chatroom/viewmodel/ReactionViewModel.kt +++ b/app/src/main/java/chat/rocket/android/chatroom/viewmodel/ReactionViewModel.kt @@ -1,9 +1,9 @@ package chat.rocket.android.chatroom.viewmodel data class ReactionViewModel( - val messageId: String, - val shortname: String, - val unicode: CharSequence, - val count: Int, - val usernames: List = emptyList() + val messageId: String, + val shortname: String, + val unicode: CharSequence, + val count: Int, + val usernames: List = emptyList() ) \ No newline at end of file diff --git a/app/src/main/java/chat/rocket/android/chatroom/viewmodel/ViewModelMapper.kt b/app/src/main/java/chat/rocket/android/chatroom/viewmodel/ViewModelMapper.kt index cc2d4fa2d4..d0fa1b8b97 100644 --- a/app/src/main/java/chat/rocket/android/chatroom/viewmodel/ViewModelMapper.kt +++ b/app/src/main/java/chat/rocket/android/chatroom/viewmodel/ViewModelMapper.kt @@ -74,12 +74,18 @@ class ViewModelMapper @Inject constructor( suspend fun map( messages: List, - roomViewModel: RoomViewModel = RoomViewModel(roles = emptyList(), isBroadcast = true) + roomViewModel: RoomViewModel = RoomViewModel(roles = emptyList(), isBroadcast = true), + asNotReversed: Boolean = false ): List> = withContext(CommonPool) { val list = ArrayList>(messages.size) - messages.forEach { list.addAll(translate(it, roomViewModel)) } + messages.forEach { + list.addAll( + if (asNotReversed) translateAsNotReversed(it, roomViewModel) + else translate(it, roomViewModel) + ) + } return@withContext list } @@ -123,6 +129,56 @@ class ViewModelMapper @Inject constructor( return@withContext list } + private suspend fun translateAsNotReversed( + message: Message, + roomViewModel: RoomViewModel + ): List> = + withContext(CommonPool) { + val list = ArrayList>() + + mapMessage(message).let { + if (list.isNotEmpty()) { + it.preview = list.first().preview + } + list.add(it) + } + + message.attachments?.forEach { + val attachment = mapAttachment(message, it) + attachment?.let { + list.add(attachment) + } + } + + message.urls?.forEach { + val url = mapUrl(message, it) + url?.let { + list.add(url) + } + } + + for (i in list.size - 1 downTo 0) { + val next = if (i - 1 < 0) null else list[i - 1] + list[i].nextDownStreamMessage = next + } + + if (isBroadcastReplyAvailable(roomViewModel, message)) { + roomsInteractor.getById(currentServer, message.roomId)?.let { chatRoom -> + val replyViewModel = mapMessageReply(message, chatRoom) + list.first().nextDownStreamMessage = replyViewModel + list.add(0, replyViewModel) + } + } + + list.dropLast(1).forEach { + it.reactions = emptyList() + } + list.last().reactions = getReactions(message) + list.last().nextDownStreamMessage = null + + return@withContext list + } + private fun isBroadcastReplyAvailable(roomViewModel: RoomViewModel, message: Message): Boolean { val senderUsername = message.sender?.username return roomViewModel.isRoom && roomViewModel.isBroadcast && diff --git a/app/src/main/java/chat/rocket/android/favoritemessages/presentation/FavoriteMessagesPresenter.kt b/app/src/main/java/chat/rocket/android/favoritemessages/presentation/FavoriteMessagesPresenter.kt index 73aed83549..c18ab22d0f 100644 --- a/app/src/main/java/chat/rocket/android/favoritemessages/presentation/FavoriteMessagesPresenter.kt +++ b/app/src/main/java/chat/rocket/android/favoritemessages/presentation/FavoriteMessagesPresenter.kt @@ -35,7 +35,7 @@ class FavoriteMessagesPresenter @Inject constructor( view.showLoading() roomsInteractor.getById(serverUrl, roomId)?.let { val favoriteMessages = client.getFavoriteMessages(roomId, it.type, offset) - val messageList = mapper.map(favoriteMessages.result) + val messageList = mapper.map(favoriteMessages.result, asNotReversed = true) view.showFavoriteMessages(messageList) offset += 1 * 30 }.ifNull { diff --git a/app/src/main/java/chat/rocket/android/favoritemessages/ui/FavoriteMessagesFragment.kt b/app/src/main/java/chat/rocket/android/favoritemessages/ui/FavoriteMessagesFragment.kt index b6d28cfa53..08a760d55a 100644 --- a/app/src/main/java/chat/rocket/android/favoritemessages/ui/FavoriteMessagesFragment.kt +++ b/app/src/main/java/chat/rocket/android/favoritemessages/ui/FavoriteMessagesFragment.kt @@ -69,8 +69,7 @@ class FavoriteMessagesFragment : Fragment(), FavoriteMessagesView { adapter = ChatRoomAdapter(enableActions = false) recycler_view.adapter = adapter val linearLayoutManager = - LinearLayoutManager(context, LinearLayoutManager.VERTICAL, true) - linearLayoutManager.stackFromEnd = true + LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) recycler_view.layoutManager = linearLayoutManager recycler_view.itemAnimator = DefaultItemAnimator() if (favoriteMessages.size >= 30) { diff --git a/app/src/main/java/chat/rocket/android/pinnedmessages/presentation/PinnedMessagesPresenter.kt b/app/src/main/java/chat/rocket/android/pinnedmessages/presentation/PinnedMessagesPresenter.kt index 1a04bf3a9e..10699f9cbf 100644 --- a/app/src/main/java/chat/rocket/android/pinnedmessages/presentation/PinnedMessagesPresenter.kt +++ b/app/src/main/java/chat/rocket/android/pinnedmessages/presentation/PinnedMessagesPresenter.kt @@ -36,7 +36,7 @@ class PinnedMessagesPresenter @Inject constructor( view.showLoading() roomsInteractor.getById(serverUrl, roomId)?.let { val pinnedMessages = client.getPinnedMessages(roomId, it.type, offset) - val messageList = mapper.map(pinnedMessages.result) + val messageList = mapper.map(pinnedMessages.result, asNotReversed = true) view.showPinnedMessages(messageList) offset += 1 * 30 }.ifNull { diff --git a/app/src/main/java/chat/rocket/android/pinnedmessages/ui/PinnedMessagesFragment.kt b/app/src/main/java/chat/rocket/android/pinnedmessages/ui/PinnedMessagesFragment.kt index 118c6a5f27..acdc68a76b 100644 --- a/app/src/main/java/chat/rocket/android/pinnedmessages/ui/PinnedMessagesFragment.kt +++ b/app/src/main/java/chat/rocket/android/pinnedmessages/ui/PinnedMessagesFragment.kt @@ -71,8 +71,7 @@ class PinnedMessagesFragment : Fragment(), PinnedMessagesView { adapter = ChatRoomAdapter(enableActions = false) recycler_view_pinned.adapter = adapter val linearLayoutManager = - LinearLayoutManager(context, LinearLayoutManager.VERTICAL, true) - linearLayoutManager.stackFromEnd = true + LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) recycler_view_pinned.layoutManager = linearLayoutManager recycler_view_pinned.itemAnimator = DefaultItemAnimator() if (pinnedMessages.size >= 30) { From 04e7dcafa6fd005352320302e9004ec45139ff58 Mon Sep 17 00:00:00 2001 From: Leonardo Aramaki Date: Mon, 28 May 2018 19:55:47 -0300 Subject: [PATCH 33/45] Remove animation from composer buttons --- .../chat/rocket/android/chatroom/ui/ChatRoomFragment.kt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt b/app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt index 14e359e517..ac4f15e405 100644 --- a/app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt +++ b/app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt @@ -651,7 +651,6 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR button_join_chat.setVisible(true) button_join_chat.setOnClickListener { presenter.joinChat(chatRoomId) } } else { - button_send.alpha = 0f button_send.setVisible(false) button_show_attachment_options.alpha = 1f button_show_attachment_options.setVisible(true) @@ -787,14 +786,14 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR private fun setupComposeButtons(charSequence: CharSequence) { if (charSequence.isNotEmpty() && playComposeMessageButtonsAnimation) { - button_show_attachment_options.fadeOut(1F, 0F, 120) - button_send.fadeIn(0F, 1F, 120) + button_show_attachment_options.setVisible(false) + button_send.setVisible(true) playComposeMessageButtonsAnimation = false } if (charSequence.isEmpty()) { - button_send.fadeOut(1F, 0F, 120) - button_show_attachment_options.fadeIn(0F, 1F, 120) + button_send.setVisible(false) + button_show_attachment_options.setVisible(true) playComposeMessageButtonsAnimation = true } } From 6c35524eb065a5895550c059e839d7f75664f115 Mon Sep 17 00:00:00 2001 From: Leonardo Aramaki Date: Mon, 28 May 2018 21:03:28 -0300 Subject: [PATCH 34/45] When converting Users to ChatRooms show the username first in order to better identify users with the same full name --- .../rocket/android/chatrooms/presentation/ChatRoomsPresenter.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/chat/rocket/android/chatrooms/presentation/ChatRoomsPresenter.kt b/app/src/main/java/chat/rocket/android/chatrooms/presentation/ChatRoomsPresenter.kt index c30ea599c4..ec2e2edc25 100644 --- a/app/src/main/java/chat/rocket/android/chatrooms/presentation/ChatRoomsPresenter.kt +++ b/app/src/main/java/chat/rocket/android/chatrooms/presentation/ChatRoomsPresenter.kt @@ -210,7 +210,7 @@ class ChatRoomsPresenter @Inject constructor( } else { null }, - name = it.name ?: "", + name = it.username ?: it.name ?: "", fullName = it.name, readonly = false, updatedAt = null, From 524f47e562d65a4b0e659838de27f09b672f74be Mon Sep 17 00:00:00 2001 From: Leonardo Aramaki Date: Mon, 28 May 2018 21:33:00 -0300 Subject: [PATCH 35/45] Check if room is a DM before showing the join chat button --- .../java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt b/app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt index 14e359e517..40157f45cc 100644 --- a/app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt +++ b/app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt @@ -31,6 +31,8 @@ import chat.rocket.android.helper.KeyboardHelper import chat.rocket.android.helper.MessageParser import chat.rocket.android.util.extensions.* import chat.rocket.android.widget.emoji.* +import chat.rocket.common.model.RoomType +import chat.rocket.common.model.roomTypeOf import chat.rocket.core.internal.realtime.socket.model.State import chat.rocket.core.model.ChatRoom import dagger.android.support.AndroidSupportInjection @@ -646,7 +648,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR if (isChatRoomReadOnly && !canPost) { text_room_is_read_only.setVisible(true) input_container.setVisible(false) - } else if (!isSubscribed) { + } else if (!isSubscribed && roomTypeOf(chatRoomType) != RoomType.DIRECT_MESSAGE) { input_container.setVisible(false) button_join_chat.setVisible(true) button_join_chat.setOnClickListener { presenter.joinChat(chatRoomId) } From bc8f66aa240da9000c86df889bdd4fe80063fdf8 Mon Sep 17 00:00:00 2001 From: Leonardo Aramaki Date: Mon, 28 May 2018 23:09:37 -0300 Subject: [PATCH 36/45] Hop to chat room when notification tapping --- .../presentation/ChatRoomsPresenter.kt | 6 ++++ .../android/chatrooms/ui/ChatRoomsFragment.kt | 35 ++++++++++++++++--- .../main/presentation/MainNavigator.kt | 6 ++-- .../main/presentation/MainPresenter.kt | 2 +- .../rocket/android/main/ui/MainActivity.kt | 6 +++- .../chat/rocket/android/push/PushManager.kt | 3 +- .../presentation/ChangeServerNavigator.kt | 7 ++-- .../presentation/ChangeServerPresenter.kt | 4 +-- .../android/server/ui/ChangeServerActivity.kt | 7 ++-- 9 files changed, 58 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/chat/rocket/android/chatrooms/presentation/ChatRoomsPresenter.kt b/app/src/main/java/chat/rocket/android/chatrooms/presentation/ChatRoomsPresenter.kt index c30ea599c4..0b46bd75da 100644 --- a/app/src/main/java/chat/rocket/android/chatrooms/presentation/ChatRoomsPresenter.kt +++ b/app/src/main/java/chat/rocket/android/chatrooms/presentation/ChatRoomsPresenter.kt @@ -640,4 +640,10 @@ class ChatRoomsPresenter @Inject constructor( manager.removeRoomsAndSubscriptionsChannel(subscriptionsChannel) manager.removeActiveUserChannel(activeUserChannel) } + + fun goToChatRoomWithId(chatRoomId: String) { + launchUI(strategy) { + chatRoomsInteractor.getById(currentServer, chatRoomId)?.let { loadChatRoom(it) } + } + } } \ No newline at end of file diff --git a/app/src/main/java/chat/rocket/android/chatrooms/ui/ChatRoomsFragment.kt b/app/src/main/java/chat/rocket/android/chatrooms/ui/ChatRoomsFragment.kt index d5cfe94c81..fe5eefbf55 100644 --- a/app/src/main/java/chat/rocket/android/chatrooms/ui/ChatRoomsFragment.kt +++ b/app/src/main/java/chat/rocket/android/chatrooms/ui/ChatRoomsFragment.kt @@ -1,8 +1,6 @@ package chat.rocket.android.chatrooms.ui import android.app.AlertDialog -import android.content.Context -import android.content.SharedPreferences import android.os.Bundle import android.os.Handler import android.support.v4.app.Fragment @@ -11,7 +9,12 @@ import android.support.v7.util.DiffUtil import android.support.v7.widget.DefaultItemAnimator import android.support.v7.widget.LinearLayoutManager import android.support.v7.widget.SearchView -import android.view.* +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup import android.widget.CheckBox import android.widget.RadioGroup import androidx.core.view.isVisible @@ -24,7 +27,12 @@ import chat.rocket.android.helper.SharedPreferenceHelper import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.domain.SettingsRepository -import chat.rocket.android.util.extensions.* +import chat.rocket.android.util.extensions.fadeIn +import chat.rocket.android.util.extensions.fadeOut +import chat.rocket.android.util.extensions.inflate +import chat.rocket.android.util.extensions.setVisible +import chat.rocket.android.util.extensions.showToast +import chat.rocket.android.util.extensions.ui import chat.rocket.android.widget.DividerItemDecoration import chat.rocket.common.model.RoomType import chat.rocket.core.internal.realtime.socket.model.State @@ -36,6 +44,8 @@ import kotlinx.coroutines.experimental.NonCancellable.isActive import timber.log.Timber import javax.inject.Inject +private const val BUNDLE_CHAT_ROOM_ID = "BUNDLE_CHAT_ROOM_ID" + class ChatRoomsFragment : Fragment(), ChatRoomsView { @Inject lateinit var presenter: ChatRoomsPresenter @@ -50,15 +60,30 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView { private var listJob: Job? = null private var sectionedAdapter: SimpleSectionedRecyclerViewAdapter? = null + private var chatRoomId: String? = null companion object { - fun newInstance() = ChatRoomsFragment() + fun newInstance(chatRoomId: String? = null): ChatRoomsFragment { + return ChatRoomsFragment().apply { + arguments = Bundle(1).apply { + putString(BUNDLE_CHAT_ROOM_ID, chatRoomId) + } + } + } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) AndroidSupportInjection.inject(this) setHasOptionsMenu(true) + val bundle = arguments + if (bundle != null) { + chatRoomId = bundle.getString(BUNDLE_CHAT_ROOM_ID) + chatRoomId?.let { + presenter.goToChatRoomWithId(it) + chatRoomId = null + } + } } override fun onDestroy() { diff --git a/app/src/main/java/chat/rocket/android/main/presentation/MainNavigator.kt b/app/src/main/java/chat/rocket/android/main/presentation/MainNavigator.kt index 675dd8fb7b..b8fe187854 100644 --- a/app/src/main/java/chat/rocket/android/main/presentation/MainNavigator.kt +++ b/app/src/main/java/chat/rocket/android/main/presentation/MainNavigator.kt @@ -12,9 +12,9 @@ import chat.rocket.android.util.extensions.addFragment class MainNavigator(internal val activity: MainActivity) { - fun toChatList() { + fun toChatList(chatRoomId: String? = null) { activity.addFragment("ChatRoomsFragment", R.id.fragment_container) { - ChatRoomsFragment.newInstance() + ChatRoomsFragment.newInstance(chatRoomId) } } @@ -43,7 +43,7 @@ class MainNavigator(internal val activity: MainActivity) { } fun toNewServer(serverUrl: String? = null) { - activity.startActivity(activity.changeServerIntent(serverUrl)) + activity.startActivity(activity.changeServerIntent(serverUrl = serverUrl)) activity.finish() } diff --git a/app/src/main/java/chat/rocket/android/main/presentation/MainPresenter.kt b/app/src/main/java/chat/rocket/android/main/presentation/MainPresenter.kt index 68aea4e274..8c41bbe9e9 100644 --- a/app/src/main/java/chat/rocket/android/main/presentation/MainPresenter.kt +++ b/app/src/main/java/chat/rocket/android/main/presentation/MainPresenter.kt @@ -49,7 +49,7 @@ class MainPresenter @Inject constructor( private val userDataChannel = Channel() - fun toChatList() = navigator.toChatList() + fun toChatList(chatRoomId: String? = null) = navigator.toChatList(chatRoomId) fun toUserProfile() = navigator.toUserProfile() diff --git a/app/src/main/java/chat/rocket/android/main/ui/MainActivity.kt b/app/src/main/java/chat/rocket/android/main/ui/MainActivity.kt index 5e68a252e7..2b72311cdb 100644 --- a/app/src/main/java/chat/rocket/android/main/ui/MainActivity.kt +++ b/app/src/main/java/chat/rocket/android/main/ui/MainActivity.kt @@ -18,6 +18,7 @@ import chat.rocket.android.main.presentation.MainPresenter import chat.rocket.android.main.presentation.MainView import chat.rocket.android.main.viewmodel.NavHeaderViewModel import chat.rocket.android.server.domain.model.Account +import chat.rocket.android.server.ui.INTENT_CHAT_ROOM_ID import chat.rocket.android.util.extensions.fadeIn import chat.rocket.android.util.extensions.fadeOut import chat.rocket.android.util.extensions.rotateBy @@ -52,6 +53,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp private var expanded = false private lateinit var googleApiClient: GoogleApiClient private val headerLayout by lazy { view_navigation.getHeaderView(0) } + private var chatRoomId: String? = null override fun onCreate(savedInstanceState: Bundle?) { AndroidInjection.inject(this) @@ -69,6 +71,8 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp } } + chatRoomId = intent.getStringExtra(INTENT_CHAT_ROOM_ID) + presenter.connect() presenter.loadCurrentInfo() setupToolbar() @@ -111,7 +115,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp override fun onResume() { super.onResume() if (!isFragmentAdded) { - presenter.toChatList() + presenter.toChatList(chatRoomId) isFragmentAdded = true } } diff --git a/app/src/main/java/chat/rocket/android/push/PushManager.kt b/app/src/main/java/chat/rocket/android/push/PushManager.kt index 48aadec394..0003cd481f 100644 --- a/app/src/main/java/chat/rocket/android/push/PushManager.kt +++ b/app/src/main/java/chat/rocket/android/push/PushManager.kt @@ -26,7 +26,6 @@ import chat.rocket.android.server.domain.siteName import chat.rocket.android.server.ui.changeServerIntent import chat.rocket.common.model.RoomType import chat.rocket.common.model.roomTypeOf -import chat.rocket.common.util.ifNull import com.squareup.moshi.Json import com.squareup.moshi.Moshi import kotlinx.coroutines.experimental.runBlocking @@ -300,7 +299,7 @@ class PushManager @Inject constructor( } private fun getContentIntent(context: Context, notificationId: Int, pushMessage: PushMessage, grouped: Boolean = false): PendingIntent { - val notificationIntent = context.changeServerIntent(pushMessage.info.host) + val notificationIntent = context.changeServerIntent(pushMessage.info.host, chatRoomId = pushMessage.info.roomId) // TODO - add support to go directly to the chatroom /*if (!grouped) { notificationIntent.putExtra(EXTRA_ROOM_ID, pushMessage.info.roomId) diff --git a/app/src/main/java/chat/rocket/android/server/presentation/ChangeServerNavigator.kt b/app/src/main/java/chat/rocket/android/server/presentation/ChangeServerNavigator.kt index c489da5dd4..dad5bd2998 100644 --- a/app/src/main/java/chat/rocket/android/server/presentation/ChangeServerNavigator.kt +++ b/app/src/main/java/chat/rocket/android/server/presentation/ChangeServerNavigator.kt @@ -4,6 +4,7 @@ import android.content.Intent import chat.rocket.android.authentication.ui.newServerIntent import chat.rocket.android.main.ui.MainActivity import chat.rocket.android.server.ui.ChangeServerActivity +import chat.rocket.android.server.ui.INTENT_CHAT_ROOM_ID class ChangeServerNavigator (internal val activity: ChangeServerActivity) { fun toServerScreen() { @@ -11,8 +12,10 @@ class ChangeServerNavigator (internal val activity: ChangeServerActivity) { activity.finish() } - fun toChatRooms() { - activity.startActivity(Intent(activity, MainActivity::class.java)) + fun toChatRooms(chatRoomId: String? = null) { + activity.startActivity(Intent(activity, MainActivity::class.java).also { + it.putExtra(INTENT_CHAT_ROOM_ID, chatRoomId) + }) activity.finish() } diff --git a/app/src/main/java/chat/rocket/android/server/presentation/ChangeServerPresenter.kt b/app/src/main/java/chat/rocket/android/server/presentation/ChangeServerPresenter.kt index b321378f1c..57bf7d0c8c 100644 --- a/app/src/main/java/chat/rocket/android/server/presentation/ChangeServerPresenter.kt +++ b/app/src/main/java/chat/rocket/android/server/presentation/ChangeServerPresenter.kt @@ -21,7 +21,7 @@ class ChangeServerPresenter @Inject constructor( private val localRepository: LocalRepository, private val connectionManager: ConnectionManagerFactory ) { - fun loadServer(newUrl: String?) { + fun loadServer(newUrl: String?, chatRoomId: String? = null) { launchUI(strategy) { view.showProgress() var url = newUrl @@ -56,7 +56,7 @@ class ChangeServerPresenter @Inject constructor( saveCurrentServerInteractor.save(serverUrl) view.hideProgress() - navigator.toChatRooms() + navigator.toChatRooms(chatRoomId) }.ifNull { view.hideProgress() navigator.toServerScreen() diff --git a/app/src/main/java/chat/rocket/android/server/ui/ChangeServerActivity.kt b/app/src/main/java/chat/rocket/android/server/ui/ChangeServerActivity.kt index 6029affcb4..e2e3cec7ae 100644 --- a/app/src/main/java/chat/rocket/android/server/ui/ChangeServerActivity.kt +++ b/app/src/main/java/chat/rocket/android/server/ui/ChangeServerActivity.kt @@ -21,7 +21,8 @@ class ChangeServerActivity : AppCompatActivity(), ChangeServerView { super.onCreate(savedInstanceState) val serverUrl: String? = intent.getStringExtra(INTENT_SERVER_URL) - presenter.loadServer(serverUrl) + val chatRoomId: String? = intent.getStringExtra(INTENT_CHAT_ROOM_ID) + presenter.loadServer(serverUrl, chatRoomId) } override fun showInvalidCredentials() { @@ -40,11 +41,13 @@ class ChangeServerActivity : AppCompatActivity(), ChangeServerView { 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" +const val INTENT_CHAT_ROOM_ID = "INTENT_CHAT_ROOM_ID" -fun Context.changeServerIntent(serverUrl: String? = null): Intent { +fun Context.changeServerIntent(serverUrl: String? = null, chatRoomId: String? = ""): Intent { return Intent(this, ChangeServerActivity::class.java).apply { serverUrl?.let { url -> putExtra(INTENT_SERVER_URL, url) + putExtra(INTENT_CHAT_ROOM_ID, chatRoomId) } flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK } From a09e2c3ad0820fe57642d69c7c2756092a4c037b Mon Sep 17 00:00:00 2001 From: Leonardo Aramaki Date: Tue, 29 May 2018 19:45:43 -0300 Subject: [PATCH 37/45] Call realtime createDirectMessage method on opening a new DM --- .../chatrooms/presentation/ChatRoomsPresenter.kt | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/chat/rocket/android/chatrooms/presentation/ChatRoomsPresenter.kt b/app/src/main/java/chat/rocket/android/chatrooms/presentation/ChatRoomsPresenter.kt index ec2e2edc25..fedf0acc32 100644 --- a/app/src/main/java/chat/rocket/android/chatrooms/presentation/ChatRoomsPresenter.kt +++ b/app/src/main/java/chat/rocket/android/chatrooms/presentation/ChatRoomsPresenter.kt @@ -34,6 +34,7 @@ import chat.rocket.common.model.SimpleUser import chat.rocket.common.model.User import chat.rocket.common.util.ifNull import chat.rocket.core.internal.model.Subscription +import chat.rocket.core.internal.realtime.createDirectMessage 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.Type @@ -124,8 +125,19 @@ class ChatRoomsPresenter @Inject constructor( if (myself?.username == null) { view.showMessage(R.string.msg_generic_error) } else { + val id = if (isDirectMessage && !chatRoom.open) { + retryIO("createDirectMessage(${chatRoom.name})") { + client.createDirectMessage(chatRoom.name) + } + val fromTo = mutableListOf(myself.id, chatRoom.id).apply { + sort() + } + fromTo.joinToString("") + } else { + chatRoom.id + } val isChatRoomOwner = chatRoom.user?.username == myself.username || isDirectMessage - navigator.toChatRoom(chatRoom.id, roomName, + navigator.toChatRoom(id, roomName, chatRoom.type.toString(), chatRoom.readonly ?: false, chatRoom.lastSeen ?: -1, chatRoom.open, isChatRoomOwner) From 41c30a553fe2c94b1dc6a1c70d624e666758cb9b Mon Sep 17 00:00:00 2001 From: Leonardo Aramaki Date: Fri, 1 Jun 2018 10:09:15 -0300 Subject: [PATCH 38/45] Check if fragment already has a view and is still attached --- .../chat/rocket/android/chatroom/ui/ChatRoomFragment.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt b/app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt index fecd14aeca..2d8bba2d56 100644 --- a/app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt +++ b/app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt @@ -276,9 +276,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ) { // TODO: We should rely solely on the user being able to post, but we cannot guarantee // that the "(channels|groups).roles" endpoint is supported by the server in use. - setupMessageComposer(userCanPost) - isBroadcastChannel = channelIsBroadcast - if (isBroadcastChannel && !userCanMod) activity?.invalidateOptionsMenu() + ui { + setupMessageComposer(userCanPost) + isBroadcastChannel = channelIsBroadcast + if (isBroadcastChannel && !userCanMod) activity?.invalidateOptionsMenu() + } } override fun openDirectMessage(chatRoom: ChatRoom, permalink: String) { From d0563b24488140256c1c9e9995d9cabd7bc621fb Mon Sep 17 00:00:00 2001 From: Leonardo Aramaki Date: Fri, 1 Jun 2018 10:31:10 -0300 Subject: [PATCH 39/45] Use named arguments to avoid overriding the password --- .../rocket/android/profile/presentation/ProfilePresenter.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/chat/rocket/android/profile/presentation/ProfilePresenter.kt b/app/src/main/java/chat/rocket/android/profile/presentation/ProfilePresenter.kt index 8017b6f68b..2fd2e779d8 100644 --- a/app/src/main/java/chat/rocket/android/profile/presentation/ProfilePresenter.kt +++ b/app/src/main/java/chat/rocket/android/profile/presentation/ProfilePresenter.kt @@ -58,7 +58,8 @@ class ProfilePresenter @Inject constructor(private val view: ProfileView, if(avatarUrl!="") { retryIO { client.setAvatar(avatarUrl) } } - val user = retryIO { client.updateProfile(myselfId, email, name, username) } + val user = retryIO { client.updateProfile( + userId = myselfId, email = email, name = name, username = username) } view.showProfileUpdateSuccessfullyMessage() loadUserProfile() } catch (exception: RocketChatException) { From 46302916c11025ba8bdc95eec833ef8ad6b37b83 Mon Sep 17 00:00:00 2001 From: Filipe de Lima Brito Date: Fri, 1 Jun 2018 10:41:29 -0300 Subject: [PATCH 40/45] Fix file list. --- .../chat/rocket/android/files/viewmodel/FileViewModel.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/chat/rocket/android/files/viewmodel/FileViewModel.kt b/app/src/main/java/chat/rocket/android/files/viewmodel/FileViewModel.kt index 6c89cdc9cc..7ae3507788 100644 --- a/app/src/main/java/chat/rocket/android/files/viewmodel/FileViewModel.kt +++ b/app/src/main/java/chat/rocket/android/files/viewmodel/FileViewModel.kt @@ -41,9 +41,10 @@ class FileViewModel( } private fun getFileUploadDate(): String { - return DateTimeHelper.getDateTime( - DateTimeHelper.getLocalDateTime(genericAttachment.uploadedAt) - ) + genericAttachment.uploadedAt?.let { + return DateTimeHelper.getDateTime(DateTimeHelper.getLocalDateTime(it)) + } + return "" } private fun getFileUrl(): String? { From f6ad8e18a752a09eea1ef5136b6500c3f7517089 Mon Sep 17 00:00:00 2001 From: Rafael Kellermann Streit Date: Fri, 1 Jun 2018 19:09:38 -0300 Subject: [PATCH 41/45] Increment version of the Beta --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index cc2b87e321..dfb23860af 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,7 +13,7 @@ android { applicationId "chat.rocket.android" minSdkVersion 21 targetSdkVersion versions.targetSdk - versionCode 2022 + versionCode 2023 versionName "2.3.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" multiDexEnabled true From d2967ec0d7f1b52115cddd8860079a88044caf4a Mon Sep 17 00:00:00 2001 From: Filipe de Lima Brito Date: Fri, 1 Jun 2018 20:58:46 -0300 Subject: [PATCH 42/45] Improves the Google For Smart Lock feature on the app. --- .../login/presentation/LoginPresenter.kt | 6 +- .../login/presentation/LoginView.kt | 4 +- .../authentication/login/ui/LoginFragment.kt | 216 ++++-------------- .../signup/presentation/SignupPresenter.kt | 6 +- .../signup/presentation/SignupView.kt | 4 +- .../signup/ui/SignupFragment.kt | 58 ++--- .../rocket/android/helper/SmartLockHelper.kt | 146 ++++++++++++ .../layout/fragment_authentication_log_in.xml | 11 + 8 files changed, 226 insertions(+), 225 deletions(-) create mode 100644 app/src/main/java/chat/rocket/android/helper/SmartLockHelper.kt diff --git a/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginPresenter.kt b/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginPresenter.kt index 1a02c13934..0c24fc78ad 100644 --- a/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginPresenter.kt +++ b/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginPresenter.kt @@ -55,7 +55,6 @@ class LoginPresenter @Inject constructor( private lateinit var credentialSecret: String private lateinit var deepLinkUserId: String private lateinit var deepLinkToken: String - private var loginCredentials: Credential? = null fun setupView() { setupConnectionInfo(currentServer) @@ -330,10 +329,7 @@ class LoginPresenter @Inject constructor( saveToken(token) registerPushToken() if (loginType == TYPE_LOGIN_USER_EMAIL) { - loginCredentials = Credential.Builder(usernameOrEmail) - .setPassword(password) - .build() - view.saveSmartLockCredentials(loginCredentials) + view.saveSmartLockCredentials(usernameOrEmail, password) } navigator.toChatList() } else if (loginType == TYPE_LOGIN_OAUTH) { diff --git a/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginView.kt b/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginView.kt index ff33b7e18c..6bf56c31ae 100644 --- a/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginView.kt +++ b/app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginView.kt @@ -226,7 +226,7 @@ interface LoginView : LoadingView, MessageView { fun alertWrongPassword() /** - * Save credentials via google smart lock + * Saves Google Smart Lock credentials. */ - fun saveSmartLockCredentials(loginCredential: Credential?) + fun saveSmartLockCredentials(id: String, password: String) } \ No newline at end of file diff --git a/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt b/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt index 558acec9c4..185a14481c 100644 --- a/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt +++ b/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt @@ -2,28 +2,27 @@ package chat.rocket.android.authentication.login.ui import DrawableHelper import android.app.Activity -import android.app.PendingIntent import android.content.Intent -import android.content.IntentSender import android.graphics.PorterDuff import android.os.Build import android.os.Bundle import android.support.v4.app.Fragment -import android.support.v4.app.FragmentActivity import android.text.style.ClickableSpan import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.ViewTreeObserver -import android.widget.* +import android.widget.Button +import android.widget.ImageButton +import android.widget.LinearLayout +import android.widget.ScrollView import androidx.core.view.isVisible import androidx.core.view.postDelayed import chat.rocket.android.R import chat.rocket.android.authentication.domain.model.LoginDeepLinkInfo import chat.rocket.android.authentication.login.presentation.LoginPresenter import chat.rocket.android.authentication.login.presentation.LoginView -import chat.rocket.android.helper.KeyboardHelper -import chat.rocket.android.helper.TextHelper +import chat.rocket.android.helper.* import chat.rocket.android.util.extensions.* import chat.rocket.android.webview.cas.ui.INTENT_CAS_TOKEN import chat.rocket.android.webview.cas.ui.casWebViewIntent @@ -31,37 +30,24 @@ import chat.rocket.android.webview.oauth.ui.INTENT_OAUTH_CREDENTIAL_SECRET import chat.rocket.android.webview.oauth.ui.INTENT_OAUTH_CREDENTIAL_TOKEN import chat.rocket.android.webview.oauth.ui.oauthWebViewIntent import chat.rocket.common.util.ifNull -import com.google.android.gms.auth.api.Auth import com.google.android.gms.auth.api.credentials.* -import com.google.android.gms.common.api.CommonStatusCodes -import com.google.android.gms.common.api.GoogleApiClient -import com.google.android.gms.common.api.ResolvingResultCallbacks -import com.google.android.gms.common.api.Status import dagger.android.support.AndroidSupportInjection import kotlinx.android.synthetic.main.fragment_authentication_log_in.* -import timber.log.Timber import javax.inject.Inject +internal const val REQUEST_CODE_FOR_CAS = 4 +internal const val REQUEST_CODE_FOR_OAUTH = 5 -internal const val REQUEST_CODE_FOR_CAS = 1 -internal const val REQUEST_CODE_FOR_OAUTH = 2 -internal const val MULTIPLE_CREDENTIALS_READ = 3 -internal const val NO_CREDENTIALS_EXIST = 4 -internal const val SAVE_CREDENTIALS = 5 - -lateinit var googleApiClient: GoogleApiClient - -class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks { +class LoginFragment : Fragment(), LoginView { @Inject lateinit var presenter: LoginPresenter private var isOauthViewEnable = false private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener { areLoginOptionsNeeded() } - private var isOauthSuccessful = false private var isGlobalLayoutListenerSetUp = false private var deepLinkInfo: LoginDeepLinkInfo? = null - private var credentialsToBeSaved: Credential? = null + private val credentialsClient by lazy { Credentials.getClient(requireActivity()) } companion object { private const val DEEP_LINK_INFO = "DeepLinkInfo" @@ -73,17 +59,9 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks } } - override fun onConnected(bundle: Bundle?) { - saveSmartLockCredentials(credentialsToBeSaved) - } - - override fun onConnectionSuspended(errorCode: Int) { - } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) AndroidSupportInjection.inject(this) - buildGoogleApiClient() deepLinkInfo = arguments?.getParcelable(DEEP_LINK_INFO) } @@ -120,153 +98,37 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks if (resultCode == Activity.RESULT_OK) { if (data != null) { when (requestCode) { - REQUEST_CODE_FOR_CAS -> data.apply { - presenter.authenticateWithCas(getStringExtra(INTENT_CAS_TOKEN)) + REQUEST_CODE_FOR_MULTIPLE_ACCOUNTS_RESOLUTION -> { + onCredentialRetrieved(data.getParcelableExtra(Credential.EXTRA_KEY)) } - REQUEST_CODE_FOR_OAUTH -> { - isOauthSuccessful = true - data.apply { - presenter.authenticateWithOauth( - getStringExtra(INTENT_OAUTH_CREDENTIAL_TOKEN), - getStringExtra(INTENT_OAUTH_CREDENTIAL_SECRET) - ) - } + REQUEST_CODE_FOR_SIGN_IN_REQUIRED -> { + //use the hints to autofill sign in forms to reduce the info to be filled. + val credential: Credential = data.getParcelableExtra(Credential.EXTRA_KEY) + text_username_or_email.setText(credential.id) + text_password.setText(credential.password) } - MULTIPLE_CREDENTIALS_READ -> { - val loginCredentials: Credential = - data.getParcelableExtra(Credential.EXTRA_KEY) - handleCredential(loginCredentials) + REQUEST_CODE_FOR_SAVE_RESOLUTION -> { + showMessage(getString(R.string.message_credentials_saved_successfully)) } - NO_CREDENTIALS_EXIST -> { - //use the hints to autofill sign in forms to reduce the info to be filled - val loginCredentials: Credential = - data.getParcelableExtra(Credential.EXTRA_KEY) - val email = loginCredentials.id - val password = loginCredentials.password - - text_username_or_email.setText(email) - text_password.setText(password) + REQUEST_CODE_FOR_CAS -> { + presenter.authenticateWithCas(data.getStringExtra(INTENT_CAS_TOKEN)) } - SAVE_CREDENTIALS -> Toast.makeText( - context, - getString(R.string.message_credentials_saved_successfully), - Toast.LENGTH_SHORT - ).show() - } - } - } - //cancel button pressed by the user in case of reading from smart lock - else if (resultCode == Activity.RESULT_CANCELED && requestCode == REQUEST_CODE_FOR_OAUTH) { - Timber.d("Returned from oauth") - } - } - - override fun onDestroy() { - super.onDestroy() - googleApiClient.let { - activity?.let { it1 -> it.stopAutoManage(it1) } - it.disconnect() - } - } - - private fun buildGoogleApiClient() { - googleApiClient = GoogleApiClient.Builder(context!!) - .enableAutoManage(activity as FragmentActivity, { - Timber.e("ERROR: Connection to client failed") - }) - .addConnectionCallbacks(this) - .addApi(Auth.CREDENTIALS_API) - .build() - } - - override fun onStart() { - super.onStart() - if (!isOauthSuccessful) { - requestCredentials() - } - } - - private fun requestCredentials() { - val request: CredentialRequest = CredentialRequest.Builder() - .setPasswordLoginSupported(true) - .build() - - Auth.CredentialsApi.request(googleApiClient, request) - .setResultCallback { credentialRequestResult -> - val status = credentialRequestResult.status - when { - status.isSuccess -> handleCredential(credentialRequestResult.credential) - (status.statusCode == CommonStatusCodes.RESOLUTION_REQUIRED) -> resolveResult( - status, - MULTIPLE_CREDENTIALS_READ - ) - (status.statusCode == CommonStatusCodes.SIGN_IN_REQUIRED) -> { - val hintRequest: HintRequest = HintRequest.Builder() - .setHintPickerConfig( - CredentialPickerConfig.Builder() - .setShowCancelButton(true) - .build() - ) - .setEmailAddressIdentifierSupported(true) - .setAccountTypes(IdentityProviders.GOOGLE) - .build() - val intent: PendingIntent = - Auth.CredentialsApi.getHintPickerIntent(googleApiClient, hintRequest) - try { - startIntentSenderForResult( - intent.intentSender, - NO_CREDENTIALS_EXIST, - null, - 0, - 0, - 0, - null - ) - } catch (e: IntentSender.SendIntentException) { - Timber.e("ERROR: Could not start hint picker Intent") - } + REQUEST_CODE_FOR_OAUTH -> { + presenter.authenticateWithOauth( + data.getStringExtra(INTENT_OAUTH_CREDENTIAL_TOKEN), + data.getStringExtra(INTENT_OAUTH_CREDENTIAL_SECRET) + ) } - else -> Timber.d("ERROR: nothing happening") } } - } - - private fun handleCredential(loginCredentials: Credential) { - if (loginCredentials.accountType == null) { - presenter.authenticateWithUserAndPassword( - loginCredentials.id, - loginCredentials.password.toString() - ) } } - private fun resolveResult(status: Status, requestCode: Int) { - try { - status.startResolutionForResult(activity, requestCode) - } catch (e: IntentSender.SendIntentException) { - Timber.e("Failed to send Credentials intent") - } - } - - override fun saveSmartLockCredentials(loginCredential: Credential?) { - credentialsToBeSaved = loginCredential - if (credentialsToBeSaved == null) { - return - } - - activity?.let { - Auth.CredentialsApi.save(googleApiClient, credentialsToBeSaved).setResultCallback( - object : ResolvingResultCallbacks(it, SAVE_CREDENTIALS) { - override fun onSuccess(status: Status) { - Timber.d("credentials save:SUCCESS:$status") - credentialsToBeSaved = null - } - - override fun onUnresolvableFailure(status: Status) { - Timber.e("credentials save:FAILURE:$status") - credentialsToBeSaved = null - } - }) + override fun onResume() { + super.onResume() + image_key.setOnClickListener { + requestStoredCredentials() + image_key.isVisible = false } } @@ -286,6 +148,24 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks } } + private fun requestStoredCredentials() { + activity?.let { + SmartLockHelper.requestStoredCredentials(credentialsClient, it)?.let { + onCredentialRetrieved(it) + } + } + } + + private fun onCredentialRetrieved(credential: Credential) { + presenter.authenticateWithUserAndPassword(credential.id, credential.password.toString()) + } + + override fun saveSmartLockCredentials(id: String, password: String) { + activity?.let { + SmartLockHelper.save(credentialsClient, it, id, password) + } + } + override fun showLoading() { ui { view_loading.isVisible = true diff --git a/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupPresenter.kt b/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupPresenter.kt index da2c69156e..38df71b42c 100644 --- a/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupPresenter.kt +++ b/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupPresenter.kt @@ -15,7 +15,6 @@ import chat.rocket.core.internal.rest.login import chat.rocket.core.internal.rest.me import chat.rocket.core.internal.rest.signup import chat.rocket.core.model.Myself -import com.google.android.gms.auth.api.credentials.Credential import javax.inject.Inject class SignupPresenter @Inject constructor( @@ -64,10 +63,7 @@ class SignupPresenter @Inject constructor( localRepository.save(LocalRepository.CURRENT_USERNAME_KEY, me.username) saveAccount(me) registerPushToken() - val loginCredentials = Credential.Builder(email) - .setPassword(password) - .build() - view.saveSmartLockCredentials(loginCredentials) + view.saveSmartLockCredentials(username, password) navigator.toChatList() } catch (exception: RocketChatException) { exception.message?.let { diff --git a/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupView.kt b/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupView.kt index 8730e84b1b..5feb798f46 100644 --- a/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupView.kt +++ b/app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupView.kt @@ -27,7 +27,7 @@ interface SignupView : LoadingView, MessageView { fun alertBlankEmail() /** - * Save credentials via google smart lock + * Saves Google Smart Lock credentials. */ - fun saveSmartLockCredentials(loginCredential: Credential) + fun saveSmartLockCredentials(id: String, password: String) } \ No newline at end of file diff --git a/app/src/main/java/chat/rocket/android/authentication/signup/ui/SignupFragment.kt b/app/src/main/java/chat/rocket/android/authentication/signup/ui/SignupFragment.kt index 975f099f74..5e57d860da 100644 --- a/app/src/main/java/chat/rocket/android/authentication/signup/ui/SignupFragment.kt +++ b/app/src/main/java/chat/rocket/android/authentication/signup/ui/SignupFragment.kt @@ -1,7 +1,7 @@ package chat.rocket.android.authentication.signup.ui import DrawableHelper -import android.app.Activity.RESULT_OK +import android.app.Activity import android.content.Intent import android.os.Build import android.os.Bundle @@ -11,30 +11,24 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.ViewTreeObserver -import android.widget.Toast import chat.rocket.android.R -import chat.rocket.android.authentication.login.ui.googleApiClient +import chat.rocket.android.R.string.message_credentials_saved_successfully import chat.rocket.android.authentication.signup.presentation.SignupPresenter import chat.rocket.android.authentication.signup.presentation.SignupView import chat.rocket.android.helper.KeyboardHelper +import chat.rocket.android.helper.SmartLockHelper import chat.rocket.android.helper.TextHelper import chat.rocket.android.util.extensions.* -import com.google.android.gms.auth.api.Auth -import com.google.android.gms.auth.api.credentials.Credential -import com.google.android.gms.common.api.ResolvingResultCallbacks -import com.google.android.gms.common.api.Status +import com.google.android.gms.auth.api.credentials.Credentials import dagger.android.support.AndroidSupportInjection import kotlinx.android.synthetic.main.fragment_authentication_sign_up.* -import timber.log.Timber import javax.inject.Inject internal const val SAVE_CREDENTIALS = 1 class SignupFragment : Fragment(), SignupView { - @Inject lateinit var presenter: SignupPresenter - private lateinit var credentialsToBeSaved: Credential private val layoutListener = ViewTreeObserver.OnGlobalLayoutListener { if (KeyboardHelper.isSoftKeyboardShown(relative_layout.rootView)) { bottom_container.setVisible(false) @@ -120,44 +114,16 @@ class SignupFragment : Fragment(), SignupView { } } - override fun saveSmartLockCredentials(loginCredential: Credential) { - credentialsToBeSaved = loginCredential - googleApiClient.let { - if (it.isConnected) { - saveCredentials() - } - } - } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (requestCode == SAVE_CREDENTIALS) { - if (resultCode == RESULT_OK) { - Toast.makeText( - context, - getString(R.string.message_credentials_saved_successfully), - Toast.LENGTH_SHORT - ).show() - } else { - Timber.e("ERROR: Cancelled by user") + if (resultCode == Activity.RESULT_OK) { + if (data != null) { + if (requestCode == SAVE_CREDENTIALS) { + showMessage(getString(message_credentials_saved_successfully)) + } } } } - private fun saveCredentials() { - activity?.let { - Auth.CredentialsApi.save(googleApiClient, credentialsToBeSaved).setResultCallback( - object : ResolvingResultCallbacks(it, SAVE_CREDENTIALS) { - override fun onSuccess(status: Status) { - Timber.d("save:SUCCESS:$status") - } - - override fun onUnresolvableFailure(status: Status) { - Timber.e("save:FAILURE:$status") - } - }) - } - } - override fun showLoading() { ui { enableUserInput(false) @@ -188,6 +154,12 @@ class SignupFragment : Fragment(), SignupView { showMessage(getString(R.string.msg_generic_error)) } + override fun saveSmartLockCredentials(id: String, password: String) { + activity?.let { + SmartLockHelper.save(Credentials.getClient(it), it, id, password) + } + } + private fun tintEditTextDrawableStart() { ui { val personDrawable = diff --git a/app/src/main/java/chat/rocket/android/helper/SmartLockHelper.kt b/app/src/main/java/chat/rocket/android/helper/SmartLockHelper.kt new file mode 100644 index 0000000000..6c09f5deff --- /dev/null +++ b/app/src/main/java/chat/rocket/android/helper/SmartLockHelper.kt @@ -0,0 +1,146 @@ +package chat.rocket.android.helper + +import android.app.Activity +import android.content.IntentSender +import android.support.v4.app.FragmentActivity +import com.google.android.gms.auth.api.credentials.* +import com.google.android.gms.common.api.CommonStatusCodes +import com.google.android.gms.common.api.ResolvableApiException +import timber.log.Timber + +const val REQUEST_CODE_FOR_SIGN_IN_REQUIRED = 1 +const val REQUEST_CODE_FOR_MULTIPLE_ACCOUNTS_RESOLUTION = 2 +const val REQUEST_CODE_FOR_SAVE_RESOLUTION = 3 + +/** + * This class handles some cases of Google Smart Lock for passwords like the request to retrieve + * credentials, to retrieve sign-in hints and to store the credentials. + * + * See https://developers.google.com/identity/smartlock-passwords/android/overview for futher + * information. + */ +object SmartLockHelper { + + /** + * Requests for stored Google Smart Lock credentials. + * Note that in case of exception it will try to start a sign in + * ([REQUEST_CODE_FOR_SIGN_IN_REQUIRED]) or "multiple account" + * ([REQUEST_CODE_FOR_MULTIPLE_ACCOUNTS_RESOLUTION]) resolution. + * + * @param credentialsClient The credential client. + * @param activity The activity. + * @return null or the [Credential] result. + */ + fun requestStoredCredentials( + credentialsClient: CredentialsClient, + activity: Activity + ): Credential? { + var credential: Credential? = null + + val credentialRequest = CredentialRequest.Builder() + .setPasswordLoginSupported(true) + .build() + + credentialsClient.request(credentialRequest) + .addOnCompleteListener { + when { + it.isSuccessful -> { + credential = it.result.credential + } + it.exception is ResolvableApiException -> { + val resolvableApiException = (it.exception as ResolvableApiException) + if (resolvableApiException.statusCode == CommonStatusCodes.SIGN_IN_REQUIRED) { + provideSignInHint(credentialsClient, activity) + } else { + // This is most likely the case where the user has multiple saved + // credentials and needs to pick one. This requires showing UI to + // resolve the read request. + resolveResult( + resolvableApiException, + REQUEST_CODE_FOR_MULTIPLE_ACCOUNTS_RESOLUTION, + activity + ) + } + } + } + } + return credential + } + + /** + * Saves a user credential to Google Smart Lock. + * Note that in case of exception it will try to start a save resolution, + * so the activity/fragment should expected for a request code + * ([REQUEST_CODE_FOR_SAVE_RESOLUTION]) on onActivityResult call. + * + * @param credentialsClient The credential client. + * @param activity The activity. + * @param id The user id credential. + * @param password The user password credential. + */ + fun save( + credentialsClient: CredentialsClient, + activity: FragmentActivity, + id: String, + password: String + ) { + val credential = Credential.Builder(id) + .setPassword(password) + .build() + + credentialsClient.save(credential) + .addOnCompleteListener { + val exception = it.exception + if (exception is ResolvableApiException) { + // Try to resolve the save request. This will prompt the user if + // the credential is new. + try { + exception.startResolutionForResult( + activity, + REQUEST_CODE_FOR_SAVE_RESOLUTION + ) + } catch (e: IntentSender.SendIntentException) { + Timber.e("Failed to send resolution. Exception is: $e") + } + } + } + } + + private fun provideSignInHint(credentialsClient: CredentialsClient, activity: Activity) { + val hintRequest = HintRequest.Builder() + .setHintPickerConfig( + CredentialPickerConfig.Builder() + .setShowCancelButton(true) + .build() + ) + .setEmailAddressIdentifierSupported(true) + .build() + + try { + val intent = credentialsClient.getHintPickerIntent(hintRequest) + activity.startIntentSenderForResult( + intent.intentSender, + REQUEST_CODE_FOR_SIGN_IN_REQUIRED, + null, + 0, + 0, + 0, + null + ) + } catch (e: IntentSender.SendIntentException) { + Timber.e("Could not start hint picker Intent. Exception is: $e") + } + } + + private fun resolveResult( + exception: ResolvableApiException, + requestCode: Int, + activity: Activity + ) { + try { + exception.startResolutionForResult(activity, requestCode) + } catch (e: IntentSender.SendIntentException) { + Timber.e("Failed to send resolution. Exception is: $e") + } + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_authentication_log_in.xml b/app/src/main/res/layout/fragment_authentication_log_in.xml index f8e85a5b22..335646a013 100644 --- a/app/src/main/res/layout/fragment_authentication_log_in.xml +++ b/app/src/main/res/layout/fragment_authentication_log_in.xml @@ -32,6 +32,17 @@ app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@+id/text_headline" /> + + Date: Fri, 1 Jun 2018 21:07:11 -0300 Subject: [PATCH 43/45] Shows the key icon only the the login form is enabled. --- .../rocket/android/authentication/login/ui/LoginFragment.kt | 1 + app/src/main/res/layout/fragment_authentication_log_in.xml | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt b/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt index 185a14481c..3d4675f229 100644 --- a/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt +++ b/app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt @@ -198,6 +198,7 @@ class LoginFragment : Fragment(), LoginView { ui { text_username_or_email.isVisible = true text_password.isVisible = true + image_key.isVisible = true } } diff --git a/app/src/main/res/layout/fragment_authentication_log_in.xml b/app/src/main/res/layout/fragment_authentication_log_in.xml index 335646a013..6a6f15443d 100644 --- a/app/src/main/res/layout/fragment_authentication_log_in.xml +++ b/app/src/main/res/layout/fragment_authentication_log_in.xml @@ -36,9 +36,10 @@ android:id="@+id/image_key" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_marginEnd="10dp" android:src="@drawable/ic_vpn_key_black_24dp" android:tint="@color/colorDrawableTintGrey" - android:layout_marginEnd="10dp" + android:visibility="gone" app:layout_constraintBottom_toBottomOf="@+id/text_username_or_email" app:layout_constraintEnd_toEndOf="@+id/text_username_or_email" app:layout_constraintTop_toTopOf="@+id/text_username_or_email" /> From bf2c54960aba21ff5a5d64e59feb8b48e0a93eea Mon Sep 17 00:00:00 2001 From: Rafael Kellermann Streit Date: Fri, 1 Jun 2018 21:32:00 -0300 Subject: [PATCH 44/45] Increment app version again --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index dfb23860af..4a28000edc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,7 +13,7 @@ android { applicationId "chat.rocket.android" minSdkVersion 21 targetSdkVersion versions.targetSdk - versionCode 2023 + versionCode 2024 versionName "2.3.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" multiDexEnabled true From b1fc4c56639841ae07bc4f6b68c5ef47a0ab6621 Mon Sep 17 00:00:00 2001 From: Filipe de Lima Brito Date: Mon, 4 Jun 2018 14:44:23 -0300 Subject: [PATCH 45/45] Update recommended server version. --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 4a28000edc..52e14139a3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -38,7 +38,7 @@ android { buildTypes { release { buildConfigField "String", "REQUIRED_SERVER_VERSION", '"0.62.0"' - buildConfigField "String", "RECOMMENDED_SERVER_VERSION", '"0.63.0"' + buildConfigField "String", "RECOMMENDED_SERVER_VERSION", '"0.64.2"' signingConfig signingConfigs.release minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' @@ -46,7 +46,7 @@ android { debug { buildConfigField "String", "REQUIRED_SERVER_VERSION", '"0.62.0"' - buildConfigField "String", "RECOMMENDED_SERVER_VERSION", '"0.63.0"' + buildConfigField "String", "RECOMMENDED_SERVER_VERSION", '"0.64.2"' signingConfig signingConfigs.debug applicationIdSuffix ".dev" }