Unverified Commit 00aa5de8 authored by Shailesh Baldaniya's avatar Shailesh Baldaniya Committed by GitHub

Merge branch 'develop' into upstrem-button-action

parents 611b0086 7301cde4
...@@ -64,7 +64,7 @@ jobs: ...@@ -64,7 +64,7 @@ jobs:
- store_artifacts: - store_artifacts:
path: app/build/reports/ path: app/build/reports/
destination: reports destination: reports
build-apk: build-play-apk:
docker: docker:
- image: circleci/android:api-27-alpha - image: circleci/android:api-27-alpha
environment: environment:
...@@ -93,7 +93,40 @@ jobs: ...@@ -93,7 +93,40 @@ jobs:
- run: - run:
name: Build APK name: Build APK
command: | command: |
./gradlew assembleRelease --info --console=plain --stacktrace ./gradlew assemblePlayRelease --info --console=plain --stacktrace
- store_artifacts:
path: app/build/outputs/apk
destination: apks
build-foss-apk:
docker:
- image: circleci/android:api-27-alpha
environment:
JVM_OPTS: -Xmx3200m
steps:
- checkout
- run:
name: restore files from ENV
command: |
echo $ROCKET_JKS_BASE64 | base64 --decode > Rocket.jks
echo $ROCKET_PLAY_JSON | base64 --decode > app/rocket-chat.json
- run:
name: checkout Rocket.Chat.Kotlin.SDK
command: git clone https://github.com/RocketChat/Rocket.Chat.Kotlin.SDK.git ../Rocket.Chat.Kotlin.SDK
- restore_cache:
key: kotlin-sdk-{{ .Revision }}
- restore_cache:
key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}-{{ checksum "player/build.gradle" }}
- run:
name: Download Dependencies
command: ./gradlew androidDependencies --quiet --console=plain
- save_cache:
paths:
- ~/.gradle
key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}-{{ checksum "player/build.gradle" }}
- run:
name: Build APK
command: |
./gradlew assembleFossRelease --info --console=plain --stacktrace
- store_artifacts: - store_artifacts:
path: app/build/outputs/apk path: app/build/outputs/apk
destination: apks destination: apks
...@@ -112,6 +145,9 @@ workflows: ...@@ -112,6 +145,9 @@ workflows:
- develop - develop
- develop-2.x - develop-2.x
- master - master
- build-apk: - build-play-apk:
requires:
- build-kotlin-sdk
- build-foss-apk:
requires: requires:
- build-kotlin-sdk - build-kotlin-sdk
#!/bin/bash #!/bin/bash
CURRENT_DIR=$(pwd) CURRENT_DIR=$(pwd)
# The SDK dir should be 2 directories up in the tree, so we use dirname 2 times
# to get the common parent dir of the SDK and the app
GIT=$(which git) GIT=$(which git)
cd ../..
tmp=$(pwd) if [ "$#" -eq 1 ] && [ ! -z "$1" ]; then
SDK_DIR="$tmp/Rocket.Chat.Kotlin.SDK" # if in an argument is given this is the (relative) path to SDK_DIR
cd "${CURRENT_DIR}" SDK_DIR=$(readlink -f $1)
else
# The SDK dir should be 2 directories up in the tree, so we use dirname 2 times
# to get the common parent dir of the SDK and the app
cd ../..
tmp=$(pwd)
SDK_DIR="$tmp/Rocket.Chat.Kotlin.SDK"
cd "${CURRENT_DIR}"
fi
echo "CURRENT DIR: $CURRENT_DIR" echo "CURRENT DIR: $CURRENT_DIR"
echo "SDK DIR: $SDK_DIR" echo "SDK DIR: $SDK_DIR"
...@@ -99,4 +105,4 @@ cp -v "${SDK_DIR}"/core/build/libs/core-0.1-SNAPSHOT.jar "${CURRENT_DIR}"/libs/c ...@@ -99,4 +105,4 @@ cp -v "${SDK_DIR}"/core/build/libs/core-0.1-SNAPSHOT.jar "${CURRENT_DIR}"/libs/c
echo "$SHA" > "${SDK_DIR}"/.last_commit_hash echo "$SHA" > "${SDK_DIR}"/.last_commit_hash
exit 0 exit 0
\ No newline at end of file
def taskRequests = getGradle().getStartParameter().getTaskRequests().toString()
def isPlay = !(taskRequests.contains("Foss") || taskRequests.contains("foss"))
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'io.fabric' if (isPlay) { apply plugin: 'io.fabric' }
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-kapt'
...@@ -12,11 +15,15 @@ android { ...@@ -12,11 +15,15 @@ android {
applicationId "chat.rocket.android" applicationId "chat.rocket.android"
minSdkVersion versions.minSdk minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk targetSdkVersion versions.targetSdk
versionCode 2034 versionCode 2036
versionName "2.5.0" versionName "2.5.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true multiDexEnabled true
def gitSha = 'git rev-parse --short HEAD'.execute([], project.rootDir).text.trim()
def buildTime = new GregorianCalendar().format("MM-dd-yyyy' 'h:mm:ss a z")
buildConfigField "String", "GIT_SHA", "\"${gitSha}\""
javaCompileOptions { javaCompileOptions {
annotationProcessorOptions { annotationProcessorOptions {
arguments = ["room.schemaLocation": "$projectDir/schemas".toString()] arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
...@@ -57,6 +64,20 @@ android { ...@@ -57,6 +64,20 @@ android {
} }
} }
flavorDimensions "type"
productFlavors {
// includes proprietary libs
play {
dimension "type"
}
// only foss
foss {
dimension "type"
}
}
packagingOptions { packagingOptions {
exclude 'META-INF/core.kotlin_module' exclude 'META-INF/core.kotlin_module'
exclude 'META-INF/main.kotlin_module' exclude 'META-INF/main.kotlin_module'
...@@ -91,9 +112,6 @@ dependencies { ...@@ -91,9 +112,6 @@ dependencies {
kapt libraries.daggerProcessor kapt libraries.daggerProcessor
kapt libraries.daggerAndroidApt kapt libraries.daggerAndroidApt
implementation libraries.fcm
implementation libraries.playServicesAuth
implementation libraries.room implementation libraries.room
kapt libraries.roomProcessor kapt libraries.roomProcessor
implementation libraries.lifecycleExtensions implementation libraries.lifecycleExtensions
...@@ -126,7 +144,10 @@ dependencies { ...@@ -126,7 +144,10 @@ dependencies {
implementation "com.github.luciofm:livedata-ktx:b1e8bbc25a" implementation "com.github.luciofm:livedata-ktx:b1e8bbc25a"
implementation('com.crashlytics.sdk.android:crashlytics:2.9.2@aar') { playImplementation libraries.fcm
playImplementation libraries.playServicesAuth
playImplementation('com.crashlytics.sdk.android:crashlytics:2.9.4@aar') {
transitive = true transitive = true
} }
...@@ -148,13 +169,16 @@ androidExtensions { ...@@ -148,13 +169,16 @@ androidExtensions {
// FIXME - build and install the sdk into the app/libs directory // FIXME - build and install the sdk into the app/libs directory
// We were having some issues with the kapt generated files from the sdk when importing as a module // We were having some issues with the kapt generated files from the sdk when importing as a module
def sdk_location=project.properties['sdk_location'] ?: ""
task compileSdk(type:Exec) { task compileSdk(type:Exec) {
if (System.getProperty('os.name').toLowerCase(Locale.ROOT).contains('windows')) { if (System.getProperty('os.name').toLowerCase(Locale.ROOT).contains('windows')) {
commandLine 'cmd', '/c', 'build-sdk.sh' commandLine 'cmd', '/c', 'build-sdk.sh', sdk_location
} else { } else {
commandLine './build-sdk.sh' commandLine './build-sdk.sh', sdk_location
} }
} }
preBuild.dependsOn compileSdk preBuild.dependsOn compileSdk
if (isPlay) {
apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.gms.google-services'
}
#Thu Feb 15 15:50:42 BRST 2018 #Wed Aug 01 21:56:00 EDT 2018
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-all.zip
package chat.rocket.android.dagger.module
import chat.rocket.android.chatroom.di.MessageServiceProvider
import chat.rocket.android.chatroom.service.MessageService
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module abstract class ServiceBuilder {
@ContributesAndroidInjector(modules = [MessageServiceProvider::class])
abstract fun bindMessageService(): MessageService
}
package chat.rocket.android.helper
import android.app.Activity
import android.content.Intent
import androidx.fragment.app.FragmentActivity
fun FragmentActivity.saveCredentials(id: String, password: String) {
}
fun Activity.requestStoredCredentials(): Pair<String, String>? = null
fun getCredentials(data: Intent): Pair<String, String>? = null
fun hasCredentialsSupport() = false
\ No newline at end of file
package chat.rocket.android.helper
import timber.log.Timber
import android.util.Log
class CrashlyticsTree : Timber.Tree() {
override fun log(priority: Int, tag: String?, message: String, throwable: Throwable?) {
Log.println(priority, tag, message)
if (throwable != null) {
Log.e(tag,throwable.toString())
}
}
}
package chat.rocket.android.push
class FirebaseTokenService {
}
\ No newline at end of file
package chat.rocket.android.util
import android.content.Context
fun setupCrashlytics(context: Context) {
//Do absolutely nothing
}
package chat.rocket.android.util
import chat.rocket.android.main.ui.MainActivity
import chat.rocket.android.main.presentation.MainPresenter
fun refreshFCMToken(presenter: MainPresenter) {
//Do absolutely nothing
}
fun invalidateFirebaseToken(token: String) {
//Do absolutely nothing
}
\ No newline at end of file
...@@ -95,23 +95,6 @@ ...@@ -95,23 +95,6 @@
</intent-filter> </intent-filter>
</receiver> </receiver>
<service
android:name=".push.FirebaseTokenService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
</intent-filter>
</service>
<service
android:name=".push.FirebaseMessagingService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<service <service
android:name=".chatroom.service.MessageService" android:name=".chatroom.service.MessageService"
android:exported="true" android:exported="true"
...@@ -122,4 +105,4 @@ ...@@ -122,4 +105,4 @@
android:value="12ac6e94f850aaffcdff52001af77ca415d06a43" /> android:value="12ac6e94f850aaffcdff52001af77ca415d06a43" />
</application> </application>
</manifest> </manifest>
\ No newline at end of file
...@@ -4,6 +4,7 @@ import android.os.Bundle ...@@ -4,6 +4,7 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import chat.rocket.android.BuildConfig import chat.rocket.android.BuildConfig
import chat.rocket.android.R import chat.rocket.android.R
...@@ -29,8 +30,9 @@ class AboutFragment : Fragment() { ...@@ -29,8 +30,9 @@ class AboutFragment : Fragment() {
} }
private fun setupViews() { private fun setupViews() {
text_version_name.text = getString(R.string.msg_version, BuildConfig.VERSION_NAME) text_version_name.text = BuildConfig.VERSION_NAME
text_build_number.text = getString(R.string.msg_build, BuildConfig.VERSION_CODE) text_build_number.text = getString(R.string.msg_build, BuildConfig.VERSION_CODE,
BuildConfig.GIT_SHA, BuildConfig.FLAVOR)
} }
private fun setupToolbar() { private fun setupToolbar() {
......
...@@ -22,6 +22,9 @@ class AppLifecycleObserver @Inject constructor( ...@@ -22,6 +22,9 @@ class AppLifecycleObserver @Inject constructor(
@OnLifecycleEvent(Lifecycle.Event.ON_START) @OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onEnterForeground() { fun onEnterForeground() {
changeTemporaryStatus(UserStatus.Online()) changeTemporaryStatus(UserStatus.Online())
serverInteractor.get()?.let { currentServer ->
factory.create(currentServer).resetReconnectionTimer()
}
} }
@OnLifecycleEvent(Lifecycle.Event.ON_STOP) @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
......
...@@ -13,15 +13,13 @@ import chat.rocket.android.dagger.DaggerAppComponent ...@@ -13,15 +13,13 @@ import chat.rocket.android.dagger.DaggerAppComponent
import chat.rocket.android.dagger.qualifier.ForMessages import chat.rocket.android.dagger.qualifier.ForMessages
import chat.rocket.android.helper.CrashlyticsTree import chat.rocket.android.helper.CrashlyticsTree
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.infrastructure.installCrashlyticsWrapper
import chat.rocket.android.server.domain.AccountsRepository import chat.rocket.android.server.domain.AccountsRepository
import chat.rocket.android.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.SITE_URL import chat.rocket.android.server.domain.SITE_URL
import chat.rocket.android.server.domain.TokenRepository import chat.rocket.android.server.domain.TokenRepository
import chat.rocket.android.emoji.EmojiRepository import chat.rocket.android.emoji.EmojiRepository
import com.crashlytics.android.Crashlytics import chat.rocket.android.util.setupCrashlytics
import com.crashlytics.android.core.CrashlyticsCore
import com.facebook.drawee.backends.pipeline.DraweeConfig import com.facebook.drawee.backends.pipeline.DraweeConfig
import com.facebook.drawee.backends.pipeline.Fresco import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.imagepipeline.core.ImagePipelineConfig import com.facebook.imagepipeline.core.ImagePipelineConfig
...@@ -31,7 +29,6 @@ import dagger.android.DispatchingAndroidInjector ...@@ -31,7 +29,6 @@ import dagger.android.DispatchingAndroidInjector
import dagger.android.HasActivityInjector import dagger.android.HasActivityInjector
import dagger.android.HasBroadcastReceiverInjector import dagger.android.HasBroadcastReceiverInjector
import dagger.android.HasServiceInjector import dagger.android.HasServiceInjector
import io.fabric.sdk.android.Fabric
import timber.log.Timber import timber.log.Timber
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import javax.inject.Inject import javax.inject.Inject
...@@ -89,7 +86,7 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -89,7 +86,7 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
AndroidThreeTen.init(this) AndroidThreeTen.init(this)
EmojiRepository.load(this) EmojiRepository.load(this)
setupCrashlytics() setupCrashlytics(this)
setupFresco() setupFresco()
setupTimber() setupTimber()
...@@ -125,15 +122,6 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -125,15 +122,6 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
} }
} }
private fun setupCrashlytics() {
val core = CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build()
Fabric.with(this, Crashlytics.Builder().core(core).build())
installCrashlyticsWrapper(this@RocketChatApplication,
getCurrentServerInteractor, settingsInteractor,
accountRepository, localRepository)
}
private fun setupFresco() { private fun setupFresco() {
Fresco.initialize(this, imagePipelineConfig, draweeConfig) Fresco.initialize(this, imagePipelineConfig, draweeConfig)
} }
...@@ -169,4 +157,4 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje ...@@ -169,4 +157,4 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
private fun LocalRepository.needOldMessagesCleanUp() = getBoolean(CLEANUP_OLD_MESSAGES_NEEDED, true) private fun LocalRepository.needOldMessagesCleanUp() = getBoolean(CLEANUP_OLD_MESSAGES_NEEDED, true)
private fun LocalRepository.setOldMessagesCleanedUp() = save(CLEANUP_OLD_MESSAGES_NEEDED, false) private fun LocalRepository.setOldMessagesCleanedUp() = save(CLEANUP_OLD_MESSAGES_NEEDED, false)
private const val CLEANUP_OLD_MESSAGES_NEEDED = "CLEANUP_OLD_MESSAGES_NEEDED" private const val CLEANUP_OLD_MESSAGES_NEEDED = "CLEANUP_OLD_MESSAGES_NEEDED"
\ No newline at end of file
...@@ -11,8 +11,8 @@ import timber.log.Timber ...@@ -11,8 +11,8 @@ import timber.log.Timber
@Parcelize @Parcelize
data class LoginDeepLinkInfo( data class LoginDeepLinkInfo(
val url: String, val url: String,
val userId: String, val userId: String?,
val token: String val token: String?
) : Parcelable ) : Parcelable
fun Intent.getLoginDeepLinkInfo(): LoginDeepLinkInfo? { fun Intent.getLoginDeepLinkInfo(): LoginDeepLinkInfo? {
......
...@@ -2,7 +2,6 @@ package chat.rocket.android.authentication.login.presentation ...@@ -2,7 +2,6 @@ package chat.rocket.android.authentication.login.presentation
import chat.rocket.android.core.behaviours.LoadingView import chat.rocket.android.core.behaviours.LoadingView
import chat.rocket.android.core.behaviours.MessageView import chat.rocket.android.core.behaviours.MessageView
import com.google.android.gms.auth.api.credentials.Credential
interface LoginView : LoadingView, MessageView { interface LoginView : LoadingView, MessageView {
...@@ -190,6 +189,21 @@ interface LoginView : LoadingView, MessageView { ...@@ -190,6 +189,21 @@ interface LoginView : LoadingView, MessageView {
*/ */
fun setupGitlabButtonListener(gitlabUrl: String, state: String) fun setupGitlabButtonListener(gitlabUrl: String, state: String)
/**
* Shows the "login by WordPress" view if it is enable by the server settings.
*
* REMARK: We must set up the Gitlab button listener before enabling it [setupWordpressButtonListener].
*/
fun enableLoginByWordpress()
/**
* Setups the WordPress button when tapped.
*
* @param wordpressUrl The WordPress OAuth URL to authenticate with.
* @param state A random string generated by the app, which you'll verify later (to protect against forgery attacks).
*/
fun setupWordpressButtonListener(wordpressUrl: String, state: String)
/** /**
* Adds a custom OAuth button in the oauth view. * Adds a custom OAuth button in the oauth view.
* *
......
...@@ -30,11 +30,13 @@ import chat.rocket.android.webview.oauth.ui.INTENT_OAUTH_CREDENTIAL_SECRET ...@@ -30,11 +30,13 @@ import chat.rocket.android.webview.oauth.ui.INTENT_OAUTH_CREDENTIAL_SECRET
import chat.rocket.android.webview.oauth.ui.INTENT_OAUTH_CREDENTIAL_TOKEN import chat.rocket.android.webview.oauth.ui.INTENT_OAUTH_CREDENTIAL_TOKEN
import chat.rocket.android.webview.oauth.ui.oauthWebViewIntent import chat.rocket.android.webview.oauth.ui.oauthWebViewIntent
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
import com.google.android.gms.auth.api.credentials.*
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_authentication_log_in.* import kotlinx.android.synthetic.main.fragment_authentication_log_in.*
import javax.inject.Inject import javax.inject.Inject
internal const val REQUEST_CODE_FOR_SIGN_IN_REQUIRED = 1
internal const val REQUEST_CODE_FOR_MULTIPLE_ACCOUNTS_RESOLUTION = 2
internal const val REQUEST_CODE_FOR_SAVE_RESOLUTION = 3
internal const val REQUEST_CODE_FOR_CAS = 4 internal const val REQUEST_CODE_FOR_CAS = 4
internal const val REQUEST_CODE_FOR_SAML = 5 internal const val REQUEST_CODE_FOR_SAML = 5
internal const val REQUEST_CODE_FOR_OAUTH = 6 internal const val REQUEST_CODE_FOR_OAUTH = 6
...@@ -48,7 +50,6 @@ class LoginFragment : Fragment(), LoginView { ...@@ -48,7 +50,6 @@ class LoginFragment : Fragment(), LoginView {
} }
private var isGlobalLayoutListenerSetUp = false private var isGlobalLayoutListenerSetUp = false
private var deepLinkInfo: LoginDeepLinkInfo? = null private var deepLinkInfo: LoginDeepLinkInfo? = null
private val credentialsClient by lazy { Credentials.getClient(requireActivity()) }
companion object { companion object {
private const val DEEP_LINK_INFO = "DeepLinkInfo" private const val DEEP_LINK_INFO = "DeepLinkInfo"
...@@ -85,6 +86,10 @@ class LoginFragment : Fragment(), LoginView { ...@@ -85,6 +86,10 @@ class LoginFragment : Fragment(), LoginView {
}.ifNull { }.ifNull {
presenter.setupView() presenter.setupView()
} }
if (!hasCredentialsSupport()) {
image_key.isVisible = false
}
} }
override fun onDestroyView() { override fun onDestroyView() {
...@@ -100,13 +105,15 @@ class LoginFragment : Fragment(), LoginView { ...@@ -100,13 +105,15 @@ class LoginFragment : Fragment(), LoginView {
if (data != null) { if (data != null) {
when (requestCode) { when (requestCode) {
REQUEST_CODE_FOR_MULTIPLE_ACCOUNTS_RESOLUTION -> { REQUEST_CODE_FOR_MULTIPLE_ACCOUNTS_RESOLUTION -> {
onCredentialRetrieved(data.getParcelableExtra(Credential.EXTRA_KEY)) getCredentials(data)?.let {
onCredentialRetrieved(it.first, it.second)
}
} }
REQUEST_CODE_FOR_SIGN_IN_REQUIRED -> { REQUEST_CODE_FOR_SIGN_IN_REQUIRED -> {
//use the hints to autofill sign in forms to reduce the info to be filled. getCredentials(data)?.let { credential ->
val credential: Credential = data.getParcelableExtra(Credential.EXTRA_KEY) text_username_or_email.setText(credential.first)
text_username_or_email.setText(credential.id) text_password.setText(credential.second)
text_password.setText(credential.password) }
} }
REQUEST_CODE_FOR_SAVE_RESOLUTION -> { REQUEST_CODE_FOR_SAVE_RESOLUTION -> {
showMessage(getString(R.string.message_credentials_saved_successfully)) showMessage(getString(R.string.message_credentials_saved_successfully))
...@@ -154,19 +161,19 @@ class LoginFragment : Fragment(), LoginView { ...@@ -154,19 +161,19 @@ class LoginFragment : Fragment(), LoginView {
private fun requestStoredCredentials() { private fun requestStoredCredentials() {
activity?.let { activity?.let {
SmartLockHelper.requestStoredCredentials(credentialsClient, it)?.let { it.requestStoredCredentials()?.let { credentials ->
onCredentialRetrieved(it) onCredentialRetrieved(credentials.first, credentials.second)
} }
} }
} }
private fun onCredentialRetrieved(credential: Credential) { private fun onCredentialRetrieved(id: String, password: String) {
presenter.authenticateWithUserAndPassword(credential.id, credential.password.toString()) presenter.authenticateWithUserAndPassword(id, password)
} }
override fun saveSmartLockCredentials(id: String, password: String) { override fun saveSmartLockCredentials(id: String, password: String) {
activity?.let { activity?.let {
SmartLockHelper.save(credentialsClient, it, id, password) it.saveCredentials(id, password)
} }
} }
...@@ -443,6 +450,24 @@ class LoginFragment : Fragment(), LoginView { ...@@ -443,6 +450,24 @@ class LoginFragment : Fragment(), LoginView {
} }
} }
override fun enableLoginByWordpress() {
ui {
button_wordpress.isClickable = true
}
}
override fun setupWordpressButtonListener(wordpressUrl: String, state: String) {
ui { activity ->
button_wordpress.setOnClickListener {
startActivityForResult(
activity.oauthWebViewIntent(wordpressUrl, state),
REQUEST_CODE_FOR_OAUTH
)
activity.overridePendingTransition(R.anim.slide_up, R.anim.hold)
}
}
}
override fun addCustomOauthServiceButton( override fun addCustomOauthServiceButton(
customOauthUrl: String, customOauthUrl: String,
state: String, state: String,
...@@ -488,11 +513,11 @@ class LoginFragment : Fragment(), LoginView { ...@@ -488,11 +513,11 @@ class LoginFragment : Fragment(), LoginView {
override fun setupFabListener() { override fun setupFabListener() {
ui { ui {
button_fab.isVisible = true button_fab.isVisible = true
button_fab.setOnClickListener({ button_fab.setOnClickListener {
button_fab.hide() button_fab.hide()
showRemainingSocialAccountsView() showRemainingSocialAccountsView()
scrollToBottom() scrollToBottom()
}) }
} }
} }
......
...@@ -104,6 +104,8 @@ class ServerFragment : Fragment(), ServerView { ...@@ -104,6 +104,8 @@ class ServerFragment : Fragment(), ServerView {
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
// reset deep link info, so user can come back and log to another server...
deepLinkInfo = null
relative_layout.viewTreeObserver.removeOnGlobalLayoutListener(layoutListener) relative_layout.viewTreeObserver.removeOnGlobalLayoutListener(layoutListener)
} }
...@@ -144,9 +146,9 @@ class ServerFragment : Fragment(), ServerView { ...@@ -144,9 +146,9 @@ class ServerFragment : Fragment(), ServerView {
hideLoading() hideLoading()
AlertDialog.Builder(it) AlertDialog.Builder(it)
.setMessage(getString(R.string.msg_ver_not_recommended, BuildConfig.RECOMMENDED_SERVER_VERSION)) .setMessage(getString(R.string.msg_ver_not_recommended, BuildConfig.RECOMMENDED_SERVER_VERSION))
.setPositiveButton(R.string.msg_ok, { _, _ -> .setPositiveButton(R.string.msg_ok) { _, _ ->
performConnect() performConnect()
}) }
.create() .create()
.show() .show()
} }
......
...@@ -2,7 +2,6 @@ package chat.rocket.android.authentication.signup.presentation ...@@ -2,7 +2,6 @@ package chat.rocket.android.authentication.signup.presentation
import chat.rocket.android.core.behaviours.LoadingView import chat.rocket.android.core.behaviours.LoadingView
import chat.rocket.android.core.behaviours.MessageView import chat.rocket.android.core.behaviours.MessageView
import com.google.android.gms.auth.api.credentials.Credential
interface SignupView : LoadingView, MessageView { interface SignupView : LoadingView, MessageView {
......
...@@ -16,10 +16,9 @@ import chat.rocket.android.R.string.message_credentials_saved_successfully ...@@ -16,10 +16,9 @@ import chat.rocket.android.R.string.message_credentials_saved_successfully
import chat.rocket.android.authentication.signup.presentation.SignupPresenter import chat.rocket.android.authentication.signup.presentation.SignupPresenter
import chat.rocket.android.authentication.signup.presentation.SignupView import chat.rocket.android.authentication.signup.presentation.SignupView
import chat.rocket.android.helper.KeyboardHelper import chat.rocket.android.helper.KeyboardHelper
import chat.rocket.android.helper.SmartLockHelper
import chat.rocket.android.helper.TextHelper import chat.rocket.android.helper.TextHelper
import chat.rocket.android.helper.saveCredentials
import chat.rocket.android.util.extensions.* import chat.rocket.android.util.extensions.*
import com.google.android.gms.auth.api.credentials.Credentials
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_authentication_sign_up.* import kotlinx.android.synthetic.main.fragment_authentication_sign_up.*
import javax.inject.Inject import javax.inject.Inject
...@@ -156,7 +155,7 @@ class SignupFragment : Fragment(), SignupView { ...@@ -156,7 +155,7 @@ class SignupFragment : Fragment(), SignupView {
override fun saveSmartLockCredentials(id: String, password: String) { override fun saveSmartLockCredentials(id: String, password: String) {
activity?.let { activity?.let {
SmartLockHelper.save(Credentials.getClient(it), it, id, password) it.saveCredentials(id, password)
} }
} }
......
...@@ -16,7 +16,7 @@ import dagger.android.AndroidInjector ...@@ -16,7 +16,7 @@ import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector import dagger.android.DispatchingAndroidInjector
import dagger.android.support.HasSupportFragmentInjector import dagger.android.support.HasSupportFragmentInjector
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.android.UI import chat.rocket.android.util.temp.UI
import kotlinx.coroutines.experimental.launch import kotlinx.coroutines.experimental.launch
import javax.inject.Inject import javax.inject.Inject
...@@ -56,9 +56,7 @@ class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector { ...@@ -56,9 +56,7 @@ class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
val currentFragment = supportFragmentManager.findFragmentById(R.id.fragment_container) val currentFragment = supportFragmentManager.findFragmentById(R.id.fragment_container)
if (currentFragment != null) { currentFragment?.onActivityResult(requestCode, resultCode, data)
currentFragment.onActivityResult(requestCode, resultCode, data)
}
} }
override fun supportFragmentInjector(): AndroidInjector<Fragment> { override fun supportFragmentInjector(): AndroidInjector<Fragment> {
......
...@@ -6,6 +6,7 @@ import android.view.View ...@@ -6,6 +6,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.children import androidx.core.view.children
import androidx.lifecycle.Lifecycle
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.chatroom.ui.bottomsheet.MessageActionsBottomSheet import chat.rocket.android.chatroom.ui.bottomsheet.MessageActionsBottomSheet
...@@ -94,9 +95,11 @@ abstract class BaseViewHolder<T : BaseUiModel<*>>( ...@@ -94,9 +95,11 @@ abstract class BaseViewHolder<T : BaseUiModel<*>>(
view.context?.let { view.context?.let {
if (it is ContextThemeWrapper && it.baseContext is AppCompatActivity) { if (it is ContextThemeWrapper && it.baseContext is AppCompatActivity) {
with(it.baseContext as AppCompatActivity) { with(it.baseContext as AppCompatActivity) {
val actionsBottomSheet = MessageActionsBottomSheet() if (this.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
actionsBottomSheet.addItems(menuItems, this@BaseViewHolder) val actionsBottomSheet = MessageActionsBottomSheet()
actionsBottomSheet.show(supportFragmentManager, null) actionsBottomSheet.addItems(menuItems, this@BaseViewHolder)
actionsBottomSheet.show(supportFragmentManager, null)
}
} }
} }
} }
......
...@@ -24,10 +24,9 @@ class ChatRoomAdapter( ...@@ -24,10 +24,9 @@ class ChatRoomAdapter(
private val roomId: String? = null, private val roomId: String? = null,
private val roomType: String? = null, private val roomType: String? = null,
private val roomName: String? = null, private val roomName: String? = null,
private val presenter: ChatRoomPresenter? = null, private val actionSelectListener: OnActionSelected? = null,
private val enableActions: Boolean = true, private val enableActions: Boolean = true,
private val reactionListener: EmojiReactionListener? = null, private val reactionListener: EmojiReactionListener? = null
private val context: Context? = null
) : RecyclerView.Adapter<BaseViewHolder<*>>() { ) : RecyclerView.Adapter<BaseViewHolder<*>>() {
private val dataSet = ArrayList<BaseUiModel<*>>() private val dataSet = ArrayList<BaseUiModel<*>>()
...@@ -76,7 +75,7 @@ class ChatRoomAdapter( ...@@ -76,7 +75,7 @@ class ChatRoomAdapter(
BaseUiModel.ViewType.MESSAGE_REPLY -> { BaseUiModel.ViewType.MESSAGE_REPLY -> {
val view = parent.inflate(R.layout.item_message_reply) val view = parent.inflate(R.layout.item_message_reply)
MessageReplyViewHolder(view, actionsListener, reactionListener) { roomName, permalink -> MessageReplyViewHolder(view, actionsListener, reactionListener) { roomName, permalink ->
presenter?.openDirectMessage(roomName, permalink) actionSelectListener?.openDirectMessage(roomName, permalink)
} }
} }
BaseUiModel.ViewType.ACTIONS_ATTACHMENT -> { BaseUiModel.ViewType.ACTIONS_ATTACHMENT -> {
...@@ -251,52 +250,53 @@ class ChatRoomAdapter( ...@@ -251,52 +250,53 @@ class ChatRoomAdapter(
message.apply { message.apply {
when (item.itemId) { when (item.itemId) {
R.id.action_message_info -> { R.id.action_message_info -> {
presenter?.messageInfo(id) actionSelectListener?.showMessageInfo(id)
} }
R.id.action_message_reply -> { R.id.action_message_reply -> {
if (roomName != null && roomType != null) { if (roomName != null && roomType != null) {
presenter?.citeMessage(roomName, roomType, id, true) actionSelectListener?.citeMessage(roomName, roomType, id, true)
} }
} }
R.id.action_message_quote -> { R.id.action_message_quote -> {
if (roomName != null && roomType != null) { if (roomName != null && roomType != null) {
presenter?.citeMessage(roomName, roomType, id, false) actionSelectListener?.citeMessage(roomName, roomType, id, false)
} }
} }
R.id.action_message_copy -> { R.id.action_message_copy -> {
presenter?.copyMessage(id) actionSelectListener?.copyMessage(id)
} }
R.id.action_message_edit -> { R.id.action_message_edit -> {
presenter?.editMessage(roomId, id, message.message) actionSelectListener?.editMessage(roomId, id, message.message)
} }
R.id.action_message_star -> { R.id.action_message_star -> {
if (!item.isChecked) { actionSelectListener?.toogleStar(id, !item.isChecked)
presenter?.starMessage(id)
} else {
presenter?.unstarMessage(id)
}
} }
R.id.action_message_unpin -> { R.id.action_message_unpin -> {
if (!item.isChecked) { actionSelectListener?.tooglePin(id, !item.isChecked)
presenter?.pinMessage(id)
} else {
presenter?.unpinMessage(id)
}
} }
R.id.action_message_delete -> { R.id.action_message_delete -> {
context?.let { actionSelectListener?.deleteMessage(roomId, id)
val builder = AlertDialog.Builder(it) }
builder.setTitle(it.getString(R.string.msg_delete_message)) R.id.action_menu_msg_react -> {
.setMessage(it.getString(R.string.msg_delete_description)) actionSelectListener?.showReactions(id)
.setPositiveButton(it.getString(R.string.msg_ok)) { _, _ -> presenter?.deleteMessage(roomId, id) } }
.setNegativeButton(it.getString(R.string.msg_cancel)) { _, _ -> } else -> {
.show() TODO("Not implemented")
}
} }
R.id.action_menu_msg_react -> presenter?.showReactions(id)
else -> TODO("Not implemented")
} }
} }
} }
} }
interface OnActionSelected {
fun showMessageInfo(id: String)
fun citeMessage(roomName: String, roomType: String, messageId: String, mentionAuthor: Boolean)
fun copyMessage(id: String)
fun editMessage(roomId: String, messageId: String, text: String)
fun toogleStar(id: String, star: Boolean)
fun tooglePin(id: String, pin: Boolean)
fun deleteMessage(roomId: String, id: String)
fun showReactions(id: String)
fun openDirectMessage(roomName: String, message: String)
}
} }
\ No newline at end of file
package chat.rocket.android.chatroom.adapter package chat.rocket.android.chatroom.adapter
import android.net.Uri
import android.view.View import android.view.View
import androidx.core.view.isVisible import androidx.core.view.isVisible
import chat.rocket.android.chatroom.uimodel.UrlPreviewUiModel import chat.rocket.android.chatroom.uimodel.UrlPreviewUiModel
import chat.rocket.android.util.extensions.openTabbedUrl
import chat.rocket.android.emoji.EmojiReactionListener import chat.rocket.android.emoji.EmojiReactionListener
import chat.rocket.android.util.extensions.content import chat.rocket.android.util.extensions.content
import chat.rocket.android.util.extensions.openTabbedUrl
import kotlinx.android.synthetic.main.message_url_preview.view.* import kotlinx.android.synthetic.main.message_url_preview.view.*
class UrlPreviewViewHolder(itemView: View, class UrlPreviewViewHolder(itemView: View,
...@@ -42,7 +41,7 @@ class UrlPreviewViewHolder(itemView: View, ...@@ -42,7 +41,7 @@ class UrlPreviewViewHolder(itemView: View,
private val onClickListener = { view: View -> private val onClickListener = { view: View ->
if (data != null) { if (data != null) {
view.openTabbedUrl(Uri.parse(data!!.rawData.url)) view.openTabbedUrl(data!!.rawData.url)
} }
} }
} }
\ No newline at end of file
...@@ -70,7 +70,7 @@ import chat.rocket.core.model.Command ...@@ -70,7 +70,7 @@ import chat.rocket.core.model.Command
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
import kotlinx.coroutines.experimental.CommonPool import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.DefaultDispatcher import kotlinx.coroutines.experimental.DefaultDispatcher
import kotlinx.coroutines.experimental.android.UI import chat.rocket.android.util.temp.UI
import kotlinx.coroutines.experimental.channels.Channel import kotlinx.coroutines.experimental.channels.Channel
import kotlinx.coroutines.experimental.launch import kotlinx.coroutines.experimental.launch
import kotlinx.coroutines.experimental.withContext import kotlinx.coroutines.experimental.withContext
...@@ -338,12 +338,9 @@ class ChatRoomPresenter @Inject constructor( ...@@ -338,12 +338,9 @@ class ChatRoomPresenter @Inject constructor(
val maxFileSizeAllowed = settings.uploadMaxFileSize() val maxFileSizeAllowed = settings.uploadMaxFileSize()
when { when {
fileName.isEmpty() -> { fileName.isEmpty() -> view.showInvalidFileMessage()
view.showInvalidFileMessage() fileSize > maxFileSizeAllowed && maxFileSizeAllowed !in -1..0 ->
}
fileSize > maxFileSizeAllowed -> {
view.showInvalidFileSize(fileSize, maxFileSizeAllowed) view.showInvalidFileSize(fileSize, maxFileSizeAllowed)
}
else -> { else -> {
var inputStream: InputStream? = uriInteractor.getInputStream(uri) var inputStream: InputStream? = uriInteractor.getInputStream(uri)
...@@ -392,9 +389,8 @@ class ChatRoomPresenter @Inject constructor( ...@@ -392,9 +389,8 @@ class ChatRoomPresenter @Inject constructor(
val maxFileSizeAllowed = settings.uploadMaxFileSize() val maxFileSizeAllowed = settings.uploadMaxFileSize()
when { when {
fileSize > maxFileSizeAllowed -> { fileSize > maxFileSizeAllowed && maxFileSizeAllowed !in -1..0 ->
view.showInvalidFileSize(fileSize, maxFileSizeAllowed) view.showInvalidFileSize(fileSize, maxFileSizeAllowed)
}
else -> { else -> {
retryIO("uploadFile($roomId, $fileName, $mimeType") { retryIO("uploadFile($roomId, $fileName, $mimeType") {
client.uploadFile( client.uploadFile(
......
...@@ -126,7 +126,8 @@ internal const val MENU_ACTION_PINNED_MESSAGES = 4 ...@@ -126,7 +126,8 @@ internal const val MENU_ACTION_PINNED_MESSAGES = 4
internal const val MENU_ACTION_FAVORITE_MESSAGES = 5 internal const val MENU_ACTION_FAVORITE_MESSAGES = 5
internal const val MENU_ACTION_FILES = 6 internal const val MENU_ACTION_FILES = 6
class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiReactionListener { class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiReactionListener,
ChatRoomAdapter.OnActionSelected {
@Inject @Inject
lateinit var presenter: ChatRoomPresenter lateinit var presenter: ChatRoomPresenter
@Inject @Inject
...@@ -200,6 +201,9 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -200,6 +201,9 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
} else { } else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" } requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
} }
adapter = ChatRoomAdapter(chatRoomType, chatRoomName, this,
reactionListener = this)
} }
override fun onCreateView( override fun onCreateView(
...@@ -968,4 +972,55 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR ...@@ -968,4 +972,55 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
private fun setupToolbar(toolbarTitle: String) { private fun setupToolbar(toolbarTitle: String) {
(activity as ChatRoomActivity).showToolbarTitle(toolbarTitle) (activity as ChatRoomActivity).showToolbarTitle(toolbarTitle)
} }
override fun showMessageInfo(id: String) {
presenter.messageInfo(id)
}
override fun citeMessage(roomName: String, roomType: String, messageId: String, mentionAuthor: Boolean) {
presenter.citeMessage(roomName, roomType, messageId, mentionAuthor)
}
override fun copyMessage(id: String) {
presenter.copyMessage(id)
}
override fun editMessage(roomId: String, messageId: String, text: String) {
presenter.editMessage(roomId, messageId, text)
}
override fun toogleStar(id: String, star: Boolean) {
if (star) {
presenter.starMessage(id)
} else {
presenter.unstarMessage(id)
}
}
override fun tooglePin(id: String, pin: Boolean) {
if (pin) {
presenter.pinMessage(id)
} else {
presenter.unpinMessage(id)
}
}
override fun deleteMessage(roomId: String, id: String) {
ui {
val builder = AlertDialog.Builder(it)
builder.setTitle(it.getString(R.string.msg_delete_message))
.setMessage(it.getString(R.string.msg_delete_description))
.setPositiveButton(it.getString(R.string.msg_ok)) { _, _ -> presenter.deleteMessage(roomId, id) }
.setNegativeButton(it.getString(R.string.msg_cancel)) { _, _ -> }
.show()
}
}
override fun showReactions(id: String) {
presenter.showReactions(id)
}
override fun openDirectMessage(roomName: String, message: String) {
presenter.openDirectMessage(roomName, message)
}
} }
...@@ -216,7 +216,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView { ...@@ -216,7 +216,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
AlertDialog.Builder(context) AlertDialog.Builder(context)
.setTitle(R.string.dialog_sort_title) .setTitle(R.string.dialog_sort_title)
.setView(dialogLayout) .setView(dialogLayout)
.setPositiveButton("Done") { dialog, _ -> .setPositiveButton(R.string.dialog_button_done) { dialog, _ ->
invalidateQueryOnSearch() invalidateQueryOnSearch()
updateSort() updateSort()
dialog.dismiss() dialog.dismiss()
......
...@@ -17,7 +17,7 @@ import chat.rocket.common.util.ifNull ...@@ -17,7 +17,7 @@ import chat.rocket.common.util.ifNull
import chat.rocket.core.internal.realtime.socket.model.State import chat.rocket.core.internal.realtime.socket.model.State
import chat.rocket.core.internal.rest.spotlight import chat.rocket.core.internal.rest.spotlight
import chat.rocket.core.model.SpotlightResult import chat.rocket.core.model.SpotlightResult
import kotlinx.coroutines.experimental.android.UI import chat.rocket.android.util.temp.UI
import kotlinx.coroutines.experimental.delay import kotlinx.coroutines.experimental.delay
import kotlinx.coroutines.experimental.isActive import kotlinx.coroutines.experimental.isActive
import kotlinx.coroutines.experimental.launch import kotlinx.coroutines.experimental.launch
......
...@@ -161,7 +161,7 @@ class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback ...@@ -161,7 +161,7 @@ class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback
override fun prepareToShowChatList() { override fun prepareToShowChatList() {
with(activity as MainActivity) { with(activity as MainActivity) {
setCheckedNavDrawerItem(R.id.action_chat_rooms) setCheckedNavDrawerItem(R.id.menu_action_chats)
openDrawer() openDrawer()
getDrawerLayout().postDelayed(1000) { getDrawerLayout().postDelayed(1000) {
closeDrawer() closeDrawer()
......
...@@ -330,7 +330,7 @@ class DatabaseManager(val context: Application, ...@@ -330,7 +330,7 @@ class DatabaseManager(val context: Application,
id = room.id, id = room.id,
subscriptionId = subscription.id, subscriptionId = subscription.id,
type = room.type.toString(), type = room.type.toString(),
name = room.name ?: subscription.name ?: throw NullPointerException(),// this should be filtered on the SDK name = room.name ?: subscription.name ?: throw NullPointerException(), // this should be filtered on the SDK
fullname = subscription.fullName ?: room.fullName, fullname = subscription.fullName ?: room.fullName,
userId = userId, userId = userId,
ownerId = room.user?.id, ownerId = room.user?.id,
......
...@@ -35,7 +35,7 @@ private const val INTENT_CHAT_ROOM_ID = "chat_room_id" ...@@ -35,7 +35,7 @@ private const val INTENT_CHAT_ROOM_ID = "chat_room_id"
class FavoriteMessagesFragment : Fragment(), FavoriteMessagesView { class FavoriteMessagesFragment : Fragment(), FavoriteMessagesView {
private lateinit var chatRoomId: String private lateinit var chatRoomId: String
private lateinit var adapter: ChatRoomAdapter private val adapter = ChatRoomAdapter(enableActions = false)
@Inject @Inject
lateinit var presenter: FavoriteMessagesPresenter lateinit var presenter: FavoriteMessagesPresenter
...@@ -66,7 +66,6 @@ class FavoriteMessagesFragment : Fragment(), FavoriteMessagesView { ...@@ -66,7 +66,6 @@ class FavoriteMessagesFragment : Fragment(), FavoriteMessagesView {
override fun showFavoriteMessages(favoriteMessages: List<BaseUiModel<*>>) { override fun showFavoriteMessages(favoriteMessages: List<BaseUiModel<*>>) {
ui { ui {
if (recycler_view.adapter == null) { if (recycler_view.adapter == null) {
adapter = ChatRoomAdapter(enableActions = false)
recycler_view.adapter = adapter recycler_view.adapter = adapter
val linearLayoutManager = LinearLayoutManager(context) val linearLayoutManager = LinearLayoutManager(context)
recycler_view.layoutManager = linearLayoutManager recycler_view.layoutManager = linearLayoutManager
......
...@@ -5,21 +5,19 @@ import android.content.Context ...@@ -5,21 +5,19 @@ import android.content.Context
import android.graphics.Canvas import android.graphics.Canvas
import android.graphics.Paint import android.graphics.Paint
import android.graphics.RectF import android.graphics.RectF
import android.net.Uri
import androidx.core.content.res.ResourcesCompat
import android.text.Spanned import android.text.Spanned
import android.text.style.ClickableSpan import android.text.style.ClickableSpan
import android.text.style.ReplacementSpan import android.text.style.ReplacementSpan
import android.text.style.StyleSpan
import android.util.Patterns import android.util.Patterns
import android.view.View import android.view.View
import androidx.core.content.res.ResourcesCompat
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.useRealName
import chat.rocket.android.util.extensions.openTabbedUrl
import chat.rocket.android.emoji.EmojiParser import chat.rocket.android.emoji.EmojiParser
import chat.rocket.android.emoji.EmojiRepository import chat.rocket.android.emoji.EmojiRepository
import chat.rocket.android.emoji.EmojiTypefaceSpan import chat.rocket.android.emoji.EmojiTypefaceSpan
import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.useRealName
import chat.rocket.android.util.extensions.openTabbedUrl
import chat.rocket.common.model.SimpleUser import chat.rocket.common.model.SimpleUser
import chat.rocket.core.model.Message import chat.rocket.core.model.Message
import org.commonmark.node.AbstractVisitor import org.commonmark.node.AbstractVisitor
...@@ -159,7 +157,7 @@ class MessageParser @Inject constructor( ...@@ -159,7 +157,7 @@ class MessageParser @Inject constructor(
if (node is ListItem) { if (node is ListItem) {
newLine() newLine()
builder.append("$number$delimiter ") builder.append("$number$delimiter ")
super.visit(node.firstChild as Paragraph) super.visitChildren(node)
newLine() newLine()
} }
number++ number++
...@@ -187,7 +185,7 @@ class MessageParser @Inject constructor( ...@@ -187,7 +185,7 @@ class MessageParser @Inject constructor(
if (!link.startsWith("@") && link !in consumed) { if (!link.startsWith("@") && link !in consumed) {
builder.setSpan(object : ClickableSpan() { builder.setSpan(object : ClickableSpan() {
override fun onClick(view: View) { override fun onClick(view: View) {
view.openTabbedUrl(getUri(link)) view.openTabbedUrl(link)
} }
}, matcher.start(0), matcher.end(0)) }, matcher.start(0), matcher.end(0))
consumed.add(link) consumed.add(link)
...@@ -195,14 +193,6 @@ class MessageParser @Inject constructor( ...@@ -195,14 +193,6 @@ class MessageParser @Inject constructor(
} }
visitChildren(text) visitChildren(text)
} }
private fun getUri(link: String): Uri {
val uri = Uri.parse(link)
if (uri.scheme == null) {
return Uri.parse("http://$link")
}
return uri
}
} }
class MentionSpan( class MentionSpan(
......
...@@ -92,6 +92,59 @@ object OauthHelper { ...@@ -92,6 +92,59 @@ object OauthHelper {
"&scope=email" "&scope=email"
} }
/**
* Returns the WordPress-Com Oauth URL.
*
* @param clientId The WordPress-Com client ID.
* @param serverUrl The server URL.
* @param state An unguessable random string used to protect against forgery attacks.
* @return The WordPress-Com Oauth URL.
*/
fun getWordpressComOauthUrl(clientId: String, serverUrl: String, state: String): String {
return "https://public-api.wordpress.com/oauth2/authorize" +
"?client_id=$clientId" +
"&redirect_uri=${serverUrl.removeTrailingSlash()}/_oauth/wordpress?close" +
"&state=$state" +
"&response_type=code" +
"&scope=auth"
}
/**
* Returns the WordPress custom Oauth URL.
*
* @param host The WordPress custom OAuth host.
* @param authorizePath The WordPress custom OAuth authorization path.
* @param clientId The WordPress custom OAuth client ID.
* @param serverUrl The server URL.
* @param serviceName The service name.
* @param state An unguessable random string used to protect against forgery attacks.
* @param scope The WordPress custom OAuth scope.
* @return The WordPress custom Oauth URL.
*/
fun getWordpressCustomOauthUrl(
host: String,
authorizePath: String,
clientId: String,
serverUrl: String,
serviceName: String,
state: String,
scope: String
): String {
(authorizePath +
"?client_id=$clientId" +
"&redirect_uri=${serverUrl.removeTrailingSlash()}/_oauth/$serviceName?close" +
"&state=$state" +
"&scope=$scope" +
"&response_type=code"
).let {
return if (it.contains(host)) {
it
} else {
host + it
}
}
}
/** /**
* Returns the Custom Oauth URL. * Returns the Custom Oauth URL.
* *
......
...@@ -2,7 +2,6 @@ package chat.rocket.android.helper ...@@ -2,7 +2,6 @@ package chat.rocket.android.helper
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.SettingsRepository import chat.rocket.android.server.domain.SettingsRepository
import chat.rocket.android.server.domain.useRealName import chat.rocket.android.server.domain.useRealName
import chat.rocket.common.model.SimpleUser import chat.rocket.common.model.SimpleUser
...@@ -12,43 +11,13 @@ import javax.inject.Inject ...@@ -12,43 +11,13 @@ import javax.inject.Inject
class UserHelper @Inject constructor( class UserHelper @Inject constructor(
private val localRepository: LocalRepository, private val localRepository: LocalRepository,
private val getCurrentServerInteractor: GetCurrentServerInteractor, private val getCurrentServerInteractor: GetCurrentServerInteractor,
settingsRepository: SettingsRepository private val settingsRepository: SettingsRepository
) { ) {
private val settings: PublicSettings = settingsRepository.get(getCurrentServerInteractor.get()!!)
/**
* Return the display name for the given [user].
* If setting 'Use_Real_Name' is true then the real name will be given, or else
* the username without the '@' is yielded. The fallback for any case is the username, which
* could be null.
*/
fun displayName(user: User): String? {
return if (settings.useRealName()) user.name ?: user.username else user.username
}
fun displayName(user: SimpleUser): String {
return if (settings.useRealName()) user.name ?: user.username ?: "" else user.username ?: ""
}
/**
* Return current logged user's display name.
*
* @see displayName
*/
fun displayName(): String? {
user()?.let {
return displayName(it)
}
return null
}
/** /**
* Return current logged [User]. * Return current logged [User].
*/ */
fun user(): User? { fun user(): User? = getCurrentServerInteractor.get()?.let { localRepository.getCurrentUser(it) }
return localRepository.getCurrentUser(serverUrl())
}
/** /**
* Return the username for the current logged [User]. * Return the username for the current logged [User].
...@@ -56,13 +25,20 @@ class UserHelper @Inject constructor( ...@@ -56,13 +25,20 @@ class UserHelper @Inject constructor(
fun username(): String? = localRepository.get(LocalRepository.CURRENT_USERNAME_KEY, null) fun username(): String? = localRepository.get(LocalRepository.CURRENT_USERNAME_KEY, null)
/** /**
* Whether current [User] is admin on the current server. * Return the display name for the given [user].
* If setting 'Use_Real_Name' is true then the real name will be given, otherwise the username
* without the '@' is yielded.
*/ */
fun isAdmin(): Boolean { fun displayName(user: SimpleUser) = getCurrentServerInteractor.get()?.let {
return user()?.roles?.find { it.equals("admin", ignoreCase = true) } != null if (settingsRepository.get(it).useRealName()) {
} user.name
} else {
user.username
}
}.orEmpty()
private fun serverUrl(): String { /**
return getCurrentServerInteractor.get()!! * Whether current [User] is admin on the current server.
} */
} fun isAdmin(): Boolean = user()?.roles?.find { it.equals("admin", true) } != null
\ No newline at end of file }
...@@ -10,6 +10,7 @@ import chat.rocket.android.profile.ui.ProfileFragment ...@@ -10,6 +10,7 @@ import chat.rocket.android.profile.ui.ProfileFragment
import chat.rocket.android.server.ui.changeServerIntent import chat.rocket.android.server.ui.changeServerIntent
import chat.rocket.android.settings.ui.SettingsFragment import chat.rocket.android.settings.ui.SettingsFragment
import chat.rocket.android.util.extensions.addFragment import chat.rocket.android.util.extensions.addFragment
import chat.rocket.android.webview.adminpanel.ui.AdminPanelWebViewFragment
class MainNavigator(internal val activity: MainActivity) { class MainNavigator(internal val activity: MainActivity) {
...@@ -37,6 +38,12 @@ class MainNavigator(internal val activity: MainActivity) { ...@@ -37,6 +38,12 @@ class MainNavigator(internal val activity: MainActivity) {
} }
} }
fun toAdminPanel(webPageUrl: String, userToken: String) {
activity.addFragment("AdminPanelWebViewFragment", R.id.fragment_container) {
AdminPanelWebViewFragment.newInstance(webPageUrl, userToken)
}
}
fun toChatRoom( fun toChatRoom(
chatRoomId: String, chatRoomId: String,
chatRoomName: String, chatRoomName: String,
......
...@@ -5,11 +5,13 @@ import chat.rocket.android.db.DatabaseManagerFactory ...@@ -5,11 +5,13 @@ import chat.rocket.android.db.DatabaseManagerFactory
import chat.rocket.android.infrastructure.LocalRepository import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.android.main.uimodel.NavHeaderUiModel import chat.rocket.android.main.uimodel.NavHeaderUiModel
import chat.rocket.android.main.uimodel.NavHeaderUiModelMapper import chat.rocket.android.main.uimodel.NavHeaderUiModelMapper
import chat.rocket.android.push.GroupedPush
import chat.rocket.android.server.domain.GetAccountsInteractor import chat.rocket.android.server.domain.GetAccountsInteractor
import chat.rocket.android.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.domain.GetSettingsInteractor import chat.rocket.android.server.domain.GetSettingsInteractor
import chat.rocket.android.server.domain.PublicSettings import chat.rocket.android.server.domain.PublicSettings
import chat.rocket.android.server.domain.RefreshSettingsInteractor import chat.rocket.android.server.domain.RefreshSettingsInteractor
import chat.rocket.android.server.domain.RefreshPermissionsInteractor
import chat.rocket.android.server.domain.RemoveAccountInteractor import chat.rocket.android.server.domain.RemoveAccountInteractor
import chat.rocket.android.server.domain.SaveAccountInteractor import chat.rocket.android.server.domain.SaveAccountInteractor
import chat.rocket.android.server.domain.TokenRepository import chat.rocket.android.server.domain.TokenRepository
...@@ -19,6 +21,7 @@ import chat.rocket.android.server.infraestructure.ConnectionManagerFactory ...@@ -19,6 +21,7 @@ import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.server.presentation.CheckServerPresenter import chat.rocket.android.server.presentation.CheckServerPresenter
import chat.rocket.android.util.extension.launchUI import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.adminPanelUrl
import chat.rocket.android.util.extensions.registerPushToken import chat.rocket.android.util.extensions.registerPushToken
import chat.rocket.android.util.extensions.serverLogoUrl import chat.rocket.android.util.extensions.serverLogoUrl
import chat.rocket.android.util.retryIO import chat.rocket.android.util.retryIO
...@@ -27,14 +30,12 @@ import chat.rocket.common.RocketChatException ...@@ -27,14 +30,12 @@ import chat.rocket.common.RocketChatException
import chat.rocket.common.model.UserStatus import chat.rocket.common.model.UserStatus
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.realtime.setDefaultStatus
import chat.rocket.core.internal.rest.logout import chat.rocket.core.internal.rest.logout
import chat.rocket.core.internal.rest.me import chat.rocket.core.internal.rest.me
import chat.rocket.core.internal.rest.unregisterPushToken import chat.rocket.core.internal.rest.unregisterPushToken
import chat.rocket.core.model.Myself import chat.rocket.core.model.Myself
import kotlinx.coroutines.experimental.CommonPool import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.channels.Channel import kotlinx.coroutines.experimental.channels.Channel
import kotlinx.coroutines.experimental.launch
import kotlinx.coroutines.experimental.withContext import kotlinx.coroutines.experimental.withContext
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
...@@ -46,12 +47,14 @@ class MainPresenter @Inject constructor( ...@@ -46,12 +47,14 @@ class MainPresenter @Inject constructor(
private val tokenRepository: TokenRepository, private val tokenRepository: TokenRepository,
private val serverInteractor: GetCurrentServerInteractor, private val serverInteractor: GetCurrentServerInteractor,
private val refreshSettingsInteractor: RefreshSettingsInteractor, private val refreshSettingsInteractor: RefreshSettingsInteractor,
private val refreshPermissionsInteractor: RefreshPermissionsInteractor,
private val localRepository: LocalRepository, private val localRepository: LocalRepository,
private val navHeaderMapper: NavHeaderUiModelMapper, private val navHeaderMapper: NavHeaderUiModelMapper,
private val saveAccountInteractor: SaveAccountInteractor, private val saveAccountInteractor: SaveAccountInteractor,
private val getAccountsInteractor: GetAccountsInteractor, private val getAccountsInteractor: GetAccountsInteractor,
private val removeAccountInteractor: RemoveAccountInteractor, private val removeAccountInteractor: RemoveAccountInteractor,
private val factory: RocketChatClientFactory, private val factory: RocketChatClientFactory,
private val groupedPush: GroupedPush,
dbManagerFactory: DatabaseManagerFactory, dbManagerFactory: DatabaseManagerFactory,
getSettingsInteractor: GetSettingsInteractor, getSettingsInteractor: GetSettingsInteractor,
managerFactory: ConnectionManagerFactory managerFactory: ConnectionManagerFactory
...@@ -61,7 +64,6 @@ class MainPresenter @Inject constructor( ...@@ -61,7 +64,6 @@ class MainPresenter @Inject constructor(
private val dbManager = dbManagerFactory.create(currentServer) private val dbManager = dbManagerFactory.create(currentServer)
private val client: RocketChatClient = factory.create(currentServer) private val client: RocketChatClient = factory.create(currentServer)
private var settings: PublicSettings = getSettingsInteractor.get(serverInteractor.get()!!) private var settings: PublicSettings = getSettingsInteractor.get(serverInteractor.get()!!)
private val userDataChannel = Channel<Myself>() private val userDataChannel = Channel<Myself>()
fun toChatList(chatRoomId: String? = null) = navigator.toChatList(chatRoomId) fun toChatList(chatRoomId: String? = null) = navigator.toChatList(chatRoomId)
...@@ -70,6 +72,10 @@ class MainPresenter @Inject constructor( ...@@ -70,6 +72,10 @@ class MainPresenter @Inject constructor(
fun toSettings() = navigator.toSettings() fun toSettings() = navigator.toSettings()
fun toAdminPanel() = tokenRepository.get(currentServer)?.let {
navigator.toAdminPanel(currentServer.adminPanelUrl(), it.authToken)
}
fun toCreateChannel() = navigator.toCreateChannel() fun toCreateChannel() = navigator.toCreateChannel()
fun loadServerAccounts() { fun loadServerAccounts() {
...@@ -153,6 +159,7 @@ class MainPresenter @Inject constructor( ...@@ -153,6 +159,7 @@ class MainPresenter @Inject constructor(
fun connect() { fun connect() {
refreshSettingsInteractor.refreshAsync(currentServer) refreshSettingsInteractor.refreshAsync(currentServer)
refreshPermissionsInteractor.refreshAsync(currentServer)
manager.connect() manager.connect()
} }
...@@ -232,4 +239,12 @@ class MainPresenter @Inject constructor( ...@@ -232,4 +239,12 @@ class MainPresenter @Inject constructor(
private fun updateMyself(myself: Myself) = private fun updateMyself(myself: Myself) =
view.setupUserAccountInfo(navHeaderMapper.mapToUiModel(myself)) view.setupUserAccountInfo(navHeaderMapper.mapToUiModel(myself))
}
\ No newline at end of file fun clearNotificationsForChatroom(chatRoomId: String?) {
if (chatRoomId == null) return
groupedPush.hostToPushMessageList[currentServer]?.let { list ->
list.removeAll { it.info.roomId == chatRoomId }
}
}
}
...@@ -5,13 +5,12 @@ import android.app.Activity ...@@ -5,13 +5,12 @@ import android.app.Activity
import android.app.AlertDialog import android.app.AlertDialog
import android.app.ProgressDialog import android.app.ProgressDialog
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import android.view.Gravity
import android.view.MenuItem
import androidx.annotation.IdRes import androidx.annotation.IdRes
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.GravityCompat
import androidx.drawerlayout.widget.DrawerLayout import androidx.drawerlayout.widget.DrawerLayout
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import chat.rocket.android.BuildConfig import chat.rocket.android.BuildConfig
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.main.adapter.AccountsAdapter import chat.rocket.android.main.adapter.AccountsAdapter
...@@ -19,15 +18,16 @@ import chat.rocket.android.main.adapter.Selector ...@@ -19,15 +18,16 @@ import chat.rocket.android.main.adapter.Selector
import chat.rocket.android.main.presentation.MainPresenter import chat.rocket.android.main.presentation.MainPresenter
import chat.rocket.android.main.presentation.MainView import chat.rocket.android.main.presentation.MainView
import chat.rocket.android.main.uimodel.NavHeaderUiModel import chat.rocket.android.main.uimodel.NavHeaderUiModel
import chat.rocket.android.server.domain.PermissionsInteractor
import chat.rocket.android.server.domain.model.Account import chat.rocket.android.server.domain.model.Account
import chat.rocket.android.server.ui.INTENT_CHAT_ROOM_ID import chat.rocket.android.server.ui.INTENT_CHAT_ROOM_ID
import chat.rocket.android.util.extensions.fadeIn import chat.rocket.android.util.extensions.fadeIn
import chat.rocket.android.util.extensions.fadeOut import chat.rocket.android.util.extensions.fadeOut
import chat.rocket.android.util.extensions.rotateBy import chat.rocket.android.util.extensions.rotateBy
import chat.rocket.android.util.extensions.showToast import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.invalidateFirebaseToken
import chat.rocket.android.util.refreshFCMToken
import chat.rocket.common.model.UserStatus import chat.rocket.common.model.UserStatus
import com.google.firebase.iid.FirebaseInstanceId
import com.google.firebase.messaging.FirebaseMessaging
import dagger.android.AndroidInjection import dagger.android.AndroidInjection
import dagger.android.AndroidInjector import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector import dagger.android.DispatchingAndroidInjector
...@@ -51,11 +51,13 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, ...@@ -51,11 +51,13 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector,
lateinit var fragmentDispatchingAndroidInjector: DispatchingAndroidInjector<Fragment> lateinit var fragmentDispatchingAndroidInjector: DispatchingAndroidInjector<Fragment>
@Inject @Inject
lateinit var presenter: MainPresenter lateinit var presenter: MainPresenter
@Inject
lateinit var permissions: PermissionsInteractor
private var isFragmentAdded: Boolean = false private var isFragmentAdded: Boolean = false
private var expanded = false private var expanded = false
private val headerLayout by lazy { view_navigation.getHeaderView(0) } private val headerLayout by lazy { view_navigation.getHeaderView(0) }
private var chatRoomId: String? = null private var chatRoomId: String? = null
private var progressDialog : ProgressDialog? = null private var progressDialog: ProgressDialog? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this) AndroidInjection.inject(this)
...@@ -63,17 +65,14 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, ...@@ -63,17 +65,14 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector,
setContentView(R.layout.activity_main) setContentView(R.layout.activity_main)
launch(CommonPool) { launch(CommonPool) {
try { refreshFCMToken(presenter)
val token = FirebaseInstanceId.getInstance().token
Timber.d("FCM token: $token")
presenter.refreshToken(token)
} catch (ex: Exception) {
Timber.d(ex, "Missing play services...")
}
} }
chatRoomId = intent.getStringExtra(INTENT_CHAT_ROOM_ID) chatRoomId = intent.getStringExtra(INTENT_CHAT_ROOM_ID)
println("ChatRoomId: $chatRoomId")
presenter.clearNotificationsForChatroom(chatRoomId)
presenter.connect() presenter.connect()
presenter.loadServerAccounts() presenter.loadServerAccounts()
presenter.loadCurrentInfo() presenter.loadCurrentInfo()
...@@ -132,7 +131,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, ...@@ -132,7 +131,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector,
text_user_name.text = userDisplayName text_user_name.text = userDisplayName
} }
if (userAvatar != null) { if (userAvatar != null) {
image_avatar.setImageURI(userAvatar) setAvatar(userAvatar)
} }
if (serverLogo != null) { if (serverLogo != null) {
server_logo.setImageURI(serverLogo) server_logo.setImageURI(serverLogo)
...@@ -169,9 +168,9 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, ...@@ -169,9 +168,9 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector,
} }
headerLayout.image_avatar.setOnClickListener { headerLayout.image_avatar.setOnClickListener {
view_navigation.menu.findItem(R.id.action_profile).isChecked = true view_navigation.menu.findItem(R.id.menu_action_profile).isChecked = true
presenter.toUserProfile() presenter.toUserProfile()
drawer_layout.closeDrawer(Gravity.START) drawer_layout.closeDrawer(GravityCompat.START)
} }
} }
...@@ -208,7 +207,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, ...@@ -208,7 +207,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector,
} }
override fun invalidateToken(token: String) = override fun invalidateToken(token: String) =
FirebaseInstanceId.getInstance().deleteToken(token, FirebaseMessaging.INSTANCE_ID_SCOPE) invalidateFirebaseToken(token)
override fun showMessage(resId: Int) = showToast(resId) override fun showMessage(resId: Int) = showToast(resId)
...@@ -221,44 +220,31 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, ...@@ -221,44 +220,31 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector,
} }
fun setupNavigationView() { fun setupNavigationView() {
view_navigation.setNavigationItemSelectedListener { menuItem -> with (view_navigation.menu) {
menuItem.isChecked = true clear()
setupMenu(this)
}
view_navigation.setNavigationItemSelectedListener {
it.isChecked = true
closeDrawer() closeDrawer()
onNavDrawerItemSelected(menuItem) onNavDrawerItemSelected(it)
true true
} }
toolbar.setNavigationIcon(R.drawable.ic_menu_white_24dp) toolbar.setNavigationIcon(R.drawable.ic_menu_white_24dp)
toolbar.setNavigationOnClickListener { toolbar.setNavigationOnClickListener { openDrawer() }
openDrawer()
}
} }
private fun onNavDrawerItemSelected(menuItem: MenuItem) { fun setAvatar(avatarUrl: String) {
when (menuItem.itemId) { headerLayout.image_avatar.setImageURI(avatarUrl)
R.id.action_chat_rooms -> {
presenter.toChatList()
}
R.id.action_profile -> {
presenter.toUserProfile()
}
R.id.action_channel -> {
presenter.toCreateChannel()
}
R.id.action_settings -> {
presenter.toSettings()
}
R.id.action_logout -> {
presenter.logout()
}
}
} }
fun getDrawerLayout(): DrawerLayout = drawer_layout fun getDrawerLayout(): DrawerLayout = drawer_layout
fun openDrawer() = drawer_layout.openDrawer(Gravity.START) fun openDrawer() = drawer_layout.openDrawer(GravityCompat.START)
fun closeDrawer() = drawer_layout.closeDrawer(Gravity.START) fun closeDrawer() = drawer_layout.closeDrawer(GravityCompat.START)
fun setCheckedNavDrawerItem(@IdRes item: Int) = view_navigation.setCheckedItem(item) fun setCheckedNavDrawerItem(@IdRes item: Int) = view_navigation.setCheckedItem(item)
...@@ -270,4 +256,4 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, ...@@ -270,4 +256,4 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector,
progressDialog?.dismiss() progressDialog?.dismiss()
progressDialog = null progressDialog = null
} }
} }
\ No newline at end of file
package chat.rocket.android.main.ui
import android.view.Menu
import android.view.MenuItem
import chat.rocket.android.R
internal fun MainActivity.setupMenu(menu: Menu) {
with(menu) {
add(
R.id.menu_section_one,
R.id.menu_action_chats,
Menu.NONE,
R.string.title_chats
).setIcon(R.drawable.ic_chat_bubble_black_24dp)
.isChecked = true
add(
R.id.menu_section_one,
R.id.menu_action_create_channel,
Menu.NONE,
R.string.action_create_channel
).setIcon(R.drawable.ic_create_black_24dp)
add(
R.id.menu_section_two,
R.id.menu_action_profile,
Menu.NONE,
R.string.title_profile
).setIcon(R.drawable.ic_person_black_24dp)
add(
R.id.menu_section_two,
R.id.menu_action_settings,
Menu.NONE,
R.string.title_settings
).setIcon(R.drawable.ic_settings_black_24dp)
if (permissions.canSeeTheAdminPanel()) {
add(
R.id.menu_section_two,
R.id.menu_action_admin_panel,
Menu.NONE,
R.string.title_admin_panel
).setIcon(R.drawable.ic_settings_black_24dp)
}
add(
R.id.menu_section_three,
R.id.menu_action_logout,
Menu.NONE,
R.string.action_logout
).setIcon(R.drawable.ic_logout_black_24dp)
setGroupCheckable(R.id.menu_section_one, true, true)
setGroupCheckable(R.id.menu_section_two, true, true)
setGroupCheckable(R.id.menu_section_three, true, true)
}
}
internal fun MainActivity.onNavDrawerItemSelected(menuItem: MenuItem) {
when (menuItem.itemId) {
R.id.menu_action_chats-> presenter.toChatList()
R.id.menu_action_create_channel -> presenter.toCreateChannel()
R.id.menu_action_profile -> presenter.toUserProfile()
R.id.menu_action_settings -> presenter.toSettings()
R.id.menu_action_admin_panel -> presenter.toAdminPanel()
R.id.menu_action_logout -> presenter.logout()
}
}
...@@ -36,7 +36,7 @@ private const val BUNDLE_CHAT_ROOM_ID = "chat_room_id" ...@@ -36,7 +36,7 @@ private const val BUNDLE_CHAT_ROOM_ID = "chat_room_id"
class MentionsFragment : Fragment(), MentionsView { class MentionsFragment : Fragment(), MentionsView {
private lateinit var chatRoomId: String private lateinit var chatRoomId: String
private lateinit var adapter: ChatRoomAdapter private val adapter = ChatRoomAdapter(enableActions = false)
@Inject @Inject
lateinit var presenter: MentionsPresenter lateinit var presenter: MentionsPresenter
...@@ -68,7 +68,6 @@ class MentionsFragment : Fragment(), MentionsView { ...@@ -68,7 +68,6 @@ class MentionsFragment : Fragment(), MentionsView {
override fun showMentions(mentions: List<BaseUiModel<*>>) { override fun showMentions(mentions: List<BaseUiModel<*>>) {
ui { ui {
if (recycler_view.adapter == null) { if (recycler_view.adapter == null) {
adapter = ChatRoomAdapter(enableActions = false)
recycler_view.adapter = adapter recycler_view.adapter = adapter
val linearLayoutManager = LinearLayoutManager(context) val linearLayoutManager = LinearLayoutManager(context)
......
...@@ -36,7 +36,7 @@ private const val BUNDLE_CHAT_ROOM_ID = "chat_room_id" ...@@ -36,7 +36,7 @@ private const val BUNDLE_CHAT_ROOM_ID = "chat_room_id"
class PinnedMessagesFragment : Fragment(), PinnedMessagesView { class PinnedMessagesFragment : Fragment(), PinnedMessagesView {
private lateinit var chatRoomId: String private lateinit var chatRoomId: String
private lateinit var adapter: ChatRoomAdapter private val adapter = ChatRoomAdapter(enableActions = false)
@Inject @Inject
lateinit var presenter: PinnedMessagesPresenter lateinit var presenter: PinnedMessagesPresenter
...@@ -68,7 +68,6 @@ class PinnedMessagesFragment : Fragment(), PinnedMessagesView { ...@@ -68,7 +68,6 @@ class PinnedMessagesFragment : Fragment(), PinnedMessagesView {
override fun showPinnedMessages(pinnedMessages: List<BaseUiModel<*>>) { override fun showPinnedMessages(pinnedMessages: List<BaseUiModel<*>>) {
ui { ui {
if (recycler_view_pinned.adapter == null) { if (recycler_view_pinned.adapter == null) {
adapter = ChatRoomAdapter(enableActions = false)
recycler_view_pinned.adapter = adapter recycler_view_pinned.adapter = adapter
val linearLayoutManager = LinearLayoutManager(context) val linearLayoutManager = LinearLayoutManager(context)
......
package chat.rocket.android.profile.presentation package chat.rocket.android.profile.presentation
import android.graphics.Bitmap
import android.net.Uri
import chat.rocket.android.chatroom.domain.UriInteractor
import chat.rocket.android.core.behaviours.showMessage import chat.rocket.android.core.behaviours.showMessage
import chat.rocket.android.core.lifecycle.CancelStrategy import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.helper.UserHelper
import chat.rocket.android.server.domain.GetCurrentServerInteractor import chat.rocket.android.server.domain.GetCurrentServerInteractor
import chat.rocket.android.server.infraestructure.RocketChatClientFactory import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.extension.compressImageAndGetByteArray
import chat.rocket.android.util.extension.launchUI import chat.rocket.android.util.extension.launchUI
import chat.rocket.android.util.extensions.avatarUrl import chat.rocket.android.util.extensions.avatarUrl
import chat.rocket.android.util.retryIO import chat.rocket.android.util.retryIO
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.common.util.ifNull import chat.rocket.common.util.ifNull
import chat.rocket.core.RocketChatClient import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.me import chat.rocket.core.internal.rest.resetAvatar
import chat.rocket.core.internal.rest.setAvatar import chat.rocket.core.internal.rest.setAvatar
import chat.rocket.core.internal.rest.updateProfile import chat.rocket.core.internal.rest.updateProfile
import java.util.*
import javax.inject.Inject import javax.inject.Inject
class ProfilePresenter @Inject constructor( class ProfilePresenter @Inject constructor(
private val view: ProfileView, private val view: ProfileView,
private val strategy: CancelStrategy, private val strategy: CancelStrategy,
private val uriInteractor: UriInteractor,
val userHelper: UserHelper,
serverInteractor: GetCurrentServerInteractor, serverInteractor: GetCurrentServerInteractor,
factory: RocketChatClientFactory factory: RocketChatClientFactory
) { ) {
private val serverUrl = serverInteractor.get()!! private val serverUrl = serverInteractor.get()!!
private val client: RocketChatClient = factory.create(serverUrl) private val client: RocketChatClient = factory.create(serverUrl)
private lateinit var myselfId: String private val myselfId = userHelper.user()?.id ?: ""
private var myselfName = userHelper.user()?.name ?: ""
private var myselfUsername = userHelper.username() ?: ""
private var myselfEmailAddress = userHelper.user()?.emails?.getOrNull(0)?.address ?: ""
fun loadUserProfile() { fun loadUserProfile() {
launchUI(strategy) { launchUI(strategy) {
view.showLoading() view.showLoading()
try { try {
val myself = retryIO("me") { client.me() } view.showProfile(
val id = myself.id serverUrl.avatarUrl(myselfUsername),
val username = myself.username myselfName,
if (id == null || username == null) { myselfUsername,
myselfEmailAddress
)
} catch (exception: RocketChatException) {
view.showMessage(exception)
} finally {
view.hideLoading()
}
}
}
fun updateUserProfile(email: String, name: String, username: String) {
launchUI(strategy) {
view.showLoading()
try {
retryIO { client.updateProfile(myselfId, email, name, username) }
myselfEmailAddress = email
myselfName = name
myselfUsername = username
view.showProfileUpdateSuccessfullyMessage()
loadUserProfile()
} catch (exception: RocketChatException) {
exception.message?.let {
view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage() view.showGenericErrorMessage()
} else {
myselfId = id
val avatarUrl = serverUrl.avatarUrl(username)
val email = myself.emails?.getOrNull(0)?.address
view.showProfile(
avatarUrl,
myself.name ?: "",
myself.username ?: "",
email
)
} }
} finally {
view.hideLoading()
}
}
}
fun updateAvatar(uri: Uri) {
launchUI(strategy) {
view.showLoading()
try {
retryIO {
client.setAvatar(
uriInteractor.getFileName(uri) ?: uri.toString(),
uriInteractor.getMimeType(uri)
) {
uriInteractor.getInputStream(uri)
}
}
view.reloadUserAvatar(serverUrl.avatarUrl(myselfUsername))
} catch (exception: RocketChatException) { } catch (exception: RocketChatException) {
view.showMessage(exception) exception.message?.let {
view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
}
} finally { } finally {
view.hideLoading() view.hideLoading()
} }
} }
} }
fun updateUserProfile(email: String, name: String, username: String, avatarUrl: String = "") { fun preparePhotoAndUpdateAvatar(bitmap: Bitmap) {
launchUI(strategy) { launchUI(strategy) {
view.showLoading() view.showLoading()
try { try {
if (avatarUrl != "") { val byteArray = bitmap.compressImageAndGetByteArray("image/png")
retryIO { client.setAvatar(avatarUrl) }
retryIO {
client.setAvatar(
UUID.randomUUID().toString() + ".png",
"image/png"
) {
byteArray?.inputStream()
}
} }
val user = retryIO { view.reloadUserAvatar(serverUrl.avatarUrl(myselfUsername))
client.updateProfile( } catch (exception: RocketChatException) {
userId = myselfId, email = email, name = name, username = username exception.message?.let {
) view.showMessage(it)
}.ifNull {
view.showGenericErrorMessage()
} }
view.showProfileUpdateSuccessfullyMessage() } finally {
loadUserProfile() view.hideLoading()
}
}
}
fun resetAvatar() {
launchUI(strategy) {
view.showLoading()
try {
retryIO { client.resetAvatar(myselfId) }
view.reloadUserAvatar(serverUrl.avatarUrl(myselfUsername))
} catch (exception: RocketChatException) { } catch (exception: RocketChatException) {
exception.message?.let { exception.message?.let {
view.showMessage(it) view.showMessage(it)
......
...@@ -15,6 +15,13 @@ interface ProfileView : LoadingView, MessageView { ...@@ -15,6 +15,13 @@ interface ProfileView : LoadingView, MessageView {
*/ */
fun showProfile(avatarUrl: String, name: String, username: String, email: String?) fun showProfile(avatarUrl: String, name: String, username: String, email: String?)
/**
* Reloads the user avatar (after successfully updating it).
*
* @param avatarUrl The user avatar URL.
*/
fun reloadUserAvatar(avatarUrl: String)
/** /**
* Shows a profile update successfully message * Shows a profile update successfully message
*/ */
......
package chat.rocket.android.profile.ui package chat.rocket.android.profile.ui
import DrawableHelper import DrawableHelper
import android.app.Activity
import android.content.Intent
import android.graphics.Bitmap
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.appcompat.view.ActionMode
import android.view.* import android.view.*
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode
import androidx.core.net.toUri
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.main.ui.MainActivity import chat.rocket.android.main.ui.MainActivity
import chat.rocket.android.profile.presentation.ProfilePresenter import chat.rocket.android.profile.presentation.ProfilePresenter
import chat.rocket.android.profile.presentation.ProfileView import chat.rocket.android.profile.presentation.ProfileView
import chat.rocket.android.util.extension.asObservable import chat.rocket.android.util.extension.asObservable
import chat.rocket.android.util.extensions.* import chat.rocket.android.util.extension.dispatchImageSelection
import chat.rocket.android.util.extension.dispatchTakePicture
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.showToast
import chat.rocket.android.util.extensions.textContent
import chat.rocket.android.util.extensions.ui
import com.facebook.drawee.backends.pipeline.Fresco
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
import io.reactivex.rxkotlin.Observables import io.reactivex.rxkotlin.Observables
import kotlinx.android.synthetic.main.avatar_profile.* import kotlinx.android.synthetic.main.avatar_profile.*
import kotlinx.android.synthetic.main.fragment_profile.* import kotlinx.android.synthetic.main.fragment_profile.*
import kotlinx.android.synthetic.main.update_avatar_options.*
import javax.inject.Inject import javax.inject.Inject
private const val REQUEST_CODE_FOR_PERFORM_SAF = 1
private const val REQUEST_CODE_FOR_PERFORM_CAMERA = 2
class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback { class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
@Inject @Inject
lateinit var presenter: ProfilePresenter lateinit var presenter: ProfilePresenter
private lateinit var currentName: String private var currentName = ""
private lateinit var currentUsername: String private var currentUsername = ""
private lateinit var currentEmail: String private var currentEmail = ""
private lateinit var currentAvatar: String
private var actionMode: ActionMode? = null private var actionMode: ActionMode? = null
private val editTextsDisposable = CompositeDisposable() private val editTextsDisposable = CompositeDisposable()
...@@ -50,11 +63,12 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback { ...@@ -50,11 +63,12 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setupToolbar() setupToolbar()
setupListeners()
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
tintEditTextDrawableStart() tintEditTextDrawableStart()
} }
presenter.loadUserProfile() presenter.loadUserProfile()
subscribeEditTexts()
} }
override fun onDestroyView() { override fun onDestroyView() {
...@@ -62,56 +76,61 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback { ...@@ -62,56 +76,61 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
unsubscribeEditTexts() unsubscribeEditTexts()
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
if (resultData != null && resultCode == Activity.RESULT_OK) {
if (requestCode == REQUEST_CODE_FOR_PERFORM_SAF) {
presenter.updateAvatar(resultData.data)
} else if (requestCode == REQUEST_CODE_FOR_PERFORM_CAMERA) {
presenter.preparePhotoAndUpdateAvatar(resultData.extras["data"] as Bitmap)
}
}
}
override fun showProfile(avatarUrl: String, name: String, username: String, email: String?) { override fun showProfile(avatarUrl: String, name: String, username: String, email: String?) {
ui { ui {
image_avatar.setImageURI(avatarUrl) image_avatar.setImageURI(avatarUrl)
text_name.textContent = name text_name.textContent = name
text_username.textContent = username text_username.textContent = username
text_email.textContent = email ?: "" text_email.textContent = email ?: ""
text_avatar_url.textContent = ""
currentName = name currentName = name
currentUsername = username currentUsername = username
currentEmail = email ?: "" currentEmail = email ?: ""
currentAvatar = avatarUrl
profile_container.setVisible(true) profile_container.isVisible = true
subscribeEditTexts()
} }
} }
override fun reloadUserAvatar(avatarUrl: String) {
Fresco.getImagePipeline().evictFromCache(avatarUrl.toUri())
image_avatar.setImageURI(avatarUrl)
(activity as MainActivity).setAvatar(avatarUrl)
}
override fun showProfileUpdateSuccessfullyMessage() { override fun showProfileUpdateSuccessfullyMessage() {
showMessage(getString(R.string.msg_profile_update_successfully)) showMessage(getString(R.string.msg_profile_update_successfully))
} }
override fun showLoading() { override fun showLoading() {
enableUserInput(false) enableUserInput(false)
ui { ui { view_loading.isVisible = true }
view_loading.setVisible(true)
}
} }
override fun hideLoading() { override fun hideLoading() {
ui { ui {
if (view_loading != null) { if (view_loading != null) {
view_loading.setVisible(false) view_loading.isVisible = false
} }
} }
enableUserInput(true) enableUserInput(true)
} }
override fun showMessage(resId: Int) { override fun showMessage(resId: Int) {
ui { ui { showToast(resId) }
showToast(resId)
}
} }
override fun showMessage(message: String) { override fun showMessage(message: String) {
ui { ui { showToast(message) }
showToast(message)
}
} }
override fun showGenericErrorMessage() = showMessage(getString(R.string.msg_generic_error)) override fun showGenericErrorMessage() = showMessage(getString(R.string.msg_generic_error))
...@@ -130,8 +149,7 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback { ...@@ -130,8 +149,7 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
presenter.updateUserProfile( presenter.updateUserProfile(
text_email.textContent, text_email.textContent,
text_name.textContent, text_name.textContent,
text_username.textContent, text_username.textContent
text_avatar_url.textContent
) )
mode.finish() mode.finish()
true true
...@@ -151,6 +169,37 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback { ...@@ -151,6 +169,37 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
getString(R.string.title_profile) getString(R.string.title_profile)
} }
private fun setupListeners() {
image_avatar.setOnClickListener { showUpdateAvatarOptions() }
view_dim.setOnClickListener { hideUpdateAvatarOptions() }
button_open_gallery.setOnClickListener {
dispatchImageSelection(REQUEST_CODE_FOR_PERFORM_SAF)
hideUpdateAvatarOptions()
}
button_take_photo.setOnClickListener {
dispatchTakePicture(REQUEST_CODE_FOR_PERFORM_CAMERA)
hideUpdateAvatarOptions()
}
button_reset_avatar.setOnClickListener {
hideUpdateAvatarOptions()
presenter.resetAvatar()
}
}
private fun showUpdateAvatarOptions() {
view_dim.isVisible = true
layout_update_avatar_options.isVisible = true
}
private fun hideUpdateAvatarOptions() {
layout_update_avatar_options.isVisible = false
view_dim.isVisible = false
}
private fun tintEditTextDrawableStart() { private fun tintEditTextDrawableStart() {
(activity as MainActivity).apply { (activity as MainActivity).apply {
val personDrawable = val personDrawable =
...@@ -158,14 +207,12 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback { ...@@ -158,14 +207,12 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
val atDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_at_black_24dp, this) val atDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_at_black_24dp, this)
val emailDrawable = val emailDrawable =
DrawableHelper.getDrawableFromId(R.drawable.ic_email_black_24dp, this) DrawableHelper.getDrawableFromId(R.drawable.ic_email_black_24dp, this)
val linkDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_link_black_24dp, this)
val drawables = arrayOf(personDrawable, atDrawable, emailDrawable, linkDrawable) val drawables = arrayOf(personDrawable, atDrawable, emailDrawable)
DrawableHelper.wrapDrawables(drawables) DrawableHelper.wrapDrawables(drawables)
DrawableHelper.tintDrawables(drawables, this, R.color.colorDrawableTintGrey) DrawableHelper.tintDrawables(drawables, this, R.color.colorDrawableTintGrey)
DrawableHelper.compoundDrawables( DrawableHelper.compoundDrawables(
arrayOf(text_name, text_username, text_email, text_avatar_url), arrayOf(text_name, text_username, text_email), drawables
drawables
) )
} }
} }
...@@ -174,13 +221,11 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback { ...@@ -174,13 +221,11 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
editTextsDisposable.add(Observables.combineLatest( editTextsDisposable.add(Observables.combineLatest(
text_name.asObservable(), text_name.asObservable(),
text_username.asObservable(), text_username.asObservable(),
text_email.asObservable(), text_email.asObservable()
text_avatar_url.asObservable() ) { text_name, text_username, text_email ->
) { text_name, text_username, text_email, text_avatar_url ->
return@combineLatest (text_name.toString() != currentName || return@combineLatest (text_name.toString() != currentName ||
text_username.toString() != currentUsername || text_username.toString() != currentUsername ||
text_email.toString() != currentEmail || text_email.toString() != currentEmail)
(text_avatar_url.toString() != "" && text_avatar_url.toString() != currentAvatar))
}.subscribe { isValid -> }.subscribe { isValid ->
if (isValid) { if (isValid) {
startActionMode() startActionMode()
...@@ -190,9 +235,7 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback { ...@@ -190,9 +235,7 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
}) })
} }
private fun unsubscribeEditTexts() { private fun unsubscribeEditTexts() = editTextsDisposable.clear()
editTextsDisposable.clear()
}
private fun startActionMode() { private fun startActionMode() {
if (actionMode == null) { if (actionMode == null) {
...@@ -207,7 +250,6 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback { ...@@ -207,7 +250,6 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
text_username.isEnabled = value text_username.isEnabled = value
text_username.isEnabled = value text_username.isEnabled = value
text_email.isEnabled = value text_email.isEnabled = value
text_avatar_url.isEnabled = value
} }
} }
} }
...@@ -4,7 +4,6 @@ import android.app.NotificationManager ...@@ -4,7 +4,6 @@ import android.app.NotificationManager
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import androidx.core.app.NotificationManagerCompat
import androidx.core.app.RemoteInput import androidx.core.app.RemoteInput
import android.widget.Toast import android.widget.Toast
import chat.rocket.android.R import chat.rocket.android.R
...@@ -12,7 +11,7 @@ import chat.rocket.android.server.infraestructure.ConnectionManagerFactory ...@@ -12,7 +11,7 @@ import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import chat.rocket.common.RocketChatException import chat.rocket.common.RocketChatException
import chat.rocket.core.internal.rest.sendMessage import chat.rocket.core.internal.rest.sendMessage
import dagger.android.AndroidInjection import dagger.android.AndroidInjection
import kotlinx.coroutines.experimental.android.UI import chat.rocket.android.util.temp.UI
import kotlinx.coroutines.experimental.launch import kotlinx.coroutines.experimental.launch
import timber.log.Timber import timber.log.Timber
import java.util.* import java.util.*
......
...@@ -12,4 +12,4 @@ class GroupedPush { ...@@ -12,4 +12,4 @@ class GroupedPush {
// Map a hostname to a list of push messages that pertain to it. // Map a hostname to a list of push messages that pertain to it.
val hostToPushMessageList = HashMap<String, MutableList<PushMessage>>() val hostToPushMessageList = HashMap<String, MutableList<PushMessage>>()
} }
\ No newline at end of file
...@@ -18,6 +18,7 @@ import androidx.core.app.NotificationManagerCompat ...@@ -18,6 +18,7 @@ import androidx.core.app.NotificationManagerCompat
import androidx.core.app.RemoteInput import androidx.core.app.RemoteInput
import android.text.Html import android.text.Html
import android.text.Spanned import android.text.Spanned
import androidx.core.content.ContextCompat
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.main.ui.MainActivity import chat.rocket.android.main.ui.MainActivity
import chat.rocket.android.server.domain.GetAccountInteractor import chat.rocket.android.server.domain.GetAccountInteractor
...@@ -36,10 +37,6 @@ import java.util.* ...@@ -36,10 +37,6 @@ import java.util.*
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import javax.inject.Inject import javax.inject.Inject
/**
* Refer to: https://github.com/RocketChat/Rocket.Chat.Android/blob/9e846b7fde8fe0c74b9e0117c37ce49293308db5/app/src/main/java/chat/rocket/android/push/PushManager.kt
* for old source code.
*/
class PushManager @Inject constructor( class PushManager @Inject constructor(
private val groupedPushes: GroupedPush, private val groupedPushes: GroupedPush,
private val manager: NotificationManager, private val manager: NotificationManager,
...@@ -80,7 +77,7 @@ class PushManager @Inject constructor( ...@@ -80,7 +77,7 @@ class PushManager @Inject constructor(
showNotification(pushMessage) showNotification(pushMessage)
} catch (ex: Exception) { } catch (ex: Exception) {
Timber.d(ex, "Error parsing PUSH message: $data") Timber.e(ex, "Error parsing PUSH message: $data")
ex.printStackTrace() ex.printStackTrace()
} }
} }
...@@ -101,7 +98,7 @@ class PushManager @Inject constructor( ...@@ -101,7 +98,7 @@ class PushManager @Inject constructor(
val groupTuple = getGroupForHost(host) val groupTuple = getGroupForHost(host)
groupTuple.second.incrementAndGet() groupTuple.second.incrementAndGet()
val notIdListForHostname: MutableList<PushMessage>? = groupedPushes.hostToPushMessageList.get(host) val notIdListForHostname: MutableList<PushMessage>? = groupedPushes.hostToPushMessageList[host]
if (notIdListForHostname == null) { if (notIdListForHostname == null) {
groupedPushes.hostToPushMessageList[host] = arrayListOf(pushMessage) groupedPushes.hostToPushMessageList[host] = arrayListOf(pushMessage)
} else { } else {
...@@ -365,14 +362,14 @@ class PushManager @Inject constructor( ...@@ -365,14 +362,14 @@ class PushManager @Inject constructor(
val res = context.resources val res = context.resources
val smallIcon = res.getIdentifier( val smallIcon = res.getIdentifier(
"rocket_chat_notification", "drawable", context.packageName) "rocket_chat_notification", "drawable", context.packageName)
with(this, { with(this) {
setAutoCancel(true) setAutoCancel(true)
setShowWhen(true) setShowWhen(true)
color = context.resources.getColor(R.color.colorPrimary) color = ContextCompat.getColor(context, R.color.colorPrimary)
setDefaults(Notification.DEFAULT_ALL) setDefaults(Notification.DEFAULT_ALL)
setSmallIcon(smallIcon) setSmallIcon(smallIcon)
setSound(alarmSound) setSound(alarmSound)
}) }
return this return this
} }
} }
......
package chat.rocket.android.server.domain package chat.rocket.android.server.domain
import chat.rocket.android.helper.UserHelper import chat.rocket.android.helper.UserHelper
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.core.model.Permission
import javax.inject.Inject import javax.inject.Inject
// Creating rooms // Creating rooms
const val CREATE_PUBLIC_CHANNELS = "create-c" private const val CREATE_PUBLIC_CHANNELS = "create-c"
const val CREATE_DIRECT_MESSAGES = "create-d" private const val CREATE_DIRECT_MESSAGES = "create-d"
const val CREATE_PRIVATE_CHANNELS = "create-p" private const val CREATE_PRIVATE_CHANNELS = "create-p"
// Messages // Messages
const val DELETE_MESSAGE = "delete-message" private const val DELETE_MESSAGE = "delete-message"
const val FORCE_DELETE_MESSAGE = "force-delete-message" private const val FORCE_DELETE_MESSAGE = "force-delete-message"
const val EDIT_MESSAGE = "edit-message" private const val EDIT_MESSAGE = "edit-message"
const val PIN_MESSAGE = "pin-message" private const val PIN_MESSAGE = "pin-message"
const val POST_READONLY = "post-readonly" private const val POST_READONLY = "post-readonly"
private const val VIEW_STATISTICS = "view-statistics"
private const val VIEW_ROOM_ADMINISTRATION = "view-room-administration"
private const val VIEW_USER_ADMINISTRATION = "view-user-administration"
private const val VIEW_PRIVILEGED_SETTING = "view-privileged-setting"
class PermissionsInteractor @Inject constructor( class PermissionsInteractor @Inject constructor(
private val settingsRepository: SettingsRepository, private val settingsRepository: SettingsRepository,
...@@ -23,14 +26,8 @@ class PermissionsInteractor @Inject constructor( ...@@ -23,14 +26,8 @@ class PermissionsInteractor @Inject constructor(
private val getCurrentServerInteractor: GetCurrentServerInteractor, private val getCurrentServerInteractor: GetCurrentServerInteractor,
private val userHelper: UserHelper private val userHelper: UserHelper
) { ) {
private fun publicSettings(): PublicSettings? = settingsRepository.get(currentServerUrl()!!) private fun publicSettings(): PublicSettings? = settingsRepository.get(currentServerUrl()!!)
fun saveAll(permissions: List<Permission>) {
val url = currentServerUrl()!!
permissions.forEach { permissionsRepository.save(url, it) }
}
/** /**
* Check whether the user is allowed to delete a message. * Check whether the user is allowed to delete a message.
*/ */
...@@ -71,6 +68,28 @@ class PermissionsInteractor @Inject constructor( ...@@ -71,6 +68,28 @@ class PermissionsInteractor @Inject constructor(
} == true || userHelper.isAdmin() } == true || userHelper.isAdmin()
} }
fun canSeeTheAdminPanel(): Boolean {
currentServerUrl()?.let { serverUrl ->
val viewStatistics =
permissionsRepository.get(serverUrl, VIEW_STATISTICS)
val viewRoomAdministration =
permissionsRepository.get(serverUrl, VIEW_ROOM_ADMINISTRATION)
val viewUserAdministration =
permissionsRepository.get(serverUrl, VIEW_USER_ADMINISTRATION)
val viewPrivilegedSetting =
permissionsRepository.get(serverUrl, VIEW_PRIVILEGED_SETTING)
userHelper.user()?.roles?.let { userRolesList ->
return viewStatistics?.roles?.any { userRolesList.contains(it) } == true ||
viewRoomAdministration?.roles?.any { userRolesList.contains(it) } == true ||
viewUserAdministration?.roles?.any { userRolesList.contains(it) } == true ||
viewPrivilegedSetting?.roles?.any { userRolesList.contains(it) } == true
}
}
return false
}
private fun currentServerUrl(): String? { private fun currentServerUrl(): String? {
return getCurrentServerInteractor.get() return getCurrentServerInteractor.get()
} }
......
...@@ -5,20 +5,20 @@ import chat.rocket.core.model.Permission ...@@ -5,20 +5,20 @@ import chat.rocket.core.model.Permission
interface PermissionsRepository { interface PermissionsRepository {
/** /**
* Store [permission] locally. * Stores a list of [Permission] locally.
* *
* @param url The server url from where we're interest to store the permission. * @param url The server url to store the permission.
* @param permission The permission to store. * @param permissionList The permission list to store.
*/ */
fun save(url: String, permission: Permission) fun save(url: String, permissionList: List<Permission>)
/** /**
* Get permission given by the [permissionId] and for the server [url]. * Gets permission given by the [permissionId] and for the server [url].
* *
* @param url The server url from where we're interested on getting the permissions. * @param url The server url to get the permissions from.
* @param permissionId the id of the permission to get. * @param permissionId the ID of the permission to get.
* *
* @return The interested [Permission] or null if not found. * @return The [Permission] or null if not found.
*/ */
fun get(url: String, permissionId: String): Permission? fun get(url: String, permissionId: String): Permission?
} }
\ No newline at end of file
package chat.rocket.android.server.domain
import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.retryIO
import chat.rocket.core.internal.rest.permissions
import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.launch
import timber.log.Timber
import javax.inject.Inject
/**
* This class reloads the current logged server permission whenever its used.
*/
class RefreshPermissionsInteractor @Inject constructor(
private val factory: RocketChatClientFactory,
private val repository: PermissionsRepository
) {
fun refreshAsync(server: String) {
launch(CommonPool) {
try {
factory.create(server).let { client ->
val permissions = retryIO(
description = "permissions",
times = 5,
maxDelay = 5000,
initialDelay = 300
) {
client.permissions()
}
repository.save(server, permissions)
}
} catch (ex: Exception) {
Timber.e(ex, "Error refreshing permissions for: $server")
}
}
}
}
\ No newline at end of file
...@@ -4,36 +4,75 @@ import chat.rocket.android.server.infraestructure.RocketChatClientFactory ...@@ -4,36 +4,75 @@ import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import chat.rocket.android.util.retryIO import chat.rocket.android.util.retryIO
import chat.rocket.core.internal.rest.settings import chat.rocket.core.internal.rest.settings
import kotlinx.coroutines.experimental.CommonPool import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.async
import kotlinx.coroutines.experimental.launch import kotlinx.coroutines.experimental.launch
import kotlinx.coroutines.experimental.withContext import kotlinx.coroutines.experimental.withContext
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
/**
* This class reloads the current logged server settings whenever needed.
*/
class RefreshSettingsInteractor @Inject constructor( class RefreshSettingsInteractor @Inject constructor(
private val factory: RocketChatClientFactory, private val factory: RocketChatClientFactory,
private val repository: SettingsRepository private val repository: SettingsRepository
) { ) {
private var settingsFilter = arrayOf( private var settingsFilter = arrayOf(
LDAP_ENABLE, CAS_ENABLE, CAS_LOGIN_URL, LDAP_ENABLE,
CAS_ENABLE,
CAS_LOGIN_URL,
ACCOUNT_REGISTRATION, ACCOUNT_LOGIN_FORM, ACCOUNT_PASSWORD_RESET, ACCOUNT_CUSTOM_FIELDS, ACCOUNT_REGISTRATION,
ACCOUNT_GOOGLE, ACCOUNT_FACEBOOK, ACCOUNT_GITHUB, ACCOUNT_LINKEDIN, ACCOUNT_METEOR, ACCOUNT_LOGIN_FORM,
ACCOUNT_TWITTER, ACCOUNT_WORDPRESS, ACCOUNT_GITLAB, ACCOUNT_GITLAB_URL, ACCOUNT_PASSWORD_RESET,
ACCOUNT_CUSTOM_FIELDS,
ACCOUNT_GOOGLE,
ACCOUNT_FACEBOOK,
ACCOUNT_GITHUB,
ACCOUNT_LINKEDIN,
ACCOUNT_METEOR,
ACCOUNT_TWITTER,
ACCOUNT_GITLAB,
ACCOUNT_GITLAB_URL,
ACCOUNT_WORDPRESS,
ACCOUNT_WORDPRESS_URL,
SITE_URL, SITE_NAME, FAVICON_512, FAVICON_196, USE_REALNAME, ALLOW_ROOM_NAME_SPECIAL_CHARS, SITE_URL,
FAVORITE_ROOMS, UPLOAD_STORAGE_TYPE, UPLOAD_MAX_FILE_SIZE, UPLOAD_WHITELIST_MIMETYPES, SITE_NAME,
HIDE_USER_JOIN, HIDE_USER_LEAVE, FAVICON_512,
HIDE_TYPE_AU, HIDE_MUTE_UNMUTE, HIDE_TYPE_RU, ALLOW_MESSAGE_DELETING, FAVICON_196,
ALLOW_MESSAGE_EDITING, ALLOW_MESSAGE_PINNING, ALLOW_MESSAGE_STARRING, SHOW_DELETED_STATUS, SHOW_EDITED_STATUS, USE_REALNAME,
WIDE_TILE_310, STORE_LAST_MESSAGE, MESSAGE_READ_RECEIPT_ENABLED, MESSAGE_READ_RECEIPT_STORE_USERS) ALLOW_ROOM_NAME_SPECIAL_CHARS,
FAVORITE_ROOMS,
UPLOAD_STORAGE_TYPE,
UPLOAD_MAX_FILE_SIZE,
UPLOAD_WHITELIST_MIMETYPES,
HIDE_USER_JOIN,
HIDE_USER_LEAVE,
HIDE_TYPE_AU,
HIDE_MUTE_UNMUTE,
HIDE_TYPE_RU,
ALLOW_MESSAGE_DELETING,
ALLOW_MESSAGE_EDITING,
ALLOW_MESSAGE_PINNING,
ALLOW_MESSAGE_STARRING,
SHOW_DELETED_STATUS,
SHOW_EDITED_STATUS,
WIDE_TILE_310,
STORE_LAST_MESSAGE,
MESSAGE_READ_RECEIPT_ENABLED,
MESSAGE_READ_RECEIPT_STORE_USERS
)
suspend fun refresh(server: String) { suspend fun refresh(server: String) {
withContext(CommonPool) { withContext(CommonPool) {
factory.create(server).let { client -> factory.create(server).let { client ->
val settings = retryIO(description = "settings", times = 5, val settings = retryIO(
maxDelay = 5000, initialDelay = 300) { description = "settings",
times = 5,
maxDelay = 5000,
initialDelay = 300
) {
client.settings(*settingsFilter) client.settings(*settingsFilter)
} }
repository.save(server, settings) repository.save(server, settings)
......
package chat.rocket.android.server.domain
import chat.rocket.core.model.Value
import javax.inject.Inject
class SaveSettingsInteractor @Inject constructor(private val repository: SettingsRepository) {
fun save(url: String, settings: Map<String, Value<Any>>) = repository.save(url, settings)
}
\ No newline at end of file
...@@ -19,9 +19,10 @@ const val ACCOUNT_GITHUB = "Accounts_OAuth_Github" ...@@ -19,9 +19,10 @@ const val ACCOUNT_GITHUB = "Accounts_OAuth_Github"
const val ACCOUNT_LINKEDIN = "Accounts_OAuth_Linkedin" const val ACCOUNT_LINKEDIN = "Accounts_OAuth_Linkedin"
const val ACCOUNT_METEOR = "Accounts_OAuth_Meteor" const val ACCOUNT_METEOR = "Accounts_OAuth_Meteor"
const val ACCOUNT_TWITTER = "Accounts_OAuth_Twitter" const val ACCOUNT_TWITTER = "Accounts_OAuth_Twitter"
const val ACCOUNT_WORDPRESS = "Accounts_OAuth_Wordpress"
const val ACCOUNT_GITLAB = "Accounts_OAuth_Gitlab" const val ACCOUNT_GITLAB = "Accounts_OAuth_Gitlab"
const val ACCOUNT_GITLAB_URL = "API_Gitlab_URL" const val ACCOUNT_GITLAB_URL = "API_Gitlab_URL"
const val ACCOUNT_WORDPRESS = "Accounts_OAuth_Wordpress"
const val ACCOUNT_WORDPRESS_URL = "API_Wordpress_URL"
const val SITE_URL = "Site_Url" const val SITE_URL = "Site_Url"
const val SITE_NAME = "Site_Name" const val SITE_NAME = "Site_Name"
...@@ -71,6 +72,7 @@ fun PublicSettings.isTwitterAuthenticationEnabled(): Boolean = this[ACCOUNT_TWIT ...@@ -71,6 +72,7 @@ fun PublicSettings.isTwitterAuthenticationEnabled(): Boolean = this[ACCOUNT_TWIT
fun PublicSettings.isGitlabAuthenticationEnabled(): Boolean = this[ACCOUNT_GITLAB]?.value == true fun PublicSettings.isGitlabAuthenticationEnabled(): Boolean = this[ACCOUNT_GITLAB]?.value == true
fun PublicSettings.gitlabUrl(): String? = this[ACCOUNT_GITLAB_URL]?.value as String? fun PublicSettings.gitlabUrl(): String? = this[ACCOUNT_GITLAB_URL]?.value as String?
fun PublicSettings.isWordpressAuthenticationEnabled(): Boolean = this[ACCOUNT_WORDPRESS]?.value == true fun PublicSettings.isWordpressAuthenticationEnabled(): Boolean = this[ACCOUNT_WORDPRESS]?.value == true
fun PublicSettings.wordpressUrl(): String? = this[ACCOUNT_WORDPRESS_URL]?.value as String?
fun PublicSettings.useRealName(): Boolean = this[USE_REALNAME]?.value == true fun PublicSettings.useRealName(): Boolean = this[USE_REALNAME]?.value == true
fun PublicSettings.useSpecialCharsOnRoom(): Boolean = this[ALLOW_ROOM_NAME_SPECIAL_CHARS]?.value == true fun PublicSettings.useSpecialCharsOnRoom(): Boolean = this[ALLOW_ROOM_NAME_SPECIAL_CHARS]?.value == true
......
...@@ -2,7 +2,6 @@ package chat.rocket.android.server.infraestructure ...@@ -2,7 +2,6 @@ package chat.rocket.android.server.infraestructure
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import chat.rocket.android.db.DatabaseManager import chat.rocket.android.db.DatabaseManager
import chat.rocket.android.infrastructure.LocalRepository
import chat.rocket.common.model.BaseRoom import chat.rocket.common.model.BaseRoom
import chat.rocket.common.model.User import chat.rocket.common.model.User
import chat.rocket.common.model.UserStatus import chat.rocket.common.model.UserStatus
...@@ -194,6 +193,14 @@ class ConnectionManager( ...@@ -194,6 +193,14 @@ class ConnectionManager(
client.setTemporaryStatus(userStatus) client.setTemporaryStatus(userStatus)
} }
fun resetReconnectionTimer() {
// if we are waiting to reconnect, immediately try to reconnect
// and reset the reconnection counter
if (client.state is State.Waiting) {
client.connect(resetCounter = true)
}
}
private fun resubscribeRooms() { private fun resubscribeRooms() {
roomMessagesChannels.toList().map { (roomId, channel) -> roomMessagesChannels.toList().map { (roomId, channel) ->
client.subscribeRoomMessages(roomId) { _, id -> client.subscribeRoomMessages(roomId) { _, id ->
......
...@@ -9,11 +9,12 @@ class SharedPreferencesPermissionsRepository( ...@@ -9,11 +9,12 @@ class SharedPreferencesPermissionsRepository(
private val localRepository: LocalRepository, private val localRepository: LocalRepository,
moshi: Moshi moshi: Moshi
) : PermissionsRepository { ) : PermissionsRepository {
private val adapter = moshi.adapter(Permission::class.java) private val adapter = moshi.adapter(Permission::class.java)
override fun save(url: String, permission: Permission) { override fun save(url: String, permissionList: List<Permission>) {
localRepository.save(getPermissionKey(url, permission.id), adapter.toJson(permission)) for (permission in permissionList) {
localRepository.save(getPermissionKey(url, permission.id), adapter.toJson(permission))
}
} }
override fun get(url: String, permissionId: String): Permission? { override fun get(url: String, permissionId: String): Permission? {
......
...@@ -19,9 +19,6 @@ import kotlinx.android.synthetic.main.fragment_settings.* ...@@ -19,9 +19,6 @@ import kotlinx.android.synthetic.main.fragment_settings.*
import kotlin.reflect.KClass import kotlin.reflect.KClass
class SettingsFragment : Fragment(), SettingsView, AdapterView.OnItemClickListener { class SettingsFragment : Fragment(), SettingsView, AdapterView.OnItemClickListener {
companion object {
fun newInstance() = SettingsFragment()
}
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
...@@ -69,4 +66,8 @@ class SettingsFragment : Fragment(), SettingsView, AdapterView.OnItemClickListen ...@@ -69,4 +66,8 @@ class SettingsFragment : Fragment(), SettingsView, AdapterView.OnItemClickListen
startActivity(Intent(activity, classType.java)) startActivity(Intent(activity, classType.java))
activity?.overridePendingTransition(R.anim.open_enter, R.anim.open_exit) activity?.overridePendingTransition(R.anim.open_enter, R.anim.open_exit)
} }
companion object {
fun newInstance() = SettingsFragment()
}
} }
...@@ -4,7 +4,7 @@ import android.os.Looper ...@@ -4,7 +4,7 @@ import android.os.Looper
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.android.UI import chat.rocket.android.util.temp.UI
import kotlinx.coroutines.experimental.launch import kotlinx.coroutines.experimental.launch
inline fun Fragment.ui(crossinline block: (activity: FragmentActivity) -> Unit): Job? { inline fun Fragment.ui(crossinline block: (activity: FragmentActivity) -> Unit): Job? {
......
...@@ -3,6 +3,7 @@ package chat.rocket.android.util.extensions ...@@ -3,6 +3,7 @@ package chat.rocket.android.util.extensions
import android.graphics.Color import android.graphics.Color
import android.util.Patterns import android.util.Patterns
import chat.rocket.common.model.Token import chat.rocket.common.model.Token
import okhttp3.HttpUrl
import timber.log.Timber import timber.log.Timber
fun String.removeTrailingSlash(): String { fun String.removeTrailingSlash(): String {
...@@ -50,6 +51,8 @@ fun String.termsOfServiceUrl() = "${removeTrailingSlash()}/terms-of-service" ...@@ -50,6 +51,8 @@ fun String.termsOfServiceUrl() = "${removeTrailingSlash()}/terms-of-service"
fun String.privacyPolicyUrl() = "${removeTrailingSlash()}/privacy-policy" fun String.privacyPolicyUrl() = "${removeTrailingSlash()}/privacy-policy"
fun String.adminPanelUrl() = "${removeTrailingSlash()}/admin/info?layout=embedded"
fun String.isValidUrl(): Boolean = Patterns.WEB_URL.matcher(this).matches() fun String.isValidUrl(): Boolean = Patterns.WEB_URL.matcher(this).matches()
fun String.parseColor(): Int { fun String.parseColor(): Int {
...@@ -64,4 +67,11 @@ fun String.parseColor(): Int { ...@@ -64,4 +67,11 @@ fun String.parseColor(): Int {
fun String.userId(userId: String?): String? { fun String.userId(userId: String?): String? {
return userId?.let { this.replace(it, "") } return userId?.let { this.replace(it, "") }
}
fun String.lowercaseUrl(): String? {
val httpUrl = HttpUrl.parse(this)
val newScheme = httpUrl?.scheme()?.toLowerCase()
return httpUrl?.newBuilder()?.scheme(newScheme)?.build()?.toString()
} }
\ No newline at end of file
...@@ -7,15 +7,27 @@ import android.view.View ...@@ -7,15 +7,27 @@ import android.view.View
import chat.rocket.android.R import chat.rocket.android.R
import timber.log.Timber import timber.log.Timber
fun View.openTabbedUrl(url: Uri) { fun View.openTabbedUrl(url: String) {
with(this) { with(this) {
val uri = url.ensureScheme()
val tabsbuilder = CustomTabsIntent.Builder() val tabsbuilder = CustomTabsIntent.Builder()
tabsbuilder.setToolbarColor(ResourcesCompat.getColor(context.resources, R.color.colorPrimary, context.theme)) tabsbuilder.setToolbarColor(ResourcesCompat.getColor(context.resources, R.color.colorPrimary, context.theme))
val customTabsIntent = tabsbuilder.build() val customTabsIntent = tabsbuilder.build()
try { try {
customTabsIntent.launchUrl(context, url) customTabsIntent.launchUrl(context, uri)
} catch (ex: Exception) { } catch (ex: Exception) {
Timber.d(ex, "Unable to launch URL") Timber.d(ex, "Unable to launch URL")
} }
} }
}
private fun String.ensureScheme(): Uri? {
// check if the URL starts with a http(s) scheme
val url = if (!this.matches(Regex("^([h|H][t|T][t|T][p|P]).*"))) {
"http://$this"
} else {
this
}
return Uri.parse(url.lowercaseUrl())
} }
\ No newline at end of file
...@@ -4,12 +4,11 @@ import androidx.lifecycle.LiveData ...@@ -4,12 +4,11 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import kotlinx.coroutines.experimental.CommonPool import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.Job import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.android.UI import chat.rocket.android.util.temp.UI
import kotlinx.coroutines.experimental.launch import kotlinx.coroutines.experimental.launch
import kotlinx.coroutines.experimental.withContext import kotlinx.coroutines.experimental.withContext
import kotlin.coroutines.experimental.CoroutineContext import kotlin.coroutines.experimental.CoroutineContext
class TransformedLiveData<Source, Output>( class TransformedLiveData<Source, Output>(
private val runContext: CoroutineContext = CommonPool, private val runContext: CoroutineContext = CommonPool,
private val source: LiveData<Source>, private val source: LiveData<Source>,
......
package chat.rocket.android.webview.adminpanel.ui
import android.annotation.SuppressLint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import chat.rocket.android.R
import chat.rocket.android.util.extensions.inflate
import kotlinx.android.synthetic.main.fragment_admin_panel_web_view.*
private const val BUNDLE_WEB_PAGE_URL = "web_page_url"
private const val BUNDLE_USER_TOKEN = "user_token"
class AdminPanelWebViewFragment : Fragment() {
private lateinit var webPageUrl: String
private lateinit var userToken: String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val bundle = arguments
if (bundle != null) {
webPageUrl = bundle.getString(BUNDLE_WEB_PAGE_URL)
userToken = bundle.getString(BUNDLE_USER_TOKEN)
} else {
requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" }
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = container?.inflate(R.layout.fragment_admin_panel_web_view)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupToolbar()
setupWebView()
}
private fun setupToolbar() {
(activity as AppCompatActivity?)?.supportActionBar?.title =
getString(R.string.title_admin_panel)
}
@SuppressLint("SetJavaScriptEnabled")
private fun setupWebView() {
with(web_view.settings) {
javaScriptEnabled = true
domStorageEnabled = true
}
web_view.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView, url: String) {
super.onPageFinished(view, url)
view_loading.hide()
web_view.evaluateJavascript("Meteor.loginWithToken('$userToken', function() { })") {}
}
}
web_view.loadUrl(webPageUrl)
}
companion object {
fun newInstance(webPageUrl: String, userToken: String) = AdminPanelWebViewFragment().apply {
arguments = Bundle(2).apply {
putString(BUNDLE_WEB_PAGE_URL, webPageUrl)
putString(BUNDLE_USER_TOKEN, userToken)
}
}
}
}
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z" />
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M3.9,12c0,-1.71 1.39,-3.1 3.1,-3.1h4L11,7L7,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5h4v-1.9L7,15.1c-1.71,0 -3.1,-1.39 -3.1,-3.1zM8,13h8v-2L8,11v2zM17,7h-4v1.9h4c1.71,0 3.1,1.39 3.1,3.1s-1.39,3.1 -3.1,3.1h-4L13,17h4c2.76,0 5,-2.24 5,-5s-2.24,-5 -5,-5z"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="24dp"
android:height="24dp" android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:viewportWidth="24.0"> android:viewportHeight="24.0">
<path <path
android:fillColor="#FF000000" android:fillColor="#FF000000"
android:pathData="M10.09,15.59L11.5,17l5,-5 -5,-5 -1.41,1.41L12.67,11H3v2h9.67l-2.58,2.59zM19,3H5c-1.11,0 -2,0.9 -2,2v4h2V5h14v14H5v-4H3v4c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z" /> android:pathData="M10.09,15.59L11.5,17l5,-5 -5,-5 -1.41,1.41L12.67,11H3v2h9.67l-2.58,2.59zM19,3H5c-1.11,0 -2,0.9 -2,2v4h2V5h14v14H5v-4H3v4c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z" />
</vector> </vector>
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0" />
<path
android:fillColor="#FF000000"
android:pathData="M9,2L7.17,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2h-3.17L15,2L9,2zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z" />
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="290dp"
android:height="40dp"
android:viewportWidth="290"
android:viewportHeight="40">
<path
android:fillColor="#428BBA"
android:fillType="evenOdd"
android:pathData="M2,0L288,0A2,2 0,0 1,290 2L290,38A2,2 0,0 1,288 40L2,40A2,2 0,0 1,0 38L0,2A2,2 0,0 1,2 0z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#FFFFFFFF"
android:fillType="evenOdd"
android:pathData="M134.878,23.188L136.628,14.625L138.589,14.625L135.964,26L134.073,26L131.909,17.695L129.698,26L127.8,26L125.175,14.625L127.136,14.625L128.901,23.172L131.073,14.625L132.729,14.625L134.878,23.188ZM139.824,21.695C139.824,20.867 139.988,20.121 140.316,19.457C140.645,18.793 141.105,18.283 141.699,17.926C142.293,17.569 142.975,17.391 143.746,17.391C144.887,17.391 145.813,17.758 146.523,18.492C147.234,19.227 147.618,20.201 147.676,21.414L147.684,21.859C147.684,22.693 147.523,23.437 147.203,24.094C146.883,24.75 146.424,25.258 145.828,25.617C145.232,25.977 144.543,26.156 143.762,26.156C142.569,26.156 141.615,25.759 140.898,24.965C140.182,24.171 139.824,23.112 139.824,21.789L139.824,21.695ZM141.723,21.859C141.723,22.729 141.902,23.41 142.262,23.902C142.621,24.395 143.121,24.641 143.762,24.641C144.402,24.641 144.901,24.391 145.258,23.891C145.615,23.391 145.793,22.659 145.793,21.695C145.793,20.841 145.609,20.164 145.242,19.664C144.875,19.164 144.376,18.914 143.746,18.914C143.126,18.914 142.634,19.16 142.27,19.652C141.905,20.145 141.723,20.88 141.723,21.859ZM154.286,19.281C154.036,19.24 153.778,19.219 153.513,19.219C152.643,19.219 152.057,19.552 151.755,20.219L151.755,26L149.857,26L149.857,17.547L151.669,17.547L151.716,18.492C152.174,17.758 152.81,17.391 153.622,17.391C153.893,17.391 154.117,17.427 154.294,17.5L154.286,19.281ZM155.553,21.711C155.553,20.409 155.855,19.363 156.459,18.574C157.063,17.785 157.873,17.391 158.889,17.391C159.785,17.391 160.509,17.703 161.061,18.328L161.061,14L162.959,14L162.959,26L161.241,26L161.147,25.125C160.579,25.813 159.821,26.156 158.873,26.156C157.884,26.156 157.083,25.758 156.471,24.961C155.859,24.164 155.553,23.081 155.553,21.711ZM157.451,21.875C157.451,22.734 157.617,23.405 157.948,23.887C158.278,24.368 158.748,24.609 159.358,24.609C160.134,24.609 160.701,24.263 161.061,23.57L161.061,19.961C160.712,19.284 160.149,18.945 159.373,18.945C158.759,18.945 158.285,19.189 157.951,19.676C157.618,20.163 157.451,20.896 157.451,21.875ZM167.671,21.773L167.671,26L165.695,26L165.695,14.625L170.046,14.625C171.317,14.625 172.326,14.956 173.074,15.617C173.821,16.279 174.195,17.154 174.195,18.242C174.195,19.357 173.829,20.224 173.097,20.844C172.365,21.464 171.341,21.773 170.023,21.773L167.671,21.773ZM167.671,20.188L170.046,20.188C170.749,20.188 171.286,20.022 171.656,19.691C172.025,19.361 172.21,18.883 172.21,18.258C172.21,17.643 172.023,17.152 171.648,16.785C171.273,16.418 170.757,16.229 170.101,16.219L167.671,16.219L167.671,20.188ZM180.735,19.281C180.485,19.24 180.227,19.219 179.962,19.219C179.092,19.219 178.506,19.552 178.204,20.219L178.204,26L176.305,26L176.305,17.547L178.118,17.547L178.165,18.492C178.623,17.758 179.258,17.391 180.071,17.391C180.342,17.391 180.566,17.427 180.743,17.5L180.735,19.281ZM186.08,26.156C184.877,26.156 183.901,25.777 183.154,25.02C182.407,24.262 182.033,23.253 182.033,21.992L182.033,21.758C182.033,20.914 182.196,20.16 182.521,19.496C182.847,18.832 183.304,18.315 183.892,17.945C184.481,17.576 185.137,17.391 185.861,17.391C187.012,17.391 187.901,17.758 188.529,18.492C189.157,19.227 189.47,20.266 189.47,21.609L189.47,22.375L183.947,22.375C184.004,23.073 184.237,23.625 184.646,24.031C185.055,24.438 185.569,24.641 186.189,24.641C187.059,24.641 187.767,24.289 188.314,23.586L189.338,24.563C188.999,25.068 188.547,25.46 187.982,25.738C187.417,26.017 186.783,26.156 186.08,26.156ZM185.853,18.914C185.332,18.914 184.912,19.096 184.592,19.461C184.271,19.826 184.067,20.333 183.978,20.984L187.595,20.984L187.595,20.844C187.554,20.208 187.384,19.728 187.088,19.402C186.791,19.077 186.379,18.914 185.853,18.914ZM196.253,23.703C196.253,23.365 196.113,23.107 195.835,22.93C195.556,22.753 195.094,22.596 194.448,22.461C193.802,22.326 193.263,22.154 192.831,21.945C191.883,21.487 191.409,20.823 191.409,19.953C191.409,19.224 191.716,18.615 192.331,18.125C192.945,17.635 193.727,17.391 194.675,17.391C195.685,17.391 196.501,17.641 197.124,18.141C197.746,18.641 198.057,19.289 198.057,20.086L196.159,20.086C196.159,19.721 196.024,19.418 195.753,19.176C195.482,18.934 195.123,18.813 194.675,18.813C194.258,18.813 193.918,18.909 193.655,19.102C193.392,19.294 193.261,19.552 193.261,19.875C193.261,20.167 193.383,20.393 193.628,20.555C193.873,20.716 194.367,20.879 195.112,21.043C195.857,21.207 196.442,21.402 196.866,21.629C197.291,21.855 197.606,22.128 197.811,22.445C198.017,22.763 198.12,23.148 198.12,23.602C198.12,24.362 197.805,24.978 197.175,25.449C196.544,25.921 195.719,26.156 194.698,26.156C194.005,26.156 193.388,26.031 192.847,25.781C192.305,25.531 191.883,25.188 191.581,24.75C191.279,24.312 191.128,23.841 191.128,23.336L192.972,23.336C192.998,23.784 193.167,24.129 193.479,24.371C193.792,24.613 194.206,24.734 194.722,24.734C195.222,24.734 195.602,24.639 195.862,24.449C196.123,24.259 196.253,24.01 196.253,23.703ZM205.082,23.703C205.082,23.365 204.943,23.107 204.664,22.93C204.385,22.753 203.923,22.596 203.277,22.461C202.632,22.326 202.092,22.154 201.66,21.945C200.712,21.487 200.238,20.823 200.238,19.953C200.238,19.224 200.546,18.615 201.16,18.125C201.775,17.635 202.556,17.391 203.504,17.391C204.514,17.391 205.331,17.641 205.953,18.141C206.576,18.641 206.887,19.289 206.887,20.086L204.988,20.086C204.988,19.721 204.853,19.418 204.582,19.176C204.311,18.934 203.952,18.813 203.504,18.813C203.087,18.813 202.747,18.909 202.484,19.102C202.221,19.294 202.09,19.552 202.09,19.875C202.09,20.167 202.212,20.393 202.457,20.555C202.702,20.716 203.197,20.879 203.941,21.043C204.686,21.207 205.271,21.402 205.695,21.629C206.12,21.855 206.435,22.128 206.641,22.445C206.846,22.763 206.949,23.148 206.949,23.602C206.949,24.362 206.634,24.978 206.004,25.449C205.374,25.921 204.548,26.156 203.527,26.156C202.835,26.156 202.217,26.031 201.676,25.781C201.134,25.531 200.712,25.188 200.41,24.75C200.108,24.312 199.957,23.841 199.957,23.336L201.801,23.336C201.827,23.784 201.996,24.129 202.309,24.371C202.621,24.613 203.035,24.734 203.551,24.734C204.051,24.734 204.431,24.639 204.691,24.449C204.952,24.259 205.082,24.01 205.082,23.703Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#FFFFFFFF"
android:fillType="nonZero"
android:pathData="M104.26,11C99.146,11 95,15.146 95,20.26C95,25.375 99.146,29.521 104.26,29.521C109.375,29.521 113.521,25.375 113.521,20.26C113.521,15.146 109.375,11 104.26,11M104.26,11.556C105.995,11.552 107.69,12.07 109.127,13.042C110.054,13.669 110.852,14.467 111.479,15.394C112.451,16.83 112.969,18.526 112.965,20.26C112.969,21.995 112.451,23.69 111.479,25.127C110.852,26.054 110.054,26.852 109.127,27.479C107.69,28.451 105.995,28.969 104.26,28.965C102.526,28.969 100.83,28.451 99.394,27.479C98.467,26.852 97.669,26.054 97.042,25.127C96.07,23.69 95.552,21.995 95.556,20.26C95.552,18.526 96.07,16.83 97.042,15.394C97.669,14.467 98.467,13.669 99.393,13.042C100.83,12.07 102.526,11.552 104.26,11.556"
android:strokeWidth="1"
android:strokeColor="#00000000" />
<path
android:fillColor="#FFFFFFFF"
android:fillType="nonZero"
android:pathData="M111.032,16.558C111.066,16.804 111.084,17.068 111.084,17.351C111.084,18.134 110.937,19.014 110.497,20.115L108.14,26.93C110.516,25.549 111.978,23.008 111.977,20.26C111.979,18.966 111.654,17.693 111.032,16.559L111.032,16.558ZM104.396,20.935L102.08,27.663C103.635,28.121 105.294,28.078 106.823,27.54C106.802,27.506 106.783,27.471 106.768,27.434L104.396,20.935ZM109.47,19.871C109.47,18.917 109.128,18.257 108.834,17.743C108.443,17.106 108.076,16.569 108.076,15.933C108.076,15.223 108.613,14.563 109.372,14.563C109.406,14.563 109.438,14.567 109.472,14.569C108.05,13.264 106.19,12.541 104.26,12.544C101.662,12.543 99.238,13.851 97.813,16.022C97.994,16.028 98.165,16.032 98.309,16.032C99.116,16.032 100.366,15.934 100.366,15.934C100.782,15.909 100.831,16.52 100.415,16.57C100.415,16.57 99.997,16.618 99.532,16.643L102.343,25.002L104.031,19.937L102.829,16.643C102.414,16.618 102.02,16.57 102.02,16.57C101.604,16.545 101.652,15.909 102.068,15.934C102.068,15.934 103.342,16.032 104.101,16.032C104.908,16.032 106.158,15.934 106.158,15.934C106.574,15.909 106.623,16.52 106.207,16.57C106.207,16.57 105.788,16.618 105.324,16.643L108.113,24.938L108.908,22.415C109.263,21.313 109.47,20.531 109.47,19.872L109.47,19.871ZM96.544,20.26C96.544,23.217 98.233,25.915 100.893,27.205L97.211,17.12C96.77,18.108 96.543,19.178 96.544,20.26Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
...@@ -33,8 +33,7 @@ ...@@ -33,8 +33,7 @@
android:id="@+id/view_navigation" android:id="@+id/view_navigation"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
app:headerLayout="@layout/nav_header" app:headerLayout="@layout/nav_header" />
app:menu="@menu/navigation" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/accounts_list" android:id="@+id/accounts_list"
......
...@@ -35,7 +35,8 @@ ...@@ -35,7 +35,8 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/image_app_name" app:layout_constraintTop_toBottomOf="@+id/image_app_name"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:textColor="@color/colorSecondaryText" /> android:textColor="@color/colorSecondaryText"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"/>
<TextView <TextView
android:id="@+id/text_build_number" android:id="@+id/text_build_number"
......
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".webview.adminpanel.ui.AdminPanelWebViewFragment">
<WebView
android:id="@+id/web_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.wang.avi.AVLoadingIndicatorView
android:id="@+id/view_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:indicatorColor="@color/colorBlack"
app:indicatorName="BallPulseIndicator"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
android:src="@drawable/ic_vpn_key_black_24dp" android:src="@drawable/ic_vpn_key_black_24dp"
android:tint="@color/colorDrawableTintGrey" android:tint="@color/colorDrawableTintGrey"
android:visibility="gone" android:visibility="gone"
tools:visibility="visible"
app:layout_constraintBottom_toBottomOf="@+id/text_username_or_email" app:layout_constraintBottom_toBottomOf="@+id/text_username_or_email"
app:layout_constraintEnd_toEndOf="@+id/text_username_or_email" app:layout_constraintEnd_toEndOf="@+id/text_username_or_email"
app:layout_constraintTop_toTopOf="@+id/text_username_or_email" /> app:layout_constraintTop_toTopOf="@+id/text_username_or_email" />
...@@ -218,6 +219,18 @@ ...@@ -218,6 +219,18 @@
android:src="@drawable/ic_gitlab" android:src="@drawable/ic_gitlab"
android:visibility="gone" android:visibility="gone"
tools:visibility="gone" /> tools:visibility="gone" />
<ImageButton
android:id="@+id/button_wordpress"
android:layout_width="290dp"
android:layout_height="40dp"
android:layout_marginTop="16dp"
android:clickable="false"
android:contentDescription="@string/msg_content_description_log_in_using_gitlab"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_wordpress"
android:visibility="gone"
tools:visibility="gone" />
</LinearLayout> </LinearLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton <com.google.android.material.floatingactionbutton.FloatingActionButton
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:paddingBottom="16dp" android:paddingBottom="16dp"
app:layout_behavior=" com.google.android.material.bottomsheet.BottomSheetBehavior"> app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<com.facebook.drawee.view.SimpleDraweeView <com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/image_bottom_sheet_avatar" android:id="@+id/image_bottom_sheet_avatar"
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/relative_layout"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/relative_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:focusableInTouchMode="true" android:focusableInTouchMode="true"
...@@ -48,20 +48,11 @@ ...@@ -48,20 +48,11 @@
android:id="@+id/text_email" android:id="@+id/text_email"
style="@style/Profile.EditText" style="@style/Profile.EditText"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:drawableStart="@drawable/ic_email_black_24dp" android:drawableStart="@drawable/ic_email_black_24dp"
android:hint="@string/msg_email" android:hint="@string/msg_email"
android:inputType="textEmailAddress" /> android:inputType="textEmailAddress" />
<EditText
android:id="@+id/text_avatar_url"
style="@style/Profile.EditText"
android:layout_marginTop="16dp"
android:drawableStart="@drawable/ic_link_black_24dp"
android:hint="@string/msg_avatar_url"
android:inputType="text"
android:layout_marginBottom="16dp"/>
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>
<com.wang.avi.AVLoadingIndicatorView <com.wang.avi.AVLoadingIndicatorView
...@@ -72,4 +63,19 @@ ...@@ -72,4 +63,19 @@
app:indicatorColor="@color/colorBlack" app:indicatorColor="@color/colorBlack"
app:indicatorName="BallPulseIndicator" /> app:indicatorName="BallPulseIndicator" />
<View
android:id="@+id/view_dim"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorDim"
android:visibility="gone" />
<include
android:id="@+id/layout_update_avatar_options"
layout="@layout/update_avatar_options"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:visibility="gone" />
</RelativeLayout> </RelativeLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/style_attachment_options"
android:orientation="vertical">
<Button
android:id="@+id/button_open_gallery"
style="?android:attr/borderlessButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableStart="@drawable/ic_image_black_24dp"
android:drawablePadding="20dp"
android:gravity="start|center"
android:text="@string/action_select_photo_from_gallery" />
<Button
android:id="@+id/button_take_photo"
style="?android:attr/borderlessButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableStart="@drawable/ic_photo_camera_black_24dp"
android:drawablePadding="20dp"
android:gravity="start|center"
android:text="@string/action_take_photo" />
<Button
android:id="@+id/button_reset_avatar"
style="?android:attr/borderlessButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableStart="@drawable/ic_close_black_24dp"
android:drawablePadding="20dp"
android:gravity="start|center"
android:text="@string/action_reset_avatar" />
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group
android:id="@+id/menu_section_1"
android:checkableBehavior="single">
<item
android:id="@+id/action_chat_rooms"
android:checked="true"
android:icon="@drawable/ic_chat_bubble_black_24dp"
android:title="@string/title_chats" />
<item
android:id="@+id/action_channel"
android:icon="@drawable/ic_create_black_24dp"
android:title="@string/action_create_channel" />
</group>
<group
android:id="@+id/menu_section_2"
android:checkableBehavior="single">
<item
android:id="@+id/action_profile"
android:icon="@drawable/ic_person_black_24dp"
android:title="@string/title_profile" />
<item
android:id="@+id/action_settings"
android:icon="@drawable/ic_settings_black_24dp"
android:title="@string/title_settings" />
</group>
<group
android:id="@+id/menu_section_3"
android:checkableBehavior="single">
<item
android:id="@+id/action_logout"
android:icon="@drawable/ic_exit_to_app_black_24dp"
android:title="@string/action_logout" />
</group>
</menu>
\ No newline at end of file
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
<string name="title_profile">Profil</string> <string name="title_profile">Profil</string>
<string name="title_members">Benutzer (%d)</string> <string name="title_members">Benutzer (%d)</string>
<string name="title_settings">Einstellungen</string> <string name="title_settings">Einstellungen</string>
<string name="title_admin_panel">Admin panel</string> <!-- TODO Add translation -->
<string name="title_password">Ändere Passwort</string> <string name="title_password">Ändere Passwort</string>
<string name="title_update_profile">Update Profil</string> <string name="title_update_profile">Update Profil</string>
<string name="title_about">Über</string> <string name="title_about">Über</string>
...@@ -38,6 +39,9 @@ ...@@ -38,6 +39,9 @@
<string name="action_invisible">Unsichtbar</string> <string name="action_invisible">Unsichtbar</string>
<string name="action_drawing">Zeichnung</string> <string name="action_drawing">Zeichnung</string>
<string name="action_save_to_gallery">Sichern in Gallerie</string> <string name="action_save_to_gallery">Sichern in Gallerie</string>
<string name="action_select_photo_from_gallery">Select photo from gallery</string> <!-- TODO Add translation -->
<string name="action_take_photo">Select photo from gallery</string> <!-- TODO Add translation -->
<string name="action_reset_avatar">Reset avatar</string> <!-- TODO Add translation -->
<!-- Settings List --> <!-- Settings List -->
<string-array name="settings_actions"> <string-array name="settings_actions">
...@@ -78,6 +82,7 @@ ...@@ -78,6 +82,7 @@
<string name="msg_content_description_log_in_using_meteor">Login mit Meteor</string> <string name="msg_content_description_log_in_using_meteor">Login mit Meteor</string>
<string name="msg_content_description_log_in_using_twitter">Login mit Twitter</string> <string name="msg_content_description_log_in_using_twitter">Login mit Twitter</string>
<string name="msg_content_description_log_in_using_gitlab">Login mit Gitlab</string> <string name="msg_content_description_log_in_using_gitlab">Login mit Gitlab</string>
<string name="msg_content_description_log_in_using_wordpress">Login mit WordPress</string>
<string name="msg_content_description_send_message">Sende Nachricht</string> <string name="msg_content_description_send_message">Sende Nachricht</string>
<string name="msg_content_description_show_attachment_options">Zeige Anhang Optionen</string> <string name="msg_content_description_show_attachment_options">Zeige Anhang Optionen</string>
<string name="msg_you">Du</string> <string name="msg_you">Du</string>
...@@ -94,8 +99,6 @@ ...@@ -94,8 +99,6 @@
<string name="msg_preview_photo">Bild</string> <string name="msg_preview_photo">Bild</string>
<string name="msg_preview_file">Datei</string> <string name="msg_preview_file">Datei</string>
<string name="msg_no_messages_yet">Noch keine Nachrichten</string> <string name="msg_no_messages_yet">Noch keine Nachrichten</string>
<string name="msg_version">Version %1$s</string>
<string name="msg_build">Build %1$d</string>
<string name="msg_ok">OK</string> <string name="msg_ok">OK</string>
<string name="msg_update_app_version_in_order_to_continue">Server Version veraltet. Bitte kontaktieren Sie ihren Server Administrator.</string> <string name="msg_update_app_version_in_order_to_continue">Server Version veraltet. Bitte kontaktieren Sie ihren Server Administrator.</string>
<string name="msg_ver_not_recommended"> <string name="msg_ver_not_recommended">
...@@ -169,7 +172,6 @@ ...@@ -169,7 +172,6 @@
<string name="action_msg_share">Teilen</string> <string name="action_msg_share">Teilen</string>
<string name="action_title_editing">Nachricht bearbeiten</string> <string name="action_title_editing">Nachricht bearbeiten</string>
<string name="action_msg_add_reaction">Reaktion hinzufügen</string> <string name="action_msg_add_reaction">Reaktion hinzufügen</string>
<string name="action_share">Teilen</string>
<!-- Permission messages --> <!-- Permission messages -->
<string name="permission_editing_not_allowed">Bearbeiten nicht erlaubt</string> <string name="permission_editing_not_allowed">Bearbeiten nicht erlaubt</string>
...@@ -257,6 +259,7 @@ ...@@ -257,6 +259,7 @@
<string name="dialog_sort_by_activity">Aktivität</string> <string name="dialog_sort_by_activity">Aktivität</string>
<string name="dialog_group_by_type">Räume nach Typ</string> <string name="dialog_group_by_type">Räume nach Typ</string>
<string name="dialog_group_favourites">Räume nach Favoriten</string> <string name="dialog_group_favourites">Räume nach Favoriten</string>
<string name="dialog_button_done">Done</string><!-- TODO Add translation -->
<string name="chatroom_header">Kopf</string> <string name="chatroom_header">Kopf</string>
<!--ChatRooms Headers--> <!--ChatRooms Headers-->
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
<string name="title_profile">Perfil</string> <string name="title_profile">Perfil</string>
<string name="title_members">Miembros (%d)</string> <string name="title_members">Miembros (%d)</string>
<string name="title_settings">Configuraciones</string> <string name="title_settings">Configuraciones</string>
<string name="title_admin_panel">Admin panel</string> <!-- TODO Add translation -->
<string name="title_password">Cambia la contraseña</string> <string name="title_password">Cambia la contraseña</string>
<string name="title_update_profile">Actualización del perfil</string> <string name="title_update_profile">Actualización del perfil</string>
<string name="title_about">Acerca de</string> <string name="title_about">Acerca de</string>
...@@ -36,8 +37,11 @@ ...@@ -36,8 +37,11 @@
<string name="action_away">Ausente</string> <string name="action_away">Ausente</string>
<string name="action_busy">Ocupado</string> <string name="action_busy">Ocupado</string>
<string name="action_invisible">Invisible</string> <string name="action_invisible">Invisible</string>
<string name="action_drawing">dibujo</string> <string name="action_drawing">Dibujo</string>
<string name="action_save_to_gallery">Guardar en la galería</string> <string name="action_save_to_gallery">Guardar en la galería</string>
<string name="action_select_photo_from_gallery">Select photo from gallery</string> <!-- TODO Add translation -->
<string name="action_take_photo">Select photo from gallery</string> <!-- TODO Add translation -->
<string name="action_reset_avatar">Reset avatar</string> <!-- TODO Add translation -->
<!-- Settings List --> <!-- Settings List -->
<string-array name="settings_actions"> <string-array name="settings_actions">
...@@ -77,6 +81,7 @@ ...@@ -77,6 +81,7 @@
<string name="msg_content_description_log_in_using_meteor">Inicia sesión usando Meteor</string> <string name="msg_content_description_log_in_using_meteor">Inicia sesión usando Meteor</string>
<string name="msg_content_description_log_in_using_twitter">Inicia sesión usando Twitter</string> <string name="msg_content_description_log_in_using_twitter">Inicia sesión usando Twitter</string>
<string name="msg_content_description_log_in_using_gitlab">Inicia sesión usando Gitlab</string> <string name="msg_content_description_log_in_using_gitlab">Inicia sesión usando Gitlab</string>
<string name="msg_content_description_log_in_using_wordpress">Login using WordPress</string> <!-- TODO Translate-->
<string name="msg_content_description_send_message">Enviar mensaje</string> <string name="msg_content_description_send_message">Enviar mensaje</string>
<string name="msg_content_description_show_attachment_options">Mostrar opciones de archivo adjunto</string> <string name="msg_content_description_show_attachment_options">Mostrar opciones de archivo adjunto</string>
<string name="msg_you"></string> <string name="msg_you"></string>
...@@ -91,8 +96,6 @@ ...@@ -91,8 +96,6 @@
<string name="msg_preview_photo">Foto</string> <string name="msg_preview_photo">Foto</string>
<string name="msg_preview_file">Fichero</string> <string name="msg_preview_file">Fichero</string>
<string name="msg_no_messages_yet">Aún no hay mensajes</string> <string name="msg_no_messages_yet">Aún no hay mensajes</string>
<string name="msg_version">Versión %1$s</string>
<string name="msg_build">Build %1$d</string>
<string name="msg_ok">OK</string> <string name="msg_ok">OK</string>
<string name="msg_update_app_version_in_order_to_continue">Versión del servidor actualizada. Póngase en contacto con el administrador del servidor para actualizar la versión del servidor y continuar.</string> <string name="msg_update_app_version_in_order_to_continue">Versión del servidor actualizada. Póngase en contacto con el administrador del servidor para actualizar la versión del servidor y continuar.</string>
<string name="msg_ver_not_recommended"> <string name="msg_ver_not_recommended">
...@@ -134,7 +137,6 @@ ...@@ -134,7 +137,6 @@
<string name="msg_member_not_found">Miembro no encontrado</string> <string name="msg_member_not_found">Miembro no encontrado</string>
<string name="msg_channel_created_successfully">Canal creado con éxito</string> <string name="msg_channel_created_successfully">Canal creado con éxito</string>
<!-- System messages --> <!-- System messages -->
<string name="message_room_name_changed">Nombre de la sala cambiado para: %1$s por %2$s</string> <string name="message_room_name_changed">Nombre de la sala cambiado para: %1$s por %2$s</string>
<string name="message_user_added_by">Usuario %1$s añadido por %2$s</string> <string name="message_user_added_by">Usuario %1$s añadido por %2$s</string>
...@@ -252,6 +254,7 @@ ...@@ -252,6 +254,7 @@
<string name="dialog_sort_by_activity">Actividad</string> <string name="dialog_sort_by_activity">Actividad</string>
<string name="dialog_group_by_type">Agrupar por tipo</string> <string name="dialog_group_by_type">Agrupar por tipo</string>
<string name="dialog_group_favourites">Agrupar favoritos</string> <string name="dialog_group_favourites">Agrupar favoritos</string>
<string name="dialog_button_done">Done</string><!-- TODO Add translation -->
<string name="chatroom_header">Cabezazo</string> <string name="chatroom_header">Cabezazo</string>
<!--ChatRooms Headers--> <!--ChatRooms Headers-->
...@@ -262,6 +265,7 @@ ...@@ -262,6 +265,7 @@
<string name="header_unknown">Desconocido</string> <string name="header_unknown">Desconocido</string>
<!--Notifications--> <!--Notifications-->
<string name="share_label">Edita mensaje compartido</string>
<string name="notif_action_reply_hint">RESPUESTA</string> <string name="notif_action_reply_hint">RESPUESTA</string>
<string name="notif_error_sending">La respuesta ha fallado. Inténtalo de nuevo.</string> <string name="notif_error_sending">La respuesta ha fallado. Inténtalo de nuevo.</string>
<string name="notif_success_sending">Mensaje enviado a %1$s!</string> <string name="notif_success_sending">Mensaje enviado a %1$s!</string>
...@@ -272,4 +276,5 @@ ...@@ -272,4 +276,5 @@
<string name="msg_file_description">Descripción del archivo</string> <string name="msg_file_description">Descripción del archivo</string>
<string name="msg_send">Enviar</string> <string name="msg_send">Enviar</string>
<string name="msg_sent_attachment">Envió un archivo</string> <string name="msg_sent_attachment">Envió un archivo</string>
</resources> </resources>
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
<string name="title_profile">Profil</string> <string name="title_profile">Profil</string>
<string name="title_members">Membres (%d)</string> <string name="title_members">Membres (%d)</string>
<string name="title_settings">Paramètres</string> <string name="title_settings">Paramètres</string>
<string name="title_admin_panel">Admin panel</string> <!-- TODO Add translation -->
<string name="title_password">Changer le mot de passe</string> <string name="title_password">Changer le mot de passe</string>
<string name="title_update_profile">Update profile</string> <string name="title_update_profile">Update profile</string>
<string name="title_about">Sur</string> <string name="title_about">Sur</string>
...@@ -27,8 +28,7 @@ ...@@ -27,8 +28,7 @@
<string name="action_search">Chercher</string> <string name="action_search">Chercher</string>
<string name="action_update">Mettre à jour</string> <string name="action_update">Mettre à jour</string>
<string name="action_settings">Paramètres</string> <string name="action_settings">Paramètres</string>
// TODO: Add proper translation. <string name="action_create_channel">Create channel</string> <!-- TODO Add translation -->
<string name="action_create_channel">Create channel</string>
<string name="action_create">Create</string> <string name="action_create">Create</string>
<string name="action_logout">Se déconnecter</string> <string name="action_logout">Se déconnecter</string>
<string name="action_files">Fichiers</string> <string name="action_files">Fichiers</string>
...@@ -40,8 +40,10 @@ ...@@ -40,8 +40,10 @@
<string name="action_busy">Occupé</string> <string name="action_busy">Occupé</string>
<string name="action_invisible">Invisible</string> <string name="action_invisible">Invisible</string>
<string name="action_drawing">Dessin</string> <string name="action_drawing">Dessin</string>
// TODO: Add proper translation. <string name="action_save_to_gallery">Save to gallery</string> <!-- TODO Add translation -->
<string name="action_save_to_gallery">Save to gallery</string> <string name="action_select_photo_from_gallery">Select photo from gallery</string> <!-- TODO Add translation -->
<string name="action_take_photo">Select photo from gallery</string> <!-- TODO Add translation -->
<string name="action_reset_avatar">Reset avatar</string> <!-- TODO Add translation -->
<!-- Settings List --> <!-- Settings List -->
<string-array name="settings_actions"> <string-array name="settings_actions">
...@@ -85,6 +87,7 @@ ...@@ -85,6 +87,7 @@
<string name="msg_content_description_log_in_using_meteor">Connectez-vous en utilisant Meteor</string> <string name="msg_content_description_log_in_using_meteor">Connectez-vous en utilisant Meteor</string>
<string name="msg_content_description_log_in_using_twitter">Connectez-vous en utilisant Twitter</string> <string name="msg_content_description_log_in_using_twitter">Connectez-vous en utilisant Twitter</string>
<string name="msg_content_description_log_in_using_gitlab">Connectez-vous en utilisant Gitlab</string> <string name="msg_content_description_log_in_using_gitlab">Connectez-vous en utilisant Gitlab</string>
<string name="msg_content_description_log_in_using_wordpress">Login using WordPress</string> <!-- TODO Translate-->
<string name="msg_content_description_send_message">Envoyer message</string> <string name="msg_content_description_send_message">Envoyer message</string>
<string name="msg_content_description_show_attachment_options">Afficher les options de fichiers</string> <string name="msg_content_description_show_attachment_options">Afficher les options de fichiers</string>
<string name="msg_you">Toi</string> <string name="msg_you">Toi</string>
...@@ -99,8 +102,6 @@ ...@@ -99,8 +102,6 @@
<string name="msg_preview_photo">Photo</string> <string name="msg_preview_photo">Photo</string>
<string name="msg_preview_file">File</string> <string name="msg_preview_file">File</string>
<string name="msg_no_messages_yet">Aucun message pour le moment</string> <string name="msg_no_messages_yet">Aucun message pour le moment</string>
<string name="msg_version">Version %1$s</string>
<string name="msg_build">Build %1$d</string>
<string name="msg_ok">OK</string> <string name="msg_ok">OK</string>
// TODO: Add proper translation. // TODO: Add proper translation.
<string name="msg_update_app_version_in_order_to_continue">Out to date server version. Please contact the server admin to update the server version in order to continue.</string> <string name="msg_update_app_version_in_order_to_continue">Out to date server version. Please contact the server admin to update the server version in order to continue.</string>
...@@ -185,7 +186,6 @@ ...@@ -185,7 +186,6 @@
<string name="action_msg_share">Partager</string> <string name="action_msg_share">Partager</string>
<string name="action_title_editing">Modification du message</string> <string name="action_title_editing">Modification du message</string>
<string name="action_msg_add_reaction">Ajouter une réaction</string> <string name="action_msg_add_reaction">Ajouter une réaction</string>
<string name="action_share">Partager</string>
<!-- Permission messages --> <!-- Permission messages -->
<string name="permission_editing_not_allowed">L\'édition n\'est pas autorisée</string> <string name="permission_editing_not_allowed">L\'édition n\'est pas autorisée</string>
...@@ -279,6 +279,7 @@ ...@@ -279,6 +279,7 @@
<string name="dialog_sort_by_activity">Activité</string> <string name="dialog_sort_by_activity">Activité</string>
<string name="dialog_group_by_type">Grouper par type</string> <string name="dialog_group_by_type">Grouper par type</string>
<string name="dialog_group_favourites">Grouper favoris</string> <string name="dialog_group_favourites">Grouper favoris</string>
<string name="dialog_button_done">Done</string><!-- TODO Add translation -->
<string name="chatroom_header">Entête</string> <string name="chatroom_header">Entête</string>
<!--ChatRooms Headers--> <!--ChatRooms Headers-->
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
<string name="title_profile">प्रोफाइल</string> <string name="title_profile">प्रोफाइल</string>
<string name="title_members">सदस्य (%d)</string> <string name="title_members">सदस्य (%d)</string>
<string name="title_settings">सेटिंग्स</string> <string name="title_settings">सेटिंग्स</string>
<string name="title_admin_panel">Admin panel</string> <!-- TODO Add translation -->
<string name="title_password">पासवर्ड बदलें</string> <string name="title_password">पासवर्ड बदलें</string>
<string name="title_update_profile">प्रोफ़ाइल अपडेट करें</string> <string name="title_update_profile">प्रोफ़ाइल अपडेट करें</string>
<string name="title_about">परिचय</string> <string name="title_about">परिचय</string>
...@@ -39,7 +40,9 @@ ...@@ -39,7 +40,9 @@
<string name="action_invisible">अदृश्य</string> <string name="action_invisible">अदृश्य</string>
<string name="action_save_to_gallery">गैलरी में सहेजें</string> <string name="action_save_to_gallery">गैलरी में सहेजें</string>
<string name="action_drawing">चित्रकारी</string> <string name="action_drawing">चित्रकारी</string>
<string name="action_share">शेयर</string> <string name="action_select_photo_from_gallery">Select photo from gallery</string> <!-- TODO Add translation -->
<string name="action_take_photo">Select photo from gallery</string> <!-- TODO Add translation -->
<string name="action_reset_avatar">Reset avatar</string> <!-- TODO Add translation -->
<!-- Settings List --> <!-- Settings List -->
<string-array name="settings_actions"> <string-array name="settings_actions">
...@@ -79,6 +82,7 @@ ...@@ -79,6 +82,7 @@
<string name="msg_content_description_log_in_using_meteor">Meteor द्वारा लॉगिन करें</string> <string name="msg_content_description_log_in_using_meteor">Meteor द्वारा लॉगिन करें</string>
<string name="msg_content_description_log_in_using_twitter">Twitter द्वारा लॉगिन करें</string> <string name="msg_content_description_log_in_using_twitter">Twitter द्वारा लॉगिन करें</string>
<string name="msg_content_description_log_in_using_gitlab">Gitlab द्वारा लॉगिन करें</string> <string name="msg_content_description_log_in_using_gitlab">Gitlab द्वारा लॉगिन करें</string>
<string name="msg_content_description_log_in_using_wordpress">Login using WordPress</string> <!-- TODO Translate-->
<string name="msg_content_description_send_message">मेसेज भेजें</string> <string name="msg_content_description_send_message">मेसेज भेजें</string>
<string name="msg_content_description_show_attachment_options">अटैचमेंट विकल्प दिखाएं</string> <string name="msg_content_description_show_attachment_options">अटैचमेंट विकल्प दिखाएं</string>
<string name="msg_you">आप</string> <string name="msg_you">आप</string>
...@@ -93,8 +97,6 @@ ...@@ -93,8 +97,6 @@
<string name="msg_preview_file">फ़ाइल</string> <string name="msg_preview_file">फ़ाइल</string>
<string name="msg_unread_messages">अपठित संदेश</string> <string name="msg_unread_messages">अपठित संदेश</string>
<string name="msg_no_messages_yet">अभी तक कोई पोस्ट नहीं</string> <string name="msg_no_messages_yet">अभी तक कोई पोस्ट नहीं</string>
<string name="msg_version">वर्शन %1$s</string>
<string name="msg_build">बिल्ड %1$d</string>
<string name="msg_update_app_version_in_order_to_continue">पुराना सर्वर वर्शन। जारी रखने के लिए सर्वर वर्शन को अद्यतन करने के लिए कृपया सर्वर व्यवस्थापक से संपर्क करें।</string> <string name="msg_update_app_version_in_order_to_continue">पुराना सर्वर वर्शन। जारी रखने के लिए सर्वर वर्शन को अद्यतन करने के लिए कृपया सर्वर व्यवस्थापक से संपर्क करें।</string>
<string name="msg_ok">ठीक है</string> <string name="msg_ok">ठीक है</string>
<string name="msg_ver_not_recommended"> <string name="msg_ver_not_recommended">
...@@ -259,6 +261,7 @@ ...@@ -259,6 +261,7 @@
<string name="dialog_sort_by_activity">गतिविधि</string> <string name="dialog_sort_by_activity">गतिविधि</string>
<string name="dialog_group_by_type">प्रकार के आधार पर समूह</string> <string name="dialog_group_by_type">प्रकार के आधार पर समूह</string>
<string name="dialog_group_favourites">पसंदीदा समूह</string> <string name="dialog_group_favourites">पसंदीदा समूह</string>
<string name="dialog_button_done">Done</string><!-- TODO Add translation -->
<string name="chatroom_header">हैडर</string> <string name="chatroom_header">हैडर</string>
<!--ChatRooms Headers--> <!--ChatRooms Headers-->
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
<string name="title_profile">Perfil</string> <string name="title_profile">Perfil</string>
<string name="title_members">Membros (%d)</string> <string name="title_members">Membros (%d)</string>
<string name="title_settings">Configurações</string> <string name="title_settings">Configurações</string>
<string name="title_admin_panel">Painel administrativo</string>
<string name="title_password">Alterar senha</string> <string name="title_password">Alterar senha</string>
<string name="title_update_profile">Editar perfil</string> <string name="title_update_profile">Editar perfil</string>
<string name="title_about">Sobre</string> <string name="title_about">Sobre</string>
...@@ -39,6 +40,9 @@ ...@@ -39,6 +40,9 @@
<string name="action_invisible">Invisível</string> <string name="action_invisible">Invisível</string>
<string name="action_drawing">Desenhando</string> <string name="action_drawing">Desenhando</string>
<string name="action_save_to_gallery">Salvar na galeria</string> <string name="action_save_to_gallery">Salvar na galeria</string>
<string name="action_select_photo_from_gallery">Escolher foto da galeria</string>
<string name="action_take_photo">Tirar foto</string>
<string name="action_reset_avatar">Resetar avatar</string>
<!-- Settings List --> <!-- Settings List -->
<string-array name="settings_actions"> <string-array name="settings_actions">
...@@ -78,6 +82,7 @@ ...@@ -78,6 +82,7 @@
<string name="msg_content_description_log_in_using_meteor">Fazer login através do Meteor</string> <string name="msg_content_description_log_in_using_meteor">Fazer login através do Meteor</string>
<string name="msg_content_description_log_in_using_twitter">Fazer login através do Twitter</string> <string name="msg_content_description_log_in_using_twitter">Fazer login através do Twitter</string>
<string name="msg_content_description_log_in_using_gitlab">Fazer login através do Gitlab</string> <string name="msg_content_description_log_in_using_gitlab">Fazer login através do Gitlab</string>
<string name="msg_content_description_log_in_using_wordpress">Fazer login através do WordPress</string>
<string name="msg_content_description_send_message">Enviar mensagem</string> <string name="msg_content_description_send_message">Enviar mensagem</string>
<string name="msg_content_description_show_attachment_options">Mostrar opções de anexo</string> <string name="msg_content_description_show_attachment_options">Mostrar opções de anexo</string>
<string name="msg_you">Você</string> <string name="msg_you">Você</string>
...@@ -92,8 +97,6 @@ ...@@ -92,8 +97,6 @@
<string name="msg_preview_photo">Foto</string> <string name="msg_preview_photo">Foto</string>
<string name="msg_preview_file">Arquivo</string> <string name="msg_preview_file">Arquivo</string>
<string name="msg_no_messages_yet">Nenhuma mensagem ainda</string> <string name="msg_no_messages_yet">Nenhuma mensagem ainda</string>
<string name="msg_version">Versão %1$s</string>
<string name="msg_build">Build %1$d</string>
<string name="msg_ok">OK</string> <string name="msg_ok">OK</string>
<string name="msg_update_app_version_in_order_to_continue">Versão do servidor desatualizada. Por favor, entre em contato com o administrador do sistema para continuar.</string> <string name="msg_update_app_version_in_order_to_continue">Versão do servidor desatualizada. Por favor, entre em contato com o administrador do sistema para continuar.</string>
<string name="msg_ver_not_recommended"> <string name="msg_ver_not_recommended">
...@@ -257,6 +260,7 @@ ...@@ -257,6 +260,7 @@
<string name="dialog_sort_by_activity">Atividade</string> <string name="dialog_sort_by_activity">Atividade</string>
<string name="dialog_group_by_type">Agrupar por tipo</string> <string name="dialog_group_by_type">Agrupar por tipo</string>
<string name="dialog_group_favourites">Grupos favoritos</string> <string name="dialog_group_favourites">Grupos favoritos</string>
<string name="dialog_button_done">Done</string><!-- TODO Add translation -->
<string name="chatroom_header">Cabeçalho</string> <string name="chatroom_header">Cabeçalho</string>
<!--ChatRooms Headers--> <!--ChatRooms Headers-->
...@@ -271,7 +275,6 @@ ...@@ -271,7 +275,6 @@
<string name="notif_action_reply_hint">RESPONDER</string> <string name="notif_action_reply_hint">RESPONDER</string>
<string name="notif_error_sending">Falha ao enviar a mensagem.</string> <string name="notif_error_sending">Falha ao enviar a mensagem.</string>
<string name="notif_success_sending">Mensagem enviada para %1$s!</string> <string name="notif_success_sending">Mensagem enviada para %1$s!</string>
<string name="action_share">Compartilhar</string>
<string name="read_by">Lida por</string> <string name="read_by">Lida por</string>
<string name="message_information_title">Informações da mensagem</string> <string name="message_information_title">Informações da mensagem</string>
<string name="msg_log_out">Deslogando…</string> <string name="msg_log_out">Deslogando…</string>
......
This diff is collapsed.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="menu_section_one" type="id"/>
<item name="menu_section_two" type="id"/>
<item name="menu_section_three" type="id"/>
<item name="menu_action_chats" type="id"/>
<item name="menu_action_create_channel" type="id"/>
<item name="menu_action_profile" type="id"/>
<item name="menu_action_settings" type="id"/>
<item name="menu_action_admin_panel" type="id"/>
<item name="menu_action_logout" type="id"/>
</resources>
\ No newline at end of file
<resources> <resources xmlns:tools="http://schemas.android.com/tools">
<string name="app_name" translatable="false">Rocket.Chat</string> <string name="app_name" translatable="false">Rocket.Chat</string>
<!-- Titles --> <!-- Titles -->
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
<string name="title_profile">Profile</string> <string name="title_profile">Profile</string>
<string name="title_members">Members (%d)</string> <string name="title_members">Members (%d)</string>
<string name="title_settings">Settings</string> <string name="title_settings">Settings</string>
<string name="title_admin_panel">Admin panel</string>
<string name="title_password">Change Password</string> <string name="title_password">Change Password</string>
<string name="title_update_profile">Update profile</string> <string name="title_update_profile">Update profile</string>
<string name="title_about">About</string> <string name="title_about">About</string>
...@@ -40,6 +41,9 @@ ...@@ -40,6 +41,9 @@
<string name="action_invisible">Invisible</string> <string name="action_invisible">Invisible</string>
<string name="action_drawing">Drawing</string> <string name="action_drawing">Drawing</string>
<string name="action_save_to_gallery">Save to gallery</string> <string name="action_save_to_gallery">Save to gallery</string>
<string name="action_select_photo_from_gallery">Select photo from gallery</string>
<string name="action_take_photo">Take photo</string>
<string name="action_reset_avatar">Reset avatar</string>
<!-- Settings List --> <!-- Settings List -->
<string-array name="settings_actions"> <string-array name="settings_actions">
...@@ -80,6 +84,7 @@ ...@@ -80,6 +84,7 @@
<string name="msg_content_description_log_in_using_meteor">Login using Meteor</string> <string name="msg_content_description_log_in_using_meteor">Login using Meteor</string>
<string name="msg_content_description_log_in_using_twitter">Login using Twitter</string> <string name="msg_content_description_log_in_using_twitter">Login using Twitter</string>
<string name="msg_content_description_log_in_using_gitlab">Login using Gitlab</string> <string name="msg_content_description_log_in_using_gitlab">Login using Gitlab</string>
<string name="msg_content_description_log_in_using_wordpress">Login using WordPress</string>
<string name="msg_content_description_send_message">Send message</string> <string name="msg_content_description_send_message">Send message</string>
<string name="msg_content_description_show_attachment_options">Show attachment options</string> <string name="msg_content_description_show_attachment_options">Show attachment options</string>
<string name="msg_you">You</string> <string name="msg_you">You</string>
...@@ -96,8 +101,7 @@ ...@@ -96,8 +101,7 @@
<string name="msg_preview_photo">Photo</string> <string name="msg_preview_photo">Photo</string>
<string name="msg_preview_file">File</string> <string name="msg_preview_file">File</string>
<string name="msg_no_messages_yet">No messages yet</string> <string name="msg_no_messages_yet">No messages yet</string>
<string name="msg_version">Version %1$s</string> <string name="msg_build" tools:ignore="MissingTranslation">Build %1$d - %2$s - %3$s</string>
<string name="msg_build">Build %1$d</string>
<string name="msg_ok">OK</string> <string name="msg_ok">OK</string>
<string name="msg_update_app_version_in_order_to_continue">Out to date server version. Please contact the server admin to update the server version in order to continue.</string> <string name="msg_update_app_version_in_order_to_continue">Out to date server version. Please contact the server admin to update the server version in order to continue.</string>
<string name="msg_ver_not_recommended"> <string name="msg_ver_not_recommended">
...@@ -171,7 +175,6 @@ ...@@ -171,7 +175,6 @@
<string name="action_msg_share">Share</string> <string name="action_msg_share">Share</string>
<string name="action_title_editing">Editing Message</string> <string name="action_title_editing">Editing Message</string>
<string name="action_msg_add_reaction">Add reaction</string> <string name="action_msg_add_reaction">Add reaction</string>
<string name="action_share">Share</string>
<!-- Permission messages --> <!-- Permission messages -->
<string name="permission_editing_not_allowed">Editing is not allowed</string> <string name="permission_editing_not_allowed">Editing is not allowed</string>
...@@ -259,6 +262,7 @@ ...@@ -259,6 +262,7 @@
<string name="dialog_sort_by_activity">Activity</string> <string name="dialog_sort_by_activity">Activity</string>
<string name="dialog_group_by_type">Group by type</string> <string name="dialog_group_by_type">Group by type</string>
<string name="dialog_group_favourites">Group favourites</string> <string name="dialog_group_favourites">Group favourites</string>
<string name="dialog_button_done">Done</string>
<string name="chatroom_header">Header</string> <string name="chatroom_header">Header</string>
<!--ChatRooms Headers--> <!--ChatRooms Headers-->
...@@ -275,4 +279,6 @@ ...@@ -275,4 +279,6 @@
<string name="notif_success_sending">Message sent to %1$s!</string> <string name="notif_success_sending">Message sent to %1$s!</string>
<string name="read_by">Read by</string> <string name="read_by">Read by</string>
<string name="message_information_title">Message information</string> <string name="message_information_title">Message information</string>
<string name="foss" tools:ignore="MissingTranslation">(FOSS)</string>
</resources> </resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="chat.rocket.android">
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<permission
android:name="${applicationId}.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="${applicationId}.permission.C2D_MESSAGE" />
<application
android:name=".app.RocketChatApplication"
android:allowBackup="true"
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:supportsRtl="true">
<receiver
android:name="com.google.android.gms.gcm.GcmReceiver"
android:exported="true"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="${applicationId}" />
</intent-filter>
</receiver>
<service
android:name=".push.FirebaseTokenService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
</intent-filter>
</service>
<service
android:name=".push.FirebaseMessagingService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>
</manifest>
package chat.rocket.android.helper
import android.app.Activity
import android.content.Intent
import androidx.fragment.app.FragmentActivity
import com.google.android.gms.auth.api.credentials.Credential
import com.google.android.gms.auth.api.credentials.Credentials
fun FragmentActivity.saveCredentials(id: String, password: String) {
val credentialsClient = Credentials.getClient(this)
SmartLockHelper.save(credentialsClient, this, id, password)
}
fun Activity.requestStoredCredentials(): Pair<String, String>? {
val credentialsClient = Credentials.getClient(this)
return SmartLockHelper.requestStoredCredentials(credentialsClient, this)?.let {
null
}
}
fun getCredentials(data: Intent): Pair<String, String>? {
val credentials: Credential = data.getParcelableExtra(Credential.EXTRA_KEY)
return credentials?.let { cred ->
cred.password?.let {
Pair(cred.id, it)
}
}
}
fun hasCredentialsSupport() = true
\ No newline at end of file
package chat.rocket.android.util
import chat.rocket.android.BuildConfig
import io.fabric.sdk.android.Fabric
import com.crashlytics.android.Crashlytics
import com.crashlytics.android.core.CrashlyticsCore
import android.content.Context
import chat.rocket.android.app.RocketChatApplication
import chat.rocket.android.infrastructure.installCrashlyticsWrapper
fun setupCrashlytics(context: Context) {
val core = CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG).build()
Fabric.with(context, Crashlytics.Builder().core(core).build())
installCrashlyticsWrapper(context as RocketChatApplication,
context.getCurrentServerInteractor, context.settingsInteractor,
context.accountRepository, context.localRepository)
}
\ No newline at end of file
package chat.rocket.android.util
import chat.rocket.android.R
import chat.rocket.android.main.ui.MainActivity
import chat.rocket.android.main.presentation.MainPresenter
import com.google.firebase.iid.FirebaseInstanceId
import com.google.firebase.messaging.FirebaseMessaging
import timber.log.Timber
suspend fun refreshFCMToken(presenter: MainPresenter) {
try {
val token = FirebaseInstanceId.getInstance().token
Timber.d("FCM token: $token")
presenter.refreshToken(token)
} catch (ex: Exception) {
Timber.d(ex, "Missing play services...")
}
}
fun invalidateFirebaseToken(token: String) {
FirebaseInstanceId.getInstance().deleteToken(token, FirebaseMessaging.INSTANCE_ID_SCOPE)
}
...@@ -10,10 +10,10 @@ buildscript { ...@@ -10,10 +10,10 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.3.0-alpha03' classpath 'com.android.tools.build:gradle:3.3.0-alpha05'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
classpath "org.jetbrains.dokka:dokka-gradle-plugin:${versions.dokka}" classpath "org.jetbrains.dokka:dokka-gradle-plugin:${versions.dokka}"
classpath 'com.google.gms:google-services:3.2.0' classpath 'com.google.gms:google-services:4.0.2'
classpath 'io.fabric.tools:gradle:1.25.4' classpath 'io.fabric.tools:gradle:1.25.4'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
...@@ -34,4 +34,4 @@ allprojects { ...@@ -34,4 +34,4 @@ allprojects {
task clean(type: Delete) { task clean(type: Delete) {
delete rootProject.buildDir delete rootProject.buildDir
} }
\ No newline at end of file
...@@ -18,6 +18,11 @@ android { ...@@ -18,6 +18,11 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
} }
} }
packagingOptions {
exclude 'META-INF/core.kotlin_module'
exclude 'META-INF/main.kotlin_module'
}
} }
dependencies { dependencies {
......
...@@ -5,12 +5,12 @@ ext { ...@@ -5,12 +5,12 @@ ext {
compileSdk : 28, compileSdk : 28,
targetSdk : 28, targetSdk : 28,
minSdk : 21, minSdk : 21,
buildTools : '28.0.0-rc2', buildTools : '28.0.1',
dokka : '0.9.16', dokka : '0.9.16',
// For app // For app
kotlin : '1.2.51', kotlin : '1.2.51',
coroutine : '0.23.1', coroutine : '0.24.0',
appCompat : '1.0.0-beta01', appCompat : '1.0.0-beta01',
recyclerview : '1.0.0-beta01', recyclerview : '1.0.0-beta01',
...@@ -21,9 +21,9 @@ ext { ...@@ -21,9 +21,9 @@ ext {
dagger : '2.16', dagger : '2.16',
firebase : '15.0.0', firebase : '15.0.0',
playServices : '15.0.0', playServices : '15.0.1',
exoPlayer : '2.6.0', exoPlayer : '2.8.2',
flexbox : '0.3.2', flexbox : '1.0.0',
material : '1.0.0-beta01', material : '1.0.0-beta01',
room : '2.0.0-beta01', room : '2.0.0-beta01',
...@@ -33,30 +33,30 @@ ext { ...@@ -33,30 +33,30 @@ ext {
rxAndroid : '2.0.2', rxAndroid : '2.0.2',
moshi : '1.6.0', moshi : '1.6.0',
okhttp : '3.10.0', okhttp : '3.11.0',
timber : '4.7.0', timber : '4.7.1',
threeTenABP : '1.0.5', threeTenABP : '1.1.0',
rxBinding : '2.0.0', rxBinding : '2.1.1',
fresco : '1.9.0', fresco : '1.10.0',
kotshi : '1.0.2', kotshi : '1.0.4',
frescoImageViewer : '0.5.1', frescoImageViewer : '0.5.1',
markwon : '1.0.6', markwon : '1.1.0',
aVLoadingIndicatorView: '2.1.3', aVLoadingIndicatorView: '2.1.3',
// For wearable // For wearable
wear : '2.3.0', wear : '2.3.0',
playServicesWearable : '15.0.1', playServicesWearable : '15.0.1',
supportWearable : '26.1.0', supportWearable : '27.1.1',
// For testing // For testing
junit : '4.12', junit : '4.12',
truth : '0.36', truth : '0.42',
espresso : '3.1.0-alpha2', espresso : '3.1.0-alpha4',
mockito : '2.10.0' mockito : '2.21.0'
] ]
libraries = [ libraries = [
kotlin : "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${versions.kotlin}", kotlin : "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${versions.kotlin}",
...@@ -121,10 +121,10 @@ ext { ...@@ -121,10 +121,10 @@ ext {
wearSupport : "com.android.support:wear:${versions.supportWearable}", wearSupport : "com.android.support:wear:${versions.supportWearable}",
// For testing // For testing
junit : "junit:junit:$versions.junit", junit : "junit:junit:${versions.junit}",
espressoCore : "androidx.test.espresso:espresso-core:${versions.espresso}", espressoCore : "androidx.test.espresso:espresso-core:${versions.espresso}",
espressoIntents : "androidx.test.espresso:espresso-intents:${versions.espresso}", espressoIntents : "androidx.test.espresso:espresso-intents:${versions.espresso}",
roomTest : "android.arch.persistence.room:testing:${versions.room}", roomTest : "android.arch.persistence.room:testing:${versions.room}",
truth : "com.google.truth:truth:$versions.truth" truth : "com.google.truth:truth:${versions.truth}"
] ]
} }
\ No newline at end of file
...@@ -111,6 +111,12 @@ class DrawingActivity : DaggerAppCompatActivity(), DrawView { ...@@ -111,6 +111,12 @@ class DrawingActivity : DaggerAppCompatActivity(), DrawView {
} }
private fun colorSelector() { private fun colorSelector() {
image_color_black.setOnClickListener {
custom_draw_view.setColor(
ResourcesCompat.getColor(resources, R.color.color_black, null)
)
scaleColorView(image_color_black)
}
image_color_red.setOnClickListener { image_color_red.setOnClickListener {
custom_draw_view.setColor( custom_draw_view.setColor(
ResourcesCompat.getColor(resources, R.color.color_red, null) ResourcesCompat.getColor(resources, R.color.color_red, null)
......
...@@ -26,6 +26,8 @@ android { ...@@ -26,6 +26,8 @@ android {
} }
dependencies { dependencies {
implementation project(':util')
implementation libraries.androidKtx implementation libraries.androidKtx
implementation libraries.appCompat implementation libraries.appCompat
implementation libraries.kotlin implementation libraries.kotlin
......
...@@ -17,11 +17,10 @@ import chat.rocket.android.emoji.R ...@@ -17,11 +17,10 @@ import chat.rocket.android.emoji.R
import kotlinx.android.synthetic.main.emoji_category_layout.view.* import kotlinx.android.synthetic.main.emoji_category_layout.view.*
import kotlinx.android.synthetic.main.emoji_row_item.view.* import kotlinx.android.synthetic.main.emoji_row_item.view.*
import kotlinx.coroutines.experimental.CommonPool import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.android.UI import chat.rocket.android.util.temp.UI
import kotlinx.coroutines.experimental.launch import kotlinx.coroutines.experimental.launch
import kotlinx.coroutines.experimental.withContext import kotlinx.coroutines.experimental.withContext
internal class EmojiPagerAdapter(private val listener: EmojiKeyboardListener) : PagerAdapter() { internal class EmojiPagerAdapter(private val listener: EmojiKeyboardListener) : PagerAdapter() {
private val adapters = hashMapOf<EmojiCategory, EmojiAdapter>() private val adapters = hashMapOf<EmojiCategory, EmojiAdapter>()
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment