feat: Add login activity
This commit is contained in:
parent
7f4de4cb30
commit
afa253420a
|
@ -5,22 +5,26 @@
|
|||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.NeatCalendar"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".LoginActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.NeatCalendar.Splash">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
|
81
app/src/main/java/com/p_vacho/neat_calendar/LoginActivity.kt
Normal file
81
app/src/main/java/com/p_vacho/neat_calendar/LoginActivity.kt
Normal file
|
@ -0,0 +1,81 @@
|
|||
package com.p_vacho.neat_calendar
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.widget.Button
|
||||
import android.widget.EditText
|
||||
import android.widget.Toast
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import com.p_vacho.neat_calendar.auth.AuthRepository
|
||||
import com.p_vacho.neat_calendar.auth.TokenManager
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class LoginActivity : AppCompatActivity() {
|
||||
private lateinit var authRepository: AuthRepository
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
setContentView(R.layout.activity_login)
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
|
||||
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
||||
insets
|
||||
}
|
||||
|
||||
val tokenManager = TokenManager(this)
|
||||
authRepository = AuthRepository(tokenManager)
|
||||
|
||||
val loginButton = findViewById<Button>(R.id.loginButton)
|
||||
val registerButton = findViewById<Button>(R.id.registerButton)
|
||||
|
||||
loginButton.setOnClickListener {
|
||||
handleLoginSubmit()
|
||||
}
|
||||
|
||||
registerButton.setOnClickListener {
|
||||
navigateToRegisterActivity()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleLoginSubmit() {
|
||||
val usernameInput = findViewById<EditText>(R.id.usernameInput)
|
||||
val passwordInput = findViewById<EditText>(R.id.passwordInput)
|
||||
|
||||
val username = usernameInput.text.toString().trim()
|
||||
val password = passwordInput.text.toString().trim()
|
||||
|
||||
if (username.isEmpty() || password.isEmpty()) {
|
||||
Toast.makeText(this, "Please fill in all fields", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
performLogin(username, password)
|
||||
}
|
||||
}
|
||||
|
||||
private fun performLogin(username: String, password: String) {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
val success = authRepository.login(username, password)
|
||||
if (success) {
|
||||
navigateToMainActivity()
|
||||
} else {
|
||||
Toast.makeText(this@LoginActivity, "Login failed. Please try again.", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun navigateToMainActivity() {
|
||||
val intent = Intent(this, MainActivity::class.java)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
startActivity(intent)
|
||||
finish() // Close the login screen
|
||||
}
|
||||
|
||||
private fun navigateToRegisterActivity() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package com.p_vacho.neat_calendar
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
|
@ -7,6 +8,8 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
|||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import com.p_vacho.neat_calendar.api.RetrofitClient
|
||||
import com.p_vacho.neat_calendar.auth.AuthRepository
|
||||
import com.p_vacho.neat_calendar.auth.TokenManager
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -14,28 +17,28 @@ import kotlinx.coroutines.withContext
|
|||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
private var apiReachable = false // Variable to track API reachability
|
||||
private var apiReachable = false
|
||||
private var loggedIn = false
|
||||
private lateinit var authRepository: AuthRepository
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
// Attach the splash screen to the activity before initialization begins
|
||||
val splashScreen = installSplashScreen()
|
||||
|
||||
// Keep the splash screen visible until initialization is complete
|
||||
splashScreen.setKeepOnScreenCondition { !apiReachable }
|
||||
splashScreen.setKeepOnScreenCondition { !apiReachable || !loggedIn }
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
// Check API availability in a coroutine
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
apiReachable = RetrofitClient.isApiReachable()
|
||||
if (!apiReachable) {
|
||||
// Handle unreachable API, e.g., show an error message or retry
|
||||
showApiErrorDialog()
|
||||
}
|
||||
}
|
||||
var tokenManager = TokenManager(this)
|
||||
authRepository = AuthRepository(tokenManager)
|
||||
|
||||
// Perform initialization (api reachability check & login)
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
initialize()
|
||||
}
|
||||
// Handle window insets for proper layout adjustment
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
|
||||
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
|
@ -44,21 +47,47 @@ class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun initialize() {
|
||||
if (!apiReachable) {
|
||||
apiReachable = checkApiReachability()
|
||||
}
|
||||
|
||||
if (!apiReachable) showApiErrorDialog()
|
||||
|
||||
loggedIn = checkUserLogin()
|
||||
if (!loggedIn) navigateToLoginActivity()
|
||||
}
|
||||
|
||||
|
||||
private suspend fun checkApiReachability(): Boolean {
|
||||
return RetrofitClient.isApiReachable()
|
||||
}
|
||||
|
||||
private suspend fun checkUserLogin(): Boolean {
|
||||
if (authRepository.validateRefreshToken() != null) {
|
||||
// Get a brand new access token (even if the old access token is still valid,
|
||||
// it's better to just get a new one so it doesn't expire as quickly on us)
|
||||
return authRepository.refreshAccessToken()
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private fun navigateToLoginActivity() {
|
||||
val intent = Intent(this, LoginActivity::class.java)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
startActivity(intent)
|
||||
finish() // Close MainActivity
|
||||
}
|
||||
|
||||
// Show an error dialog if the API is unreachable
|
||||
private fun showApiErrorDialog() {
|
||||
var retryCount = 0;
|
||||
|
||||
val dialog = androidx.appcompat.app.AlertDialog.Builder(this)
|
||||
.setTitle("Error")
|
||||
.setMessage(
|
||||
"The API is unreachable.\n" +
|
||||
"Please check your network connection."
|
||||
)
|
||||
.setPositiveButton("Retry", null) // Listener set later for custom handling
|
||||
.setNegativeButton("Close") { _, _ ->
|
||||
// Close the app or navigate to an error screen
|
||||
finish()
|
||||
}
|
||||
.setMessage("The API is unreachable.\nPlease check your network connection.")
|
||||
.setPositiveButton("Retry", null)
|
||||
.setNegativeButton("Close") { _, _ -> finish() } // Close the app
|
||||
.setCancelable(false)
|
||||
.create()
|
||||
|
||||
|
@ -68,12 +97,11 @@ class MainActivity : AppCompatActivity() {
|
|||
retryButton.setOnClickListener {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
retryCount++
|
||||
val reachable = RetrofitClient.isApiReachable()
|
||||
if (reachable) {
|
||||
apiReachable = true
|
||||
apiReachable = checkApiReachability()
|
||||
if (apiReachable) {
|
||||
dialog.dismiss() // Close dialog and proceed
|
||||
initialize() // re-run initialization
|
||||
} else {
|
||||
// Update dialog message for failed retry
|
||||
dialog.setMessage(
|
||||
"Retry failed. Attempts: $retryCount\n" +
|
||||
"Please check you network and try again."
|
||||
|
|
68
app/src/main/res/layout/activity_login.xml
Normal file
68
app/src/main/res/layout/activity_login.xml
Normal file
|
@ -0,0 +1,68 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="48dp"
|
||||
tools:context=".LoginActivity">
|
||||
|
||||
<!-- Username Field -->
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/usernameInputLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:hint="@string/username">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/usernameInput"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="text" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<!-- Password Field -->
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/passwordInputLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@id/usernameInputLayout"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:hint="@string/password">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/passwordInput"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textPassword" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<!-- Login Button -->
|
||||
<Button
|
||||
android:id="@+id/loginButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:text="@string/login"
|
||||
app:layout_constraintTop_toBottomOf="@id/passwordInputLayout"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<!-- Register Button -->
|
||||
<Button
|
||||
android:id="@+id/registerButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
android:text="@string/register"
|
||||
app:layout_constraintTop_toBottomOf="@id/loginButton"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -1,3 +1,7 @@
|
|||
<resources>
|
||||
<string name="app_name">NeatCalendar</string>
|
||||
<string name="username">Username</string>
|
||||
<string name="password">Password</string>
|
||||
<string name="login">Login</string>
|
||||
<string name="register">Register</string>
|
||||
</resources>
|
Loading…
Reference in a new issue