Skip to content

Commit

Permalink
Migrate login to binding and user ID retrieval
Browse files Browse the repository at this point in the history
Signed-off-by: Aron Heinecke <aron.heinecke@t-online.de>
  • Loading branch information
0xpr03 committed May 21, 2021
1 parent b90f820 commit a2a3aaa
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 108 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,16 @@ import okhttp3.Response
import java.io.IOException

private const val API_KEY_USERNAME = "name"
private const val API_KEY_ERROR = "error"
private const val API_KEY_USER_ID = "id"

/**
* Class that handles authentication w/ login credentials and retrieves user information.
*/
class LoginDataSource {
fun login(data: LoginData, liveData: MutableLiveData<Result<LoggedInUserView>>) {
val request = Utils.buildAPI(data.apiBackend, "users/${data.userID}", data.apiToken).build()
getHttpBase(data.apiToken).newCall(request).enqueue(object : Callback {
fun login(backend: String, token: String, liveData: MutableLiveData<Result<LoggedInUserView>>) {
val request = Utils.buildAPI(backend, "users/me", token).build()
getHttpBase(token).newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
Log.w(this@LoginDataSource::class.java.name, "onFailure $e")
liveData.postValue(Result.Error(e))
Expand All @@ -40,7 +42,11 @@ class LoginDataSource {
val answer = JsonParser.parseString(bodyString)
if (answer.isJsonObject) {
val jsonObj = answer.asJsonObject
if (jsonObj.has(API_KEY_USERNAME)) {
if (jsonObj.has(API_KEY_ERROR)) {
// {"error":"Unauthorized."}
Result.Error(InvalidResponseException(code, it.toString()))
} else if (jsonObj.has(API_KEY_USERNAME) && jsonObj.has(API_KEY_USER_ID)) {
val data = LoginData(jsonObj.get(API_KEY_USER_ID).asInt,token,backend)
Result.Success(
LoggedInUserView(
jsonObj.get(API_KEY_USERNAME).asString,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,66 +7,53 @@ import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import android.view.View
import android.view.inputmethod.EditorInfo
import android.widget.Button
import android.widget.EditText
import android.widget.ProgressBar
import android.widget.Toast
import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import com.google.android.material.textfield.TextInputEditText
import com.google.gson.Gson
import com.google.gson.JsonSyntaxException
import com.google.zxing.integration.android.IntentIntegrator
import de.tu_darmstadt.seemoo.LARS.MainActivity
import de.tu_darmstadt.seemoo.LARS.R
import de.tu_darmstadt.seemoo.LARS.Utils
import de.tu_darmstadt.seemoo.LARS.data.model.LoginData
import de.tu_darmstadt.seemoo.LARS.databinding.ActivityLoginBinding


class LoginActivity : AppCompatActivity() {
private lateinit var loginViewModel: LoginViewModel
private lateinit var binding: ActivityLoginBinding

@SuppressLint("ApplySharedPref")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

setContentView(R.layout.activity_login)

val apiToken = findViewById<TextInputEditText>(R.id.apiToken)
val apiEndpoint = findViewById<TextInputEditText>(R.id.apiEndpoint)
val userID = findViewById<TextInputEditText>(R.id.userID)
val login = findViewById<Button>(R.id.login)
val loading = findViewById<ProgressBar>(R.id.loading)
val scanLogin = findViewById<Button>(R.id.scanLogin)
binding = ActivityLoginBinding.inflate(layoutInflater)
setContentView(binding.root)

loginViewModel = ViewModelProvider(this)[LoginViewModel::class.java]

loginViewModel.loginFormState.observe(this@LoginActivity, Observer {
val loginState = it ?: return@Observer

login.isEnabled = loginState.isDataValid
binding.login.isEnabled = loginState.isDataValid

if (loginState.endpointError != null) {
apiEndpoint.error = getString(loginState.endpointError)
}

if (loginState.userIDError != null) {
userID.error = getString(loginState.userIDError)
binding.apiEndpoint.error = getString(loginState.endpointError)
}

if (loginState.tokenError != null) {
apiToken.error = getString(loginState.tokenError)
binding.apiToken.error = getString(loginState.tokenError)
}

})

loginViewModel.loginResult.observe(this@LoginActivity, Observer {
val loginResult = it ?: return@Observer

loading.visibility = View.GONE
binding.loading.visibility = View.GONE
if (loginResult.error != null) {
showLoginFailed(loginResult.error, loginResult.errorDetail)
}
Expand All @@ -90,7 +77,7 @@ class LoginActivity : AppCompatActivity() {

})

scanLogin.setOnClickListener {
binding.scanLogin.setOnClickListener {
val integrator = IntentIntegrator(this)
integrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE)
integrator.setPrompt("Scan QR-Login Code")
Expand All @@ -100,56 +87,30 @@ class LoginActivity : AppCompatActivity() {
integrator.initiateScan()
}

login.setOnClickListener {
loading.visibility = View.VISIBLE
binding.login.setOnClickListener {
binding.loading.visibility = View.VISIBLE
loginViewModel.login(
apiToken.text.toString(),
apiEndpoint.text.toString(),
Integer.parseInt(userID.text.toString())
binding.apiToken.text.toString(),
binding.apiEndpoint.text.toString()
)
}

apiEndpoint.apply {
afterTextChanged {
loginViewModel.loginDataChanged(
apiEndpoint.text.toString(),
apiToken.text.toString(),
userID.text.toString()
)
}
}

apiToken.apply {
binding.apiEndpoint.apply {
afterTextChanged {
loginViewModel.loginDataChanged(
apiEndpoint.text.toString(),
apiToken.text.toString(),
userID.text.toString()
binding.apiEndpoint.text.toString(),
binding.apiToken.text.toString()
)
}
}

userID.apply {
binding.apiToken.apply {
afterTextChanged {
loginViewModel.loginDataChanged(
apiEndpoint.text.toString(),
apiToken.text.toString(),
userID.text.toString()
binding.apiEndpoint.text.toString(),
binding.apiToken.text.toString()
)
}

setOnEditorActionListener { _, actionId, _ ->
when (actionId) {
EditorInfo.IME_ACTION_DONE ->
loginViewModel.login(
apiToken.text.toString(),
apiEndpoint.text.toString(),
Integer.parseInt(userID.text.toString())
)
}

false
}
}
}

Expand All @@ -164,12 +125,8 @@ class LoginActivity : AppCompatActivity() {
try {
val loginData: LoginData = gson.fromJson(result.contents, LoginData::class.java)
Log.d(this::class.java.name, "Data: ${loginData.apiToken}")
val apiToken: EditText = findViewById(R.id.apiToken)
val apiEndpoint: EditText = findViewById(R.id.apiEndpoint)
val userID: EditText = findViewById(R.id.userID)
apiToken.setText(loginData.apiToken)
apiEndpoint.setText(loginData.apiBackend)
userID.setText(loginData.userID.toString())
binding.apiToken.setText(loginData.apiToken)
binding.apiEndpoint.setText(loginData.apiBackend)
} catch (e: JsonSyntaxException) {
Toast.makeText(this, R.string.invalid_login_json, Toast.LENGTH_LONG).show()
Log.d(this::class.java.name, "Can't parse login json", e)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,27 +42,16 @@ class LoginViewModel : ViewModel() {
}
}

fun login(apiToken: String, endpoint: String, userID: Int) {
fun login(apiToken: String, endpoint: String) {
// can be launched in a separate asynchronous job
LoginDataSource().login(LoginData(userID, apiToken, endpoint), backgroundResult)
LoginDataSource().login(endpoint, apiToken, backgroundResult)
}

fun loginDataChanged(endpoint: String, apiToken: String, userID: String) {
fun loginDataChanged(endpoint: String, apiToken: String) {
if (!isTokenValid(apiToken)) {
_loginForm.value = LoginFormState(tokenError = R.string.invalid_token)
} else if (!isEndpointValid(endpoint)) {
_loginForm.value = LoginFormState(endpointError = R.string.invalid_api_endpoint)
} else {
try {
val uid = Integer.parseInt(userID)
if (uid < 1) {
_loginForm.value = LoginFormState(userIDError = R.string.invalid_user_id)
}

_loginForm.value = LoginFormState(isDataValid = true)
} catch (e: NumberFormatException) {
_loginForm.value = LoginFormState(userIDError = R.string.invalid_user_id)
}
}
}

Expand Down
27 changes: 1 addition & 26 deletions app/src/main/res/layout/activity_login.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,30 +24,6 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<com.google.android.material.textfield.TextInputLayout
android:id="@+id/userID_cont"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="24dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/apiToken_cont">

<com.google.android.material.textfield.TextInputEditText
android:id="@+id/userID"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autofillHints="password"
android:digits="0123456789"
android:hint="@string/prompt_user_id"
android:imeActionLabel="@string/action_sign_in_short"
android:imeOptions="actionDone"
android:inputType="number"
android:selectAllOnFocus="true" />
</com.google.android.material.textfield.TextInputLayout>

<Button
android:id="@+id/login"
android:layout_width="wrap_content"
Expand All @@ -61,8 +37,7 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/userID_cont"
app:layout_constraintVertical_bias="0.2" />
app:layout_constraintTop_toBottomOf="@+id/apiToken_cont" />

<com.google.android.material.textfield.TextInputLayout
android:id="@+id/apiToken_cont"
Expand Down

0 comments on commit a2a3aaa

Please sign in to comment.