Unverified Commit 962248a2 authored by Lucio Maciel's avatar Lucio Maciel Committed by GitHub

Merge branch 'develop-2.x' into improvement/svg-support

parents ca5d10a7 046b60b9
......@@ -14,6 +14,7 @@ android {
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true
}
buildTypes {
release {
......@@ -21,23 +22,35 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
packagingOptions {
exclude 'META-INF/core.kotlin_module'
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':core')
implementation libraries.kotlin
implementation libraries.coroutines
implementation libraries.coroutinesAndroid
implementation libraries.appCompat
implementation libraries.recyclerview
implementation libraries.design
implementation libraries.constraintLayout
implementation libraries.cardView
implementation libraries.dagger
implementation libraries.daggerSupport
kapt libraries.daggerProcessor
kapt libraries.daggerAndroidApt
implementation libraries.moshi
implementation libraries.moshiKotlin
implementation libraries.room
kapt libraries.roomProcessor
implementation libraries.roomRxjava
......@@ -69,3 +82,9 @@ dependencies {
repositories {
mavenCentral()
}
kotlin {
experimental {
coroutines "enable"
}
}
\ No newline at end of file
......@@ -15,28 +15,28 @@
android:theme="@style/AppTheme">
<activity
android:name=".app.AuthenticationActivity"
android:name=".authentication.ui.AuthenticationActivity"
android:configChanges="orientation"
android:screenOrientation="portrait"
android:theme="@style/AuthenticationTheme">
<!--<intent-filter>-->
<!--<action android:name="android.intent.action.MAIN" />-->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<!--<category android:name="android.intent.category.DEFAULT" />-->
<!--<category android:name="android.intent.category.LAUNCHER" />-->
<!--</intent-filter>-->
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".app.MainActivity"
android:theme="@style/AppTheme">
<intent-filter>
<!--<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</intent-filter>-->
</activity>
</application>
......
package chat.rocket.android
import android.app.Activity
import android.app.Fragment
abstract class BaseActivity : Activity() {
protected fun addFragment(fragment: Fragment, tag: String, layoutId: Int) {
fragmentManager.beginTransaction().add(layoutId, fragment, tag).commit()
}
}
\ No newline at end of file
package chat.rocket.android.app
import android.os.Bundle
import chat.rocket.android.BaseActivity
import chat.rocket.android.R
class AuthenticationActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_authentication)
LayoutHelper.androidBug5497Workaround(this)
addFragment(AuthenticationSignUpFragment(), "authenticationSignUpFragment", R.id.fragment_container)
}
}
\ No newline at end of file
package chat.rocket.android.app
import android.app.Fragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import chat.rocket.android.R
import kotlinx.android.synthetic.main.fragment_authentication_server.*
class AuthenticationServerFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? = inflater?.inflate(R.layout.fragment_authentication_server, container, false)
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
text_server_url.setSelection(text_server_url.length())
activity.window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
}
}
\ No newline at end of file
package chat.rocket.android.app
import DrawableHelper
import android.app.Fragment
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import chat.rocket.android.R
import kotlinx.android.synthetic.main.fragment_authentication_sign_up.*
class AuthenticationSignUpFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? = inflater?.inflate(R.layout.fragment_authentication_sign_up, container, false)
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
activity.window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
tintEditTextDrawableStart()
}
setupGlobalLayoutListener()
}
private fun tintEditTextDrawableStart() {
val context = activity.applicationContext
val personDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_person_black_24dp, context)
val atDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_at_black_24dp, context)
val lockDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_lock_black_24dp, context)
val emailDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_email_black_24dp, context)
val drawables = arrayOf(personDrawable, atDrawable, lockDrawable, emailDrawable)
DrawableHelper.wrapDrawables(drawables)
DrawableHelper.tintDrawables(drawables, context, R.color.colorDrawableTintGrey)
DrawableHelper.compoundDrawables(arrayOf(text_name, text_username, text_password, text_email), drawables)
}
private fun setupGlobalLayoutListener() {
constraint_layout.viewTreeObserver.addOnGlobalLayoutListener {
if (KeyboardHelper.isSoftKeyboardShown(constraint_layout.rootView)) {
text_new_user_agreement.visibility = View.GONE
} else {
text_new_user_agreement.visibility = View.VISIBLE
}
}
}
}
\ No newline at end of file
package chat.rocket.android.app
import DrawableHelper
import android.app.Fragment
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import chat.rocket.android.R
import kotlinx.android.synthetic.main.fragment_authentication_two_fa.*
class AuthenticationTwoFAFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? = inflater?.inflate(R.layout.fragment_authentication_two_fa, container, false)
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
activity.window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
tintEditTextDrawableStart()
}
}
private fun tintEditTextDrawableStart() {
val context = activity.applicationContext
val lockDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_vpn_key_black_24dp, context)
DrawableHelper.wrapDrawable(lockDrawable)
DrawableHelper.tintDrawable(lockDrawable, context, R.color.colorDrawableTintGrey)
DrawableHelper.compoundDrawable(text_two_factor_auth, lockDrawable)
}
}
\ No newline at end of file
......@@ -4,13 +4,14 @@ import android.app.Activity
import android.graphics.Rect
import android.util.Log
import android.view.View
import android.view.ViewTreeObserver
import android.widget.FrameLayout
//TODO: check if this code has memory leak.
object LayoutHelper {
private lateinit var childOfContent: View
class LayoutHelper {
private var childOfContent: View? = null
private var usableHeightPrevious: Int = 0
private lateinit var frameLayoutParams: FrameLayout.LayoutParams
private var frameLayoutParams: FrameLayout.LayoutParams? = null
/**
* Workaround to adjust the layout when in the full screen mode.
......@@ -22,38 +23,48 @@ object LayoutHelper {
*
* @param activity The Activity to adjust the layout.
*/
fun androidBug5497Workaround(activity: Activity) {
fun install(activity: Activity) {
try {
val content = activity.findViewById<View>(android.R.id.content) as FrameLayout
childOfContent = content.getChildAt(0)
childOfContent.viewTreeObserver.addOnGlobalLayoutListener({ resizeChildOfContent() })
frameLayoutParams = childOfContent.layoutParams as FrameLayout.LayoutParams
childOfContent?.viewTreeObserver?.addOnGlobalLayoutListener(listener)
frameLayoutParams = childOfContent?.layoutParams as FrameLayout.LayoutParams
} catch (exception : ClassCastException) {
// TODO: are we using the android.util.Log for logging that type of errors? or should we use the SDK logger?
Log.e("ERROR", exception.message)
}
}
private val listener = ViewTreeObserver.OnGlobalLayoutListener {
resizeChildOfContent()
}
private fun resizeChildOfContent() {
val usableHeightNow = computeUsableHeight()
if (usableHeightNow != usableHeightPrevious) {
val usableHeightSansKeyboard = childOfContent.rootView.height
val usableHeightSansKeyboard = childOfContent?.rootView?.height ?: 0
val heightDifference = usableHeightSansKeyboard - usableHeightNow
if (heightDifference > usableHeightSansKeyboard / 4) {
// keyboard probably just became visible
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference
frameLayoutParams?.height = usableHeightSansKeyboard - heightDifference
} else {
// keyboard probably just became hidden
frameLayoutParams.height = usableHeightNow
frameLayoutParams?.height = usableHeightNow
}
childOfContent.requestLayout()
childOfContent?.requestLayout()
usableHeightPrevious = usableHeightNow
}
}
private fun computeUsableHeight(): Int {
val rect = Rect()
childOfContent.getWindowVisibleDisplayFrame(rect)
childOfContent?.getWindowVisibleDisplayFrame(rect)
return rect.bottom - rect.top
}
fun remove() {
childOfContent?.viewTreeObserver?.removeOnGlobalLayoutListener(listener)
childOfContent = null
frameLayoutParams = null
}
}
\ No newline at end of file
package chat.rocket.android.app
import android.os.Bundle
import chat.rocket.android.BaseActivity
import android.support.v7.app.AppCompatActivity
import chat.rocket.android.R
import chat.rocket.android.app.chatlist.ChatListFragment
import chat.rocket.android.util.addFragment
class MainActivity : BaseActivity() {
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
addFragment(ChatListFragment(), "ChatListFragment", R.id.fragment_container)
addFragment("ChatListFragment", R.id.fragment_container) {
ChatListFragment()
}
}
}
\ No newline at end of file
......@@ -4,9 +4,8 @@ import android.app.Activity
import android.app.Application
import chat.rocket.android.BuildConfig
import chat.rocket.android.app.utils.CustomImageFormatConfigurator
import chat.rocket.android.dagger.DaggerApplicationComponent
import com.facebook.drawee.backends.pipeline.DraweeConfig
import chat.rocket.android.dagger.DaggerAppComponent
import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.imagepipeline.core.ImagePipelineConfig
import com.jakewharton.threetenabp.AndroidThreeTen
......@@ -18,15 +17,13 @@ import javax.inject.Inject
class RocketChatApplication : Application(), HasActivityInjector {
@Inject lateinit var activityInjector: DispatchingAndroidInjector<Activity>
@Inject
lateinit var activityDispatchingAndroidInjector: DispatchingAndroidInjector<Activity>
override fun onCreate() {
super.onCreate()
DaggerApplicationComponent.builder()
.application(this)
.build()
.inject(this)
DaggerAppComponent.builder().application(this).build().inject(this)
AndroidThreeTen.init(this)
......@@ -53,6 +50,6 @@ class RocketChatApplication : Application(), HasActivityInjector {
}
override fun activityInjector(): AndroidInjector<Activity> {
return activityInjector
return activityDispatchingAndroidInjector
}
}
\ No newline at end of file
package chat.rocket.android.app.chatlist
import android.app.Fragment
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.widget.DividerItemDecoration
import android.support.v7.widget.LinearLayoutManager
import android.view.LayoutInflater
......@@ -13,9 +13,9 @@ import org.threeten.bp.LocalDateTime
class ChatListFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? = inflater?.inflate(R.layout.fragment_chat_list, container, false)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = inflater.inflate(R.layout.fragment_chat_list, container, false)
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
showChatList(createDumpData())
}
......@@ -85,9 +85,10 @@ class ChatListFragment : Fragment() {
// REMARK: The presenter should call this method. The presenter also need to sort the chat list by latest message (compared by its date).
private fun showChatList(dataSet: List<Chat>) {
val context = activity.applicationContext
recycler_view.adapter = ChatListAdapter(dataSet.toMutableList(), context)
recycler_view.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
recycler_view.addItemDecoration(DividerItemDecoration(activity, DividerItemDecoration.VERTICAL))
activity?.apply {
recycler_view.adapter = ChatListAdapter(dataSet.toMutableList(), this)
recycler_view.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
recycler_view.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL))
}
}
}
package chat.rocket.android.authentication.di
import chat.rocket.android.authentication.infraestructure.AuthTokenRepository
import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.authentication.ui.AuthenticationActivity
import chat.rocket.android.dagger.scope.PerActivity
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module
class AuthenticationModule {
@Provides
@PerActivity
fun provideAuthenticationNavigator(activity: AuthenticationActivity) = AuthenticationNavigator(activity)
@Provides
@PerActivity
fun provideAuthTokenRepository(): AuthTokenRepository {
return AuthTokenRepository()
}
@Provides
fun provideJob(): Job {
return Job()
}
}
package chat.rocket.android.authentication.infraestructure
import chat.rocket.common.model.Token
import chat.rocket.core.TokenRepository
class AuthTokenRepository : TokenRepository {
var savedToken: Token? = null
override fun get(): Token? {
return savedToken
}
override fun save(token: Token) {
savedToken = token
}
}
\ No newline at end of file
package chat.rocket.android.authentication.login.di
import android.arch.lifecycle.LifecycleOwner
import chat.rocket.android.authentication.login.presentation.LoginView
import chat.rocket.android.authentication.login.ui.LoginFragment
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module
@PerFragment
class LoginFragmentModule {
@Provides
fun loginView(frag: LoginFragment): LoginView {
return frag
}
@Provides
fun provideLifecycleOwner(frag: LoginFragment): LifecycleOwner {
return frag
}
@Provides
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs)
}
}
package chat.rocket.android.authentication.login.di
import chat.rocket.android.authentication.login.ui.LoginFragment
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module abstract class LoginFragmentProvider {
@ContributesAndroidInjector(modules = arrayOf(LoginFragmentModule::class))
abstract fun provideLoginFragment(): LoginFragment
}
package chat.rocket.android.authentication.login.presentation
import chat.rocket.android.authentication.infraestructure.AuthTokenRepository
import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.util.launchUI
import chat.rocket.common.RocketChatException
import chat.rocket.common.RocketChatTwoFactorException
import chat.rocket.common.util.PlatformLogger
import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.login
import okhttp3.HttpUrl
import okhttp3.OkHttpClient
import javax.inject.Inject
class LoginPresenter @Inject constructor(private val view: LoginView,
private val strategy: CancelStrategy,
private val navigator: AuthenticationNavigator,
private val okHttpClient: OkHttpClient,
private val logger: PlatformLogger,
private val repository: AuthTokenRepository) {
val client: RocketChatClient = RocketChatClient.create {
httpClient = okHttpClient
restUrl = HttpUrl.parse(navigator.currentServer)!!
websocketUrl = navigator.currentServer!!
tokenRepository = repository
platformLogger = logger
}
fun authenticate(username: String, password: String) {
// TODO - validate input
launchUI(strategy) {
view.showLoading()
try {
val token = client.login(username, password)
navigator.toChatList()
} catch (ex: RocketChatException) {
when(ex) {
is RocketChatTwoFactorException ->
navigator.toTwoFA(navigator.currentServer!!, username, password)
else ->
view.onLoginError(ex.message)
}
} finally {
view.hideLoading()
}
}
}
fun signup() {
navigator.toSignUp(navigator.currentServer!!)
}
}
\ No newline at end of file
package chat.rocket.android.authentication.login.presentation
import chat.rocket.android.core.behaviours.LoadingView
interface LoginView : LoadingView {
fun onLoginError(message: String?)
}
\ No newline at end of file
package chat.rocket.android.app
package chat.rocket.android.authentication.login.ui
import DrawableHelper
import android.app.Fragment
import android.app.ProgressDialog
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.support.v4.app.Fragment
import android.view.*
import android.widget.ScrollView
import android.widget.Toast
import chat.rocket.android.R
import chat.rocket.android.app.KeyboardHelper
import chat.rocket.android.authentication.login.presentation.LoginPresenter
import chat.rocket.android.authentication.login.presentation.LoginView
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_authentication_log_in.*
import javax.inject.Inject
class AuthenticationLoginFragment : Fragment() {
class LoginFragment : Fragment(), LoginView {
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? = inflater?.inflate(R.layout.fragment_authentication_log_in, container, false)
companion object {
private const val SERVER_URL = "server_url"
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
fun newInstance(url: String) = LoginFragment().apply {
arguments = Bundle(1).apply {
putString(SERVER_URL, url)
}
}
}
var progress: ProgressDialog? = null
lateinit var serverUrl: String
@Inject
lateinit var presenter: LoginPresenter
override fun onCreate(savedInstanceState: Bundle?) {
AndroidSupportInjection.inject(this)
super.onCreate(savedInstanceState)
// TODO - research a better way to initialize parameters on fragments.
serverUrl = arguments?.getString(SERVER_URL) ?: "https://open.rocket.chat"
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = inflater.inflate(R.layout.fragment_authentication_log_in, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
activity.window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN)
activity?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN)
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
tintEditTextDrawableStart()
......@@ -40,22 +68,38 @@ class AuthenticationLoginFragment : Fragment() {
// Just an example: if the server allow the new users registration then show the respective interface.
shouldShowSignUpMsgView(true)
button_log_in.setOnClickListener {
presenter.authenticate(text_username_or_email.text.toString(), text_password.text.toString())
}
private fun tintEditTextDrawableStart() {
val context = activity.applicationContext
text_new_to_rocket_chat.setOnClickListener {
presenter.signup()
}
}
val personDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_assignment_ind_black_24dp, context)
val lockDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_lock_black_24dp, context)
override fun onDestroyView() {
scroll_view.viewTreeObserver.removeOnGlobalLayoutListener(layoutListener)
super.onDestroyView()
}
private fun tintEditTextDrawableStart() {
activity?.applicationContext?.apply {
val personDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_assignment_ind_black_24dp, this)
val lockDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_lock_black_24dp, this)
val drawables = arrayOf(personDrawable, lockDrawable)
DrawableHelper.wrapDrawables(drawables)
DrawableHelper.tintDrawables(drawables, context, R.color.colorDrawableTintGrey)
DrawableHelper.tintDrawables(drawables, this, R.color.colorDrawableTintGrey)
DrawableHelper.compoundDrawables(arrayOf(text_username_or_email, text_password), drawables)
}
}
private fun setupGlobalLayoutListener() {
scroll_view.viewTreeObserver.addOnGlobalLayoutListener({
scroll_view.viewTreeObserver.addOnGlobalLayoutListener(layoutListener)
}
val layoutListener = ViewTreeObserver.OnGlobalLayoutListener {
if (KeyboardHelper.isSoftKeyboardShown(scroll_view.rootView)) {
shouldShowOauthView(false)
shouldShowSignUpMsgView(false)
......@@ -67,7 +111,6 @@ class AuthenticationLoginFragment : Fragment() {
shouldShowLoginButton(false)
}
}
})
}
private fun shouldShowOauthView(show: Boolean) {
......@@ -152,4 +195,22 @@ class AuthenticationLoginFragment : Fragment() {
button_fab.hide()
}, 1500)
}
override fun showLoading() {
// TODO - change for a proper progress indicator
progress = ProgressDialog.show(activity, "Authenticating",
"Verifying user credentials", true, true)
}
override fun hideLoading() {
progress?.apply {
cancel()
}
progress = null
}
override fun onLoginError(message: String?) {
// TODO - show a proper error message
Toast.makeText(activity, message, Toast.LENGTH_LONG).show()
}
}
\ No newline at end of file
package chat.rocket.android.authentication.presentation
import android.content.Intent
import chat.rocket.android.R
import chat.rocket.android.app.MainActivity
import chat.rocket.android.authentication.ui.AuthenticationActivity
import chat.rocket.android.authentication.login.ui.LoginFragment
import chat.rocket.android.authentication.signup.ui.SignupFragment
import chat.rocket.android.authentication.twofactor.ui.TwoFAFragment
import chat.rocket.android.util.addFragmentBackStack
class AuthenticationNavigator(internal val activity: AuthenticationActivity) {
var currentServer: String? = null
fun toLogin(server: String) {
currentServer = server
activity.addFragmentBackStack("loginFragment", R.id.fragment_container) {
LoginFragment.newInstance(server)
}
}
fun toChatList() {
val chatRoom = Intent(activity, MainActivity::class.java).apply {
//TODO any parameter to pass
}
activity.startActivity(chatRoom)
activity.finish()
}
fun toTwoFA(server: String, username: String, password: String) {
currentServer = server
activity.addFragmentBackStack("twoFAFragment", R.id.fragment_container) {
TwoFAFragment.newInstance(server, username, password)
}
}
fun toSignUp(server: String) {
currentServer = server
activity.addFragmentBackStack("signupFragment", R.id.fragment_container) {
SignupFragment.newInstance(server)
}
}
}
package chat.rocket.android.authentication.server.di
import chat.rocket.android.authentication.server.presentation.ServerView
import chat.rocket.android.authentication.server.ui.ServerFragment
import dagger.Module
import dagger.Provides
@Module
class ServerFragmentModule {
@Provides
fun serverView(frag: ServerFragment): ServerView {
return frag
}
}
package chat.rocket.android.authentication.server.di
import chat.rocket.android.authentication.server.ui.ServerFragment
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module abstract class ServerFragmentProvider {
@ContributesAndroidInjector(modules = arrayOf(ServerFragmentModule::class))
abstract fun provideServerFragment(): ServerFragment
}
\ No newline at end of file
package chat.rocket.android.authentication.server.presentation
import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import javax.inject.Inject
class ServerPresenter @Inject constructor(private val view: ServerView,
private val navigator: AuthenticationNavigator) {
fun login(server: String) {
// TODO - validate server URL and get server settings and info before going to Login screen
navigator.toLogin(server)
}
}
\ No newline at end of file
package chat.rocket.android.authentication.server.presentation
interface ServerView
\ No newline at end of file
package chat.rocket.android.authentication.server.ui
import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import chat.rocket.android.R
import chat.rocket.android.authentication.server.presentation.ServerPresenter
import chat.rocket.android.authentication.server.presentation.ServerView
import chat.rocket.android.util.ifEmpty
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_authentication_server.*
import javax.inject.Inject
class ServerFragment : Fragment(), ServerView {
@Inject
lateinit var presenter: ServerPresenter
override fun onCreate(savedInstanceState: Bundle?) {
AndroidSupportInjection.inject(this)
super.onCreate(savedInstanceState)
}
override fun onCreateView(inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?): View? =
inflater.inflate(R.layout.fragment_authentication_server, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
text_server_url.setSelection(text_server_url.length())
activity?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
button_connect.setOnClickListener {
val url = text_server_url.text.toString().ifEmpty(text_server_url.hint.toString())
presenter.login(server_protocol_label.text.toString() + url)
}
}
companion object {
fun newInstance() = ServerFragment()
}
}
\ No newline at end of file
package chat.rocket.android.authentication.signup.di
import android.arch.lifecycle.LifecycleOwner
import chat.rocket.android.authentication.signup.presentation.SignupView
import chat.rocket.android.authentication.signup.ui.SignupFragment
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module
@PerFragment
class SignupFragmentModule {
@Provides
fun signupView(frag: SignupFragment): SignupView {
return frag
}
@Provides
fun provideLifecycleOwner(frag: SignupFragment): LifecycleOwner {
return frag
}
@Provides
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs)
}
}
package chat.rocket.android.authentication.signup.di
import chat.rocket.android.authentication.signup.ui.SignupFragment
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module abstract class SignupFragmentProvider {
@ContributesAndroidInjector(modules = arrayOf(SignupFragmentModule::class))
abstract fun provideSignupFragment(): SignupFragment
}
package chat.rocket.android.authentication.signup.presentation
import chat.rocket.android.authentication.infraestructure.AuthTokenRepository
import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.util.launchUI
import chat.rocket.common.RocketChatException
import chat.rocket.common.util.PlatformLogger
import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.login
import chat.rocket.core.internal.rest.signup
import okhttp3.HttpUrl
import okhttp3.OkHttpClient
import timber.log.Timber
import javax.inject.Inject
class SignupPresenter @Inject constructor(private val view: SignupView,
private val strategy: CancelStrategy,
private val navigator: AuthenticationNavigator,
private val okHttpClient: OkHttpClient,
private val logger: PlatformLogger,
private val repository: AuthTokenRepository) {
val client: RocketChatClient = RocketChatClient.create {
httpClient = okHttpClient
restUrl = HttpUrl.parse(navigator.currentServer)!!
websocketUrl = navigator.currentServer!!
tokenRepository = repository
platformLogger = logger
}
fun signup(email: String, name: String, username: String, password: String) {
// TODO - validate input
launchUI(strategy) {
view.showLoading()
try {
val user = client.signup(email, name, username, password)
Timber.d("Created user: $user")
val token = client.login(username, password)
Timber.d("Logged in: $token")
navigator.toChatList()
} catch (ex: RocketChatException) {
view.onSignupError(ex.message)
} finally {
view.hideLoading()
}
}
}
}
\ No newline at end of file
package chat.rocket.android.authentication.signup.presentation
import chat.rocket.android.core.behaviours.LoadingView
interface SignupView : LoadingView {
fun onSignupError(message: String? = "Unknown error")
}
\ No newline at end of file
package chat.rocket.android.authentication.signup.ui
import DrawableHelper
import android.app.ProgressDialog
import android.os.Build
import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.*
import android.widget.Toast
import chat.rocket.android.R
import chat.rocket.android.app.KeyboardHelper
import chat.rocket.android.authentication.signup.presentation.SignupPresenter
import chat.rocket.android.authentication.signup.presentation.SignupView
import chat.rocket.android.util.content
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_authentication_sign_up.*
import javax.inject.Inject
class SignupFragment : Fragment(), SignupView {
companion object {
private const val SERVER_URL = "server_url"
fun newInstance(url: String) = SignupFragment().apply {
arguments = Bundle(1).apply {
putString(SERVER_URL, url)
}
}
}
@Inject
lateinit var presenter: SignupPresenter
var progress: ProgressDialog? = null
lateinit var serverUrl: String
override fun onCreate(savedInstanceState: Bundle?) {
AndroidSupportInjection.inject(this)
super.onCreate(savedInstanceState)
// TODO - research a better way to initialize parameters on fragments.
serverUrl = arguments?.getString(SERVER_URL) ?: "https://open.rocket.chat"
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = inflater.inflate(R.layout.fragment_authentication_sign_up, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
activity?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
tintEditTextDrawableStart()
}
setupGlobalLayoutListener()
button_sign_up.setOnClickListener {
val email = text_email.content
val name = text_name.content
val username = text_username.content
val password = text_password.content
presenter.signup(email, name, username, password)
}
}
override fun onDestroyView() {
constraint_layout.viewTreeObserver.removeOnGlobalLayoutListener(layoutListener)
super.onDestroyView()
}
private fun tintEditTextDrawableStart() {
activity?.applicationContext?.apply {
val personDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_person_black_24dp, this)
val atDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_at_black_24dp, this)
val lockDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_lock_black_24dp, this)
val emailDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_email_black_24dp, this)
val drawables = arrayOf(personDrawable, atDrawable, lockDrawable, emailDrawable)
DrawableHelper.wrapDrawables(drawables)
DrawableHelper.tintDrawables(drawables, this, R.color.colorDrawableTintGrey)
DrawableHelper.compoundDrawables(arrayOf(text_name, text_username, text_password, text_email), drawables)
}
}
private fun setupGlobalLayoutListener() {
constraint_layout.viewTreeObserver.addOnGlobalLayoutListener(layoutListener)
}
val layoutListener = ViewTreeObserver.OnGlobalLayoutListener {
if (KeyboardHelper.isSoftKeyboardShown(constraint_layout.rootView)) {
text_new_user_agreement.visibility = View.GONE
} else {
text_new_user_agreement.visibility = View.VISIBLE
}
}
override fun showLoading() {
// TODO - change for a proper progress indicator
progress = ProgressDialog.show(activity, "Authenticating",
"Registering user", true, true)
}
override fun hideLoading() {
progress?.apply {
cancel()
}
progress = null
}
override fun onSignupError(message: String?) {
// TODO - show a proper error message
Toast.makeText(activity, message, Toast.LENGTH_LONG).show()
}
}
\ No newline at end of file
package chat.rocket.android.authentication.twofactor.di
import android.arch.lifecycle.LifecycleOwner
import chat.rocket.android.authentication.twofactor.presentation.TwoFAView
import chat.rocket.android.authentication.twofactor.ui.TwoFAFragment
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.dagger.scope.PerFragment
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.experimental.Job
@Module
@PerFragment
class TwoFAFragmentModule {
@Provides
fun loginView(frag: TwoFAFragment): TwoFAView {
return frag
}
@Provides
fun provideLifecycleOwner(frag: TwoFAFragment): LifecycleOwner {
return frag
}
@Provides
fun provideCancelStrategy(owner: LifecycleOwner, jobs: Job): CancelStrategy {
return CancelStrategy(owner, jobs)
}
}
package chat.rocket.android.authentication.twofactor.di
import chat.rocket.android.authentication.twofactor.ui.TwoFAFragment
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module abstract class TwoFAFragmentProvider {
@ContributesAndroidInjector(modules = arrayOf(TwoFAFragmentModule::class))
abstract fun provideTwoFAFragment(): TwoFAFragment
}
package chat.rocket.android.authentication.twofactor.presentation
import chat.rocket.android.authentication.infraestructure.AuthTokenRepository
import chat.rocket.android.authentication.presentation.AuthenticationNavigator
import chat.rocket.android.core.lifecycle.CancelStrategy
import chat.rocket.android.util.launchUI
import chat.rocket.common.RocketChatException
import chat.rocket.common.util.PlatformLogger
import chat.rocket.core.RocketChatClient
import chat.rocket.core.internal.rest.login
import okhttp3.HttpUrl
import okhttp3.OkHttpClient
import javax.inject.Inject
class TwoFAPresenter @Inject constructor(private val view: TwoFAView,
private val strategy: CancelStrategy,
private val navigator: AuthenticationNavigator,
private val okHttpClient: OkHttpClient,
private val logger: PlatformLogger,
private val repository: AuthTokenRepository) {
val client: RocketChatClient = RocketChatClient.create {
httpClient = okHttpClient
restUrl = HttpUrl.parse(navigator.currentServer)!!
websocketUrl = navigator.currentServer!!
tokenRepository = repository
platformLogger = logger
}
fun authenticate(username: String, password: String, pin: String) {
// TODO - validate input
launchUI(strategy) {
view.showLoading()
try {
val token = client.login(username, password, pin)
navigator.toChatList()
} catch (ex: RocketChatException) {
view.onLoginError(ex.message)
} finally {
view.hideLoading()
}
}
}
fun signup() {
navigator.toSignUp(navigator.currentServer!!)
}
}
\ No newline at end of file
package chat.rocket.android.authentication.twofactor.presentation
import chat.rocket.android.authentication.login.presentation.LoginView
interface TwoFAView : LoginView
\ No newline at end of file
package chat.rocket.android.authentication.twofactor.ui
import DrawableHelper
import android.app.ProgressDialog
import android.os.Build
import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.Toast
import chat.rocket.android.R
import chat.rocket.android.authentication.twofactor.presentation.TwoFAPresenter
import chat.rocket.android.authentication.twofactor.presentation.TwoFAView
import chat.rocket.android.util.content
import dagger.android.support.AndroidSupportInjection
import kotlinx.android.synthetic.main.fragment_authentication_two_fa.*
import javax.inject.Inject
class TwoFAFragment : Fragment(), TwoFAView {
companion object {
private const val SERVER_URL = "server_url"
private const val USERNAME = "username"
private const val PASSWORD = "password"
fun newInstance(url: String, username: String, password: String) = TwoFAFragment().apply {
arguments = Bundle(1).apply {
putString(SERVER_URL, url)
putString(USERNAME, username)
putString(PASSWORD, password)
}
}
}
var progress: ProgressDialog? = null
lateinit var serverUrl: String
lateinit var username: String
lateinit var password: String
@Inject
lateinit var presenter: TwoFAPresenter
override fun onCreate(savedInstanceState: Bundle?) {
AndroidSupportInjection.inject(this)
super.onCreate(savedInstanceState)
// TODO - research a better way to initialize parameters on fragments.
serverUrl = arguments?.getString(SERVER_URL) ?: "https://open.rocket.chat"
username = arguments?.getString(USERNAME) ?: ""
password = arguments?.getString(PASSWORD) ?: ""
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = inflater.inflate(R.layout.fragment_authentication_two_fa, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
activity?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
tintEditTextDrawableStart()
}
button_log_in.setOnClickListener {
presenter.authenticate(username, password, text_two_factor_auth.content)
}
}
private fun tintEditTextDrawableStart() {
activity?.applicationContext?.apply {
val lockDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_vpn_key_black_24dp, this)
DrawableHelper.wrapDrawable(lockDrawable)
DrawableHelper.tintDrawable(lockDrawable, this, R.color.colorDrawableTintGrey)
DrawableHelper.compoundDrawable(text_two_factor_auth, lockDrawable)
}
}
override fun showLoading() {
// TODO - change for a proper progress indicator
progress = ProgressDialog.show(activity, "Authenticating",
"Verifying user credentials", true, true)
}
override fun hideLoading() {
progress?.apply {
cancel()
}
progress = null
}
override fun onLoginError(message: String?) {
// TODO - show a proper error message
Toast.makeText(activity, message, Toast.LENGTH_LONG).show()
}
}
\ No newline at end of file
package chat.rocket.android.authentication.ui
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.app.AppCompatActivity
import chat.rocket.android.R
import chat.rocket.android.app.LayoutHelper
import chat.rocket.android.authentication.server.ui.ServerFragment
import chat.rocket.android.util.addFragment
import dagger.android.AndroidInjection
import dagger.android.AndroidInjector
import dagger.android.DispatchingAndroidInjector
import dagger.android.support.HasSupportFragmentInjector
import javax.inject.Inject
class AuthenticationActivity : AppCompatActivity(), HasSupportFragmentInjector {
@Inject
lateinit var fragmentDispatchingAndroidInjector: DispatchingAndroidInjector<Fragment>
val layoutHelper = LayoutHelper()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AndroidInjection.inject(this)
setContentView(R.layout.activity_authentication)
layoutHelper.install(this)
addFragment("authenticationServerFragment", R.id.fragment_container) {
ServerFragment.newInstance()
}
}
override fun onDestroy() {
layoutHelper.remove()
super.onDestroy()
}
override fun supportFragmentInjector(): AndroidInjector<Fragment> {
return fragmentDispatchingAndroidInjector
}
}
\ No newline at end of file
package chat.rocket.android.core.behaviours
interface LoadingView {
fun showLoading()
fun hideLoading()
}
\ No newline at end of file
package chat.rocket.android.core.lifecycle
import android.arch.lifecycle.Lifecycle
import android.arch.lifecycle.LifecycleObserver
import android.arch.lifecycle.LifecycleOwner
import android.arch.lifecycle.OnLifecycleEvent
import kotlinx.coroutines.experimental.Job
import javax.inject.Inject
class CancelStrategy @Inject constructor(owner: LifecycleOwner, val jobs: Job) : LifecycleObserver {
init {
owner.lifecycle.addObserver(this)
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onDestroy() {
jobs.cancel()
}
}
\ No newline at end of file
package chat.rocket.android.dagger
import android.app.Application
import javax.inject.Singleton
import chat.rocket.android.app.RocketChatApplication
import chat.rocket.android.dagger.module.ActivityBindingModule
import chat.rocket.android.dagger.module.ApplicationModule
import chat.rocket.android.dagger.module.ActivityBuilder
import chat.rocket.android.dagger.module.AppModule
import dagger.BindsInstance
import dagger.Component
import dagger.android.support.AndroidSupportInjectionModule
import javax.inject.Singleton
@Singleton
@Component(modules = arrayOf(ActivityBindingModule::class, ApplicationModule::class,
AndroidSupportInjectionModule::class))
interface ApplicationComponent {
@Component(modules = arrayOf(AndroidSupportInjectionModule::class, AppModule::class, ActivityBuilder::class))
interface AppComponent {
@Component.Builder
interface Builder {
@BindsInstance
fun application(application: Application): Builder
fun build(): ApplicationComponent
fun build(): AppComponent
}
fun inject(application: RocketChatApplication)
fun inject(app: RocketChatApplication)
/*@Component.Builder
abstract class Builder : AndroidInjector.Builder<RocketChatApplication>()*/
}
package chat.rocket.android.dagger.module;
import dagger.Module;
@Module
public abstract class ActivityBindingModule {
}
package chat.rocket.android.dagger.module
import chat.rocket.android.app.MainActivity
import chat.rocket.android.authentication.di.*
import chat.rocket.android.authentication.login.di.LoginFragmentProvider
import chat.rocket.android.authentication.server.di.ServerFragmentProvider
import chat.rocket.android.authentication.signup.di.SignupFragmentProvider
import chat.rocket.android.authentication.twofactor.di.TwoFAFragmentProvider
import chat.rocket.android.authentication.ui.AuthenticationActivity
import chat.rocket.android.dagger.scope.PerActivity
import dagger.Module
import dagger.android.ContributesAndroidInjector
@Module
abstract class ActivityBuilder {
@PerActivity
@ContributesAndroidInjector(modules = arrayOf(
AuthenticationModule::class,
LoginFragmentProvider::class,
ServerFragmentProvider::class,
SignupFragmentProvider::class,
TwoFAFragmentProvider::class
))
abstract fun bindAuthenticationActivity(): AuthenticationActivity
@ContributesAndroidInjector
abstract fun bindMainActivity(): MainActivity
}
......@@ -3,14 +3,19 @@ package chat.rocket.android.dagger.module
import android.app.Application
import android.arch.persistence.room.Room
import android.content.Context
import chat.rocket.android.BuildConfig
import chat.rocket.android.app.RocketChatDatabase
import chat.rocket.android.server.infraestructure.ServerDao
import chat.rocket.android.util.TimberLogger
import chat.rocket.common.util.PlatformLogger
import dagger.Module
import dagger.Provides
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import javax.inject.Singleton
@Module
class ApplicationModule {
class AppModule {
@Provides
@Singleton
......@@ -29,4 +34,45 @@ class ApplicationModule {
fun provideServerDao(database: RocketChatDatabase): ServerDao {
return database.serverDao()
}
/*@Provides
@Singleton
@IntoSet
fun provideHttpLoggingInterceptor(): Interceptor {
val interceptor = HttpLoggingInterceptor()
if (BuildConfig.DEBUG) {
interceptor.level = HttpLoggingInterceptor.Level.BODY
} else {
interceptor.level = HttpLoggingInterceptor.Level.HEADERS
}
return interceptor
}*/
@Provides
@Singleton
fun provideHttpLoggingInterceptor(): HttpLoggingInterceptor {
val interceptor = HttpLoggingInterceptor()
if (BuildConfig.DEBUG) {
interceptor.level = HttpLoggingInterceptor.Level.BODY
} else {
interceptor.level = HttpLoggingInterceptor.Level.HEADERS
}
return interceptor
}
@Provides
@Singleton
fun provideOkHttpClient(logger: HttpLoggingInterceptor): OkHttpClient {
return OkHttpClient.Builder().apply {
addInterceptor(logger)
}.build()
}
@Provides
@Singleton
fun providePlatformLogger(): PlatformLogger {
return TimberLogger()
}
}
package chat.rocket.android.dagger.scope
import javax.inject.Scope
@Scope
@Retention(AnnotationRetention.RUNTIME)
annotation class PerFragment
\ No newline at end of file
package chat.rocket.android.login.ui;
import android.annotation.SuppressLint;
import android.support.v7.app.AppCompatActivity;
@SuppressLint("Registered")
public class LoginActivity extends AppCompatActivity {
}
package chat.rocket.android.util
import android.support.v4.app.Fragment
import android.support.v7.app.AppCompatActivity
fun AppCompatActivity.addFragment(tag: String, layoutId: Int, newInstance: () -> Fragment) {
val fragment = supportFragmentManager.findFragmentByTag(tag) ?: newInstance()
supportFragmentManager.beginTransaction().replace(layoutId, fragment, tag).commit()
}
fun AppCompatActivity.addFragmentBackStack(tag: String, layoutId: Int, newInstance: () -> Fragment) {
val fragment = supportFragmentManager.findFragmentByTag(tag) ?: newInstance()
supportFragmentManager.beginTransaction().replace(layoutId, fragment, tag).addToBackStack(tag).commit()
}
\ No newline at end of file
package chat.rocket.android.util
import chat.rocket.android.core.lifecycle.CancelStrategy
import kotlinx.coroutines.experimental.CoroutineScope
import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.launch
/**
* Launches a coroutine on the UI context.
*
* @param strategy a CancelStrategy for canceling the coroutine job
*/
fun launchUI(strategy: CancelStrategy, block: suspend CoroutineScope.() -> Unit): Job {
return launch(context = UI, parent = strategy.jobs, block = block)
}
\ No newline at end of file
package chat.rocket.android.util
import android.widget.TextView
fun String.ifEmpty(value: String): String {
if (isEmpty()) {
return value
}
return this
}
var TextView.content: String
get() = this.text.toString()
set(value) { this.text = value }
\ No newline at end of file
package chat.rocket.android.util
import chat.rocket.common.util.PlatformLogger
import timber.log.Timber
class TimberLogger : PlatformLogger {
override fun debug(s: String) {
Timber.d(s)
}
override fun info(s: String) {
Timber.i(s)
}
override fun warn(s: String) {
Timber.w(s)
}
}
\ No newline at end of file
......@@ -168,7 +168,7 @@
<TextView
android:id="@+id/text_new_to_rocket_chat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_height="48dp"
android:layout_marginBottom="16dp"
android:layout_marginEnd="@dimen/screen_edge_left_and_right_margins"
android:layout_marginStart="@dimen/screen_edge_left_and_right_margins"
......
......@@ -9,14 +9,24 @@
android:layout_centerHorizontal="true"
android:text="@string/title_sign_in_your_server" />
<TextView
android:id="@+id/server_protocol_label"
style="@style/AuthenticationLabel"
android:layout_below="@id/text_headline"
android:layout_marginTop="32dp"
android:gravity="center_vertical"
android:text="@string/default_protocol"/>
<EditText
android:id="@+id/text_server_url"
style="@style/AuthenticationEditText"
android:layout_below="@id/text_headline"
android:layout_toEndOf="@id/server_protocol_label"
android:layout_marginTop="32dp"
android:layout_marginStart="0dp"
android:paddingStart="0dp"
android:imeOptions="actionDone"
android:inputType="textUri"
android:text="@string/msg_https" />
android:hint="@string/default_server" />
<Button
android:id="@+id/button_connect"
......
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AuthenticationEditText" parent="Widget.AppCompat.EditText">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">50dp</item>
<item name="android:layout_marginStart">@dimen/screen_edge_left_and_right_margins</item>
<item name="android:layout_marginEnd">@dimen/screen_edge_left_and_right_margins</item>
<item name="android:paddingStart">@dimen/edit_text_margin</item>
<item name="android:paddingEnd">@dimen/edit_text_margin</item>
<item name="android:maxLines">1</item>
<item name="android:drawablePadding">@dimen/edit_text_drawable_padding</item>
<item name="android:drawableTint">@color/colorDrawableTintGrey</item>
<item name="android:fontFamily">sans-serif</item>
<item name="android:background">@drawable/style_edit_text</item>
</style>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="default_protocol" translatable="false">https://</string>
<string name="default_server" translatable="false">open.rocket.chat</string>
</resources>
\ No newline at end of file
......@@ -10,7 +10,6 @@
<string name="action_connect">Connect</string>
<!-- Regular information messages -->
<string name="msg_https" translatable="false">https://</string>
<string name="msg_username">username</string>
<string name="msg_username_or_email">username or email</string>
<string name="msg_password">password</string>
......
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="AppTheme" parent="android:Theme.Material.Light.NoActionBar">
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
......@@ -10,7 +10,7 @@
<item name="colorControlHighlight">@color/colorPrimary</item>
</style>
<style name="AuthenticationTheme" parent="android:Theme.Material.NoActionBar.Fullscreen">
<style name="AuthenticationTheme" parent="Theme.AppCompat.NoActionBar">
<item name="android:navigationBarColor">@color/colorPrimary</item>
</style>
......@@ -32,6 +32,18 @@
<item name="android:paddingEnd">@dimen/edit_text_margin</item>
<item name="android:maxLines">1</item>
<item name="android:drawablePadding">@dimen/edit_text_drawable_padding</item>
<item name="android:drawableTint" tools:ignore="NewApi">@color/colorDrawableTintGrey</item>
<item name="android:fontFamily">sans-serif</item>
<item name="android:background">@drawable/style_edit_text</item>
</style>
<style name="AuthenticationLabel" parent="TextAppearance.AppCompat.Medium">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">50dp</item>
<item name="android:layout_marginStart">@dimen/screen_edge_left_and_right_margins</item>
<item name="android:paddingStart">@dimen/edit_text_margin</item>
<item name="android:maxLines">1</item>
<item name="android:drawablePadding">@dimen/edit_text_drawable_padding</item>
<item name="android:fontFamily">sans-serif</item>
<item name="android:background">@drawable/style_edit_text</item>
</style>
......
......@@ -9,6 +9,7 @@ buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
classpath "org.jetbrains.dokka:dokka-gradle-plugin:${versions.dokka}"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
......@@ -19,6 +20,10 @@ allprojects {
repositories {
google()
jcenter()
maven {
url "https://oss.sonatype.org/content/repositories/snapshots/"
}
maven { url 'https://jitpack.io' }
}
apply from: rootProject.file('dependencies.gradle')
......
......@@ -9,6 +9,8 @@ machine:
ANDROID_HOME: /usr/local/android-sdk-linux
GRADLE_OPTS: '-Xmx1024m -Dorg.gradle.jvmargs="-Xmx1024m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError"'
JAVA_OPTS: "-Xms518m -Xmx1024m"
pre:
- git clone https://github.com/RocketChat/Rocket.Chat.Kotlin.SDK.git Rocket.Chat.Kotlin.Sdk
dependencies:
pre:
......@@ -28,14 +30,18 @@ dependencies:
# - echo $GOOGLE_SERVICES_BASE64 | base64 --decode > app/src/release/google-services.json
# - echo $API_KEY_STRINGS_BASE64 | base64 --decode > app/src/release/res/values/api_key_strings.xml
- mkdir -p $ANDROID_HOME/licenses/
- echo 8933bad161af4178b1185d1a37fbf41ea5269c55 >> $ANDROID_HOME/licenses/android-sdk-license
- echo d56f5187479451eabf01fb78af6dfcb131a6481e >> $ANDROID_HOME/licenses/android-sdk-license
- echo y | android update sdk --no-ui --all --filter tools,platform-tools
- echo y | android update sdk --no-ui --all --filter android-27
- echo y | android update sdk --no-ui --all --filter extra-android-m2repository,extra-android-support
- echo y | android update sdk --no-ui --all --filter extra-google-m2repository,extra-google-google_play_services
- echo y | android update sdk --no-ui --all --filter build-tools-27.0.0
#- yes | sdkmanager --licenses
cache_directories:
- /usr/local/android-sdk-linux/tools
- /usr/local/android-sdk-linux/build-tools/27.0.0
#- /usr/local/android-sdk-linux/tools
#- /usr/local/android-sdk-linux/build-tools/27.0.0
test:
override:
......
......@@ -7,15 +7,17 @@ ext {
targetSdk : 27,
buildTools : '27.0.0',
kotlin : '1.2.0',
coroutine : '0.20',
dokka : '0.9.15',
// Main dependencies
support : '27.0.0',
support : '27.0.2',
constraintLayout : '1.0.2',
dagger : '2.11',
room : '1.0.0-beta1',
dagger : '2.13',
room : '1.0.0',
rxjava : '2.1.4',
rxandroid : '2.0.1',
moshi : '1.5.0',
moshi : '1.6.0-SNAPSHOT',
okhttp : '3.9.0',
fresco : '1.5.0',
timber : '4.5.1',
......@@ -31,13 +33,16 @@ ext {
]
libraries = [
kotlin : "org.jetbrains.kotlin:kotlin-stdlib-jre7:${versions.kotlin}",
kotlin : "org.jetbrains.kotlin:kotlin-stdlib-jre8:${versions.kotlin}",
coroutines : "org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions.coroutine}",
coroutinesAndroid : "org.jetbrains.kotlinx:kotlinx-coroutines-android:${versions.coroutine}",
appCompat : "com.android.support:appcompat-v7:${versions.support}",
annotations : "com.android.support:support-annotations:${versions.support}",
recyclerview : "com.android.support:recyclerview-v7:${versions.support}",
design : "com.android.support:design:${versions.support}",
constraintLayout : "com.android.support.constraint:constraint-layout:${versions.constraintLayout}",
cardView : "com.android.support:cardview-v7:${versions.support}",
dagger : "com.google.dagger:dagger:${versions.dagger}",
daggerSupport : "com.google.dagger:dagger-android-support:${versions.dagger}",
......@@ -52,6 +57,7 @@ ext {
rxandroid : "io.reactivex.rxjava2:rxandroid:${versions.rxandroid}",
moshi : "com.squareup.moshi:moshi:${versions.moshi}",
moshiKotlin : "com.squareup.moshi:moshi-kotlin:${versions.moshi}",
okhttp : "com.squareup.okhttp3:okhttp:${versions.okhttp}",
okhttpLogger : "com.squareup.okhttp3:logging-interceptor:${versions.okhttp}",
......
include ':app'
include ':app', 'common', 'core'
project(':common').projectDir = new File(settingsDir, '../Rocket.Chat.Kotlin.Sdk/common')
project(':core').projectDir = new File(settingsDir, '../Rocket.Chat.Kotlin.Sdk/core')
\ No newline at end of file
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