Commit a29c9287 authored by Filipe de Lima Brito's avatar Filipe de Lima Brito

Merge branch 'develop-2.x' into feature/support-viewing-video

parents 3243881d f4521db1
...@@ -12,34 +12,47 @@ android { ...@@ -12,34 +12,47 @@ android {
minSdkVersion 21 minSdkVersion 21
targetSdkVersion versions.targetSdk targetSdkVersion versions.targetSdk
versionCode 1 versionCode 1
versionName "1.0" versionName "2.0.0-beta1"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true
} }
buildTypes { buildTypes {
release { release {
minifyEnabled false minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
} }
} }
packagingOptions {
exclude 'META-INF/core.kotlin_module'
}
} }
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':core')
implementation project(':player') implementation project(':player')
implementation libraries.kotlin implementation libraries.kotlin
implementation libraries.coroutines
implementation libraries.coroutinesAndroid
implementation libraries.appCompat implementation libraries.appCompat
implementation libraries.recyclerview implementation libraries.recyclerview
implementation libraries.design implementation libraries.design
implementation libraries.constraintLayout implementation libraries.constraintLayout
implementation libraries.cardView
implementation libraries.dagger implementation libraries.dagger
implementation libraries.daggerSupport implementation libraries.daggerSupport
kapt libraries.daggerProcessor kapt libraries.daggerProcessor
kapt libraries.daggerAndroidApt kapt libraries.daggerAndroidApt
implementation libraries.moshi
implementation libraries.moshiKotlin
implementation libraries.room implementation libraries.room
kapt libraries.roomProcessor kapt libraries.roomProcessor
implementation libraries.roomRxjava implementation libraries.roomRxjava
...@@ -70,4 +83,10 @@ dependencies { ...@@ -70,4 +83,10 @@ dependencies {
repositories { repositories {
mavenCentral() mavenCentral()
}
kotlin {
experimental {
coroutines "enable"
}
} }
\ No newline at end of file
...@@ -15,10 +15,17 @@ ...@@ -15,10 +15,17 @@
android:theme="@style/AppTheme"> android:theme="@style/AppTheme">
<activity <activity
android:name=".app.AuthenticationActivity" android:name=".authentication.ui.AuthenticationActivity"
android:configChanges="orientation" android:configChanges="orientation"
android:screenOrientation="portrait" android:screenOrientation="portrait"
android:theme="@style/AuthenticationTheme" /> android:theme="@style/AuthenticationTheme">
<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>
</activity>
<activity <activity
android:name=".app.MainActivity" android:name=".app.MainActivity"
...@@ -27,12 +34,6 @@ ...@@ -27,12 +34,6 @@
<activity <activity
android:name=".app.ChatRoomActivity" android:name=".app.ChatRoomActivity"
android:theme="@style/AppTheme"> android:theme="@style/AppTheme">
<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>
</activity> </activity>
</application> </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
package chat.rocket.android.app package chat.rocket.android.app
import android.os.Bundle import android.os.Bundle
import chat.rocket.android.BaseActivity import android.support.v7.app.AppCompatActivity
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.app.chatroom.MessageFragment import chat.rocket.android.app.chatroom.MessageFragment
import chat.rocket.android.util.addFragment
class ChatRoomActivity : BaseActivity() { class ChatRoomActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_chat_room) setContentView(R.layout.activity_chat_room)
addFragment(MessageFragment(), "MessageFragment", R.id.fragment_container) addFragment("MessageFragment", R.id.fragment_container) {
MessageFragment()
}
} }
} }
\ No newline at end of file
...@@ -4,13 +4,14 @@ import android.app.Activity ...@@ -4,13 +4,14 @@ import android.app.Activity
import android.graphics.Rect import android.graphics.Rect
import android.util.Log import android.util.Log
import android.view.View import android.view.View
import android.view.ViewTreeObserver
import android.widget.FrameLayout import android.widget.FrameLayout
//TODO: check if this code has memory leak. //TODO: check if this code has memory leak.
object LayoutHelper { class LayoutHelper {
private lateinit var childOfContent: View private var childOfContent: View? = null
private var usableHeightPrevious: Int = 0 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. * Workaround to adjust the layout when in the full screen mode.
...@@ -22,38 +23,48 @@ object LayoutHelper { ...@@ -22,38 +23,48 @@ object LayoutHelper {
* *
* @param activity The Activity to adjust the layout. * @param activity The Activity to adjust the layout.
*/ */
fun androidBug5497Workaround(activity: Activity) { fun install(activity: Activity) {
try { try {
val content = activity.findViewById<View>(android.R.id.content) as FrameLayout val content = activity.findViewById<View>(android.R.id.content) as FrameLayout
childOfContent = content.getChildAt(0) childOfContent = content.getChildAt(0)
childOfContent.viewTreeObserver.addOnGlobalLayoutListener({ resizeChildOfContent() }) childOfContent?.viewTreeObserver?.addOnGlobalLayoutListener(listener)
frameLayoutParams = childOfContent.layoutParams as FrameLayout.LayoutParams frameLayoutParams = childOfContent?.layoutParams as FrameLayout.LayoutParams
} catch (exception : ClassCastException) { } catch (exception : ClassCastException) {
// TODO: are we using the android.util.Log for logging that type of errors? // 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) Log.e("ERROR", exception.message)
} }
} }
private val listener = ViewTreeObserver.OnGlobalLayoutListener {
resizeChildOfContent()
}
private fun resizeChildOfContent() { private fun resizeChildOfContent() {
val usableHeightNow = computeUsableHeight() val usableHeightNow = computeUsableHeight()
if (usableHeightNow != usableHeightPrevious) { if (usableHeightNow != usableHeightPrevious) {
val usableHeightSansKeyboard = childOfContent.rootView.height val usableHeightSansKeyboard = childOfContent?.rootView?.height ?: 0
val heightDifference = usableHeightSansKeyboard - usableHeightNow val heightDifference = usableHeightSansKeyboard - usableHeightNow
if (heightDifference > usableHeightSansKeyboard / 4) { if (heightDifference > usableHeightSansKeyboard / 4) {
// keyboard probably just became visible // keyboard probably just became visible
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference frameLayoutParams?.height = usableHeightSansKeyboard - heightDifference
} else { } else {
// keyboard probably just became hidden // keyboard probably just became hidden
frameLayoutParams.height = usableHeightNow frameLayoutParams?.height = usableHeightNow
} }
childOfContent.requestLayout() childOfContent?.requestLayout()
usableHeightPrevious = usableHeightNow usableHeightPrevious = usableHeightNow
} }
} }
private fun computeUsableHeight(): Int { private fun computeUsableHeight(): Int {
val rect = Rect() val rect = Rect()
childOfContent.getWindowVisibleDisplayFrame(rect) childOfContent?.getWindowVisibleDisplayFrame(rect)
return rect.bottom - rect.top 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 package chat.rocket.android.app
import android.os.Bundle import android.os.Bundle
import chat.rocket.android.BaseActivity import android.support.v7.app.AppCompatActivity
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.app.chatlist.ChatListFragment import chat.rocket.android.app.chatlist.ChatListFragment
import chat.rocket.android.util.addFragment
class MainActivity : BaseActivity() { class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) 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 ...@@ -4,9 +4,8 @@ import android.app.Activity
import android.app.Application import android.app.Application
import chat.rocket.android.BuildConfig import chat.rocket.android.BuildConfig
import chat.rocket.android.app.utils.CustomImageFormatConfigurator import chat.rocket.android.app.utils.CustomImageFormatConfigurator
import chat.rocket.android.dagger.DaggerApplicationComponent
import com.facebook.drawee.backends.pipeline.DraweeConfig import com.facebook.drawee.backends.pipeline.DraweeConfig
import chat.rocket.android.dagger.DaggerAppComponent
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
import com.jakewharton.threetenabp.AndroidThreeTen import com.jakewharton.threetenabp.AndroidThreeTen
...@@ -18,15 +17,13 @@ import javax.inject.Inject ...@@ -18,15 +17,13 @@ import javax.inject.Inject
class RocketChatApplication : Application(), HasActivityInjector { class RocketChatApplication : Application(), HasActivityInjector {
@Inject lateinit var activityInjector: DispatchingAndroidInjector<Activity> @Inject
lateinit var activityDispatchingAndroidInjector: DispatchingAndroidInjector<Activity>
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
DaggerApplicationComponent.builder() DaggerAppComponent.builder().application(this).build().inject(this)
.application(this)
.build()
.inject(this)
AndroidThreeTen.init(this) AndroidThreeTen.init(this)
...@@ -53,6 +50,6 @@ class RocketChatApplication : Application(), HasActivityInjector { ...@@ -53,6 +50,6 @@ class RocketChatApplication : Application(), HasActivityInjector {
} }
override fun activityInjector(): AndroidInjector<Activity> { override fun activityInjector(): AndroidInjector<Activity> {
return activityInjector return activityDispatchingAndroidInjector
} }
} }
\ No newline at end of file
package chat.rocket.android.app.chatlist package chat.rocket.android.app.chatlist
import android.app.Fragment
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.widget.DividerItemDecoration import android.support.v7.widget.DividerItemDecoration
import android.support.v7.widget.LinearLayoutManager import android.support.v7.widget.LinearLayoutManager
import android.view.LayoutInflater import android.view.LayoutInflater
...@@ -14,9 +14,9 @@ import org.threeten.bp.LocalDateTime ...@@ -14,9 +14,9 @@ import org.threeten.bp.LocalDateTime
class ChatListFragment : Fragment() { 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) super.onViewCreated(view, savedInstanceState)
showChatList(createDumpData()) showChatList(createDumpData())
} }
...@@ -87,9 +87,10 @@ class ChatListFragment : Fragment() { ...@@ -87,9 +87,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). // 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>) { private fun showChatList(dataSet: List<Chat>) {
val context = activity.applicationContext activity?.apply {
recycler_view.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) recycler_view.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
recycler_view.addItemDecoration(DividerItemDecoration(activity, DividerItemDecoration.VERTICAL)) recycler_view.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL))
recycler_view.adapter = ChatListAdapter(dataSet.toMutableList(), context) recycler_view.adapter = ChatListAdapter(dataSet.toMutableList(), this)
}
} }
} }
package chat.rocket.android.app.chatroom package chat.rocket.android.app.chatroom
import android.app.Fragment
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.widget.LinearLayoutManager import android.support.v7.widget.LinearLayoutManager
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
...@@ -13,9 +13,9 @@ import org.threeten.bp.LocalDateTime ...@@ -13,9 +13,9 @@ import org.threeten.bp.LocalDateTime
class MessageFragment : Fragment() { class MessageFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? = inflater?.inflate(R.layout.fragment_message, container, false) override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = inflater.inflate(R.layout.fragment_message, container, false)
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
showMessageList(createDumpData()) showMessageList(createDumpData())
} }
...@@ -32,7 +32,9 @@ class MessageFragment : Fragment() { ...@@ -32,7 +32,9 @@ class MessageFragment : Fragment() {
// REMARK: The presenter should call this method. // REMARK: The presenter should call this method.
private fun showMessageList(dataSet: List<Message>) { private fun showMessageList(dataSet: List<Message>) {
recycler_view.layoutManager = LinearLayoutManager(activity.applicationContext, LinearLayoutManager.VERTICAL, false) activity?.apply {
recycler_view.adapter = MessageListAdapter(dataSet.toMutableList()) recycler_view.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
recycler_view.adapter = MessageListAdapter(dataSet.toMutableList())
}
} }
} }
\ No newline at end of file
...@@ -108,6 +108,7 @@ object ColorImage { ...@@ -108,6 +108,7 @@ object ColorImage {
// Return the CloseableImage // Return the CloseableImage
return CloseableColorImage(color) return CloseableColorImage(color)
} catch (e: IOException) { } catch (e: IOException) {
// TODO: are we using the android.util.Log for logging that type of errors? or should we use the SDK logger?
e.printStackTrace() e.printStackTrace()
} }
// Return nothing if an error occurred // Return nothing if an error occurred
......
...@@ -79,6 +79,7 @@ object SvgDecoder { ...@@ -79,6 +79,7 @@ object SvgDecoder {
val svg = SVG.getFromInputStream(encodedImage.inputStream) val svg = SVG.getFromInputStream(encodedImage.inputStream)
return CloseableSvgImage(svg) return CloseableSvgImage(svg)
} catch (e: SVGParseException) { } catch (e: SVGParseException) {
// TODO: are we using the android.util.Log for logging that type of errors? or should we use the SDK logger?
e.printStackTrace() e.printStackTrace()
} }
// Return nothing if an error occurred // Return nothing if an error occurred
......
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 DrawableHelper
import android.app.Fragment import android.app.ProgressDialog
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.support.v4.app.Fragment
import android.view.View import android.view.*
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.ScrollView import android.widget.ScrollView
import android.widget.Toast
import chat.rocket.android.R 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 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) 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) { if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
tintEditTextDrawableStart() tintEditTextDrawableStart()
...@@ -40,34 +68,49 @@ class AuthenticationLoginFragment : Fragment() { ...@@ -40,34 +68,49 @@ class AuthenticationLoginFragment : Fragment() {
// Just an example: if the server allow the new users registration then show the respective interface. // Just an example: if the server allow the new users registration then show the respective interface.
shouldShowSignUpMsgView(true) shouldShowSignUpMsgView(true)
}
private fun tintEditTextDrawableStart() { button_log_in.setOnClickListener {
val context = activity.applicationContext presenter.authenticate(text_username_or_email.text.toString(), text_password.text.toString())
}
val personDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_assignment_ind_black_24dp, context) text_new_to_rocket_chat.setOnClickListener {
val lockDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_lock_black_24dp, context) presenter.signup()
}
}
val drawables = arrayOf(personDrawable, lockDrawable) override fun onDestroyView() {
DrawableHelper.wrapDrawables(drawables) scroll_view.viewTreeObserver.removeOnGlobalLayoutListener(layoutListener)
DrawableHelper.tintDrawables(drawables, context, R.color.colorDrawableTintGrey) super.onDestroyView()
DrawableHelper.compoundDrawables(arrayOf(text_username_or_email, text_password), drawables) }
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, this, R.color.colorDrawableTintGrey)
DrawableHelper.compoundDrawables(arrayOf(text_username_or_email, text_password), drawables)
}
} }
private fun setupGlobalLayoutListener() { private fun setupGlobalLayoutListener() {
scroll_view.viewTreeObserver.addOnGlobalLayoutListener({ scroll_view.viewTreeObserver.addOnGlobalLayoutListener(layoutListener)
if (KeyboardHelper.isSoftKeyboardShown(scroll_view.rootView)) { }
shouldShowOauthView(false)
shouldShowSignUpMsgView(false) val layoutListener = ViewTreeObserver.OnGlobalLayoutListener {
shouldShowLoginButton(true) if (KeyboardHelper.isSoftKeyboardShown(scroll_view.rootView)) {
} else { shouldShowOauthView(false)
if (isEditTextNullOrBlank()) { shouldShowSignUpMsgView(false)
shouldShowOauthView(true) shouldShowLoginButton(true)
shouldShowSignUpMsgView(true) } else {
shouldShowLoginButton(false) if (isEditTextNullOrBlank()) {
} shouldShowOauthView(true)
shouldShowSignUpMsgView(true)
shouldShowLoginButton(false)
} }
}) }
} }
private fun shouldShowOauthView(show: Boolean) { private fun shouldShowOauthView(show: Boolean) {
...@@ -152,4 +195,22 @@ class AuthenticationLoginFragment : Fragment() { ...@@ -152,4 +195,22 @@ class AuthenticationLoginFragment : Fragment() {
button_fab.hide() button_fab.hide()
}, 1500) }, 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 package chat.rocket.android.dagger
import android.app.Application import android.app.Application
import javax.inject.Singleton
import chat.rocket.android.app.RocketChatApplication import chat.rocket.android.app.RocketChatApplication
import chat.rocket.android.dagger.module.ActivityBindingModule import chat.rocket.android.dagger.module.ActivityBuilder
import chat.rocket.android.dagger.module.ApplicationModule import chat.rocket.android.dagger.module.AppModule
import dagger.BindsInstance import dagger.BindsInstance
import dagger.Component import dagger.Component
import dagger.android.support.AndroidSupportInjectionModule import dagger.android.support.AndroidSupportInjectionModule
import javax.inject.Singleton
@Singleton @Singleton
@Component(modules = arrayOf(ActivityBindingModule::class, ApplicationModule::class, @Component(modules = arrayOf(AndroidSupportInjectionModule::class, AppModule::class, ActivityBuilder::class))
AndroidSupportInjectionModule::class)) interface AppComponent {
interface ApplicationComponent {
@Component.Builder @Component.Builder
interface Builder { interface Builder {
@BindsInstance @BindsInstance
fun application(application: Application): Builder 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 ...@@ -3,14 +3,19 @@ package chat.rocket.android.dagger.module
import android.app.Application import android.app.Application
import android.arch.persistence.room.Room import android.arch.persistence.room.Room
import android.content.Context import android.content.Context
import chat.rocket.android.BuildConfig
import chat.rocket.android.app.RocketChatDatabase import chat.rocket.android.app.RocketChatDatabase
import chat.rocket.android.server.infraestructure.ServerDao import chat.rocket.android.server.infraestructure.ServerDao
import chat.rocket.android.util.TimberLogger
import chat.rocket.common.util.PlatformLogger
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import javax.inject.Singleton import javax.inject.Singleton
@Module @Module
class ApplicationModule { class AppModule {
@Provides @Provides
@Singleton @Singleton
...@@ -29,4 +34,45 @@ class ApplicationModule { ...@@ -29,4 +34,45 @@ class ApplicationModule {
fun provideServerDao(database: RocketChatDatabase): ServerDao { fun provideServerDao(database: RocketChatDatabase): ServerDao {
return database.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 {
}
...@@ -4,7 +4,7 @@ import chat.rocket.android.server.domain.model.Server ...@@ -4,7 +4,7 @@ import chat.rocket.android.server.domain.model.Server
import chat.rocket.android.util.DataToDomain import chat.rocket.android.util.DataToDomain
class ServerEntityMapper : DataToDomain<ServerEntity, Server> { class ServerEntityMapper : DataToDomain<ServerEntity, Server> {
override fun translate(serverEntity: ServerEntity): Server { override fun translate(data: ServerEntity): Server {
return Server(serverEntity.id, serverEntity.name, serverEntity.host, serverEntity.avatar) return Server(data.id, data.name, data.host, data.avatar)
} }
} }
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 @@ ...@@ -168,7 +168,7 @@
<TextView <TextView
android:id="@+id/text_new_to_rocket_chat" android:id="@+id/text_new_to_rocket_chat"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="48dp"
android:layout_marginBottom="16dp" android:layout_marginBottom="16dp"
android:layout_marginEnd="@dimen/screen_edge_left_and_right_margins" android:layout_marginEnd="@dimen/screen_edge_left_and_right_margins"
android:layout_marginStart="@dimen/screen_edge_left_and_right_margins" android:layout_marginStart="@dimen/screen_edge_left_and_right_margins"
......
...@@ -9,14 +9,25 @@ ...@@ -9,14 +9,25 @@
android:layout_centerHorizontal="true" android:layout_centerHorizontal="true"
android:text="@string/title_sign_in_your_server" /> 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 <EditText
android:id="@+id/text_server_url" android:id="@+id/text_server_url"
style="@style/AuthenticationEditText" style="@style/AuthenticationEditText"
android:layout_below="@id/text_headline" android:layout_below="@id/text_headline"
android:layout_marginStart="0dp"
android:layout_marginTop="32dp" android:layout_marginTop="32dp"
android:layout_toEndOf="@id/server_protocol_label"
android:hint="@string/default_server"
android:imeOptions="actionDone" android:imeOptions="actionDone"
android:inputType="textUri" android:inputType="textUri"
android:text="@string/msg_https" /> android:paddingStart="0dp" />
<Button <Button
android:id="@+id/button_connect" 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
...@@ -11,7 +11,6 @@ ...@@ -11,7 +11,6 @@
<string name="action_send">Send</string> <string name="action_send">Send</string>
<!-- Regular information messages --> <!-- Regular information messages -->
<string name="msg_https" translatable="false">https://</string>
<string name="msg_username">username</string> <string name="msg_username">username</string>
<string name="msg_username_or_email">username or email</string> <string name="msg_username_or_email">username or email</string>
<string name="msg_password">password</string> <string name="msg_password">password</string>
......
<resources> <resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. --> <!-- 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="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item> <item name="colorAccent">@color/colorAccent</item>
<item name="android:statusBarColor">@color/colorPrimaryDark</item> <item name="android:statusBarColor">@color/colorPrimaryDark</item>
</style> </style>
<style name="AuthenticationTheme" parent="android:Theme.Material.NoActionBar.Fullscreen"> <style name="AuthenticationTheme" parent="Theme.AppCompat.NoActionBar">
<item name="android:navigationBarColor">@color/colorPrimary</item> <item name="android:navigationBarColor">@color/colorPrimary</item>
</style> </style>
...@@ -35,6 +36,18 @@ ...@@ -35,6 +36,18 @@
<item name="android:paddingEnd">@dimen/edit_text_margin</item> <item name="android:paddingEnd">@dimen/edit_text_margin</item>
<item name="android:maxLines">1</item> <item name="android:maxLines">1</item>
<item name="android:drawablePadding">@dimen/edit_text_drawable_padding</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:fontFamily">sans-serif</item>
<item name="android:background">@drawable/style_edit_text</item> <item name="android:background">@drawable/style_edit_text</item>
</style> </style>
......
...@@ -9,6 +9,7 @@ buildscript { ...@@ -9,6 +9,7 @@ buildscript {
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.0.1' classpath 'com.android.tools.build:gradle:3.0.1'
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}"
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files
...@@ -19,6 +20,10 @@ allprojects { ...@@ -19,6 +20,10 @@ allprojects {
repositories { repositories {
google() google()
jcenter() jcenter()
maven {
url "https://oss.sonatype.org/content/repositories/snapshots/"
}
maven { url 'https://jitpack.io' }
} }
apply from: rootProject.file('dependencies.gradle') apply from: rootProject.file('dependencies.gradle')
......
...@@ -9,6 +9,8 @@ machine: ...@@ -9,6 +9,8 @@ machine:
ANDROID_HOME: /usr/local/android-sdk-linux ANDROID_HOME: /usr/local/android-sdk-linux
GRADLE_OPTS: '-Xmx1024m -Dorg.gradle.jvmargs="-Xmx1024m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError"' GRADLE_OPTS: '-Xmx1024m -Dorg.gradle.jvmargs="-Xmx1024m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError"'
JAVA_OPTS: "-Xms518m -Xmx1024m" JAVA_OPTS: "-Xms518m -Xmx1024m"
pre:
- git clone https://github.com/RocketChat/Rocket.Chat.Kotlin.SDK.git Rocket.Chat.Kotlin.Sdk
dependencies: dependencies:
pre: pre:
...@@ -28,14 +30,18 @@ dependencies: ...@@ -28,14 +30,18 @@ dependencies:
# - echo $GOOGLE_SERVICES_BASE64 | base64 --decode > app/src/release/google-services.json # - 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 # - 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 tools,platform-tools
- echo y | android update sdk --no-ui --all --filter android-27 - 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-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 extra-google-m2repository,extra-google-google_play_services
- echo y | android update sdk --no-ui --all --filter build-tools-27.0.0 - echo y | android update sdk --no-ui --all --filter build-tools-27.0.0
#- yes | sdkmanager --licenses
cache_directories: cache_directories:
- /usr/local/android-sdk-linux/tools #- /usr/local/android-sdk-linux/tools
- /usr/local/android-sdk-linux/build-tools/27.0.0 #- /usr/local/android-sdk-linux/build-tools/27.0.0
test: test:
override: override:
......
...@@ -6,17 +6,19 @@ ext { ...@@ -6,17 +6,19 @@ ext {
compileSdk : 27, compileSdk : 27,
targetSdk : 27, targetSdk : 27,
buildTools : '27.0.0', buildTools : '27.0.0',
kotlin : '1.2.0', kotlin : '1.2.10',
coroutine : '0.20',
dokka : '0.9.15',
// Main dependencies // Main dependencies
support : '27.0.0', support : '27.0.2',
constraintLayout : '1.0.2', constraintLayout : '1.0.2',
dagger : '2.11', dagger : '2.13',
exoPlayer : '2.6.0', exoPlayer : '2.6.0',
room : '1.0.0-beta1', room : '1.0.0',
rxjava : '2.1.4', rxjava : '2.1.4',
rxandroid : '2.0.1', rxandroid : '2.0.1',
moshi : '1.5.0', moshi : '1.6.0-SNAPSHOT',
okhttp : '3.9.0', okhttp : '3.9.0',
fresco : '1.5.0', fresco : '1.5.0',
timber : '4.5.1', timber : '4.5.1',
...@@ -32,13 +34,16 @@ ext { ...@@ -32,13 +34,16 @@ ext {
] ]
libraries = [ 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}", appCompat : "com.android.support:appcompat-v7:${versions.support}",
annotations : "com.android.support:support-annotations:${versions.support}", annotations : "com.android.support:support-annotations:${versions.support}",
recyclerview : "com.android.support:recyclerview-v7:${versions.support}", recyclerview : "com.android.support:recyclerview-v7:${versions.support}",
design : "com.android.support:design:${versions.support}", design : "com.android.support:design:${versions.support}",
constraintLayout : "com.android.support.constraint:constraint-layout:${versions.constraintLayout}", constraintLayout : "com.android.support.constraint:constraint-layout:${versions.constraintLayout}",
cardView : "com.android.support:cardview-v7:${versions.support}",
dagger : "com.google.dagger:dagger:${versions.dagger}", dagger : "com.google.dagger:dagger:${versions.dagger}",
daggerSupport : "com.google.dagger:dagger-android-support:${versions.dagger}", daggerSupport : "com.google.dagger:dagger-android-support:${versions.dagger}",
...@@ -54,6 +59,7 @@ ext { ...@@ -54,6 +59,7 @@ ext {
rxandroid : "io.reactivex.rxjava2:rxandroid:${versions.rxandroid}", rxandroid : "io.reactivex.rxjava2:rxandroid:${versions.rxandroid}",
moshi : "com.squareup.moshi:moshi:${versions.moshi}", moshi : "com.squareup.moshi:moshi:${versions.moshi}",
moshiKotlin : "com.squareup.moshi:moshi-kotlin:${versions.moshi}",
okhttp : "com.squareup.okhttp3:okhttp:${versions.okhttp}", okhttp : "com.squareup.okhttp3:okhttp:${versions.okhttp}",
okhttpLogger : "com.squareup.okhttp3:logging-interceptor:${versions.okhttp}", okhttpLogger : "com.squareup.okhttp3:logging-interceptor:${versions.okhttp}",
......
include ':app', ':player' include ':app', 'common', 'core', ':player'
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