From 55cb870d42580c813091a054aa3afc3307e813ba Mon Sep 17 00:00:00 2001 From: Haidar <16521522@mahasiswa.itb.ac.id> Date: Fri, 5 Apr 2024 08:26:31 +0700 Subject: [PATCH] feat: add jwt services --- .../exe_android/data/login/ApiService.kt | 9 +++ .../exe_android/ui/login/LoginActivity.kt | 56 ++++++++++++++++--- .../ui/login/TokenExpirationService.kt | 49 ++++++++++++++++ 3 files changed, 107 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/pbd/tubes/exe_android/ui/login/TokenExpirationService.kt diff --git a/app/src/main/java/pbd/tubes/exe_android/data/login/ApiService.kt b/app/src/main/java/pbd/tubes/exe_android/data/login/ApiService.kt index e4ca829..69161f0 100644 --- a/app/src/main/java/pbd/tubes/exe_android/data/login/ApiService.kt +++ b/app/src/main/java/pbd/tubes/exe_android/data/login/ApiService.kt @@ -1,10 +1,19 @@ package pbd.tubes.exe_android.data.login +import pbd.tubes.exe_android.data.TokenResponse import retrofit2.Response import retrofit2.http.Body +import retrofit2.http.Header import retrofit2.http.POST + interface ApiService { @POST("/api/auth/login") suspend fun login(@Body credentials: LoginRequest): Response<LoginResponse> + + @POST("/api/auth/token") + suspend fun decodeToken( + @Header("Authorization") authorization: String): Response<TokenResponse> } + + diff --git a/app/src/main/java/pbd/tubes/exe_android/ui/login/LoginActivity.kt b/app/src/main/java/pbd/tubes/exe_android/ui/login/LoginActivity.kt index d92719f..8853cb4 100644 --- a/app/src/main/java/pbd/tubes/exe_android/ui/login/LoginActivity.kt +++ b/app/src/main/java/pbd/tubes/exe_android/ui/login/LoginActivity.kt @@ -14,6 +14,10 @@ import pbd.tubes.exe_android.data.login.ApiService import pbd.tubes.exe_android.data.login.LoginRequest import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory +import androidx.appcompat.app.AlertDialog +import androidx.lifecycle.Observer +import pbd.tubes.exe_android.ui.NetworkSensing.NetworkLiveData +import pbd.tubes.exe_android.ui.login.TokenExpirationCheckService class LoginActivity : AppCompatActivity() { @@ -25,6 +29,14 @@ class LoginActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + + val networkStatusLiveData = NetworkLiveData(this) + networkStatusLiveData.observe(this, Observer { isConnected -> + if (!isConnected) { + showNoInternetPopup() + } + }) + setContentView(R.layout.activity_login) val retrofit = Retrofit.Builder() @@ -39,24 +51,41 @@ class LoginActivity : AppCompatActivity() { passwordEditText = findViewById(R.id.passwordEditText) loginButton.setOnClickListener { - val email = emailEditText.text.toString() - val password = passwordEditText.text.toString() +// val email = emailEditText.text.toString() +// val password = passwordEditText.text.toString() + val email = "1@std.stei.itb.ac.id" + val password = "password_1" val loginRequest = LoginRequest(email, password) login(loginRequest) } } - private fun login(loginRequest: LoginRequest) { CoroutineScope(Dispatchers.IO).launch { try { val response = apiService.login(loginRequest) if (response.isSuccessful) { val token = response.body()?.token - token?.let { - saveToken(it) - startActivity(Intent(this@LoginActivity, MainActivity::class.java)) - finish() + token?.let { tkn -> + + val responseDecode = apiService.decodeToken("Bearer $tkn") + + if (responseDecode.isSuccessful) { + val expire = responseDecode.body()?.exp + expire?.let {exp -> + startActivity(Intent(this@LoginActivity, MainActivity::class.java)) + saveTokenExpiration(exp) + saveToken(tkn) + val serviceIntent = Intent(this@LoginActivity, TokenExpirationCheckService::class.java) + startService(serviceIntent) + finish() + } + } + else { + Log.d("Failed Token", "Failed Get decoded token (error response)") + } + + } Toast.makeText(baseContext, "Log In Success!", @@ -78,6 +107,19 @@ class LoginActivity : AppCompatActivity() { sharedPreferences.edit().putString("token", token).apply() } + private fun saveTokenExpiration(expirationTime: Long) { + val sharedPreferences = getSharedPreferences("user_session", MODE_PRIVATE) + sharedPreferences.edit().putLong("expiration_time", expirationTime).apply() + } + + private fun showNoInternetPopup() { + AlertDialog.Builder(this) + .setTitle("No Internet Connection") + .setMessage("Please check your internet connection and try again.") + .setPositiveButton("OK", null) + .show() + } + companion object { private const val BASE_URL = "https://pbd-backend-2024.vercel.app" } diff --git a/app/src/main/java/pbd/tubes/exe_android/ui/login/TokenExpirationService.kt b/app/src/main/java/pbd/tubes/exe_android/ui/login/TokenExpirationService.kt new file mode 100644 index 0000000..5711465 --- /dev/null +++ b/app/src/main/java/pbd/tubes/exe_android/ui/login/TokenExpirationService.kt @@ -0,0 +1,49 @@ +package pbd.tubes.exe_android.ui.login +import android.app.Notification +import android.app.Service +import android.content.Intent +import android.os.Handler +import android.os.IBinder +import android.os.Looper +import android.util.Log +import pbd.tubes.exe_android.LoginActivity + +class TokenExpirationCheckService : Service() { + + private val sharedPreferences by lazy { + getSharedPreferences("user_session", MODE_PRIVATE) + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + checkTokenExpiration() + return START_STICKY + } + + private fun checkTokenExpiration() { + val expirationTime = sharedPreferences.getLong("expiration_time", 0) + val currentTime = System.currentTimeMillis() / 1000 + val timeDifference = expirationTime - currentTime +// Log.d("Difference", "difference is $timeDifference") + + if (currentTime > expirationTime) { + redirectToLogin() + stopSelf() + return + } + + Handler(Looper.getMainLooper()).postDelayed({ + checkTokenExpiration() + }, (timeDifference) * 1000) + } + + private fun redirectToLogin() { + val loginIntent = Intent(this, LoginActivity::class.java) + loginIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) + sharedPreferences.edit().clear().apply() + startActivity(loginIntent) + } + + override fun onBind(intent: Intent?): IBinder? { + return null + } +} \ No newline at end of file -- GitLab