Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Task/registration screen #76

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ android {

apollo {
generateKotlinModels.set(true)
customTypeMapping = [
"Upload" : "com.apollographql.apollo.api.FileUpload"
]
}

repositories {
Expand All @@ -107,10 +110,12 @@ dependencies {
implementation 'com.google.code.gson:gson:2.8.6'

implementation androidLibs
implementation retrofitLibs
implementation apolloLibs
implementation coroutinesLibs
implementation kodeinLibs
implementation roomLibs
implementation glideLibs
kapt compilerLibs

testImplementation unitTestLibs
Expand Down
3 changes: 3 additions & 0 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,7 @@
-keepattributes SourceFile,LineNumberTable


#apollo input types
-keep class * implements com.apollographql.apollo.api.InputType { *; }


Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public void whenAppLaunch_androidBaseVisible() throws Exception {

@Test
public void whenButtonClick_startedActivity() {
onView(withId(R.id.button)).perform(click());
onView(withId(R.id.selectAvatar)).perform(click());
intended(hasComponent(SecondActivity.class.getName()));
}
}
4 changes: 4 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package="com.flatstack.android">

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

<application
android:name=".App"
Expand All @@ -20,6 +21,9 @@
<activity
android:name=".login.LoginActivity"
android:windowSoftInputMode="adjustResize" />
<activity
android:name=".registration.RegistrationActivity"
android:windowSoftInputMode="adjustResize" />
<activity
android:name=".MainActivity"
android:theme="@android:style/Theme.NoDisplay">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
fragment UserGqlFragment on User {
firstName
lastName
avatarUrl
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
mutation PresignAvatar(
$input: PresignDataInput!
) {
presignData(input: $input) {
fields {
key
value
}
url
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
mutation Login($email: String!, $password: String!) {
signin(email: $email, password: $password) {
mutation Login($input: SignInInput!) {
signin(input: $input) {
accessToken
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mutation Register($input: SignUpInput!) {
signup(input: $input) {
accessToken
}
}
17 changes: 13 additions & 4 deletions app/src/main/java/com/flatstack/android/Router.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,30 @@ import android.content.Context
import android.content.Intent
import com.flatstack.android.login.LoginActivity
import com.flatstack.android.profile.ProfileActivity
import com.flatstack.android.registration.RegistrationActivity

class Router(
private val appContext: Context
) {
fun login(context: Context = appContext, clearStack: Boolean = false) {
fun login(context: Context = appContext, shouldClearStack: Boolean = false) {
context.startActivity(Intent(context, LoginActivity::class.java).apply {
if (clearStack) {
if (shouldClearStack) {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
})
}

fun profile(context: Context, clearStack: Boolean = false) {
fun profile(context: Context, shouldClearStack: Boolean = false) {
context.startActivity(Intent(context, ProfileActivity::class.java).apply {
if (clearStack) {
if (shouldClearStack) {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
})
}

fun registration(context: Context, shouldClearStack: Boolean = false) {
context.startActivity(Intent(context, RegistrationActivity::class.java).apply {
if (shouldClearStack) {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
})
Expand Down
1 change: 1 addition & 0 deletions app/src/main/java/com/flatstack/android/di/kodein.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ fun initKodein(app: Application) =
import(viewModelModule)
import(netModule)
import(repoModule)
import(mapperModule)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.flatstack.android.di.modules
import com.flatstack.android.Router
import com.flatstack.android.login.LoginMapper
import com.flatstack.android.model.network.errors.ErrorHandler
import com.flatstack.android.util.FileUtils
import com.flatstack.android.util.StringResource
import org.kodein.di.Kodein
import org.kodein.di.generic.bind
Expand All @@ -13,5 +14,6 @@ val appModule = Kodein.Module(name = "appModule") {
bind<StringResource>() with singleton { StringResource(instance()) }
bind<ErrorHandler>() with singleton { ErrorHandler(instance(), instance(), instance()) }
bind<Router>() with singleton { Router(instance()) }
bind<FileUtils>() with singleton { FileUtils(instance()) }
bind<LoginMapper>() with singleton { LoginMapper() }
}
16 changes: 16 additions & 0 deletions app/src/main/java/com/flatstack/android/di/modules/mapperModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.flatstack.android.di.modules

import com.flatstack.android.registration.SessionFromRegistrationMapper
import com.flatstack.android.registration.mapper.PresignDataFromNetworkMapper
import com.flatstack.android.registration.mapper.PresignDataToNetworkMapper
import com.flatstack.android.registration.mapper.RegisterRequestToNetworkMapper
import org.kodein.di.Kodein
import org.kodein.di.generic.bind
import org.kodein.di.generic.provider

val mapperModule = Kodein.Module(name = "mapperModule") {
bind<SessionFromRegistrationMapper>() with provider { SessionFromRegistrationMapper() }
bind<RegisterRequestToNetworkMapper>() with provider { RegisterRequestToNetworkMapper() }
bind<PresignDataToNetworkMapper>() with provider { PresignDataToNetworkMapper() }
bind<PresignDataFromNetworkMapper>() with provider { PresignDataFromNetworkMapper() }
}
13 changes: 12 additions & 1 deletion app/src/main/java/com/flatstack/android/di/modules/netModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,22 @@ package com.flatstack.android.di.modules

import com.apollographql.apollo.ApolloClient
import com.flatstack.android.BuildConfig
import com.flatstack.android.model.network.NetworkManager
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import org.kodein.di.Kodein
import org.kodein.di.generic.bind
import org.kodein.di.generic.instance
import org.kodein.di.generic.singleton
import org.kodein.di.generic.*

val netModule = Kodein.Module(name = "netModule") {
bind<Interceptor>() with singleton { AuthorizationInterceptor(instance()) }

bind<OkHttpClient>() with singleton { OkHttpClient.Builder()
.addInterceptor(instance())
.addNetworkInterceptor(HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY })
.addInterceptor(instance<Interceptor>())
.build() }

bind<ApolloClient>() with singleton {
Expand All @@ -22,4 +26,11 @@ val netModule = Kodein.Module(name = "netModule") {
.okHttpClient(instance())
.build()
}

bind<OkHttpClient>("restClient") with singleton { OkHttpClient.Builder()
.addNetworkInterceptor(HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY })
.build()
}

bind<NetworkManager>() with provider { NetworkManager(instance("restClient")) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package com.flatstack.android.di.modules

import com.flatstack.android.login.LoginRepository
import com.flatstack.android.profile.ProfileRepository
import com.flatstack.android.registration.PresignDataRepository
import com.flatstack.android.registration.RegistrationRepository
import org.kodein.di.Kodein
import org.kodein.di.generic.bind
import org.kodein.di.generic.instance
Expand All @@ -10,4 +12,7 @@ import org.kodein.di.generic.provider
val repoModule = Kodein.Module(name = "repoModule") {
bind<LoginRepository>() with provider { LoginRepository(instance(), instance(), instance(), instance()) }
bind<ProfileRepository>() with provider { ProfileRepository(instance(), instance(), instance()) }
bind<RegistrationRepository>() with provider { RegistrationRepository(instance(), instance(), instance()) }
bind<PresignDataRepository>() with provider {
PresignDataRepository(instance(), instance(), instance(), instance()) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.flatstack.android.di.modules
import androidx.lifecycle.ViewModelProvider
import com.flatstack.android.login.LoginViewModel
import com.flatstack.android.profile.ProfileViewModel
import com.flatstack.android.registration.RegistrationViewModel
import com.flatstack.android.util.ViewModelFactory
import com.flatstack.android.util.bindViewModel
import org.kodein.di.Kodein
Expand All @@ -16,4 +17,6 @@ val viewModelModule = Kodein.Module(name = "viewModelModule") {

bindViewModel<LoginViewModel>() with provider { LoginViewModel(instance(), instance()) }
bindViewModel<ProfileViewModel>() with provider { ProfileViewModel(instance(), instance()) }
bindViewModel<RegistrationViewModel>() with provider {
RegistrationViewModel(instance(), instance(), instance(), instance(), instance()) }
}
10 changes: 8 additions & 2 deletions app/src/main/java/com/flatstack/android/login/LoginActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,17 @@ class LoginActivity : AppCompatActivity(), KodeinAware {

private fun navigateToProfile() {
val router by kodein.instance<Router>()
router.profile(context = this, clearStack = true)
router.profile(context = this, shouldClearStack = true)
}

private fun navigateToRegistration() {
val router by kodein.instance<Router>()
router.registration(context = this, shouldClearStack = false)
}

private fun initListeners() {
bt_login.setOnClickListener { login() }
bt_sign_up.setOnClickListener { navigateToRegistration() }
et_password.apply {
setOnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_GO) {
Expand All @@ -54,7 +60,7 @@ class LoginActivity : AppCompatActivity(), KodeinAware {
}

private fun login() {
val username = et_login.text.toString()
val username = et_email.text.toString()
val password = et_password.text.toString()
viewModel.login(username, password)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.flatstack.android.model.entities.Session
import com.flatstack.android.model.network.NetworkBoundResource
import com.flatstack.android.model.network.errors.ErrorHandler
import com.flatstack.android.profile.AuthorizationModel
import com.flatstack.android.type.SignInInput
import kotlinx.coroutines.*

class LoginRepository(
Expand Down Expand Up @@ -41,5 +42,5 @@ class LoginRepository(
}

private fun loginMutation(email: String, password: String) =
LoginMutation(email = email, password = password)
LoginMutation(SignInInput(email, password))
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import com.flatstack.android.profile.entities.Profile

@Database(
entities = [Profile::class, Session::class],
version = 1,
version = 3,
exportSchema = false
)
abstract class AppDatabase : RoomDatabase() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.flatstack.android.model.network

import kotlinx.coroutines.suspendCancellableCoroutine
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody.Companion.asRequestBody
import java.io.File
import java.io.IOException

class NetworkManager(private val okHttpClient: OkHttpClient) {

suspend fun uploadImageToAws(url: String, fields: Map<String, String>, file: File): Boolean =
suspendCancellableCoroutine { cancellableContinuation ->
val requestBody = MultipartBody.Builder().setType(MultipartBody.FORM)
fields.forEach {
requestBody.addFormDataPart(it.key, it.value)
}
requestBody.addFormDataPart(
FORM_DATA_KEY,
file.name,
file.asRequestBody(contentType = REQUEST_BODY_CONTENT_TYPE.toMediaTypeOrNull())
)
val request = Request.Builder()
.url(url)
.method(REQUEST_METHOD, requestBody.build())
.build()
okHttpClient
.newCall(request)
.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
cancellableContinuation.resume(false) {}
}

override fun onResponse(call: Call, response: Response) {
cancellableContinuation.resume(true) {}
}
})
}

companion object {
const val FORM_DATA_KEY = "file"
const val REQUEST_BODY_CONTENT_TYPE = "application/octet-stream"
const val REQUEST_METHOD = "POST"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class ErrorHandler(
@VisibleForTesting
suspend fun unAuthorize() {
authorizationModel.unAuthorize()
router.login(clearStack = true)
router.login(shouldClearStack = true)
}

private fun userMessage(error: Error) = when (mapToStatus(error)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
import com.bumptech.glide.Glide
import com.flatstack.android.R
import com.flatstack.android.Router
import com.flatstack.android.util.observeBy
Expand Down Expand Up @@ -38,6 +39,7 @@ class ProfileActivity : AppCompatActivity(), KodeinAware, OnRefreshListener {
showProfile()
showFirstName(it.firstName)
showLastName(it.lastName)
showAvatar(it.avatarUrl)
},
onError = ::showError,
onLoading = ::visibleProgress)
Expand Down Expand Up @@ -68,11 +70,17 @@ class ProfileActivity : AppCompatActivity(), KodeinAware, OnRefreshListener {
tv_last_name.text = lastName
}

private fun showAvatar(avatarUrl: String) {
Glide.with(this)
.load(avatarUrl)
.into(iv_avatar)
}

private fun logout() {
viewModel.logout()

val router by kodein.instance<Router>()
router.login(context = this, clearStack = true)
router.login(context = this, shouldClearStack = true)
}

private fun visibleProgress(show: Boolean) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ object ProfileMapper {
fun mapProfile(me: GetUserQuery.Me?) = me?.fragments?.userGqlFragment.run {
Profile(
firstName = this?.firstName ?: "",
lastName = this?.lastName ?: ""
lastName = this?.lastName ?: "",
avatarUrl = this?.avatarUrl ?: ""
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import androidx.room.PrimaryKey
data class Profile(
val firstName: String,
val lastName: String,
val avatarUrl: String,
@PrimaryKey(autoGenerate = true)
val id: Int = 0
)
Loading