diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index dd0109049113a2495ced8eb1ae9dce4735bb56e5..8735b0830ef76d5d228e99213ad47cb24ad7df2e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,8 @@ <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> + + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET"/> <uses-feature android:name="android.hardware.camera.any" /> <uses-permission android:name="android.permission.CAMERA" /> diff --git a/app/src/main/java/com/example/bondoman/activities/AddTransaction.kt b/app/src/main/java/com/example/bondoman/activities/AddTransaction.kt index 1d3779673f07b82b4a606adf69711f9114ecebbc..903800fa4129d63dba8df6cf422c6976fd97226c 100644 --- a/app/src/main/java/com/example/bondoman/activities/AddTransaction.kt +++ b/app/src/main/java/com/example/bondoman/activities/AddTransaction.kt @@ -2,46 +2,36 @@ package com.example.bondoman.activities import android.Manifest import android.app.Activity -import android.content.Intent -import android.os.Bundle import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import android.content.IntentFilter import android.content.pm.PackageManager import android.location.Geocoder import android.location.Location import android.os.Bundle -import android.text.TextUtils import android.util.Log import android.widget.ArrayAdapter import android.widget.Button import android.widget.EditText import android.widget.Spinner -import androidx.appcompat.app.AppCompatActivity import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import com.example.bondoman.R -import com.example.bondoman.services.TokenCheckService import com.google.android.gms.location.FusedLocationProviderClient import com.google.android.gms.location.LocationServices import java.io.IOException import java.util.Locale -class AddTransaction : AppCompatActivity() { - private lateinit var tokenExpiredReceiver: BroadcastReceiver - private lateinit var tokenServiceIntent : Intent - private var isReceiverRegistered = false +class AddTransaction : BaseActivity() { private lateinit var fusedLocationClient: FusedLocationProviderClient private lateinit var locationText : EditText + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_add_transaksi) - tokenServiceIntent= Intent(this, TokenCheckService::class.java) - startService(tokenServiceIntent) - fusedLocationClient = LocationServices.getFusedLocationProviderClient(this) + fusedLocationClient = LocationServices.getFusedLocationProviderClient(this) tokenExpiredReceiver = object : BroadcastReceiver(){ override fun onReceive(context: Context?, intent: Intent?) { if(intent != null && intent.action != null){ @@ -63,7 +53,8 @@ class AddTransaction : AppCompatActivity() { val editTextTitle = findViewById<EditText>(R.id.editTextTitle) val spinnerCategory : Spinner = findViewById(R.id.spinnerCategory) val editTextAmount = findViewById<EditText>(R.id.editTextAmount) - val editTextLocation = findViewById<EditText>(R.id.editTextLocation) + locationText = findViewById(R.id.editTextLocation) + val adapter = ArrayAdapter.createFromResource( this, @@ -78,15 +69,13 @@ class AddTransaction : AppCompatActivity() { spinnerCategory.adapter = adapter val submitButton : Button = findViewById(R.id.buttonSubmit) - locationText = findViewById(R.id.editTextLocation) submitButton.setOnClickListener { + Log.e("Submit", "Submit button is clicked") val title = editTextTitle.text.toString() val category = spinnerCategory.selectedItem.toString() - val amount = editTextAmount.text.toString().toFloatOrNull() ?: 0f - val location = editTextLocation.text.toString() val amount = findViewById<EditText>(R.id.editTextAmount).text.toString().toFloatOrNull() ?: 0f val location = locationText.text.toString() - + Log.e("Submit", "$title, $category, $amount, $location") val replyIntent = Intent() if (title.isEmpty()) { setResult(Activity.RESULT_CANCELED, replyIntent) @@ -107,29 +96,6 @@ class AddTransaction : AppCompatActivity() { finish() } - override fun onStart() { - super.onStart() - val filter = IntentFilter("com.example.bondoman.TOKEN_EXPIRED") - registerReceiver(tokenExpiredReceiver, filter) - isReceiverRegistered = true - } - - override fun onStop() { - super.onStop() - if (isReceiverRegistered) { - unregisterReceiver(tokenExpiredReceiver) - isReceiverRegistered = false - } - } - - override fun onDestroy() { - super.onDestroy() - if(isReceiverRegistered){ - unregisterReceiver(tokenExpiredReceiver) - stopService(tokenServiceIntent) - isReceiverRegistered = false - } - } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) diff --git a/app/src/main/java/com/example/bondoman/activities/BaseActivity.kt b/app/src/main/java/com/example/bondoman/activities/BaseActivity.kt new file mode 100644 index 0000000000000000000000000000000000000000..d29e3a88748019d53442ed1b8a9a901adc18d2f7 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/activities/BaseActivity.kt @@ -0,0 +1,73 @@ +package com.example.bondoman.activities + +import android.content.BroadcastReceiver +import android.content.Intent +import android.content.IntentFilter +import android.os.Bundle +import android.util.Log +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.lifecycleScope +import com.example.bondoman.interfaces.ConnectivirtyObserver +import com.example.bondoman.observer.NetworkConnectivityObserver +import com.example.bondoman.services.TokenCheckService +import kotlinx.coroutines.launch + +open class BaseActivity : AppCompatActivity() { + protected lateinit var tokenServiceIntent: Intent + protected lateinit var networkConnectivityObserver: NetworkConnectivityObserver + private var isReceiverRegistered = false + protected lateinit var tokenExpiredReceiver: BroadcastReceiver + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + Log.d("BaseActivity ", "On create") + tokenServiceIntent = Intent(this, TokenCheckService::class.java) + startService(tokenServiceIntent) + Log.d("BaseActivity :", "Service Started") + networkConnectivityObserver = NetworkConnectivityObserver(this) + observeNetworkConnectivity() + } + + protected open fun observeNetworkConnectivity() { + lifecycleScope.launch { + networkConnectivityObserver.observe().collect { status -> + if (status === ConnectivirtyObserver.Status.Unavailable || status === ConnectivirtyObserver.Status.Lost) { + showAlert() + } + } + } + } + + protected fun showAlert() { + AlertDialog.Builder(this).apply { + setTitle("Connection lost") + setMessage("You aren't connected to internet right now") + setPositiveButton("OK") { _, _ -> } + }.show() + } + + override fun onStart() { + super.onStart() + val filter = IntentFilter("com.example.bondoman.TOKEN_EXPIRED") + registerReceiver(tokenExpiredReceiver, filter) + isReceiverRegistered = true + } + + override fun onStop() { + super.onStop() + if (isReceiverRegistered) { + unregisterReceiver(tokenExpiredReceiver) + isReceiverRegistered = false + } + } + + override fun onDestroy() { + super.onDestroy() + if (isReceiverRegistered) { + unregisterReceiver(tokenExpiredReceiver) + stopService(tokenServiceIntent) + isReceiverRegistered = false + } + } +} diff --git a/app/src/main/java/com/example/bondoman/activities/EditTransaction.kt b/app/src/main/java/com/example/bondoman/activities/EditTransaction.kt index ca10024840556fb159f2170f8efe56d482afa613..679de01760c44892be4725af30832b2e569ed439 100644 --- a/app/src/main/java/com/example/bondoman/activities/EditTransaction.kt +++ b/app/src/main/java/com/example/bondoman/activities/EditTransaction.kt @@ -4,28 +4,21 @@ import android.app.Activity import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import android.content.IntentFilter import android.net.Uri import android.os.Bundle import android.util.Log import android.widget.Button import android.widget.EditText import android.widget.TextView -import androidx.appcompat.app.AppCompatActivity import com.example.bondoman.R -import com.example.bondoman.services.TokenCheckService -class EditTransaction() : AppCompatActivity() { - private lateinit var tokenExpiredReceiver: BroadcastReceiver - private lateinit var tokenServiceIntent : Intent - private var isReceiverRegistered = false +class EditTransaction() : BaseActivity() { + + private lateinit var location : EditText override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_edit_transaksi) - tokenServiceIntent= Intent(this, TokenCheckService::class.java) - startService(tokenServiceIntent) - tokenExpiredReceiver = object : BroadcastReceiver(){ override fun onReceive(context: Context?, intent: Intent?) { if(intent != null && intent.action != null){ @@ -96,29 +89,6 @@ class EditTransaction() : AppCompatActivity() { finish() } - override fun onStart() { - super.onStart() - val filter = IntentFilter("com.example.bondoman.TOKEN_EXPIRED") - registerReceiver(tokenExpiredReceiver, filter) - isReceiverRegistered = true - } - - override fun onStop() { - super.onStop() - if (isReceiverRegistered) { - unregisterReceiver(tokenExpiredReceiver) - isReceiverRegistered = false - } - } - - override fun onDestroy() { - super.onDestroy() - if(isReceiverRegistered){ - unregisterReceiver(tokenExpiredReceiver) - stopService(tokenServiceIntent) - isReceiverRegistered = false - } - } private fun seeLocation(){ val locationText = location.text.toString() @@ -127,4 +97,5 @@ class EditTransaction() : AppCompatActivity() { mapIntent.setPackage("com.google.android.apps.maps") startActivity(mapIntent) } + } \ No newline at end of file diff --git a/app/src/main/java/com/example/bondoman/activities/MainActivity.kt b/app/src/main/java/com/example/bondoman/activities/MainActivity.kt index 362f4d80ac01afe2260371c9d12f1515352a3642..8be34ac711a2d795802250987396ae78a580b433 100644 --- a/app/src/main/java/com/example/bondoman/activities/MainActivity.kt +++ b/app/src/main/java/com/example/bondoman/activities/MainActivity.kt @@ -4,28 +4,19 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter -import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.util.Log import androidx.navigation.findNavController import androidx.navigation.ui.onNavDestinationSelected import com.example.bondoman.R -import com.example.bondoman.services.TokenCheckService import com.google.android.material.bottomnavigation.BottomNavigationView -class MainActivity : AppCompatActivity() { - private lateinit var tokenExpiredReceiver: BroadcastReceiver +class MainActivity : BaseActivity() { private lateinit var randomizeReceiver: BroadcastReceiver - private lateinit var tokenServiceIntent : Intent - private var isReceiverRegistered = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) - - tokenServiceIntent= Intent(this, TokenCheckService::class.java) - startService(tokenServiceIntent) - tokenExpiredReceiver = object : BroadcastReceiver(){ override fun onReceive(context: Context?, intent: Intent?) { if(intent != null && intent.action != null){ @@ -42,13 +33,13 @@ class MainActivity : AppCompatActivity() { } } - randomizeReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { + Log.e("Randomize", "Broadcast received") if (intent?.action == "com.example.bondoman.RANDOMIZE_TRANSACTION") { val randomizeIntent = Intent(this@MainActivity, AddTransaction::class.java) randomizeIntent.putExtras(intent) - startActivity(randomizeIntent) + startActivityForResult(randomizeIntent,1) } } } @@ -62,30 +53,18 @@ class MainActivity : AppCompatActivity() { true } } - override fun onStart() { super.onStart() - val tokenIntentFilter = IntentFilter("com.example.bondoman.TOKEN_EXPIRED") - registerReceiver(tokenExpiredReceiver, tokenIntentFilter) val randomizeIntentFilter = IntentFilter("com.example.bondoman.RANDOMIZE_TRANSACTION") - registerReceiver(randomizeReceiver, randomizeIntentFilter) - isReceiverRegistered = true + registerReceiver(randomizeReceiver, randomizeIntentFilter, RECEIVER_NOT_EXPORTED) } override fun onStop() { super.onStop() - if (isReceiverRegistered) { - unregisterReceiver(tokenExpiredReceiver) - isReceiverRegistered = false - } } override fun onDestroy() { super.onDestroy() - if(isReceiverRegistered){ - unregisterReceiver(tokenExpiredReceiver) - stopService(tokenServiceIntent) - isReceiverRegistered = false - } } + } \ No newline at end of file diff --git a/app/src/main/java/com/example/bondoman/fragments/SettingsFragment.kt b/app/src/main/java/com/example/bondoman/fragments/SettingsFragment.kt index b3fef09cf849add6a337c936ecb780d8c9d94ef6..20d77ab3d52fa02b340aefa8f99c80531fa0a0b0 100644 --- a/app/src/main/java/com/example/bondoman/fragments/SettingsFragment.kt +++ b/app/src/main/java/com/example/bondoman/fragments/SettingsFragment.kt @@ -97,8 +97,10 @@ class SettingsFragment : Fragment() { handleSendButtonClick() } binding.randomizeButton.setOnClickListener { + Log.e("Random", "Randomize button is clicked") broadcastRandomizeTransaction() - binding.logoutButton.setOnClickListener{ + } + binding.logoutButton.setOnClickListener { lifecycleScope.launch { showLoading() logout() @@ -243,7 +245,7 @@ class SettingsFragment : Fragment() { randomizeIntent.putExtra("TITLE", title) randomizeIntent.putExtra("TYPE", category) randomizeIntent.putExtra("AMOUNT", amount) - + Log.e("Random", "Send Broadcast") requireActivity().sendBroadcast(randomizeIntent) } diff --git a/app/src/main/java/com/example/bondoman/fragments/TransaksiFragment.kt b/app/src/main/java/com/example/bondoman/fragments/TransaksiFragment.kt index c62887fd669ca369a7933b385c8d784adc43ade0..fe1e8330270c4f09ad068d44f8a99e3c9b4f96c0 100644 --- a/app/src/main/java/com/example/bondoman/fragments/TransaksiFragment.kt +++ b/app/src/main/java/com/example/bondoman/fragments/TransaksiFragment.kt @@ -73,7 +73,7 @@ class TransaksiFragment : Fragment() { override fun onActivityResult(requestCode: Int, resultCode: Int, intentData: Intent?) { super.onActivityResult(requestCode, resultCode, intentData) - + Log.e("Request code : ", requestCode.toString()) if (requestCode == newTransactionRequestCode && resultCode == Activity.RESULT_OK) { val title = intentData?.getStringExtra(AddTransaction.TITLE) ?: "" val amount = intentData?.getFloatExtra(AddTransaction.AMOUNT, 0.0f) ?: 0.0f diff --git a/app/src/main/java/com/example/bondoman/interfaces/ConnectivirtyObserver.kt b/app/src/main/java/com/example/bondoman/interfaces/ConnectivirtyObserver.kt new file mode 100644 index 0000000000000000000000000000000000000000..b189526810f314fc1c003163046f7788ae0fb572 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/interfaces/ConnectivirtyObserver.kt @@ -0,0 +1,12 @@ +package com.example.bondoman.interfaces + +import kotlinx.coroutines.flow.Flow + + +interface ConnectivirtyObserver { + fun observe() : Flow<Status> + + enum class Status{ + Available, Unavailable, Losing, Lost + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/bondoman/observer/NetworkConnectivityObserver.kt b/app/src/main/java/com/example/bondoman/observer/NetworkConnectivityObserver.kt new file mode 100644 index 0000000000000000000000000000000000000000..dd1ce8f86f853ec8160613f2225f880776765ec5 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/observer/NetworkConnectivityObserver.kt @@ -0,0 +1,44 @@ +package com.example.bondoman.observer + +import android.content.Context +import android.net.ConnectivityManager +import android.net.Network +import com.example.bondoman.interfaces.ConnectivirtyObserver +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.launch + +class NetworkConnectivityObserver(private val context : Context) : ConnectivirtyObserver { + private val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + override fun observe(): Flow<ConnectivirtyObserver.Status> { + return callbackFlow { + val callback = object : ConnectivityManager.NetworkCallback(){ + override fun onAvailable(network: Network) { + super.onAvailable(network) + launch { + send(ConnectivirtyObserver.Status.Available) + } + } + override fun onUnavailable() { + super.onUnavailable() + launch { + send(ConnectivirtyObserver.Status.Unavailable) + } + } + + override fun onLost(network: Network) { + super.onLost(network) + launch { + send(ConnectivirtyObserver.Status.Lost) + } + } + } + connectivityManager.registerDefaultNetworkCallback(callback) + awaitClose { + connectivityManager.unregisterNetworkCallback(callback) + } + }.distinctUntilChanged() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/bondoman/services/TokenCheckService.kt b/app/src/main/java/com/example/bondoman/services/TokenCheckService.kt index f756c3717cb0632ae2104cbc5d8ab28c89f17d54..ea5c094267d3204148cd9d9dedbf198ec053a774 100644 --- a/app/src/main/java/com/example/bondoman/services/TokenCheckService.kt +++ b/app/src/main/java/com/example/bondoman/services/TokenCheckService.kt @@ -11,50 +11,77 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import retrofit2.HttpException + +sealed class TokenValidationResult { + object Valid : TokenValidationResult() + object Invalid : TokenValidationResult() + object ConnectionError : TokenValidationResult() +} class TokenCheckService: Service() { + companion object { + @Volatile + var isOperationRunning = false + } + private val serviceScope = CoroutineScope(Dispatchers.IO) override fun onBind(intent: Intent?): IBinder? { return null } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - - serviceScope.launch { - while (true){ - val securePreferences = SecurePreferences(applicationContext) - val token = securePreferences.getToken() - val isValid = checkToken(token) - if(!isValid){ - sendTokenExpiredBroadcast() - securePreferences.clear() - stopSelf() - break - }else{ - delay(5000) + if(!isOperationRunning){ + isOperationRunning = true + serviceScope.launch { + while (true) { + val securePreferences = SecurePreferences(applicationContext) + val token = securePreferences.getToken() + when (checkToken(token)) { + TokenValidationResult.Valid -> { + Log.d("Token", "Token still valid") + delay(10000) + } + TokenValidationResult.Invalid -> { + Log.d("Token", "Token not valid") + sendTokenExpiredBroadcast() + securePreferences.clear() + stopSelf() + break + } + TokenValidationResult.ConnectionError -> { + Log.d("Token", "Connectiob error") + delay(30000) + } + } } } } + return START_STICKY } - private suspend fun checkToken(userToken : String?) : Boolean{ + + private suspend fun checkToken(userToken: String?): TokenValidationResult { val tokenService = Retro().getRetroClientInstance().create(IAuthService::class.java) return try { val response = tokenService.checkToken("Bearer $userToken") - if(response.nim == null){ - Log.d("Expired", "Token is expired") - false + if (response.nim == null) { + TokenValidationResult.Invalid + } else { + TokenValidationResult.Valid + } + } catch (e: HttpException) { + if(e.code() == 401){ + TokenValidationResult.Invalid }else{ - Log.d("Token", "Token is not expired") - true + TokenValidationResult.ConnectionError } - }catch (e : Exception){ - e.printStackTrace() - return false + TokenValidationResult.ConnectionError } } + private fun sendTokenExpiredBroadcast(){ Intent().also { intent -> intent.action = "com.example.bondoman.TOKEN_EXPIRED" diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml index 71114062d9e7c68054898799c702edb0300df9da..fad8aad8edfc0c36baaae85e4b87bd7edf1a46d2 100644 --- a/app/src/main/res/layout/fragment_settings.xml +++ b/app/src/main/res/layout/fragment_settings.xml @@ -57,12 +57,12 @@ android:textStyle="bold" /> <Button - android:id="@+id/randomize_button" + android:id="@+id/randomizeButton" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="24dp" android:background="@drawable/button_background" - android:drawableStart="@drawable/ic_randomize" + android:drawableStart="@drawable/icon_randomize" android:drawablePadding="16dp" android:paddingHorizontal="16dp" android:paddingVertical="16dp"