Commit a1b2313f authored by Lucio Maciel's avatar Lucio Maciel

Initial SDK integration and Login support

parent b5d50084
......@@ -21,12 +21,20 @@ 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
......@@ -38,6 +46,9 @@ dependencies {
kapt libraries.daggerProcessor
kapt libraries.daggerAndroidApt
implementation libraries.moshi
implementation libraries.moshiKotlin
implementation libraries.room
kapt libraries.roomProcessor
implementation libraries.roomRxjava
......
......@@ -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() {
import android.support.v4.app.Fragment
import android.support.v7.app.AppCompatActivity
protected fun addFragment(fragment: Fragment, tag: String, layoutId: Int) {
fragmentManager.beginTransaction().add(layoutId, fragment, tag).commit()
abstract class BaseActivity : AppCompatActivity() {
protected fun addFragment(tag: String, layoutId: Int, block: (Unit) -> Fragment) {
val fragment = supportFragmentManager.findFragmentByTag(tag) ?: block(Unit)
supportFragmentManager.beginTransaction().replace(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
......@@ -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?
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
......@@ -3,8 +3,7 @@ package chat.rocket.android.app
import android.app.Activity
import android.app.Application
import chat.rocket.android.BuildConfig
import chat.rocket.android.dagger.DaggerApplicationComponent
import chat.rocket.android.dagger.DaggerAppComponent
import com.facebook.drawee.backends.pipeline.Fresco
import com.jakewharton.threetenabp.AndroidThreeTen
import dagger.android.AndroidInjector
......@@ -15,15 +14,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)
Fresco.initialize(this)
......@@ -38,6 +35,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))
}
}
}
\ No newline at end of file
package chat.rocket.android.authentication.di
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
@Module
class AuthenticationModule {
@Provides
@PerActivity
fun provideAuthenticationNavigator(activity: AuthenticationActivity) = AuthenticationNavigator(activity)
}
package chat.rocket.android.authentication.di
import chat.rocket.android.authentication.presentation.LoginView
import chat.rocket.android.authentication.ui.LoginFragment
import dagger.Module
import dagger.Provides
@Module
class LoginFragmentModule {
@Provides
fun loginView(frag: LoginFragment): LoginView {
return frag
}
}
package chat.rocket.android.authentication.di
import chat.rocket.android.authentication.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.di
import chat.rocket.android.authentication.presentation.ServerView
import chat.rocket.android.authentication.ui.ServerFragment
import dagger.Module
import dagger.Provides
@Module
class ServerFragmentModule {
@Provides
fun serverView(frag: ServerFragment): ServerView {
return frag
}
}
package chat.rocket.android.authentication.di
import chat.rocket.android.authentication.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.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.ui.LoginFragment
import chat.rocket.android.authentication.ui.SignUpFragment
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 toChatRoom() {
val chatRoom = Intent(activity, MainActivity::class.java).apply {
//TODO any parameter to pass
}
activity.startActivity(chatRoom)
activity.finish()
}
fun toSignUp() {
activity.addFragmentBackStack("signupFragment", R.id.fragment_container) {
SignUpFragment.newInstance()
}
}
}
package chat.rocket.android.authentication.presentation
import chat.rocket.common.RocketChatException
import chat.rocket.common.model.Token
import chat.rocket.common.util.PlatformLogger
import chat.rocket.core.RocketChatClient
import chat.rocket.core.TokenRepository
import chat.rocket.core.internal.rest.login
import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.launch
import okhttp3.HttpUrl
import okhttp3.OkHttpClient
import javax.inject.Inject
class LoginPresenter @Inject constructor(private val view: LoginView,
private val navigator: AuthenticationNavigator,
private val okHttpClient: OkHttpClient,
private val logger: PlatformLogger) {
var job: Job? = null
val client: RocketChatClient = RocketChatClient.create {
httpClient = okHttpClient
restUrl = HttpUrl.parse(navigator.currentServer)!!
websocketUrl = navigator.currentServer!!
tokenRepository = SimpleTokenProvider()
platformLogger = logger
}
fun authenticate(username: String, password: String) {
// TODO - validate input
job = launch(UI) {
view.showProgress()
try {
val token = client.login(username, password)
view.hideProgress()
navigator.toChatRoom()
} catch (ex: RocketChatException) {
view.hideProgress()
view.onLoginError(ex.message)
}
}
}
fun unbind() {
job?.let {
it.cancel()
}.also { null }
}
fun signup() {
navigator.toSignUp()
}
}
class SimpleTokenProvider : 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.presentation
interface LoginView {
fun showProgress()
fun hideProgress()
fun onLoginError(message: String?)
}
\ No newline at end of file
package chat.rocket.android.authentication.presentation
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.presentation
interface ServerView
\ 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.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.app
package chat.rocket.android.authentication.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.presentation.LoginPresenter
import chat.rocket.android.authentication.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 onDestroy() {
presenter.unbind()
super.onDestroy()
}
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 +73,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()
}
}
override fun onDestroyView() {
scroll_view.viewTreeObserver.removeOnGlobalLayoutListener(layoutListener)
super.onDestroyView()
}
val personDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_assignment_ind_black_24dp, context)
val lockDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_lock_black_24dp, context)
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 +116,6 @@ class AuthenticationLoginFragment : Fragment() {
shouldShowLoginButton(false)
}
}
})
}
private fun shouldShowOauthView(show: Boolean) {
......@@ -152,4 +200,21 @@ class AuthenticationLoginFragment : Fragment() {
button_fab.hide()
}, 1500)
}
override fun showProgress() {
// TODO - change for a proper progress indicator
progress = ProgressDialog.show(activity, "Authenticating", "Verifying user credentials")
}
override fun hideProgress() {
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.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import chat.rocket.android.R
import chat.rocket.android.authentication.presentation.ServerPresenter
import chat.rocket.android.authentication.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.ui
import DrawableHelper
import android.os.Build
import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.*
import chat.rocket.android.R
import chat.rocket.android.app.KeyboardHelper
import kotlinx.android.synthetic.main.fragment_authentication_sign_up.*
class SignUpFragment : Fragment() {
companion object {
fun newInstance() = SignUpFragment()
}
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()
}
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
}
}
}
\ No newline at end of file
package chat.rocket.android.app
package chat.rocket.android.authentication.ui
import DrawableHelper
import android.app.Fragment
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
......@@ -11,14 +11,14 @@ import android.view.WindowManager
import chat.rocket.android.R
import kotlinx.android.synthetic.main.fragment_authentication_two_fa.*
class AuthenticationTwoFAFragment : Fragment() {
class TwoFAFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? = inflater?.inflate(R.layout.fragment_authentication_two_fa, container, false)
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?) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
activity.window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
activity?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
tintEditTextDrawableStart()
......@@ -26,12 +26,12 @@ class AuthenticationTwoFAFragment : Fragment() {
}
private fun tintEditTextDrawableStart() {
val context = activity.applicationContext
val lockDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_vpn_key_black_24dp, context)
activity?.applicationContext?.apply {
val lockDrawable = DrawableHelper.getDrawableFromId(R.drawable.ic_vpn_key_black_24dp, this)
DrawableHelper.wrapDrawable(lockDrawable)
DrawableHelper.tintDrawable(lockDrawable, context, R.color.colorDrawableTintGrey)
DrawableHelper.tintDrawable(lockDrawable, this, R.color.colorDrawableTintGrey)
DrawableHelper.compoundDrawable(text_two_factor_auth, lockDrawable)
}
}
}
\ 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.LoginFragmentProvider
import chat.rocket.android.authentication.di.AuthenticationModule
import chat.rocket.android.authentication.di.ServerFragmentProvider
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
))
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
fun String.ifEmpty(value: String): String {
if (isEmpty()) {
return value
}
return this
}
\ 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">https://</string>
<string name="default_server">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')
......
......@@ -7,6 +7,8 @@ ext {
targetSdk : 27,
buildTools : '27.0.0',
kotlin : '1.2.0',
coroutine : '0.19.3',
dokka : '0.9.15',
// Main dependencies
support : '27.0.0',
......@@ -15,7 +17,7 @@ ext {
room : '1.0.0-beta1',
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',
......@@ -30,7 +32,9 @@ 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}",
......@@ -51,6 +55,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