diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 3ce363ce09e6f8add9c547bbc22c567cd5173ab4..3218607d4337c83d955403fda438c222cf009fd3 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -48,6 +48,13 @@ dependencies { implementation("androidx.appcompat:appcompat:1.6.1") implementation("com.google.android.material:material:1.11.0") implementation("androidx.constraintlayout:constraintlayout:2.1.4") + implementation("androidx.annotation:annotation:1.7.1") + implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.7.0") + implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0") + implementation("androidx.navigation:navigation-fragment-ktx:2.7.7") + implementation("androidx.navigation:navigation-ui-ktx:2.7.7") + implementation("com.squareup.retrofit2:retrofit:2.9.0") + implementation("com.squareup.retrofit2:converter-gson:2.9.0") testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index cacc6065b2db3a227fd11d87266c8bcd99d814d8..7d66e5469809975b468b20212dd0665b0b181801 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,16 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> + <uses-feature + android:name="android.hardware.camera" + android:required="false" /> + + <uses-permission android:name="android.permission.CAMERA"/> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" + android:maxSdkVersion="32" /> + + + <uses-permission android:name="android.permission.INTERNET"/> <application android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" @@ -12,18 +22,26 @@ android:supportsRtl="true" android:theme="@style/Theme.AndroidHIT" tools:targetApi="31"> + <activity + android:name=".LoginActivity" + android:exported="false" /> <activity android:name=".DetailTransactionActivity" android:exported="false" /> <activity android:name=".MainActivity" - android:exported="true"> + android:exported="false" /> + <activity + android:name=".BondoManSplashScreen" + android:exported="true" + android:theme="@style/Theme.Design.Light.NoActionBar"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> + <service android:name=".CheckJWTBackground"/> </application> </manifest> \ No newline at end of file diff --git a/app/src/main/java/com/example/android_hit/BondoManSplashScreen.kt b/app/src/main/java/com/example/android_hit/BondoManSplashScreen.kt new file mode 100644 index 0000000000000000000000000000000000000000..be9bcb1818d35f24e99c57e12293d2150348e2c1 --- /dev/null +++ b/app/src/main/java/com/example/android_hit/BondoManSplashScreen.kt @@ -0,0 +1,22 @@ +package com.example.android_hit + +import android.content.Intent +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import java.util.Timer +import java.util.TimerTask + +class BondoManSplashScreen : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_bondo_man_splash_screen) + + Timer().schedule(object : TimerTask() { + override fun run() { + startActivity(Intent(this@BondoManSplashScreen, LoginActivity::class.java)) + finish() + } + }, 3000) + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/android_hit/CheckJWTBackground.kt b/app/src/main/java/com/example/android_hit/CheckJWTBackground.kt new file mode 100644 index 0000000000000000000000000000000000000000..086e41f8431f800ed65fa1813b134e68a284ba34 --- /dev/null +++ b/app/src/main/java/com/example/android_hit/CheckJWTBackground.kt @@ -0,0 +1,65 @@ +package com.example.android_hit + +import android.app.Service +import android.content.Intent +import android.os.Handler +import android.os.IBinder +import android.util.Log +import com.example.android_hit.utils.TokenManager +import retrofit2.Response +import kotlin.concurrent.thread + +class CheckJWTBackground:Service() { + private lateinit var sharedPref : TokenManager + private lateinit var handler: Handler + override fun onBind(intent: Intent?): IBinder? { + return null + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + sharedPref = TokenManager(this) + handler = Handler() + thread { + while (true) { + try { + Thread.sleep(2000) + + // Perform your task here + val exp = sharedPref.getEXP("EXP_TIME") + val currentTime = System.currentTimeMillis()/1000 + Log.d("THREAD", exp.toString()) + Log.d("THREAD", currentTime.toString()) + Log.e("THREAD", (exp-currentTime).toString()) + if(currentTime>exp){ + handler.post { + showConfirmationBox() + } + sharedPref.deleteToken() + stopSelf() + return@thread + } + + } catch (e: InterruptedException) { + return@thread + } + } + } + return START_STICKY + } + + override fun onDestroy() { + super.onDestroy() + } + + private fun showConfirmationBox() { + val intent = Intent(applicationContext, MainActivity::class.java) + intent.putExtra("show_confirmation", true) + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + startActivity(intent) + } + + override fun onTaskRemoved(rootIntent: Intent?) { + super.onTaskRemoved(rootIntent) + stopSelf() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/android_hit/LoginActivity.kt b/app/src/main/java/com/example/android_hit/LoginActivity.kt new file mode 100644 index 0000000000000000000000000000000000000000..7b69bc279f72626a9c3db7ca424174fc2c4917f9 --- /dev/null +++ b/app/src/main/java/com/example/android_hit/LoginActivity.kt @@ -0,0 +1,48 @@ +package com.example.android_hit + +import android.content.Intent +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import android.view.View +import android.widget.Button +import androidx.fragment.app.FragmentManager +import com.example.android_hit.utils.TokenManager + +class LoginActivity : AppCompatActivity() { + + private lateinit var fragmentManager : FragmentManager + private lateinit var toLoginPageButton : Button + private lateinit var sharedPref : TokenManager + + private fun initComponent(){ + toLoginPageButton = findViewById(R.id.toLoginPageButton) + } + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_login) + initComponent() + sharedPref = TokenManager(this) + + + //Cek kalo user dah login + val isLogin = sharedPref.isLogin("IS_LOGIN") + if(isLogin){ + //nyalain lagi background service trus dilempar ke page home + val serviceIntent = Intent(this, CheckJWTBackground::class.java) + this.startService(serviceIntent) + goToHome() + } + + toLoginPageButton.setOnClickListener { + it.visibility = View.GONE + fragmentManager = supportFragmentManager + fragmentManager.beginTransaction().replace(R.id.loginActivity, LoginFragment()).commit() + + } + + } + private fun goToHome(){ + val intent = Intent(this, MainActivity::class.java) + startActivity(intent) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/android_hit/LoginFragment.kt b/app/src/main/java/com/example/android_hit/LoginFragment.kt new file mode 100644 index 0000000000000000000000000000000000000000..b20d4e9a4f6d1429b885a8bc0596edd59e6f66d9 --- /dev/null +++ b/app/src/main/java/com/example/android_hit/LoginFragment.kt @@ -0,0 +1,138 @@ +package com.example.android_hit + +import android.annotation.SuppressLint +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.util.Log +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Button +import android.widget.EditText +import android.widget.Toast +import com.example.android_hit.api.RetrofitClient +import com.example.android_hit.data.LoginPayload +import com.example.android_hit.data.LoginResponse +import com.example.android_hit.data.TokenResponse +import com.example.android_hit.utils.CryptoManager +import com.example.android_hit.utils.TokenManager +import com.example.android_hit.utils.UserManager +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response + + +class LoginFragment : Fragment() { + + private lateinit var emailInput : EditText + private lateinit var passwordInput : EditText + private lateinit var loginButton : Button + + private lateinit var sharedPref : TokenManager + private lateinit var userPref : UserManager + + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.fragment_login, container, false) + } + + + @SuppressLint("UseRequireInsteadOfGet") + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + sharedPref = this.context?.let { TokenManager(it) }!! + userPref = this.context?.let {UserManager(it)}!! + val cryptoManager = CryptoManager() + emailInput = view.findViewById(R.id.emailInputField) + passwordInput = view.findViewById(R.id.passwordInputField) + loginButton = view.findViewById(R.id.loginButton) + + val isLogin = sharedPref.isLogin("IS_LOGIN") + if(isLogin){ + goToHome() + } + + loginButton.setOnClickListener { + val emailValue = emailInput.text.toString() + val passwordValue = passwordInput.text.toString() + val passEncrypt = cryptoManager.encrypt(passwordValue) + userPref.putEmail("EMAIL", emailValue) + userPref.putPassword("PASS", passEncrypt) + val data = LoginPayload(emailValue,passwordValue) + login(data) + + } + } + + private fun goToHome(){ + val intent = Intent(activity, MainActivity::class.java) + startActivity(intent) + } + + + //tembak api login + private fun login(data:LoginPayload){ + RetrofitClient.apiService.login(data).enqueue(object : Callback<LoginResponse> { + override fun onResponse(call: Call<LoginResponse>, response: Response<LoginResponse>) { + if (response.isSuccessful) { + val responseBody = response.body() + sharedPref.putToken("TOKEN", responseBody!!.token) + sharedPref.putIsLogin("IS_LOGIN",true) + Toast.makeText(context, "Login Success", Toast.LENGTH_SHORT).show() + + checkToken(responseBody.token) + val serviceIntent = Intent(context, CheckJWTBackground::class.java) + context?.startService(serviceIntent) + goToHome() + + + } else { + val errMes = response.errorBody()?.string() + Log.e("POST Error", "Failed to make POST request: $errMes") + Toast.makeText(context, "Login Failed: $errMes", Toast.LENGTH_SHORT).show() + } + } + override fun onFailure(call: Call<LoginResponse>, t: Throwable) { + Log.e("POST Error", "Failed to make POST request: ${t.message}") + } + }) + } + + //tembak api auth/token + private fun checkToken(token: String){ + RetrofitClient.apiService.cekToken(token = "Bearer $token").enqueue(object : Callback<TokenResponse> { + override fun onResponse(call: Call<TokenResponse>, response: Response<TokenResponse>) { + if (response.isSuccessful) { + // Handle successful response + val responseData = response.body() + if (responseData != null) { + sharedPref.putNIM("NIM_USER",responseData.nim) + Log.i("TOKEN1", responseData.nim) + } + if (responseData != null) { + sharedPref.putEXP("EXP_TIME",responseData.exp) + Log.i("TOKEN1", responseData.exp.toString()) + } + + + } else { + response.errorBody()?.let { it1 -> Log.i("TOKEN1", it1.string()) } + } + } + + override fun onFailure(call: Call<TokenResponse>, t: Throwable) { + // Handle failure + // This method is called when the HTTP call fails + } + }) + } + + + + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/android_hit/MainActivity.kt b/app/src/main/java/com/example/android_hit/MainActivity.kt index e2845ebde41b54314e7d3f4f947a93e5ebe73421..1d087c381fe8006106a27aa403462496e6511315 100644 --- a/app/src/main/java/com/example/android_hit/MainActivity.kt +++ b/app/src/main/java/com/example/android_hit/MainActivity.kt @@ -1,15 +1,36 @@ package com.example.android_hit +import android.app.AlertDialog +import android.content.DialogInterface +import android.content.Intent import androidx.appcompat.app.AppCompatActivity import android.os.Bundle +import android.util.Log +import android.widget.Toast import androidx.fragment.app.Fragment +import com.example.android_hit.api.RetrofitClient +import com.example.android_hit.data.LoginPayload +import com.example.android_hit.data.LoginResponse +import com.example.android_hit.data.TokenResponse import com.example.android_hit.databinding.ActivityMainBinding +import com.example.android_hit.utils.CryptoManager +import com.example.android_hit.utils.TokenManager +import com.example.android_hit.utils.UserManager +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding + private lateinit var userPref : UserManager + private lateinit var sharedPref : TokenManager + private lateinit var cryptoManager: CryptoManager override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) + userPref = UserManager(this) + sharedPref = TokenManager(this) + cryptoManager = CryptoManager() setContentView(binding.root) setCurrentFragment(Transaction(), HeaderTransaction()) @@ -31,6 +52,13 @@ class MainActivity : AppCompatActivity() { true } + //Keluarin pop up kalo token dah abis, sama matiin background service + if (intent.getBooleanExtra("show_confirmation", false)) { + val serviceIntent = Intent(this, CheckJWTBackground::class.java) + this.stopService(serviceIntent) + showConfirmationDialog() + } + } private fun setCurrentFragment(fragment: Fragment, header: Fragment) = @@ -39,5 +67,90 @@ class MainActivity : AppCompatActivity() { replace(R.id.header_layout, header) commit() } + private fun showConfirmationDialog() { + val alertDialogBuilder: AlertDialog.Builder = AlertDialog.Builder(this) + alertDialogBuilder.setTitle("Token Expired") + alertDialogBuilder.setMessage("Your token has expired. Do you want to renew it?") + alertDialogBuilder.setCancelable(false) + + alertDialogBuilder.setPositiveButton("Yes") { _, _ -> + reLogin() + finish() + } + + alertDialogBuilder.setNegativeButton("No") { dialog, _ -> + goToStart() + dialog.dismiss() + } + + val alertDialog: AlertDialog = alertDialogBuilder.create() + alertDialog.show() + } + + private fun goToStart(){ + val intent = Intent(this, LoginActivity::class.java) + startActivity(intent) + } + + private fun reLogin(){ + val emailValue = userPref.getEmail("EMAIL")!! + val passwordValue = userPref.getPassword("PASS")!! + val passDecrpt = cryptoManager.decrypt(passwordValue) + Log.e("RELOG", "email $emailValue") + Log.e("RELOG", "pass $passwordValue") + Log.e("RELOG","passd $passDecrpt") + + val data = LoginPayload(emailValue,passDecrpt) + RetrofitClient.apiService.login(data).enqueue(object : Callback<LoginResponse> { + override fun onResponse(call: Call<LoginResponse>, response: Response<LoginResponse>) { + if (response.isSuccessful) { + val responseBody = response.body() + sharedPref.putToken("TOKEN", responseBody!!.token) + sharedPref.putIsLogin("IS_LOGIN",true) + Toast.makeText(this@MainActivity, "Login Success", Toast.LENGTH_SHORT).show() + + checkToken(responseBody.token) + val serviceIntent = Intent(this@MainActivity, CheckJWTBackground::class.java) + this@MainActivity.startService(serviceIntent) + } else { + Log.e("POST Error", "Failed to make POST request: ${response.message()}") + Toast.makeText(this@MainActivity, "Login Failed: ${response.message()}", Toast.LENGTH_SHORT).show() + } + } + override fun onFailure(call: Call<LoginResponse>, t: Throwable) { + Log.e("POST Error", "Failed to make POST request: ${t.message}") + } + }) + + Log.e("RELOG","test 123") + } + + private fun checkToken(token: String){ + RetrofitClient.apiService.cekToken(token = "Bearer $token").enqueue(object : Callback<TokenResponse> { + override fun onResponse(call: Call<TokenResponse>, response: Response<TokenResponse>) { + if (response.isSuccessful) { + // Handle successful response + val responseData = response.body() + if (responseData != null) { + sharedPref.putNIM("NIM_USER",responseData.nim) + Log.i("TOKEN1", responseData.nim) + } + if (responseData != null) { + sharedPref.putEXP("EXP_TIME",responseData.exp) + Log.i("TOKEN1", responseData.exp.toString()) + } + + + } else { + response.errorBody()?.let { it1 -> Log.i("TOKEN1", it1.string()) } + } + } + + override fun onFailure(call: Call<TokenResponse>, t: Throwable) { + // Handle failure + // This method is called when the HTTP call fails + } + }) + } } diff --git a/app/src/main/java/com/example/android_hit/Scan.kt b/app/src/main/java/com/example/android_hit/Scan.kt index d93f4a607afd95f87cd26f347c19f821e779f517..faa0d42b9d84f915ce8f866c5d6005eabfd7977f 100644 --- a/app/src/main/java/com/example/android_hit/Scan.kt +++ b/app/src/main/java/com/example/android_hit/Scan.kt @@ -1,10 +1,23 @@ package com.example.android_hit +import android.Manifest +import android.app.Activity +import android.content.Intent +import android.content.pm.PackageManager +import android.graphics.Bitmap +import android.graphics.ImageDecoder +import android.net.Uri +import android.os.Build import android.os.Bundle -import androidx.fragment.app.Fragment +import android.provider.MediaStore import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Button +import android.widget.ImageView +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat +import androidx.fragment.app.Fragment // TODO: Rename parameter arguments, choose names that match // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER @@ -18,14 +31,20 @@ private const val ARG_PARAM2 = "param2" */ class Scan : Fragment() { // TODO: Rename and change types of parameters - private var param1: String? = null - private var param2: String? = null + var pickedPhoto : Uri? = null + var pickedBitMap : Bitmap? = null + private val CAMERA_REQUEST_CODE = 1 + private lateinit var btnCapture : Button + private lateinit var btnPick : Button + private lateinit var ivPicture : ImageView + + + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) arguments?.let { - param1 = it.getString(ARG_PARAM1) - param2 = it.getString(ARG_PARAM2) + } } @@ -34,9 +53,80 @@ class Scan : Fragment() { savedInstanceState: Bundle? ): View? { // Inflate the layout for this fragment + return inflater.inflate(R.layout.fragment_scan, container, false) } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + btnCapture = view.findViewById(R.id.captureImgBtn) + ivPicture = view.findViewById(R.id.captureImageView) + btnPick = view.findViewById(R.id.pickImgBtn) + btnCapture.isEnabled = true + + if(ActivityCompat.checkSelfPermission(requireContext(), android.Manifest.permission.CAMERA) + != PackageManager.PERMISSION_GRANTED){ + ActivityCompat.requestPermissions(requireActivity(), arrayOf(Manifest.permission.CAMERA), + 100) + } else{ + btnCapture.isEnabled = true + } + btnPick.setOnClickListener { + pickedPhoto() + } + + + btnCapture.setOnClickListener{ + val i = Intent(MediaStore.ACTION_IMAGE_CAPTURE) + startActivityForResult(i, 101) + } + + } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if(requestCode == 101){ + var pic : Bitmap? = data?.getParcelableExtra<Bitmap>("data") + ivPicture.setImageBitmap(pic) + } + if(requestCode == 2 && resultCode == Activity.RESULT_OK && data != null){ + pickedPhoto = data.data + if(Build.VERSION.SDK_INT>=20){ + val source = ImageDecoder.createSource(requireContext().contentResolver, pickedPhoto!!) + pickedBitMap = ImageDecoder.decodeBitmap(source) + ivPicture.setImageBitmap(pickedBitMap) + }else{ + pickedBitMap = MediaStore.Images.Media.getBitmap(requireContext().contentResolver, pickedPhoto) + ivPicture.setImageBitmap(pickedBitMap) + } + } + + } + + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array<out String>, + grantResults: IntArray + ) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + if(requestCode==100 && grantResults[0] == PackageManager.PERMISSION_GRANTED){ + btnCapture.isEnabled = true + } + if(grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){ + val galleryIntext = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI) + startActivityForResult(galleryIntext, 2) + + } + } + fun pickedPhoto() { + if(ContextCompat.checkSelfPermission(requireContext(), android.Manifest.permission.READ_EXTERNAL_STORAGE) + != PackageManager.PERMISSION_GRANTED){ + ActivityCompat.requestPermissions(requireActivity(), arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), + 1) + } else{ + val galleryIntext = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI) + startActivityForResult(galleryIntext, 2) + } + } companion object { /** * Use this factory method to create a new instance of @@ -46,6 +136,7 @@ class Scan : Fragment() { * @param param2 Parameter 2. * @return A new instance of fragment Scan. */ + // TODO: Rename and change types and number of parameters @JvmStatic fun newInstance(param1: String, param2: String) = diff --git a/app/src/main/java/com/example/android_hit/Settings.kt b/app/src/main/java/com/example/android_hit/Settings.kt index 33ab42eefbc2db461eb0e6443e27f5fd2ff52292..a8e9e26fe8a8854a848e9441e3651e70d525cfdd 100644 --- a/app/src/main/java/com/example/android_hit/Settings.kt +++ b/app/src/main/java/com/example/android_hit/Settings.kt @@ -1,10 +1,19 @@ package com.example.android_hit +import android.annotation.SuppressLint +import android.app.AlertDialog +import android.content.DialogInterface +import android.content.Intent import android.os.Bundle +import android.util.Log import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Button +import android.widget.TextView +import com.example.android_hit.utils.TokenManager +import com.example.android_hit.utils.UserManager // TODO: Rename parameter arguments, choose names that match // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER @@ -20,6 +29,10 @@ class Settings : Fragment() { // TODO: Rename and change types of parameters private var param1: String? = null private var param2: String? = null + private lateinit var sharedPref : TokenManager + private lateinit var logoutButton : Button + private lateinit var emailTextView : TextView + private lateinit var user: UserManager override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -29,31 +42,51 @@ class Settings : Fragment() { } } + @SuppressLint("UseRequireInsteadOfGet", "MissingInflatedId") override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { + val view = inflater.inflate(R.layout.fragment_settings, container, false) // Inflate the layout for this fragment - return inflater.inflate(R.layout.fragment_settings, container, false) - } + sharedPref = this.context?.let { TokenManager(it) }!! + user = this.context?.let { UserManager(it) }!! + logoutButton = view.findViewById(R.id.logoutButton) + emailTextView = view.findViewById(R.id.emailTextView) + + emailTextView.text = user.getEmail("EMAIL") + Log.e("SET","masuk sini") + + logoutButton.setOnClickListener { + showConfirmationDialog() - companion object { - /** - * Use this factory method to create a new instance of - * this fragment using the provided parameters. - * - * @param param1 Parameter 1. - * @param param2 Parameter 2. - * @return A new instance of fragment Settings. - */ - // TODO: Rename and change types and number of parameters - @JvmStatic - fun newInstance(param1: String, param2: String) = - Settings().apply { - arguments = Bundle().apply { - putString(ARG_PARAM1, param1) - putString(ARG_PARAM2, param2) - } + } + return view + } + private fun showConfirmationDialog() { + val alertDialogBuilder = AlertDialog.Builder(this.context) + alertDialogBuilder.apply { + setTitle("Confirmation") + setMessage("Are you sure you want to logout?") + setPositiveButton("Yes") { dialogInterface: DialogInterface, _: Int -> + Log.e("SET","masuk sini 2") + sharedPref.deleteToken() + goToStart() + dialogInterface.dismiss() + } + setNegativeButton("No") { dialogInterface: DialogInterface, _: Int -> + dialogInterface.dismiss() } + setCancelable(false) + } + + val alertDialog = alertDialogBuilder.create() + alertDialog.show() } + + private fun goToStart(){ + val intent = Intent(activity, LoginActivity::class.java) + startActivity(intent) + } + } \ No newline at end of file diff --git a/app/src/main/java/com/example/android_hit/api/ApiService.kt b/app/src/main/java/com/example/android_hit/api/ApiService.kt new file mode 100644 index 0000000000000000000000000000000000000000..b5a69afdc173f3c83f0166289b1b6d307341cee3 --- /dev/null +++ b/app/src/main/java/com/example/android_hit/api/ApiService.kt @@ -0,0 +1,21 @@ +package com.example.android_hit.api + +import com.example.android_hit.data.LoginPayload +import com.example.android_hit.data.LoginResponse +import com.example.android_hit.data.TokenResponse +import retrofit2.Call +import retrofit2.http.Body +import retrofit2.http.Header +import retrofit2.http.Headers +import retrofit2.http.POST + +interface ApiService { + + @POST("/api/auth/login") + fun login(@Body postData: LoginPayload): Call<LoginResponse> + + + + @POST("/api/auth/token") + fun cekToken(@Header("Authorization") token: String): Call<TokenResponse> +} \ No newline at end of file diff --git a/app/src/main/java/com/example/android_hit/api/RetrofitClient.kt b/app/src/main/java/com/example/android_hit/api/RetrofitClient.kt new file mode 100644 index 0000000000000000000000000000000000000000..02cd629ef6a97b3f61b8c756a9ce691d88a430ae --- /dev/null +++ b/app/src/main/java/com/example/android_hit/api/RetrofitClient.kt @@ -0,0 +1,17 @@ +package com.example.android_hit.api + +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory + +object RetrofitClient { + private const val BASE_URL = "https://pbd-backend-2024.vercel.app" + + val apiService: ApiService by lazy { + val retrofit = Retrofit.Builder() + .baseUrl(BASE_URL) + .addConverterFactory(GsonConverterFactory.create()) + .build() + + retrofit.create(ApiService::class.java) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/android_hit/data/LoginPayload.kt b/app/src/main/java/com/example/android_hit/data/LoginPayload.kt new file mode 100644 index 0000000000000000000000000000000000000000..6552e6fde150f997d35a7511fc4ea86d46119434 --- /dev/null +++ b/app/src/main/java/com/example/android_hit/data/LoginPayload.kt @@ -0,0 +1,6 @@ +package com.example.android_hit.data + +data class LoginPayload( + val email: String, + val password : String +) diff --git a/app/src/main/java/com/example/android_hit/data/LoginResponse.kt b/app/src/main/java/com/example/android_hit/data/LoginResponse.kt new file mode 100644 index 0000000000000000000000000000000000000000..772ed288cccefad5b351d23e5aa3855bb0e0c41d --- /dev/null +++ b/app/src/main/java/com/example/android_hit/data/LoginResponse.kt @@ -0,0 +1,5 @@ +package com.example.android_hit.data + +data class LoginResponse( + val token: String +) diff --git a/app/src/main/java/com/example/android_hit/data/TokenResponse.kt b/app/src/main/java/com/example/android_hit/data/TokenResponse.kt new file mode 100644 index 0000000000000000000000000000000000000000..ebd38c869913d6a31f93626ad173edf53f59da9f --- /dev/null +++ b/app/src/main/java/com/example/android_hit/data/TokenResponse.kt @@ -0,0 +1,7 @@ +package com.example.android_hit.data + +data class TokenResponse( + val nim : String, + val iat : Int, + val exp : Int +) diff --git a/app/src/main/java/com/example/android_hit/data/model/LoggedInUser.kt b/app/src/main/java/com/example/android_hit/data/model/LoggedInUser.kt new file mode 100644 index 0000000000000000000000000000000000000000..031ed8363c9092ae47a458a8ee97dd819c2d6d3e --- /dev/null +++ b/app/src/main/java/com/example/android_hit/data/model/LoggedInUser.kt @@ -0,0 +1,9 @@ +package com.example.android_hit.data.model + +/** + * Data class that captures user information for logged in users retrieved from LoginRepository + */ +data class LoggedInUser( + val userId: String, + val displayName: String +) \ No newline at end of file diff --git a/app/src/main/java/com/example/android_hit/utils/Constant.kt b/app/src/main/java/com/example/android_hit/utils/Constant.kt new file mode 100644 index 0000000000000000000000000000000000000000..821e8dd67a87cda38b8a21454a1a0ebc7397c091 --- /dev/null +++ b/app/src/main/java/com/example/android_hit/utils/Constant.kt @@ -0,0 +1,8 @@ +package com.example.android_hit.utils + +class Constant { + companion object{ + val IS_LOGIN = "PREF_IS_LOGIN" + val TOKEN = "TOKEN" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/android_hit/utils/CryptoManager.kt b/app/src/main/java/com/example/android_hit/utils/CryptoManager.kt new file mode 100644 index 0000000000000000000000000000000000000000..3b7efc4a5965cf99a2d1a26773684bb95933760f --- /dev/null +++ b/app/src/main/java/com/example/android_hit/utils/CryptoManager.kt @@ -0,0 +1,84 @@ +package com.example.android_hit.utils + +import android.security.keystore.KeyGenParameterSpec +import android.security.keystore.KeyProperties +import android.util.Base64 +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.nio.charset.StandardCharsets +import java.security.KeyStore +import javax.crypto.Cipher +import javax.crypto.KeyGenerator +import javax.crypto.SecretKey +import javax.crypto.spec.IvParameterSpec + +class CryptoManager { + + private val keyStore = KeyStore.getInstance("AndroidKeyStore").apply { + load(null) + } + fun encrypt(data: String): String { + val key = getKey() + val cipher = Cipher.getInstance(TRANSFORMATION) + cipher.init(Cipher.ENCRYPT_MODE, key) + + val byteData = data.toByteArray(StandardCharsets.UTF_8) + val encryptedData = cipher.doFinal(byteData) + val iv = cipher.iv + + // Combine IV and encrypted data for transmission/storage (optional) + val combinedData = ByteArrayOutputStream().apply { + write(iv) + write(encryptedData) + }.toByteArray() + + // Encode bytes to Base64 for easier handling (optional) + return Base64.encodeToString(combinedData, Base64.NO_WRAP) + } + + fun decrypt(data: String): String { + val combinedData = Base64.decode(data, Base64.NO_WRAP) + + val inputStream = ByteArrayInputStream(combinedData) + val iv = ByteArray(IV_LENGTH) + inputStream.read(iv) + val encryptedData = inputStream.readBytes() + + val key = getKey() + val cipher = Cipher.getInstance(TRANSFORMATION) + val ivSpec = IvParameterSpec(iv) + cipher.init(Cipher.DECRYPT_MODE, key, ivSpec) + + val decryptedData = cipher.doFinal(encryptedData) + + return String(decryptedData, StandardCharsets.UTF_8) + } + private fun getKey(): SecretKey { + val existingKey = keyStore.getEntry("secret", null) as? KeyStore.SecretKeyEntry + return existingKey?.secretKey ?: createKey() + } + + private fun createKey(): SecretKey { + return KeyGenerator.getInstance(ALGORITHM).apply { + init( + KeyGenParameterSpec.Builder( + "secret", + KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT + ) + .setBlockModes(BLOCK_MODE) + .setEncryptionPaddings(PADDING) + .setUserAuthenticationRequired(false) + .setRandomizedEncryptionRequired(true) + .build() + ) + }.generateKey() + } + + companion object { + private const val ALGORITHM = KeyProperties.KEY_ALGORITHM_AES + private const val BLOCK_MODE = KeyProperties.BLOCK_MODE_CBC + private const val PADDING = KeyProperties.ENCRYPTION_PADDING_PKCS7 + private const val TRANSFORMATION = "$ALGORITHM/$BLOCK_MODE/$PADDING" + private const val IV_LENGTH = 16 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/android_hit/utils/TokenManager.kt b/app/src/main/java/com/example/android_hit/utils/TokenManager.kt new file mode 100644 index 0000000000000000000000000000000000000000..7eb358d9745d69d296cb8fe60be588e80ffb52bd --- /dev/null +++ b/app/src/main/java/com/example/android_hit/utils/TokenManager.kt @@ -0,0 +1,59 @@ +package com.example.android_hit.utils + +import android.content.Context +import android.content.SharedPreferences + +class TokenManager( context: Context) { + private val PREF_NAME = "sharedPrefBondoMan" + private val sharedPref : SharedPreferences + val editor : SharedPreferences.Editor + + init{ + sharedPref = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) + editor = sharedPref.edit() + } + + fun putToken(key:String, value:String){ + editor.putString(key, value).apply() + } + + fun getToken(key: String) : String?{ + return sharedPref.getString(key,null) + } + + fun putIsLogin(key: String, value:Boolean){ + editor.putBoolean(key,value).apply() + } + + fun isLogin(key:String):Boolean{ + return sharedPref.getBoolean(key,false) + } + + fun putNIM(key: String, value:String){ + editor.putString(key, value).apply() + } + + fun getNIM(key: String):String?{ + return sharedPref.getString(key,null) + } + + fun putIAT(key: String, value: Int){ + editor.putInt(key, value).apply() + } + + fun getIAT(key: String):Int{ + return sharedPref.getInt(key,0) + } + + fun putEXP(key: String, value: Int){ + editor.putInt(key, value).apply() + } + + fun getEXP(key: String):Int{ + return sharedPref.getInt(key,0) + } + + fun deleteToken(){ + editor.clear().apply() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/android_hit/utils/UserManager.kt b/app/src/main/java/com/example/android_hit/utils/UserManager.kt new file mode 100644 index 0000000000000000000000000000000000000000..3808963cc420ded4b27affab20584170ac856dcc --- /dev/null +++ b/app/src/main/java/com/example/android_hit/utils/UserManager.kt @@ -0,0 +1,35 @@ +package com.example.android_hit.utils + +import android.content.Context +import android.content.SharedPreferences + +class UserManager( context: Context) { + private val PREF_NAME = "sharedPrefUserBondoMan" + private val sharedPref : SharedPreferences + val editor : SharedPreferences.Editor + + init{ + sharedPref = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) + editor = sharedPref.edit() + } + + fun putEmail(key:String, value:String){ + editor.putString(key, value).apply() + } + + fun getEmail(key: String) : String?{ + return sharedPref.getString(key,null) + } + + fun putPassword(key: String, value:String){ + editor.putString(key, value).apply() + } + + fun getPassword(key: String):String?{ + return sharedPref.getString(key,null) + } + + fun deleteUser(){ + editor.clear().apply() + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/background_gradient_splashscreen.xml b/app/src/main/res/drawable/background_gradient_splashscreen.xml new file mode 100644 index 0000000000000000000000000000000000000000..223444029fd6dde386a8e568cb49c08ee8956002 --- /dev/null +++ b/app/src/main/res/drawable/background_gradient_splashscreen.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <gradient + android:type="linear" + android:startColor="#B1B8D8" + android:centerColor="@color/white" + android:endColor="#B1B8D8" + android:angle="90"/> +</shape> \ No newline at end of file diff --git a/app/src/main/res/drawable/background_rounded_peach.xml b/app/src/main/res/drawable/background_rounded_peach.xml new file mode 100644 index 0000000000000000000000000000000000000000..632b86457a90abc4e6f0379edd8b07894020daa9 --- /dev/null +++ b/app/src/main/res/drawable/background_rounded_peach.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <corners android:radius="14dp" /> + <solid android:color="#EFDAC7" /> +</shape> \ No newline at end of file diff --git a/app/src/main/res/drawable/background_rounded_purple.xml b/app/src/main/res/drawable/background_rounded_purple.xml new file mode 100644 index 0000000000000000000000000000000000000000..202fdc493c549f80d5e7cbf66990340a8132b03f --- /dev/null +++ b/app/src/main/res/drawable/background_rounded_purple.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <corners android:radius="14dp" /> + <solid android:color="#B1B8D8" /> +</shape> \ No newline at end of file diff --git a/app/src/main/res/drawable/baseline_lock_24.xml b/app/src/main/res/drawable/baseline_lock_24.xml new file mode 100644 index 0000000000000000000000000000000000000000..57faa5bcd137c891f39068be9c535775c4a12d32 --- /dev/null +++ b/app/src/main/res/drawable/baseline_lock_24.xml @@ -0,0 +1,5 @@ +<vector android:height="24dp" android:tint="#322F50" + android:viewportHeight="24" android:viewportWidth="24" + android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> + <path android:fillColor="@android:color/white" android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM12,17c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM15.1,8L8.9,8L8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z"/> +</vector> diff --git a/app/src/main/res/drawable/baseline_mail_outline_24.xml b/app/src/main/res/drawable/baseline_mail_outline_24.xml new file mode 100644 index 0000000000000000000000000000000000000000..c1209f24816b8ee7f1267186890b9dd21b52eb30 --- /dev/null +++ b/app/src/main/res/drawable/baseline_mail_outline_24.xml @@ -0,0 +1,5 @@ +<vector android:height="24dp" android:tint="#322F50" + android:viewportHeight="24" android:viewportWidth="24" + android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> + <path android:fillColor="@android:color/white" android:pathData="M20,4L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM20,18L4,18L4,8l8,5 8,-5v10zM12,11L4,6h16l-8,5z"/> +</vector> diff --git a/app/src/main/res/drawable/baseline_save_alt_24.xml b/app/src/main/res/drawable/baseline_save_alt_24.xml new file mode 100644 index 0000000000000000000000000000000000000000..d1d7fbfcc0a4c777d3e45ad03f44e54576ce5d6f --- /dev/null +++ b/app/src/main/res/drawable/baseline_save_alt_24.xml @@ -0,0 +1,5 @@ +<vector android:height="24dp" android:tint="#322F50" + android:viewportHeight="24" android:viewportWidth="24" + android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> + <path android:fillColor="@android:color/white" android:pathData="M19,12v7L5,19v-7L3,12v7c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2v-7h-2zM13,12.67l2.59,-2.58L17,11.5l-5,5 -5,-5 1.41,-1.41L11,12.67L11,3h2z"/> +</vector> diff --git a/app/src/main/res/drawable/gallery.xml b/app/src/main/res/drawable/gallery.xml new file mode 100644 index 0000000000000000000000000000000000000000..6867b9f0863fa299fbd7d9dc488bb66af644b6a8 --- /dev/null +++ b/app/src/main/res/drawable/gallery.xml @@ -0,0 +1,28 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="29dp" + android:height="28dp" + android:viewportWidth="29" + android:viewportHeight="28"> + <group> + <clip-path + android:pathData="M0.5,0h28v28h-28z"/> + <path + android:pathData="M5.167,0.933H23.833C24.823,0.933 25.773,1.327 26.473,2.027C27.173,2.727 27.567,3.677 27.567,4.667V23.333C27.567,24.323 27.173,25.273 26.473,25.973C25.773,26.673 24.823,27.067 23.833,27.067H5.167C4.177,27.067 3.227,26.673 2.527,25.973C1.827,25.273 1.433,24.323 1.433,23.333V4.667C1.433,3.677 1.827,2.727 2.527,2.027C3.227,1.327 4.177,0.933 5.167,0.933Z" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:fillType="evenOdd" + android:strokeColor="#322F50" + android:strokeLineCap="round"/> + <path + android:pathData="M27.567,19.6L21.967,14L16.367,19.572M23.833,27.067L7.033,10.267L1.433,15.867" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="#322F50" + android:strokeLineCap="round"/> + <path + android:pathData="M21.033,9.333C22.064,9.333 22.9,8.498 22.9,7.467C22.9,6.436 22.064,5.6 21.033,5.6C20.002,5.6 19.167,6.436 19.167,7.467C19.167,8.498 20.002,9.333 21.033,9.333Z" + android:fillColor="#322F50"/> + </group> +</vector> diff --git a/app/src/main/res/drawable/icon_awal.png b/app/src/main/res/drawable/icon_awal.png new file mode 100644 index 0000000000000000000000000000000000000000..a1d2c528d513eeadf22b83b1c768e87cabd373e4 Binary files /dev/null and b/app/src/main/res/drawable/icon_awal.png differ diff --git a/app/src/main/res/drawable/icon_login.png b/app/src/main/res/drawable/icon_login.png new file mode 100644 index 0000000000000000000000000000000000000000..b768fa86ff60b4331f9b956974aadd36b1d8f9c7 Binary files /dev/null and b/app/src/main/res/drawable/icon_login.png differ diff --git a/app/src/main/res/drawable/iv_picture.xml b/app/src/main/res/drawable/iv_picture.xml new file mode 100644 index 0000000000000000000000000000000000000000..128c4ec1c0ff536f5cdf295d26d928ab0c458f10 --- /dev/null +++ b/app/src/main/res/drawable/iv_picture.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <corners android:radius="20dp" /> + <stroke + android:width="2dp" + android:color="#000000" /> +</shape> diff --git a/app/src/main/res/drawable/login_backgroud2.xml b/app/src/main/res/drawable/login_backgroud2.xml new file mode 100644 index 0000000000000000000000000000000000000000..5d1992457db7659499441f8b9ca7f47769f490fe --- /dev/null +++ b/app/src/main/res/drawable/login_backgroud2.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <gradient + android:type="linear" + android:angle="270" + android:startColor="#B1B8D8" + android:endColor="@color/white" + android:gradientRadius="25%"/> + + + +</shape> \ No newline at end of file diff --git a/app/src/main/res/drawable/login_background.xml b/app/src/main/res/drawable/login_background.xml new file mode 100644 index 0000000000000000000000000000000000000000..7298f1f647b91f3dc732a7a95bdb08285fd70a64 --- /dev/null +++ b/app/src/main/res/drawable/login_background.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <gradient + android:type="linear" + android:angle="90" + android:startColor="#B1B8D8" + android:gradientRadius="25%"/> + + + +</shape> \ No newline at end of file diff --git a/app/src/main/res/drawable/logo_bondoman.png b/app/src/main/res/drawable/logo_bondoman.png new file mode 100644 index 0000000000000000000000000000000000000000..3683cc6401ba46bc7fb50ae3a3df9e7825287cd0 Binary files /dev/null and b/app/src/main/res/drawable/logo_bondoman.png differ diff --git a/app/src/main/res/drawable/rounded_background_gradient.xml b/app/src/main/res/drawable/rounded_background_gradient.xml new file mode 100644 index 0000000000000000000000000000000000000000..11a47716004630dfb17e1d56b06599e2fab3258f --- /dev/null +++ b/app/src/main/res/drawable/rounded_background_gradient.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + + <gradient + android:type="linear" + android:angle="270" + android:startColor="#B1B8D8" + android:endColor="@color/white" + android:gradientRadius="50%"/> + <corners android:radius="30dp"/> + +</shape> \ No newline at end of file diff --git a/app/src/main/res/drawable/rounded_background_transparent.xml b/app/src/main/res/drawable/rounded_background_transparent.xml new file mode 100644 index 0000000000000000000000000000000000000000..bf186f8567cc0e3d37d05498448c7994431d0ce6 --- /dev/null +++ b/app/src/main/res/drawable/rounded_background_transparent.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid android:color="@android:color/transparent" /> + <corners android:radius="8dp" /> + <stroke android:width="1dip" android:color="@color/primary1"/> +</shape> \ No newline at end of file diff --git a/app/src/main/res/drawable/rounded_button.xml b/app/src/main/res/drawable/rounded_button.xml new file mode 100644 index 0000000000000000000000000000000000000000..487e3fe3282a180b3343bfee19267f8c1d4feda8 --- /dev/null +++ b/app/src/main/res/drawable/rounded_button.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + + <item> + <shape android:shape="oval"> + <corners android:radius="8dp" /> + <solid android:color="#B1B8D8" /> + <stroke android:width="5dp" android:color="#494666" /> + </shape> + </item> +</selector> diff --git a/app/src/main/res/drawable/rounded_corne.xml b/app/src/main/res/drawable/rounded_corne.xml new file mode 100644 index 0000000000000000000000000000000000000000..6bc2b283c38e08b88152bd79291da17aa6ddfe1b --- /dev/null +++ b/app/src/main/res/drawable/rounded_corne.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <corners android:radius="10dp"/> + <stroke android:width="1dip" android:color="@color/primary1"/> + +</shape> \ No newline at end of file diff --git a/app/src/main/res/drawable/splashscreen.png b/app/src/main/res/drawable/splashscreen.png new file mode 100644 index 0000000000000000000000000000000000000000..52af1a41865973c2dcdaa3bf056415b37837f1ab Binary files /dev/null and b/app/src/main/res/drawable/splashscreen.png differ diff --git a/app/src/main/res/drawable/square_background_gradient_down.xml b/app/src/main/res/drawable/square_background_gradient_down.xml new file mode 100644 index 0000000000000000000000000000000000000000..d0d8cf28f64f14de6df6482f60af4d1142d990b3 --- /dev/null +++ b/app/src/main/res/drawable/square_background_gradient_down.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <gradient + android:type="linear" + android:angle="270" + android:startColor="#B1B8D8" + android:endColor="@color/white"/> + +</shape> \ No newline at end of file diff --git a/app/src/main/res/drawable/square_background_gradient_up.xml b/app/src/main/res/drawable/square_background_gradient_up.xml new file mode 100644 index 0000000000000000000000000000000000000000..4139b8f35c9f7a7b476c178386bde3b72d6253e1 --- /dev/null +++ b/app/src/main/res/drawable/square_background_gradient_up.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <gradient + android:type="linear" + android:angle="90" + android:startColor="#B1B8D8" + android:endColor="@color/white"/>> + +</shape> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_bondo_man_splash_screen.xml b/app/src/main/res/layout/activity_bondo_man_splash_screen.xml new file mode 100644 index 0000000000000000000000000000000000000000..7b5e12cc0213adf901e658fd37c9d7e766ea05dc --- /dev/null +++ b/app/src/main/res/layout/activity_bondo_man_splash_screen.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout 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:layout_width="match_parent" + android:layout_height="match_parent" + + android:background="@drawable/background_gradient_splashscreen" + android:gravity="center_horizontal" + android:orientation="vertical" + tools:context=".BondoManSplashScreen"> + + <ImageView + android:id="@+id/imageView2" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + app:srcCompat="@drawable/logo_bondoman" /> +</LinearLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml new file mode 100644 index 0000000000000000000000000000000000000000..90dbd53823483052f25e69e5a6abad51fd915e19 --- /dev/null +++ b/app/src/main/res/layout/activity_login.xml @@ -0,0 +1,73 @@ +<?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/loginActivity" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@drawable/login_background" + tools:context=".LoginActivity"> + + <ImageView + android:id="@+id/logoLogin" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:layout_marginTop="24dp" + android:layout_marginEnd="8dp" + android:src="@drawable/logo_bondoman" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <ImageView + android:id="@+id/iconLogin" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="32dp" + android:src="@drawable/icon_awal" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/logoLogin" /> + + <TextView + android:id="@+id/tagline" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="32dp" + android:text="@string/tagline_awal" + android:textAlignment="center" + android:textColor="@color/primary1" + android:textSize="24sp" + android:textStyle="bold" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/iconLogin" /> + + <TextView + android:id="@+id/desc_tagline" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="24dp" + android:paddingHorizontal="30dp" + android:text="@string/desc_tagline_awal" + android:textAlignment="center" + android:textColor="@color/primary1" + android:textSize="16sp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/tagline" /> + + <Button + android:id="@+id/toLoginPageButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/login" + android:background="@color/primary1" + android:textColor="@color/white" + + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/desc_tagline" /> +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_graphs.xml b/app/src/main/res/layout/fragment_graphs.xml index 84eaf2610fc07a8e81f837bc2573234dc4460b72..9567470a125835fddebb64ff57970427de6293d6 100644 --- a/app/src/main/res/layout/fragment_graphs.xml +++ b/app/src/main/res/layout/fragment_graphs.xml @@ -1,22 +1,103 @@ <?xml version="1.0" encoding="utf-8"?> <FrameLayout 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:layout_width="match_parent" android:layout_height="match_parent" tools:context=".Graphs"> - <RelativeLayout + <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> - - <ImageView android:id="@+id/iv_graphs" - android:src="@drawable/baseline_pie_chart_24" android:layout_width="200dp" android:layout_height="200dp" - android:layout_centerInParent="true"/> + android:layout_centerInParent="true" + android:src="@drawable/baseline_pie_chart_24" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <androidx.constraintlayout.widget.ConstraintLayout + android:id="@+id/constraintLayout" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="120dp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:background="@drawable/background_rounded_purple" + android:padding="10dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + android:foregroundGravity="center_horizontal"> + + <TextView + android:id="@+id/incomeIcon" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:drawableStart="@drawable/baseline_mail_outline_24" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@+id/incomeText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Income" + app:layout_constraintTop_toBottomOf="@+id/incomeIcon" /> + + <TextView + android:id="@+id/incomeValueText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Rp 50.000.000" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toBottomOf="@+id/incomeText" /> + </androidx.constraintlayout.widget.ConstraintLayout> + + <androidx.constraintlayout.widget.ConstraintLayout + android:id="@+id/constraintLayout2" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:background="@drawable/background_rounded_peach" + android:padding="10dp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent"> + + <TextView + android:id="@+id/expenseIcon" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@drawable/rounded_background" + android:drawableStart="@drawable/baseline_mail_outline_24" + android:padding="5dp" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@+id/expenseText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Expense" + app:layout_constraintTop_toBottomOf="@+id/expenseIcon" /> + + <TextView + android:id="@+id/expenseValueText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Rp 50.000.000" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toBottomOf="@+id/expenseText" /> + </androidx.constraintlayout.widget.ConstraintLayout> + </androidx.constraintlayout.widget.ConstraintLayout> + - </RelativeLayout> + </androidx.constraintlayout.widget.ConstraintLayout> </FrameLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_login.xml b/app/src/main/res/layout/fragment_login.xml new file mode 100644 index 0000000000000000000000000000000000000000..136ab06e71b214b0baf00c8e414734c3877925e2 --- /dev/null +++ b/app/src/main/res/layout/fragment_login.xml @@ -0,0 +1,118 @@ +<?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:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@drawable/login_backgroud2" + tools:context=".LoginFragment"> + + <TextView + android:id="@+id/backButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:drawableStart="@drawable/baseline_arrow_back_ios_24" + android:layout_marginTop="20dp" + android:layout_marginLeft="10dp" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <ImageView + android:id="@+id/imageView" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="24dp" + android:src="@drawable/logo_bondoman" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@+id/textView2" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="32dp" + android:text="@string/greet" + android:textAlignment="center" + android:textColor="@color/primary1" + android:textSize="34sp" + android:textStyle="bold" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/imageView" /> + + <TextView + android:id="@+id/textView3" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="24dp" + android:text="@string/login_into" + android:textAlignment="center" + android:textColor="@color/primary1" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/textView2" /> + + <EditText + android:id="@+id/emailInputField" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="32dp" + android:background="@drawable/rounded_corne" + android:layout_marginHorizontal="24dp" + android:ems="10" + android:paddingVertical="10dp" + android:paddingStart="10dp" + android:hint="@string/email" + android:drawableStart="@drawable/baseline_mail_outline_24" + android:drawablePadding="7dp" + android:inputType="textEmailAddress" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/textView3" /> + + <EditText + android:id="@+id/passwordInputField" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="24dp" + android:layout_marginHorizontal="24dp" + android:background="@drawable/rounded_corne" + android:drawableStart="@drawable/baseline_lock_24" + android:drawablePadding="7dp" + android:paddingStart="10dp" + android:ems="10" + android:paddingVertical="10dp" + android:hint="@string/password" + android:inputType="textPassword" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/emailInputField" /> + + <Button + android:id="@+id/loginButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="32dp" + android:text="@string/login" + android:background="@color/primary1" + android:textColor="@color/white" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/passwordInputField" /> + + <ImageView + android:id="@+id/imageView3" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:src="@drawable/icon_login" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/loginButton" /> + + + + + +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_scan.xml b/app/src/main/res/layout/fragment_scan.xml index 0d70efd2b893f057fba845896075c3359f13785f..6d6fa804e90fc93faa49a79fe52bd93559e4d393 100644 --- a/app/src/main/res/layout/fragment_scan.xml +++ b/app/src/main/res/layout/fragment_scan.xml @@ -9,12 +9,51 @@ android:layout_width="match_parent" android:layout_height="match_parent"> +<!-- <ImageView--> +<!-- android:id="@+id/iv_scan"--> +<!-- android:src="@drawable/baseline_document_scanner_24"--> +<!-- android:layout_width="200dp"--> +<!-- android:layout_height="200dp"--> +<!-- android:layout_centerInParent="true"/>--> + <ImageView - android:id="@+id/iv_scan" - android:src="@drawable/baseline_document_scanner_24" - android:layout_width="200dp" - android:layout_height="200dp" - android:layout_centerInParent="true"/> + android:id="@+id/captureImageView" + android:layout_width="350dp" + android:layout_height="450dp" + android:layout_centerInParent="true" + android:background="@drawable/iv_picture" + android:scaleType="centerCrop" /> + + <android.widget.Button + android:id="@+id/captureImgBtn" + android:layout_width="69dp" + android:layout_height="71dp" + + android:layout_alignParentEnd="true" + android:layout_alignParentBottom="true" + android:layout_marginVertical="33dp" + android:layout_marginEnd="168dp" + android:layout_marginBottom="35dp" + android:background="@drawable/rounded_button" + android:text="" /> + + <Button + android:id="@+id/pickImgBtn" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentStart="true" + android:layout_alignParentBottom="true" + android:layout_marginVertical="25dp" + android:layout_marginStart="24dp" + android:layout_marginEnd="55dp" + android:layout_marginBottom="24dp" + android:layout_toStartOf="@+id/captureImgBtn" + android:background="@android:color/transparent" + android:drawableTop="@drawable/gallery" + android:drawablePadding="0dp" + android:text="Gallery" + android:textColor="@color/primary_color_1" /> + </RelativeLayout> </FrameLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml index c45ee647d8bc04faddcfd83ac778e35f7db4536d..00f9dccbe02c40ad8297f7349fedc655bba1e6a6 100644 --- a/app/src/main/res/layout/fragment_settings.xml +++ b/app/src/main/res/layout/fragment_settings.xml @@ -5,16 +5,58 @@ android:layout_height="match_parent" tools:context=".Settings"> - <RelativeLayout + <LinearLayout android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="wrap_content" + android:layout_marginTop="120dp" + android:layout_marginHorizontal="16dp" + android:background="@drawable/rounded_background_gradient" + android:gravity="center" + android:orientation="vertical"> <ImageView android:id="@+id/iv_settings" - android:src="@drawable/baseline_settings_24" android:layout_width="200dp" android:layout_height="200dp" - android:layout_centerInParent="true"/> + android:layout_centerInParent="true" + android:layout_marginVertical="10dp" + android:src="@drawable/baseline_settings_24" /> - </RelativeLayout> + <TextView + android:id="@+id/emailTextView" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="123@gmail.com" + android:textColor="@color/primary1" + android:textAlignment="center"/> + + <Button + android:id="@+id/saveTransactionButton" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@drawable/rounded_background_transparent" + android:text="Save Transaction List" + android:layout_marginVertical="10dp" + android:textColor="@color/primary1" /> + + <Button + android:id="@+id/sendTransactionButton" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@drawable/rounded_background_transparent" + android:text="Send Transaction List" + android:layout_marginVertical="10dp" + android:textColor="@color/primary1" /> + + <Button + android:id="@+id/logoutButton" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:drawableStart="@drawable/ic_launcher_foreground" + android:background="@drawable/rounded_background_transparent" + android:text="Logout" + android:layout_marginVertical="10dp" + android:textColor="@color/primary1" /> + + </LinearLayout> </FrameLayout> \ No newline at end of file diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml new file mode 100644 index 0000000000000000000000000000000000000000..121252c803a5c391956582eeffcf22416cca265a --- /dev/null +++ b/app/src/main/res/navigation/nav_graph.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<navigation 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/nav_graph" + app:startDestination="@id/loginActivity2"> + + <activity + android:id="@+id/loginActivity2" + android:name="com.example.android_hit.LoginActivity" + android:label="activity_login" + tools:layout="@layout/activity_login" /> + <fragment + android:id="@+id/loginFragment" + android:name="com.example.android_hit.LoginFragment" + android:label="fragment_login" + tools:layout="@layout/fragment_login" /> +</navigation> \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 855c3219e5c3ef9b1eacfad985d64310fde43629..8130206cff0c57ee531564f9aa653ad4c92ea6de 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -7,4 +7,12 @@ <color name="primary_color_3">#EFDAC7</color> <color name="primary_color_4">#F0F0F2</color> <color name="secondary_color_2">#DDDFEB</color> + <color name="primary1">#322F50</color> + <color name="primary2">#B1B8D8</color> + <color name="primary3">#EFDAC7</color> + <color name="primary4">#F0F0F2</color> + <color name="secondary1">#F9F9FC</color> + <color name="secondary2">#47455B</color> + <color name="secondary3">#095BFA</color> + <color name="secondary4">#D65C5C</color> </resources> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 36e4f7e80fa77a5ddd460cd699f6fde5078defbf..3f70befd6351b64a5ee1d5fee533f90809aa35da 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,5 +1,23 @@ <resources> <string name="app_name">Android-HIT</string> + <string name="tagline_awal">One Touch Solution for Your Budgeting</string> + <string name="desc_tagline_awal">Overbudget? Don’t worry! With us, managing your income and expanses is only one touch away</string> + <string name="login">Login</string> + <string name="login_into">Login to your account</string> + <string name="greet">Hi!</string> + <string name="password">Password </string> + <string name="email">Email</string> + <!-- TODO: Remove or change this placeholder text --> + <string name="hello_blank_fragment">Hello blank fragment</string> + <!-- Strings related to login --> + <string name="prompt_email">Email</string> + <string name="prompt_password">Password</string> + <string name="action_sign_in">Sign in or register</string> + <string name="action_sign_in_short">Sign in</string> + <string name="welcome">"Welcome!"</string> + <string name="invalid_username">Not a valid username</string> + <string name="invalid_password">Password must be >5 characters</string> + <string name="login_failed">"Login failed"</string> <!-- TODO: Remove or change this placeholder text --> <string name="fragment_transaction">Transaction</string> <string name="fragment_settings">Settings</string>