Commit 8407751e authored by Filip Wiesner's avatar Filip Wiesner

Started working on initial load (Entities, Dao, login phase transition)

parent 1ae4b014
package com.cvut.blackbird.flows.authentication
import android.animation.Animator
import android.content.Intent
import android.net.Uri
import androidx.lifecycle.ViewModelProviders
import android.os.Bundle
import android.view.Gravity
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.AccelerateDecelerateInterpolator
import android.view.animation.AlphaAnimation
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.navigation.findNavController
import androidx.transition.Transition
import androidx.transition.TransitionManager
import bg.devlabs.transitioner.Transitioner
import com.cvut.blackbird.R
import com.cvut.blackbird.extensions.*
......@@ -19,8 +27,11 @@ import com.cvut.blackbird.model.services.AuthService
import com.cvut.blackbird.model.services.UserInfo
import com.cvut.blackbird.model.support.AuthResult
import com.cvut.blackbird.support.glue.*
import com.github.florent37.kotlin.pleaseanimate.core.custom.CustomAnimExpectation
import com.github.florent37.kotlin.pleaseanimate.please
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.auth_fragment.*
import org.jetbrains.anko.childrenSequence
class AuthFragment : Fragment() {
private var lastValidUser = Student()
......@@ -64,7 +75,7 @@ class AuthFragment : Fragment() {
observe(viewModel.authStatus) { result ->
if (result == AuthResult.SUCCESS) {
if(UserInfo.username.isNotBlank()) onLogged()
else secondPhase()
else onAuthorized()
} else if (result != AuthResult.NOT_YET) {
snack?.setText("Error: ${result.name}")
infoMessage.text = result.errorDesc
......@@ -76,14 +87,17 @@ class AuthFragment : Fragment() {
observe(viewModel.authLoadingStatus) { if (it) snack?.dismiss() }
this bind authProgress visibilityTo viewModel.authLoadingStatus
this bind usernameStatus visibilityTo viewModel.userLoadingStatus
this bind acceptBtn enabledToSuccessOf viewModel.studentResult
this bind acceptBtn clickTo ::onLogged
send(usernameInput) .textChangeTo (viewModel::fetchStudent) .ignoreBlank()
bind(authProgress) visibilityTo viewModel.authLoadingStatus
bind(usernameStatus) visibilityTo viewModel.userLoadingStatus
bind(acceptBtn) enabledToSuccessOf viewModel.studentResult
bind(acceptBtn) clickTo ::onLogged
send(usernameInput)
.textChangeTo(viewModel::fetchStudent)
.ignoreBlank()
}
private fun secondPhase() {
private fun onAuthorized() {
val animLength: Long = 1000
val anim = AlphaAnimation(0.0f, 1.0f)
anim.duration = animLength
......@@ -102,7 +116,23 @@ class AuthFragment : Fragment() {
private fun onLogged() {
UserInfo.setStudent(lastValidUser)
// UserInfo.setStudent(lastValidUser)
please {
animate(usernameInputLayout) toBe { invisible() }
animate(acceptBtn) toBe { invisible() }
animate(infoMessage) toBe { textSize(24f) }
}.withEndAction {
infoMessage.gravity = Gravity.CENTER or Gravity.BOTTOM
infoMessage.setTextColor(getColor(R.color.colorTextLight))
} .start()
val newConstraints = ConstraintSet().apply { clone(activity, R.layout.auth_fragment_transition) }
TransitionManager.beginDelayedTransition(auth)
newConstraints.applyTo(auth)
}
private fun onAuthFinished() {
view!!.findNavController().navigate(R.id.onLogged)
}
......
package com.cvut.blackbird.model.database
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import com.cvut.blackbird.model.entities.Course
@Dao
interface CourseDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(events: List<Course>)
}
\ No newline at end of file
package com.cvut.blackbird.model.entities
import com.tickaroo.tikxml.annotation.*
@Xml(name = "atom:feed")
class CourseRoot (
@PropertyElement(name = "osearch:startIndex")
val startIndex: Int,
@PropertyElement(name = "atom:entry")
val courses: List<Course>
)
data class Course (
@Path("atom:content")
@PropertyElement(name = "course")
val name: String,
@Path("atom:content")
@Attribute(name = "xlink:href")
val link: String,
@Path("atom:content")
@PropertyElement(name = "semester")
val semester: String
)
......@@ -2,15 +2,20 @@ package com.cvut.blackbird.model.flows
import com.cvut.blackbird.BlackBirdAC
import com.cvut.blackbird.model.*
import com.cvut.blackbird.model.database.EventDao
import com.cvut.blackbird.model.entities.Student
import com.cvut.blackbird.model.services.AuthInfo
import com.cvut.blackbird.model.services.KosService
import com.cvut.blackbird.model.services.SiriusService
import com.cvut.blackbird.model.support.AuthResult
import kotlinx.coroutines.experimental.async
import javax.inject.Inject
class AuthModel: BlackBirdModel() {
@Inject
lateinit var kosService: KosService
lateinit var siriusService: SiriusService
lateinit var eventDao: EventDao
init {
......@@ -60,21 +65,36 @@ class AuthModel: BlackBirdModel() {
val result: Result<Student>
try {
val response = kosService.getStudentEx(username).execute()
val response = kosService.getStudent(username).execute()
result = if (response.body() != null)
Success(response.body()!!)
else {
val errorBody = response.errorBody()?.string()
Failure("Failed while fetching student\n" +
"Message: ${response.message()}\n" +
"Error body: $errorBody")
"Message: ${response.message()}")
}
} catch (e: Throwable) {
return Failure("Failed while fetching student\n" +
"Error message: ${e.localizedMessage}")
}
return result
}
suspend fun initLoad(): Result<Unit> {
val courses = async { fetch(kosService.getStudentsCourses()) }
val events = async { refreshEvents(siriusService, eventDao) }
val resultCourses = courses.await()
val resultEvents = events.await()
return if (resultCourses is Success && resultEvents is Success) {
Success(Unit)
} else Failure("Failed initializing account.\n" +
"Error message: " +
when {
resultCourses is Failure -> resultCourses.message
resultEvents is Failure -> resultEvents.message
else -> "Unknown Error"
}
)
}
}
\ No newline at end of file
package com.cvut.blackbird.model.services
import com.cvut.blackbird.model.entities.CourseRoot
import com.cvut.blackbird.model.entities.Student
import retrofit2.Call
import retrofit2.Response
......@@ -12,26 +13,17 @@ interface KosService {
}
@GET("students/{user}")
fun getStudentEx(
fun getStudent(
@Path("user") user: String,
@Header("Authorization") token: String = accessToken
): Call<Student>
@GET("students/{user}")
fun findStudentEx(
@Path("user") user: String,
@Header("Authorization") token: String = accessToken
): Call<Student>
// @GET("exams")
// fun getExams(
// @Header("lang") lang: String = "en",
// @Header("limit") resultsLimit: Int = 1000
// ): Observable<Response<Exam>>
//
// @GET("exams/{id}")
// fun getExamById(
// @Path("id") examId: Int
// ): Observable<Response<Exam>>
@GET("students/{user}/enrolledCourses")
fun getStudentsCourses(
@Path("user") user: String = UserInfo.username,
@Header("Authorization") token: String = accessToken,
@Query("limit") resultsLimit: Int = 1000,
@Query("lang") lang: String = "en"
): Call<CourseRoot>
}
......@@ -30,6 +30,6 @@ object UserInfo: KotprefModel() {
}
}
//object AppState: KotprefModel() {
// var offline by booleanPref()
//}
\ No newline at end of file
object AccountState: KotprefModel() {
var initialized by booleanPref()
}
\ No newline at end of file
......@@ -3,9 +3,9 @@ package com.cvut.blackbird.support.glue
import android.text.Editable
import android.text.TextWatcher
import android.widget.TextView
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
class TextGlue(val sender: TextView, var lifecycleOwner: LifecycleOwner? = null) {
class TextGlue(val sender: TextView) {
var blankIgnore = false
fun ignoreBlank(ignore: Boolean = true): TextGlue = this.apply { blankIgnore = ignore }
}
......@@ -13,16 +13,30 @@ fun send(actor: TextView) = TextGlue(actor)
infix fun TextGlue.textChangeTo(receiver: (String) -> Unit): TextGlue {
sender.addTextChangedListener(object: TextWatcher {
sender.onTextChanged {
if ((blankIgnore && it.isNotEmpty()) ||
!blankIgnore)
receiver(it)
}
return this
}
infix fun TextGlue.textChangeTo(receiver: MutableLiveData<String>) = this.let {
sender.onTextChanged { text ->
if ((blankIgnore && text.isNotEmpty()) ||
!blankIgnore)
receiver.postValue(text)
}
}
fun TextView.onTextChanged(action: (text: String) -> Unit) {
addTextChangedListener(object: TextWatcher {
override fun afterTextChanged(s: Editable?) = Unit
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
if ((blankIgnore && s?.length ?:0 > 0) ||
!blankIgnore)
receiver(s.toString())
action(s.toString())
}
})
return this
}
\ No newline at end of file
......@@ -74,6 +74,7 @@
<ImageView
android:id="@+id/cvutLogo"
android:tag="authBird"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_marginStart="8dp"
......@@ -87,6 +88,7 @@
<TextView
android:id="@+id/infoMessage"
android:tag="authInfo"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="24dp"
......@@ -103,6 +105,7 @@
<ProgressBar
android:id="@+id/authProgress"
android:tag="authProgress"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
......@@ -115,4 +118,4 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/cvutLogo" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
......@@ -8,51 +8,110 @@
android:background="@color/colorPrimaryDark"
tools:context=".flows.authentication.AuthFragment">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/usernameInputLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="8dp"
android:hint="CVUT Username"
android:textColorHint="@android:color/white"
android:visibility="invisible"
app:boxBackgroundMode="outline"
app:boxCornerRadiusBottomEnd="5dp"
app:boxCornerRadiusBottomStart="5dp"
app:boxCornerRadiusTopEnd="5dp"
app:boxCornerRadiusTopStart="5dp"
app:boxStrokeColor="@color/colorAccent"
app:boxStrokeWidth="1dp"
app:hintAnimationEnabled="true"
app:hintEnabled="true"
app:layout_constraintBottom_toTopOf="@+id/acceptBtn"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/usernameInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPersonName"
android:paddingLeft="10dp"
android:paddingRight="32dp"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
android:textColor="@android:color/white" />
</com.google.android.material.textfield.TextInputLayout>
<ProgressBar
android:id="@+id/usernameStatus"
style="?android:attr/progressBarStyle"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
android:layout_marginEnd="8dp"
android:layout_marginBottom="12dp"
android:layout_weight="1"
android:progressTint="@color/colorAccent"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="@+id/usernameInputLayout"
app:layout_constraintEnd_toEndOf="@+id/usernameInputLayout"
app:layout_constraintTop_toTopOf="@+id/usernameInputLayout"
app:layout_constraintVertical_bias="0.476" />
<Button
android:id="@+id/acceptBtn"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginBottom="8dp"
android:text="Accept"
android:textSize="18sp"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<ImageView
android:id="@+id/imageView2"
android:layout_width="200dp"
android:layout_height="200dp"
android:tag="birdLogo"
android:id="@+id/cvutLogo"
android:layout_width="256dp"
android:layout_height="256dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:tag="authBird"
android:tint="@android:color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.8"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.8"
app:srcCompat="@drawable/ic_raven" />
<ImageView
android:id="@+id/imageView"
android:layout_width="240dp"
android:layout_height="229dp"
android:tag="birdSpeech"
android:layout_marginBottom="8dp"
android:scaleX="-1"
android:tint="@android:color/white"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="@+id/imageView2"
<TextView
android:id="@+id/infoMessage"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="32dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="32dp"
android:layout_marginBottom="32dp"
android:gravity="bottom|center_horizontal"
android:tag="authInfo"
android:textColor="@android:color/darker_gray"
android:textSize="24sp"
app:layout_constraintBottom_toTopOf="@+id/authProgress"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.42"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.19999999"
app:srcCompat="@drawable/ic_speech_bubble" />
tools:text="Found user: FOO\nStudying: BAR" />
<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="45dp"
android:layout_marginTop="40dp"
android:layout_marginEnd="45dp"
android:layout_marginBottom="80dp"
android:gravity="center"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="@+id/imageView"
app:layout_constraintEnd_toEndOf="@+id/imageView"
app:layout_constraintStart_toStartOf="@+id/imageView"
app:layout_constraintTop_toTopOf="@+id/imageView"
tools:text="Preparing some stuff for you.\n\nWait a sec..." />
<ProgressBar
android:id="@+id/authProgress"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:tag="authProgress"
app:layout_constraintBottom_toTopOf="@+id/cvutLogo"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
......@@ -199,7 +199,7 @@
android:fontFamily="sans-serif-condensed"
android:text="Teachers"
android:textAllCaps="false"
android:textSize="18sp"
android:textSize="16sp"
android:textStyle="italic" />
<View
......@@ -227,7 +227,7 @@
android:fontFamily="sans-serif-condensed"
android:text="Parallel num."
android:textAllCaps="false"
android:textSize="18sp"
android:textSize="16sp"
android:textStyle="italic" />
<View
......@@ -255,7 +255,7 @@
android:fontFamily="sans-serif-condensed"
android:text="Room"
android:textAllCaps="false"
android:textSize="18sp"
android:textSize="16sp"
android:textStyle="italic" />
<View
......@@ -283,7 +283,7 @@
android:fontFamily="sans-serif-condensed"
android:text="Note"
android:textAllCaps="false"
android:textSize="18sp"
android:textSize="16sp"
android:textStyle="italic" />
<View
......
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