diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml index c11e845d4322a80c7b89b5ac0a6c913383bd0b10..4728463e69cb12388cbc07b7ceda807f9ec0c725 100644 --- a/.idea/deploymentTargetDropDown.xml +++ b/.idea/deploymentTargetDropDown.xml @@ -3,20 +3,7 @@ <component name="deploymentTargetDropDown"> <value> <entry key="app"> - <State> - <targetSelectedWithDropDown> - <Target> - <type value="QUICK_BOOT_TARGET" /> - <deviceKey> - <Key> - <type value="VIRTUAL_DEVICE_PATH" /> - <value value="$USER_HOME$/.android/avd/Pixel_5_API_34.avd" /> - </Key> - </deviceKey> - </Target> - </targetSelectedWithDropDown> - <timeTargetWasSelectedWithDropDown value="2024-03-20T06:11:55.691039Z" /> - </State> + <State /> </entry> <entry key="insert()"> <State /> diff --git a/README.md b/README.md index 880f16dc8f20592c90740ffd85fab067be9f07f8..0e850018939820466a1b050b95c72b05d5b4d70a 100644 --- a/README.md +++ b/README.md @@ -1,93 +1,319 @@ # IF3210 2024 Android SKS - - -## Getting started - -To make it easy for you to get started with GitLab, here's a list of recommended next steps. - -Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! - -## Add your files - -- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files -- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: - -``` -cd existing_repo -git remote add origin https://gitlab.informatika.org/nate/IF3210-2024-Android-SKS.git -git branch -M main -git push -uf origin main -``` - -## Integrate with your tools - -- [ ] [Set up project integrations](https://gitlab.informatika.org/nate/IF3210-2024-Android-SKS/-/settings/integrations) - -## Collaborate with your team - -- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) -- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) -- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) -- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) -- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) - -## Test and Deploy - -Use the built-in continuous integration in GitLab. - -- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) -- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) -- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) -- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) -- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) - -*** - -# Editing this README - -When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template. - -## Suggestions for a good README - -Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. - -## Name -Choose a self-explaining name for your project. - -## Description -Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. - -## Badges -On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. - -## Visuals -Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. - -## Installation -Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. - -## Usage -Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. - -## Support -Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. - -## Roadmap -If you have ideas for releases in the future, it is a good idea to list them in the README. - -## Contributing -State if you are open to contributions and what your requirements are for accepting them. - -For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. - -You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. - -## Authors and acknowledgment -Show your appreciation to those who have contributed to the project. - -## License -For open source projects, say how it is licensed. - -## Project status -If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. +## Anggota kelompok + +<table> + <tr> + <th>NIM</th> + <th>Nama</th> + <th>Panggilan</th> + </tr> + <tr> + <td>13521139</td> + <td>Nathania Calista Djunaedi</td> + <td>Nat</td> + </tr> + <tr> + <td>13521162</td> + <td>Antonio Natthan Krishna</td> + <td>Nate</td> + </tr> + <tr> + <td>13521170</td> + <td>Haziq Abiyyu Mahdy</td> + <td>Haziq</td> + </tr> +</table> + +## Deskripsi aplikasi + +Bondoman merupakan aplikasi manajemen transaksi dalam proyek pembangunan seribu candi yang dilakukan oleh Bondowoso. Secara umum, aplikasi ini memiliki fungsionalitas sebagai berikut. + +1. Otentikasi pengguna (login dan logout) +2. Create, read, update, delete transaksi +3. Scan nota – memindai nota dan mencatatnya sebagai transaksi +4. Grafik (pie chart) rangkuman transaksi +5. Penyimpanan transaksi dalam file dengan ekstensi .xls dan .xlsx +6. Pengiriman file transaksi melalui gmail +7. Pengecekan expiry JWT secara berkala dengan background service +8. Network sensing – menampilkan pesan jika perangkat tidak terhubung ke internet +9. Generate transaksi random menggunakan broadcast receiver +10. Twibbon + +## Library yang digunakan + +- [Retrofit](https://square.github.io/retrofit/) – untuk melakukan HTTP request +- [Room](https://developer.android.com/training/data-storage/room) – untuk persistence +- [CameraX](https://developer.android.com/media/camera/camerax) – untuk menggunakan kamera +- [Lottie](https://lottiefiles.com/) – asset untuk animation loading +- [Apache POI](https://poi.apache.org/) – untuk menulis file xls/xlsx +- [MPAndroidChart](https://github.com/PhilJay/MPAndroidChart) - untuk pie chart +- [Google play services](https://developers.google.com/android/guides/setup) - untuk lokasi + +## Screenshot aplikasi + +## Pembagian kerja anggota kelompok. + +<table> + <tr> + <th>No</th> + <th>Task</th> + <th>PIC</th> + </tr> + <tr> + <td>1</td> + <td colspan="2"><i>Header dan Navbar</i></td> + </tr> + <tr> + <td></td> + <td>Header</td> + <td>13521162</td> + </tr> + <tr> + <td></td> + <td>Navbar</td> + <td>13521162</td> + </tr> + <tr> + <td>2</td> + <td colspan="2"><i>Login dan Logout</i></td> + </tr> + <tr> + <td></td> + <td>Interface auth service</td> + <td>13521139</td> + </tr> + <tr> + <td></td> + <td>Auth repository</td> + <td>13521139</td> + </tr> + <tr> + <td></td> + <td>Login page</td> + <td>13521139</td> + </tr> + <tr> + <td></td> + <td>Testing auth backend service</td> + <td>13521139</td> + </tr> + <tr> + <td>3</td> + <td colspan="2"><i>Manipulasi Transaksi</i></td> + </tr> + <tr> + <td></td> + <td>Entity transaksi</td> + <td>13521162, 13521170</td> + </tr> + <tr> + <td></td> + <td>Repository transaksi</td> + <td>13521162, 13521170</td> + </tr> + <tr> + <td></td> + <td>ViewModel transaksi</td> + <td>13521162, 13521170</td> + </tr> + <tr> + <td></td> + <td>Dao transaksi</td> + <td>13521162, 13521170</td> + </tr> + <tr> + <td></td> + <td>Testing repository transaksi</td> + <td>13521170</td> + </tr> + <tr> + <td></td> + <td>Halaman add transaksi</td> + <td>13521162</td> + </tr> + <tr> + <td></td> + <td>Halaman edit transaksi</td> + <td>13521162</td> + </tr> + <tr> + <td></td> + <td>Intent google maps</td> + <td>13521139</td> + </tr> + <tr> + <td>4</td> + <td colspan="2"><i>Melihat Daftar Transaksi yang Sudah Dilakukan</i></td> + </tr> + <tr> + <td></td> + <td>Halaman transaksi</td> + <td>13521162</td> + </tr> + <tr> + <td></td> + <td>RecyclerView transaksi</td> + <td>13521162</td> + </tr> + <tr> + <td>5</td> + <td colspan="2"><i>Melakukan Scan Nota - Halaman Scan Nota</i></td> + </tr> + <tr> + <td></td> + <td>Halaman scan nota</td> + <td>13521139</td> + </tr> + <tr> + <td></td> + <td>Camera</td> + <td>13521139</td> + </tr> + <tr> + <td></td> + <td>Interface scan service</td> + <td>13521139</td> + </tr> + <tr> + <td>6</td> + <td colspan="2"><i>Melihat Graf Rangkuman Transaksi - Halaman Graf</i></td> + </tr> + <tr> + <td></td> + <td> Halaman graf portrait</td> + <td>13521170</td> + </tr> + <tr> + <td></td> + <td>Halaman graf landscape</td> + <td>13521170</td> + </tr> + <tr> + <td></td> + <td>Transaction-graph adapter</td> + <td>13521170</td> + </tr> + <tr> + <td>7</td> + <td colspan="2"><i>Menyimpan Daftar Transaksi dalam Format .xlsx, .xls - Halaman Pengaturan</i></td> + </tr> + <tr> + <td></td> + <td> Halaman pengaturan</td> + <td>13521170</td> + </tr> + <tr> + <td></td> + <td>Transaction-excel adapter (write transaction to OutputStream)</td> + <td>13521170</td> + </tr> + <tr> + <td></td> + <td>Transaction Downloader (save to download folder)</td> + <td>13521170</td> + </tr> + <tr> + <td></td> + <td>Testing transaction-excel adapter</td> + <td>13521170</td> + </tr> + <tr> + <td>8</td> + <td colspan="2"><i>Intent GMail - Halaman Pengaturan</i></td> + </tr> + <tr> + <td></td> + <td> Send to gmail</td> + <td>13521170</td> + </tr> + <tr> + <td>9</td> + <td colspan="2"><i>Background Service - Mengecek expiry JWT</i></td> + </tr> + <tr> + <td></td> + <td> Token check service</td> + <td>13521139</td> + </tr> + <tr> + <td></td> + <td> Token broadcast receiver</td> + <td>13521139</td> + </tr> + <tr> + <td>10</td> + <td colspan="2"><i>Network Sensing - Deteksi Sinyal</i></td> + </tr> + <tr> + <td></td> + <td> Network sensing</td> + <td>13521139</td> + </tr> + <tr> + <td></td> + <td>Network broadcast receiver</td> + <td>13521139</td> + </tr> + <tr> + <td>11</td> + <td colspan="2"><i>Broadcast Receiver - Randomize Transaksi dari Pengaturan</i></td> + </tr> + <tr> + <td></td> + <td> Randomize broadcast receiver</td> + <td>13521170</td> + </tr> + <tr> + <td>12</td> + <td colspan="2"><i>Twibbon</i></td> + </tr> + <tr> + <td></td> + <td>Halaman twibbon</td> + <td>13521162</td> + </tr> + <tr> + <td></td> + <td>Kamera twibbon</td> + <td>13521162</td> + </tr> + <tr> + <td>13</td> + <td colspan="2"><i>Responsivitas UI</i></td> + </tr> + <tr> + <td></td> + <td>Orientasi Layar</td> + <td>13521162</td> + </tr> + <tr> + <td></td> + <td>Size Layar</td> + <td>13521162</td> + </tr> +</table> + + + +## Alokasi jam kerja + +<table> + <tr> + <th>NIM</th> + <th>Jam kerja</th> + </tr> + <tr> + <td>13521139</td> + <td>???</td> + </tr> + <tr> + <td>13521162</td> + <td>???</td> + </tr> + <tr> + <td>13521170</td> + <td>???</td> + </tr> + +</table> diff --git a/app/build.gradle.kts b/app/build.gradle.kts index b27415d983e652133d2a97e1589f5466b2f0139c..1121e4ea78df542ef5edbb42ab0a8284fdfefa78 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -132,4 +132,7 @@ dependencies { implementation("androidx.camera:camera-video:$cameraxVersion") implementation("androidx.camera:camera-view:$cameraxVersion") implementation("androidx.camera:camera-extensions:$cameraxVersion") + + implementation("com.google.android.gms:play-services-location:17.0.0") + } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 735291997a34a0582d02c1a08935d59f88e46f77..8735b0830ef76d5d228e99213ad47cb24ad7df2e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,40 +1,59 @@ <?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-feature android:name="android.hardware.camera.any" /> - <uses-permission android:name="android.permission.CAMERA" /> + + <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" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" /> + <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> + <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> + <application android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" - android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.BondoMan" tools:targetApi="31"> <activity android:name=".activities.LoginActivity" - android:exported="true"> <!-- Make sure to set exported="true" --> + android:exported="true" + android:windowSoftInputMode="adjustResize"> <!-- Make sure to set exported="true" --> <intent-filter> <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> - <activity android:name=".activities.AddTransaction" /> - <activity android:name=".activities.EditTransaction" /> + <activity + android:name=".activities.AddTransaction" + android:theme="@style/Theme.BondoMan.ActionBar"/> + <activity + android:name=".activities.EditTransaction" + android:theme="@style/Theme.BondoMan.ActionBar"/> <activity android:name=".activities.MainActivity" android:exported="true" android:theme="@style/Theme.BondoMan.ActionBar"> + <intent-filter> + <action android:name="android.intent.action.SEND" /> + <data android:mimeType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> </activity> <service android:name=".services.TokenCheckService"/> + <provider + android:name="androidx.core.content.FileProvider" + android:authorities="${applicationId}.provider" + android:grantUriPermissions="true" + android:exported="false"> + <meta-data + android:name="android.support.FILE_PROVIDER_PATHS" + android:resource="@xml/provider_paths" /> + </provider> </application> - </manifest> \ No newline at end of file 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 04d0987d38d27f946bb8367b975a4ad699b08d3a..d9846be5ac7433129c58cbccb7fd7c5fff6ca7e1 100644 --- a/app/src/main/java/com/example/bondoman/activities/AddTransaction.kt +++ b/app/src/main/java/com/example/bondoman/activities/AddTransaction.kt @@ -1,34 +1,62 @@ package com.example.bondoman.activities +import android.Manifest import android.app.Activity -import android.app.DatePickerDialog +import android.content.BroadcastReceiver +import android.content.Context import android.content.Intent -import android.graphics.Color +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.view.View import android.widget.ArrayAdapter import android.widget.Button -import android.widget.DatePicker import android.widget.EditText +import android.widget.Spinner +import android.widget.Toast import androidx.appcompat.app.AppCompatActivity +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat import com.example.bondoman.R -import android.widget.Spinner -import com.example.bondoman.adapter.RecyclerViewAdapter -import com.example.bondoman.entities.Transaction -import com.example.bondoman.viewModels.TransactionsViewModel -import java.util.Calendar -import java.util.Date +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() { +class AddTransaction : BaseActivity() { + private lateinit var fusedLocationClient: FusedLocationProviderClient + private lateinit var editTextLocation : EditText + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_add_transaksi) + + fusedLocationClient = LocationServices.getFusedLocationProviderClient(this) + tokenExpiredReceiver = object : BroadcastReceiver(){ + override fun onReceive(context: Context?, intent: Intent?) { + if(intent != null && intent.action != null){ + Log.e("Receive", intent.action.toString()) + } + if(intent?.action == "com.example.bondoman.TOKEN_EXPIRED"){ + val loginIntent = Intent(this@AddTransaction, LoginActivity::class.java).apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + } + stopService(tokenServiceIntent) + startActivity(loginIntent) + finish() + } + } + + } supportActionBar?.title = "Add Transaction" + val editTextTitle = findViewById<EditText>(R.id.editTextTitle) val spinnerCategory : Spinner = findViewById(R.id.spinnerCategory) + val editTextAmount = findViewById<EditText>(R.id.editTextAmount) + editTextLocation = findViewById(R.id.editTextLocation) + val adapter = ArrayAdapter.createFromResource( this, @@ -36,35 +64,92 @@ class AddTransaction : AppCompatActivity() { R.layout.spinner_dropdown ) + editTextTitle.setText(intent.getStringExtra("TITLE")) + spinnerCategory.setSelection(intent.getIntExtra("TYPE", 0)) + editTextAmount.setText(intent.getFloatExtra("AMOUNT", 0f).toString()) + spinnerCategory.adapter = adapter val submitButton : Button = findViewById(R.id.buttonSubmit) submitButton.setOnClickListener { - val title = findViewById<EditText>(R.id.editTextTitle).text.toString() - val category = spinnerCategory.selectedItem.toString() - val amount = findViewById<EditText>(R.id.editTextAmount).text.toString().toFloatOrNull() ?: 0f - val location = findViewById<EditText>(R.id.editTextLocation).text.toString() - - val replyIntent = Intent() - if (title.isEmpty()) { - setResult(Activity.RESULT_CANCELED, replyIntent) + if (editTextTitle.text.toString() != "") { + val title = editTextTitle.text.toString() + val category = spinnerCategory.selectedItem.toString() + val amount = editTextAmount.text.toString().toFloatOrNull() ?: 0f + val location = editTextLocation.text.toString() + + if (amount > 0f) { + val replyIntent = Intent() + if (title.isEmpty()) { + setResult(Activity.RESULT_CANCELED, replyIntent) + } else { + replyIntent.putExtra(TITLE, title) + replyIntent.putExtra(TYPE, category) + replyIntent.putExtra(AMOUNT, amount) + replyIntent.putExtra(LOCATION, location) + setResult(Activity.RESULT_OK, replyIntent) + } + finish() + } else { + Toast.makeText(this, + "Price should be greater than 0", + Toast.LENGTH_SHORT).show() + } + } else { - replyIntent.putExtra(TITLE, title) - replyIntent.putExtra(TYPE, category) - replyIntent.putExtra(AMOUNT, amount) - replyIntent.putExtra(LOCATION, location) - setResult(Activity.RESULT_OK, replyIntent) + Toast.makeText(this, + "Name cannot be empty", + Toast.LENGTH_SHORT).show() } - finish() } + getLastLocation() + } + override fun onBackPressed() { + super.onBackPressed() + finish() } + + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + if (requestCode == REQUEST_LOCATION_PERMISSION) { + if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + getLastLocation() + } + } + } + private fun getLastLocation() { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + fusedLocationClient.lastLocation.addOnSuccessListener { location: Location? -> + location?.let { + updateLocationEditTextWithPlaceName(location.latitude, location.longitude) + } + } + }else { + ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), REQUEST_LOCATION_PERMISSION) + } + } + private fun updateLocationEditTextWithPlaceName(latitude: Double, longitude: Double) { + val geocoder = Geocoder(this, Locale.getDefault()) + try { + val addresses = geocoder.getFromLocation(latitude, longitude, 1) + if (addresses != null && addresses.isNotEmpty()) { + val address = addresses[0] + editTextLocation.setText(address.getAddressLine(0).toString()) + } else { + editTextLocation.setText("${latitude}, ${longitude}") + } + } catch (e: IOException) { + Log.e("Location", "Service Not Available", e) + } + } companion object { const val TITLE = "TITLE" const val TYPE = "TYPE" const val AMOUNT = "AMOUNT" const val LOCATION = "LOCATION" - } + const val REQUEST_LOCATION_PERMISSION = 1 // Define the constant here + } } \ No newline at end of file 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 c7ebc7d0e80ca7501904dad7efc568e5fe1b9864..967b0b325cbe83642dbbae6d673fbfd2444fd153 100644 --- a/app/src/main/java/com/example/bondoman/activities/EditTransaction.kt +++ b/app/src/main/java/com/example/bondoman/activities/EditTransaction.kt @@ -1,34 +1,49 @@ package com.example.bondoman.activities import android.app.Activity -import android.app.DatePickerDialog +import android.content.BroadcastReceiver +import android.content.Context import android.content.Intent -import android.graphics.Color +import android.net.Uri import android.os.Bundle -import android.view.View -import android.widget.ArrayAdapter +import android.util.Log import android.widget.Button -import android.widget.DatePicker import android.widget.EditText -import androidx.appcompat.app.AppCompatActivity -import com.example.bondoman.R -import android.widget.Spinner import android.widget.TextView -import java.util.Calendar +import android.widget.Toast +import com.example.bondoman.R + +class EditTransaction() : BaseActivity() { -class EditTransaction() : AppCompatActivity() { + private lateinit var location : EditText override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_edit_transaksi) + tokenExpiredReceiver = object : BroadcastReceiver(){ + override fun onReceive(context: Context?, intent: Intent?) { + if(intent != null && intent.action != null){ + Log.e("Receive", intent.action.toString()) + } + if(intent?.action == "com.example.bondoman.TOKEN_EXPIRED"){ + val loginIntent = Intent(this@EditTransaction, LoginActivity::class.java).apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + } + stopService(tokenServiceIntent) + startActivity(loginIntent) + finish() + } + } + + } supportActionBar?.title = "Edit Transaction" val date : TextView = findViewById(R.id.editTextDate) val type : TextView = findViewById(R.id.editTextType) val title : EditText = findViewById(R.id.editTextTitle) val amount : EditText = findViewById(R.id.editTextAmount) - val location : EditText = findViewById(R.id.editTextLocation) - + location = findViewById(R.id.editTextLocation) + val seeLocationText : TextView = findViewById(R.id.textViewSeeLocationLabel) val intentData = intent title.setText(intentData?.getStringExtra("title") ?: "") @@ -52,19 +67,49 @@ class EditTransaction() : AppCompatActivity() { val submitButton : Button = findViewById(R.id.buttonSubmit) submitButton.setOnClickListener{ - val replyIntent = Intent() - replyIntent.putExtra("command", "update") - if (false) { - setResult(Activity.RESULT_CANCELED, replyIntent) + if (title.text.toString() != "") { + val replyIntent = Intent() + replyIntent.putExtra("command", "update") + if (amount.text.toString().toFloat() > 0) { + if (false) { + setResult(Activity.RESULT_CANCELED, replyIntent) + } else { + replyIntent.putExtra("id", intentData?.getStringExtra("id")) + replyIntent.putExtra("title", title.text.toString()) + replyIntent.putExtra("amount", amount.text.toString()) + replyIntent.putExtra("location", location.text.toString()) + setResult(Activity.RESULT_OK, replyIntent) + } + finish() + } else { + Toast.makeText(this, + "Price should be greater than 0", + Toast.LENGTH_SHORT).show() + } } else { - replyIntent.putExtra("id", intentData?.getStringExtra("id")) - replyIntent.putExtra("title", title.text.toString()) - replyIntent.putExtra("amount", amount.text.toString()) - replyIntent.putExtra("location", location.text.toString()) - setResult(Activity.RESULT_OK, replyIntent) + Toast.makeText(this, + "Name cannot be empty", + Toast.LENGTH_SHORT).show() } - finish() + + } + seeLocationText.setOnClickListener { + seeLocation() } + } + override fun onBackPressed() { + super.onBackPressed() + finish() } + + + private fun seeLocation(){ + val locationText = location.text.toString() + val gmmIntentUri = Uri.parse("geo:0,0?q=$locationText") + val mapIntent = Intent(Intent.ACTION_VIEW, gmmIntentUri) + 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/LoginActivity.kt b/app/src/main/java/com/example/bondoman/activities/LoginActivity.kt index bf4a9d2737e7144514241de446ac8403fb484bb1..e09ec1f9ad1c6eb42017da0fe494eb3e616abc74 100644 --- a/app/src/main/java/com/example/bondoman/activities/LoginActivity.kt +++ b/app/src/main/java/com/example/bondoman/activities/LoginActivity.kt @@ -1,47 +1,114 @@ package com.example.bondoman.activities +import android.animation.ObjectAnimator +import android.app.Dialog import android.content.Intent -import androidx.appcompat.app.AppCompatActivity +import android.content.res.Configuration +import android.graphics.Color +import android.graphics.drawable.ColorDrawable import android.os.Bundle +import android.os.Handler +import android.view.animation.AnimationUtils import android.widget.Button import android.widget.EditText +import android.widget.ImageView +import android.widget.LinearLayout import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import androidx.cardview.widget.CardView import androidx.lifecycle.lifecycleScope import com.example.bondoman.R -import com.example.bondoman.apiServices.IAuthService import com.example.bondoman.lib.SecurePreferences -import com.example.bondoman.repositories.LoginRepository -import com.example.bondoman.retrofits.Retro +import com.example.bondoman.repositories.AuthRepository import kotlinx.coroutines.launch class LoginActivity : AppCompatActivity() { private lateinit var email : EditText private lateinit var password : EditText private lateinit var loginButton: Button - private lateinit var loginRepository: LoginRepository + private lateinit var loginRepository: AuthRepository + private var loadingDialog: Dialog? = null + private lateinit var layout: LinearLayout; override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_login) + layout = findViewById<LinearLayout>(R.id.layout) - val authService = Retro().getRetroClientInstance().create(IAuthService::class.java) - val securePreferences = SecurePreferences(this) + val orientation = resources.configuration.orientation + if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + layout.orientation = LinearLayout.HORIZONTAL + } else { + layout.orientation = LinearLayout.VERTICAL + } + + Handler().postDelayed({ + // Transition to the login screen after SPLASH_DURATION + val cardLogin = findViewById<CardView>(R.id.login_card) + val logo = findViewById<ImageView>(R.id.imageView) + + val slideUpAnimation = AnimationUtils.loadAnimation(this@LoginActivity, R.anim.slide_up) + val finalY = cardLogin.y + (cardLogin.height - logo.height) / 2 - loginRepository = LoginRepository(authService, securePreferences) + val animatorY = ObjectAnimator.ofFloat(logo, "translationY", 0f + logo.height, finalY) + animatorY.duration = 600 + animatorY.start() + cardLogin.startAnimation(slideUpAnimation) + cardLogin.visibility = CardView.VISIBLE + }, 1000) + + + + val securePreferences = SecurePreferences(this) + loginRepository = AuthRepository(securePreferences) + if(securePreferences.getToken() != null){ + val intent = Intent(this@LoginActivity, MainActivity::class.java) + startActivity(intent) + finish() + } email = findViewById(R.id.email) password = findViewById(R.id.password) loginButton = findViewById<Button>(R.id.login_button) loginButton.setOnClickListener{ lifecycleScope.launch { - val result = loginRepository.login(email.text.toString(), password.text.toString()) - if(result.isSuccess){ - val intent = Intent(this@LoginActivity, MainActivity::class.java) - startActivity(intent) - }else{ - val message = result.exceptionOrNull()?.message ?: "Login failed!" - Toast.makeText(this@LoginActivity, message, Toast.LENGTH_LONG).show() - } + login(email.text.toString(), password.text.toString()) } } } + + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + val orientation = newConfig.orientation + if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + layout.orientation = LinearLayout.HORIZONTAL + } else { + layout.orientation = LinearLayout.VERTICAL + } + } + + private suspend fun login(email : String, password : String){ + showLoadingDialog() + val result = loginRepository.login(email, password) + hideLoadingDialog() + if(result.isSuccess){ + val intent = Intent(this@LoginActivity, MainActivity::class.java) + startActivity(intent) + finish() + }else{ + val message = result.exceptionOrNull()?.message ?: "Login failed!" + Toast.makeText(this@LoginActivity, message, Toast.LENGTH_LONG).show() + } + } + private fun showLoadingDialog() { + loadingDialog = Dialog(this).apply { + setContentView(R.layout.loading) + setCancelable(false) + window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + show() + } + } + + private fun hideLoadingDialog() { + loadingDialog?.dismiss() + } } \ 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 c7ff18fad0564757348685416f2e6210987bdc36..09a918172d2b0607cbf3fd406ef945381bf82d8d 100644 --- a/app/src/main/java/com/example/bondoman/activities/MainActivity.kt +++ b/app/src/main/java/com/example/bondoman/activities/MainActivity.kt @@ -4,34 +4,39 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter -import androidx.appcompat.app.AppCompatActivity +import android.content.res.Configuration import android.os.Bundle import android.util.Log +import android.view.MenuItem +import android.view.View +import android.widget.LinearLayout +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.fragment.app.Fragment import androidx.navigation.findNavController +import androidx.navigation.fragment.NavHostFragment import androidx.navigation.ui.onNavDestinationSelected +import androidx.viewpager2.adapter.FragmentViewHolder import com.example.bondoman.R -import com.example.bondoman.services.TokenCheckService import com.google.android.material.bottomnavigation.BottomNavigationView +import com.google.android.material.navigation.NavigationView -class MainActivity : AppCompatActivity() { - private lateinit var tokenExpiredReceiver: BroadcastReceiver - private lateinit var tokenServiceIntent : Intent - private var isReceiverRegistered = false - +class MainActivity : BaseActivity() { + private lateinit var randomizeReceiver: BroadcastReceiver + private lateinit var navigationView : NavigationView + private lateinit var fragment : NavHostFragment + private lateinit var bottomNavigationView: BottomNavigationView 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){ Log.e("Receive", intent.action.toString()) } if(intent?.action == "com.example.bondoman.TOKEN_EXPIRED"){ - val loginIntent = Intent(this@MainActivity, LoginActivity::class.java) + val loginIntent = Intent(this@MainActivity, LoginActivity::class.java).apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + } stopService(tokenServiceIntent) startActivity(loginIntent) finish() @@ -39,9 +44,40 @@ 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) + startActivityForResult(randomizeIntent,1) + } + } + } val bottomNavigationView = findViewById<BottomNavigationView >(R.id.bottom_navigation_view) val navController = findNavController(R.id.nav_fragment) + navigationView = findViewById<NavigationView>(R.id.navigation_view) + + fragment = supportFragmentManager.findFragmentById(R.id.nav_fragment) as NavHostFragment + val navFragmentLayoutParams = fragment.view?.layoutParams as ConstraintLayout.LayoutParams + + val orientation = resources.configuration.orientation + if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + // Change layout to show left navigation + bottomNavigationView.visibility = View.GONE + navigationView.visibility = View.VISIBLE + } else { + // Set up bottom navigation bar + bottomNavigationView.visibility = View.VISIBLE + navigationView.visibility = View.GONE + } + + navigationView.setNavigationItemSelectedListener { menuItem : MenuItem -> + supportActionBar?.title = menuItem.title + menuItem.onNavDestinationSelected(navController) + true + } bottomNavigationView.setOnItemSelectedListener { menuItem -> supportActionBar?.title = menuItem.title @@ -50,28 +86,34 @@ class MainActivity : AppCompatActivity() { } } + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + val orientation = newConfig.orientation + + val navFragmentLayoutParams = fragment.view?.layoutParams as ConstraintLayout.LayoutParams + if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + // Change layout to show left navigation + bottomNavigationView.visibility = View.GONE + navigationView.visibility = View.VISIBLE + } else { + // Change layout to show bottom navigation bar + bottomNavigationView.visibility = View.VISIBLE + navigationView.visibility = View.GONE + } + } + override fun onStart() { super.onStart() - val filter = IntentFilter("com.example.bondoman.TOKEN_EXPIRED") - registerReceiver(tokenExpiredReceiver, filter) - isReceiverRegistered = true + val randomizeIntentFilter = IntentFilter("com.example.bondoman.RANDOMIZE_TRANSACTION") + 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/adapter/RecyclerViewAdapter.kt b/app/src/main/java/com/example/bondoman/adapter/RecyclerViewAdapter.kt index c579deb752c851a599164a896d57af4ee392a36d..f902d359ea6774fb28232564d0ce48dca4bdd2df 100644 --- a/app/src/main/java/com/example/bondoman/adapter/RecyclerViewAdapter.kt +++ b/app/src/main/java/com/example/bondoman/adapter/RecyclerViewAdapter.kt @@ -4,10 +4,7 @@ import android.content.Intent import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.Button -import android.widget.TextView import androidx.cardview.widget.CardView -import androidx.core.content.ContextCompat.startActivity import androidx.lifecycle.LiveData import androidx.recyclerview.widget.RecyclerView import com.example.bondoman.R diff --git a/app/src/main/java/com/example/bondoman/adapter/TransactionListAdapter.kt b/app/src/main/java/com/example/bondoman/adapter/TransactionListAdapter.kt index e37a2be9ad8340150ce60768fbc2e38e4104e2f0..81dbe187d02afcea8788ffa240d159765a87f347 100644 --- a/app/src/main/java/com/example/bondoman/adapter/TransactionListAdapter.kt +++ b/app/src/main/java/com/example/bondoman/adapter/TransactionListAdapter.kt @@ -6,20 +6,19 @@ import android.view.View import android.view.ViewGroup import android.widget.TextView import androidx.cardview.widget.CardView -import androidx.lifecycle.LiveData import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import com.example.bondoman.R import com.example.bondoman.activities.EditTransaction import com.example.bondoman.entities.Transaction +import com.example.bondoman.lib.SecurePreferences import com.example.bondoman.viewModels.TransactionsViewModel -import java.text.SimpleDateFormat import java.time.LocalDate import java.time.format.DateTimeFormatter import java.util.Locale -class TransactionListAdapter(private val transactions : TransactionsViewModel, private val startEditIntent: (Intent) -> Unit) : ListAdapter<Transaction, TransactionListAdapter.TransactionViewHolder>(TransactionComparator()) { +class TransactionListAdapter(private val transactions : TransactionsViewModel, private val startEditIntent: (Intent) -> Unit, securePreferences: SecurePreferences) : ListAdapter<Transaction, TransactionListAdapter.TransactionViewHolder>(TransactionComparator()) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TransactionViewHolder { return TransactionViewHolder.create(parent, transactions, startEditIntent) diff --git a/app/src/main/java/com/example/bondoman/apiServices/IAuthService.kt b/app/src/main/java/com/example/bondoman/apiServices/IAuthService.kt index 9112f0914cc5e33a9a6c2572a9499121eacdbf37..172847f50a5d6a714e14a4476d4b1e2dc0a53c27 100644 --- a/app/src/main/java/com/example/bondoman/apiServices/IAuthService.kt +++ b/app/src/main/java/com/example/bondoman/apiServices/IAuthService.kt @@ -1,6 +1,5 @@ package com.example.bondoman.apiServices -import retrofit2.Call import retrofit2.http.Body import retrofit2.http.Header import retrofit2.http.POST diff --git a/app/src/main/java/com/example/bondoman/fragments/GrafFragment.kt b/app/src/main/java/com/example/bondoman/fragments/GrafFragment.kt index caeb1bd6a6915ba89bce214f23d152a369b8e0ed..c81b5a2e52e0a29d08cb39b127ef2a50e77f48e5 100644 --- a/app/src/main/java/com/example/bondoman/fragments/GrafFragment.kt +++ b/app/src/main/java/com/example/bondoman/fragments/GrafFragment.kt @@ -11,6 +11,7 @@ import com.example.bondoman.database.TransactionDatabase import com.example.bondoman.databinding.FragmentGrafBinding import com.example.bondoman.entities.Transaction import com.example.bondoman.lib.ITransactionGraphAdapter +import com.example.bondoman.lib.SecurePreferences import com.example.bondoman.lib.TransactionPieChartAdapter import com.example.bondoman.repositories.TransactionRepository import com.example.bondoman.viewModels.TransactionViewModelFactory @@ -23,13 +24,14 @@ class GrafFragment : Fragment() { private var _binding: FragmentGrafBinding? = null private val binding get() = _binding!! private lateinit var graphAdapter: ITransactionGraphAdapter<PieChart> + private lateinit var securePreferences: SecurePreferences private val transactionViewModel: TransactionsViewModel by viewModels { TransactionViewModelFactory( TransactionRepository( TransactionDatabase.getInstance(requireContext(), CoroutineScope( SupervisorJob() ) - ).transactionDao()) + ).transactionDao(), securePreferences) ) } private lateinit var transactions: List<Transaction> @@ -37,6 +39,8 @@ class GrafFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) graphAdapter = TransactionPieChartAdapter() + securePreferences = SecurePreferences(requireContext()) + Log.i("EMAIL", securePreferences.getEmail() ?: "KOSONG") transactionViewModel.allTransaction.observe(this) { transactions = it } diff --git a/app/src/main/java/com/example/bondoman/fragments/LoginFragment.kt b/app/src/main/java/com/example/bondoman/fragments/LoginFragment.kt deleted file mode 100644 index 6b675b758f5f98858c81107c008f023111b5bb3b..0000000000000000000000000000000000000000 --- a/app/src/main/java/com/example/bondoman/fragments/LoginFragment.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.example.bondoman.fragments - -import android.os.Bundle -import androidx.fragment.app.Fragment -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import com.example.bondoman.R - -class LoginFragment : Fragment() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - } - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - // Inflate the layout for this fragment - return inflater.inflate(R.layout.fragment_login, container, false) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/example/bondoman/fragments/ScanFragment.kt b/app/src/main/java/com/example/bondoman/fragments/ScanFragment.kt index e3b6b0bb39b8af2d1bc669fd9803005b2e669a4e..d565eb2215871a2db1d0506260902d5cf3a65b36 100644 --- a/app/src/main/java/com/example/bondoman/fragments/ScanFragment.kt +++ b/app/src/main/java/com/example/bondoman/fragments/ScanFragment.kt @@ -19,7 +19,6 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Button -import android.widget.ListView import android.widget.TextView import android.widget.Toast import androidx.camera.core.CameraSelector @@ -29,9 +28,7 @@ import androidx.camera.core.ImageProxy import androidx.camera.core.Preview import androidx.camera.lifecycle.ProcessCameraProvider import androidx.camera.view.PreviewView -import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat -import androidx.core.content.FileProvider import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope @@ -45,7 +42,6 @@ import com.example.bondoman.viewModels.TransactionViewModelFactory import com.example.bondoman.viewModels.TransactionsViewModel import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -64,7 +60,7 @@ class ScanFragment : Fragment() { TransactionDatabase.getInstance(requireContext(), CoroutineScope( SupervisorJob() ) - ).transactionDao()) + ).transactionDao(), securePreferences) ) } private var loadingDialog: Dialog? = null @@ -253,7 +249,7 @@ class ScanFragment : Fragment() { } private fun showLoadingDialog() { loadingDialog = Dialog(requireContext()).apply { - setContentView(R.layout.loading_upload) + setContentView(R.layout.loading) setCancelable(false) window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) show() @@ -294,7 +290,6 @@ class ScanFragment : Fragment() { return imageFile } - companion object { private const val REQUEST_CODE_PERMISSIONS = 10 private val REQUIRED_PERMISSIONS = 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 72f6e9bea47a4c71b23d6c3f68dd470038bb2920..20d77ab3d52fa02b340aefa8f99c80531fa0a0b0 100644 --- a/app/src/main/java/com/example/bondoman/fragments/SettingsFragment.kt +++ b/app/src/main/java/com/example/bondoman/fragments/SettingsFragment.kt @@ -1,21 +1,32 @@ package com.example.bondoman.fragments +import android.content.Intent + +import android.net.Uri import android.os.Bundle import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.TextView +import android.widget.Toast +import androidx.appcompat.app.AlertDialog +import androidx.core.content.FileProvider import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope +import com.example.bondoman.R +import com.example.bondoman.activities.LoginActivity import com.example.bondoman.database.TransactionDatabase import com.example.bondoman.databinding.FragmentSettingsBinding import com.example.bondoman.entities.Transaction import com.example.bondoman.lib.ITransactionFileAdapter +import com.example.bondoman.lib.SecurePreferences import com.example.bondoman.lib.TransactionDownloader import com.example.bondoman.lib.TransactionExcelAdapter +import com.example.bondoman.repositories.AuthRepository import com.example.bondoman.repositories.TransactionRepository import com.example.bondoman.viewModels.TransactionViewModelFactory import com.example.bondoman.viewModels.TransactionsViewModel @@ -25,33 +36,47 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.async import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import java.io.File +import java.io.FileOutputStream import java.text.SimpleDateFormat import java.util.Date import java.util.Locale +import kotlin.random.Random class SettingsFragment : Fragment() { private var _binding: FragmentSettingsBinding? = null private val binding get() = _binding!! + private lateinit var securePreferences: SecurePreferences private val transactionViewModel: TransactionsViewModel by viewModels { TransactionViewModelFactory( TransactionRepository( - TransactionDatabase.getInstance(requireContext(), CoroutineScope( - SupervisorJob() - ) - ).transactionDao()) + TransactionDatabase.getInstance( + requireContext(), + CoroutineScope( + SupervisorJob() + ) + ).transactionDao(), securePreferences + ) ) } + private lateinit var authRepository: AuthRepository private lateinit var transactions: List<Transaction> private lateinit var transactionFileAdapter: ITransactionFileAdapter private lateinit var transactionDownloader: TransactionDownloader + private val XLSX_MIME_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + private val XLS_MIME_TYPE = "application/vnd.ms-excel" override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + securePreferences = SecurePreferences(requireContext()) transactionViewModel.allTransaction.observe(this) { transactions = it } transactionFileAdapter = TransactionExcelAdapter() transactionDownloader = TransactionDownloader() + authRepository = AuthRepository(SecurePreferences(requireContext())) + } override fun onCreateView( @@ -66,30 +91,45 @@ class SettingsFragment : Fragment() { super.onViewCreated(view, savedInstanceState) binding.loadingAnimation.isVisible = false binding.saveButton.setOnClickListener { - Log.d("SettingsFragment", "Loading started") - binding.saveButton.isClickable = false - showLoading() - val context = requireContext() - val fileName = createFileName(transactions) - this.lifecycleScope.launch { - val result = async(Dispatchers.IO) { - transactionDownloader.downloadTransactionAsFile( - context, - fileName, - transactions, - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - transactionFileAdapter - ) - } - result.await() - Log.d("SettingsFragment", "Loading finished") + showSaveTransactionDialog() + } + binding.sendButton.setOnClickListener { + handleSendButtonClick() + } + binding.randomizeButton.setOnClickListener { + Log.e("Random", "Randomize button is clicked") + broadcastRandomizeTransaction() + } + binding.logoutButton.setOnClickListener { + lifecycleScope.launch { + showLoading() + logout() hideLoading() - showSnackbar("Your transactions have been exported inside Download file") - binding.saveButton.isClickable = true } } } + private suspend fun logout() { + val response = authRepository.logout() + if (response.isSuccess) { + val intent = Intent(requireActivity(), LoginActivity::class.java) + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + startActivity(intent) + requireActivity().finish() + }else{ + withContext(Dispatchers.Main) { + val layoutInflater = LayoutInflater.from(requireContext()) + val view = layoutInflater.inflate(R.layout.custom_toast, null) + val textView = view.findViewById<TextView>(R.id.customToastText) + textView.text = "Logout failed, please try again!" + with (Toast(requireContext())) { + duration = Toast.LENGTH_LONG + setView(view) + show() + } + } + } + } private fun showLoading() { binding.loadingAnimation.isVisible = true } @@ -98,23 +138,134 @@ class SettingsFragment : Fragment() { binding.loadingAnimation.isVisible = false } - private fun createFileName(transactions: List<Transaction>): String { - val dateFormat = SimpleDateFormat("dd MM yyyy HH:mm:ss:SSS", Locale.getDefault()) + private fun createFileName(transactions: List<Transaction>, extension: String): String { + val dateFormat = SimpleDateFormat("dd-MM-yyyy HH:mm:ss:SSS", Locale.getDefault()) val currentTime = dateFormat.format(Date()) val userEmail = transactions.getOrNull(0)?.userEmail ?: "UnknownUser" val userName = userEmail.split("@").firstOrNull() ?: "UnknownUser" val fileName = "$currentTime $userName Transaction Summary" - return "$fileName.xlsx" + return "$fileName.$extension" } private fun showSnackbar(message: String) { Snackbar - .make(binding.snackbarContainer, message, Snackbar.LENGTH_INDEFINITE) + .make(binding.snackbarContainer, message, 5000) .setAction("OK") {} .show() } + private fun composeEmail(addresses: Array<String>, subject: String, text: String, attachment: Uri) { + val intent = Intent(Intent.ACTION_SEND) + intent.type = XLSX_MIME_TYPE + intent.putExtra(Intent.EXTRA_EMAIL, addresses) + intent.putExtra(Intent.EXTRA_SUBJECT, subject) + intent.putExtra(Intent.EXTRA_STREAM, attachment) + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION) + + if (intent.resolveActivity(requireActivity().packageManager) != null) { + startActivity(intent) + } + } + + private fun saveTransaction(extension: String) { + this.lifecycleScope.launch { + binding.saveButton.isClickable = false + val context = requireContext() + val fileName = createFileName(transactions, extension) + val result = async(Dispatchers.IO) { + return@async transactionDownloader.downloadTransactionAsFile( + context, + fileName, + transactions, + if (extension == "xlsx") XLSX_MIME_TYPE else XLS_MIME_TYPE, + transactionFileAdapter + ) + } + Log.d("SettingsFragment", "Loading started") + showLoading() + result.await() + Log.d("SettingsFragment", "Loading finished") + hideLoading() + showSnackbar("Your transactions have been exported inside Download file") + binding.saveButton.isClickable = true + } + } + + private fun showSaveTransactionDialog() { + var extension: String = "xlsx" + val choiceItems = arrayOf("xlsx", "xls") + val builder: AlertDialog.Builder = AlertDialog.Builder(requireContext()) + builder + .setTitle("Choose saved file format") + .setPositiveButton("OK") { dialog, _ -> + saveTransaction(extension) + } + .setNegativeButton("Cancel") { dialog, _ -> + dialog.dismiss() + } + .setSingleChoiceItems( + choiceItems, 0 + ) { dialog, which -> + extension = choiceItems[which] + } + + val dialog: AlertDialog = builder.create() + dialog.show() + } + + private fun handleSendButtonClick() { + val context = requireContext() + val fileName = createFileName(transactions, "xlsx") + val file = File(requireContext().externalCacheDir, fileName) + val outputStream = FileOutputStream(file) + + outputStream.use { + transactionFileAdapter.save(transactions, fileName, it) + } + composeEmail( + arrayOf(securePreferences.getEmail()!!), + "Bondoman Transaction Summary", + "Here's your latest transaction summary", + FileProvider.getUriForFile(context, context.applicationContext.packageName + ".provider", file) + ) + } + + private fun broadcastRandomizeTransaction() { + val randomizeIntent = Intent() + randomizeIntent.action = "com.example.bondoman.RANDOMIZE_TRANSACTION" + + val title = titleChoices.random() + val category = categoryChoices.random() + val amount = Random.nextInt(1, 1001) + + randomizeIntent.putExtra("TITLE", title) + randomizeIntent.putExtra("TYPE", category) + randomizeIntent.putExtra("AMOUNT", amount) + Log.e("Random", "Send Broadcast") + requireActivity().sendBroadcast(randomizeIntent) + } + + companion object { + private val titleChoices = arrayOf( + "Grocery Mart Purchase", + "Spring Wardrobe Update", + "Restaurant Dinner", + "Gas Station Refill", + "New Gadgets", + "Electricity Bill Payment", + "Local Farmer's Market Haul", + "Fitness Studio Membership Renewal", + "Home Essentials Restock", + "Coffee Shop Treat", + ) + + private val categoryChoices = arrayOf( + 0, + 1 + ) + } } \ No newline at end of file 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 01244ec2ef1814a277bcf9a211784ff33584bb4f..fe1e8330270c4f09ad068d44f8a99e3c9b4f96c0 100644 --- a/app/src/main/java/com/example/bondoman/fragments/TransaksiFragment.kt +++ b/app/src/main/java/com/example/bondoman/fragments/TransaksiFragment.kt @@ -7,17 +7,16 @@ import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.Toast import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.example.bondoman.R import com.example.bondoman.activities.AddTransaction -import com.example.bondoman.activities.EditTransaction import com.example.bondoman.adapter.TransactionListAdapter import com.example.bondoman.database.TransactionDatabase import com.example.bondoman.entities.Transaction +import com.example.bondoman.lib.SecurePreferences import com.example.bondoman.repositories.TransactionRepository import com.example.bondoman.viewModels.TransactionViewModelFactory import com.example.bondoman.viewModels.TransactionsViewModel @@ -30,30 +29,29 @@ class TransaksiFragment : Fragment() { private lateinit var adapter: TransactionListAdapter private val newTransactionRequestCode = 1 private val editTransactionRequestCode = 2 + private lateinit var securePreferences: SecurePreferences private val wordViewModel: TransactionsViewModel by viewModels { TransactionViewModelFactory( TransactionRepository( TransactionDatabase.getInstance(requireContext(), CoroutineScope( SupervisorJob() ) - ).transactionDao()) + ).transactionDao(), securePreferences) ) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + securePreferences = SecurePreferences(requireContext()) } - override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val view = inflater.inflate(R.layout.fragment_transaksi, container, false) - - val recyclerView: RecyclerView = view.findViewById(R.id.recycler_view) - adapter = TransactionListAdapter(wordViewModel, ::itemEditRequest) + adapter = TransactionListAdapter(wordViewModel, ::itemEditRequest, securePreferences) recyclerView.layoutManager = LinearLayoutManager(requireContext()) recyclerView.adapter = adapter @@ -75,9 +73,7 @@ class TransaksiFragment : Fragment() { override fun onActivityResult(requestCode: Int, resultCode: Int, intentData: Intent?) { super.onActivityResult(requestCode, resultCode, intentData) - - Log.i("Masuk", "Masuk") - + 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 @@ -91,7 +87,7 @@ class TransaksiFragment : Fragment() { amount = amount, location = location, date = Date(), - userEmail = "testing" + userEmail = securePreferences.getEmail() ?: "" ) wordViewModel.insert(transaction) } else if (requestCode == editTransactionRequestCode && resultCode == Activity.RESULT_OK) { diff --git a/app/src/main/java/com/example/bondoman/fragments/TwibbonFragment.kt b/app/src/main/java/com/example/bondoman/fragments/TwibbonFragment.kt index 186730e5ed5fbb0bf0d5d7213371f969ebaa5fb7..a69442a1dce9773f2d8a187dc44c084ad07dfa2c 100644 --- a/app/src/main/java/com/example/bondoman/fragments/TwibbonFragment.kt +++ b/app/src/main/java/com/example/bondoman/fragments/TwibbonFragment.kt @@ -4,10 +4,7 @@ import android.Manifest import android.content.pm.PackageManager import android.graphics.Bitmap import android.graphics.BitmapFactory -import android.graphics.ImageFormat import android.graphics.Matrix -import android.graphics.Rect -import android.graphics.YuvImage import android.hardware.camera2.CameraCharacteristics import android.media.ExifInterface import android.media.Image @@ -34,7 +31,6 @@ import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import com.example.bondoman.R import java.io.ByteArrayInputStream -import java.io.ByteArrayOutputStream import java.util.concurrent.ExecutorService import java.util.concurrent.Executors @@ -186,7 +182,7 @@ class TwibbonFragment : Fragment() { val bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size) // Rotate the bitmap if necessary - return rotateBitmap(bitmap, 90f, true) + return rotateBitmap(bitmap, 0f, true) } 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/interfaces/ITransactionDao.kt b/app/src/main/java/com/example/bondoman/interfaces/ITransactionDao.kt index a7502af0b3eb7bd154f2e6f7a16e8d73ba4e9a10..4c21ee91af898bd67eeba11bcd7192607811cf8c 100644 --- a/app/src/main/java/com/example/bondoman/interfaces/ITransactionDao.kt +++ b/app/src/main/java/com/example/bondoman/interfaces/ITransactionDao.kt @@ -13,7 +13,7 @@ import kotlinx.coroutines.flow.Flow @Dao interface ITransactionDao { - @Query("SELECT * FROM transactions WHERE userEmail = :userEmail OR userEmail != :userEmail ORDER BY date DESC") + @Query("SELECT * FROM transactions WHERE userEmail = :userEmail ORDER BY date DESC") fun getAll(userEmail : String): Flow<List<Transaction>> @Insert(onConflict = OnConflictStrategy.IGNORE) suspend fun insert(transaction: Transaction) diff --git a/app/src/main/java/com/example/bondoman/lib/TransactionDownloader.kt b/app/src/main/java/com/example/bondoman/lib/TransactionDownloader.kt index 5fd2a35237cf95865a8251e87835e4720dcf36bc..f733432b96a0646315d9a9bb9a7e2dbf9bd9c086 100644 --- a/app/src/main/java/com/example/bondoman/lib/TransactionDownloader.kt +++ b/app/src/main/java/com/example/bondoman/lib/TransactionDownloader.kt @@ -2,6 +2,7 @@ package com.example.bondoman.lib import android.content.ContentValues import android.content.Context +import android.net.Uri import android.os.Environment import android.provider.MediaStore import android.util.Log @@ -16,9 +17,8 @@ class TransactionDownloader { fileName: String, transactions: List<Transaction>, mimeType: String, - transactionFileAdapter: ITransactionFileAdapter) { - withContext(Dispatchers.IO) { - + transactionFileAdapter: ITransactionFileAdapter): Uri? { + return withContext(Dispatchers.IO) { val contentValues = ContentValues().apply { put(MediaStore.MediaColumns.DISPLAY_NAME, fileName) put(MediaStore.MediaColumns.MIME_TYPE, mimeType) @@ -30,13 +30,15 @@ class TransactionDownloader { if (uri != null) { try { resolver.openOutputStream(uri).use { outputStream -> - transactionFileAdapter.save(transactions, "love.xlsx", outputStream!!) + transactionFileAdapter.save(transactions, fileName, outputStream!!) } Log.d("File saved", "$fileName is saved successfully") } catch (e: IOException) { e.printStackTrace() } } + Log.d("TransactionDownloader", uri.toString()) + return@withContext uri } } } \ No newline at end of file diff --git a/app/src/main/java/com/example/bondoman/lib/TransactionExcelAdapter.kt b/app/src/main/java/com/example/bondoman/lib/TransactionExcelAdapter.kt index f4bb4cb455f5133432d3fd1863a1f9ac26b5f844..717e580330eeacca18a28186aec1c2b8ad945515 100644 --- a/app/src/main/java/com/example/bondoman/lib/TransactionExcelAdapter.kt +++ b/app/src/main/java/com/example/bondoman/lib/TransactionExcelAdapter.kt @@ -21,27 +21,28 @@ class TransactionExcelAdapter: ITransactionFileAdapter { val columnNames = sheet.createRow(0) columnNames.createCell(0).setCellValue("id") - columnNames.createCell(1).setCellValue("title") - columnNames.createCell(2).setCellValue("category") - columnNames.createCell(3).setCellValue("amount") - columnNames.createCell(4).setCellValue("location") - + columnNames.createCell(1).setCellValue("date") + columnNames.createCell(2).setCellValue("title") + columnNames.createCell(3).setCellValue("category") + columnNames.createCell(4).setCellValue("amount") + columnNames.createCell(5).setCellValue("location") for ((index, transaction) in transactions.withIndex()) { val row = sheet.createRow(index + 1) val idCell = row.createCell(0) idCell.setCellValue(transaction.id.toDouble()) - val titleCell = row.createCell(1) + val dateCell = row.createCell(1) + dateCell.setCellValue(transaction.date) + val titleCell = row.createCell(2) titleCell.setCellValue(transaction.title) - val categoryCell = row.createCell(2) + val categoryCell = row.createCell(3) categoryCell.setCellValue(transaction.category) - val amountCell = row.createCell(3) + val amountCell = row.createCell(4) amountCell.setCellValue(transaction.amount.toDouble()) - val locationCell = row.createCell(4) + val locationCell = row.createCell(5) locationCell.setCellValue(transaction.location) } -// val fos = FileOutputStream(fileName) workbook.write(outputStream) } } \ 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/receivers/RandomizeReceiver.kt b/app/src/main/java/com/example/bondoman/receivers/RandomizeReceiver.kt new file mode 100644 index 0000000000000000000000000000000000000000..20118bfa498be80ceae4fed38015ef05aa389b22 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/receivers/RandomizeReceiver.kt @@ -0,0 +1,14 @@ +package com.example.bondoman.receivers + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.util.Log + +class RandomizeReceiver: BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + if (intent?.action == "com.example.bondoman.RANDOMIZE_TRANSACTION") { + Log.d("AddTransaction", "Received") + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/bondoman/repositories/LoginRepository.kt b/app/src/main/java/com/example/bondoman/repositories/AuthRepository.kt similarity index 69% rename from app/src/main/java/com/example/bondoman/repositories/LoginRepository.kt rename to app/src/main/java/com/example/bondoman/repositories/AuthRepository.kt index 82998c5f4beec7491f7253c01d8150931988a50b..5192451aed57d31f4e931e2d2897d8ba7a52de2d 100644 --- a/app/src/main/java/com/example/bondoman/repositories/LoginRepository.kt +++ b/app/src/main/java/com/example/bondoman/repositories/AuthRepository.kt @@ -4,10 +4,12 @@ import android.util.Log import com.example.bondoman.apiServices.IAuthService import com.example.bondoman.apiServices.LoginRequest import com.example.bondoman.lib.SecurePreferences +import com.example.bondoman.retrofits.Retro -class LoginRepository(private val authService: IAuthService, private val securePreferences : SecurePreferences) { +class AuthRepository(private val securePreferences : SecurePreferences) { suspend fun login(email: String, password: String) : Result<String>{ + val authService = Retro().getRetroClientInstance().create(IAuthService::class.java) val request = LoginRequest().apply { this.email = email.trim() this.password = password @@ -29,4 +31,12 @@ class LoginRepository(private val authService: IAuthService, private val secureP Result.failure(e) } } + suspend fun logout() : Result<String>{ + return try { + securePreferences.clear() + Result.success("succes logout") + }catch (e : Exception){ + Result.failure(e) + } + } } diff --git a/app/src/main/java/com/example/bondoman/repositories/ScanRepository.kt b/app/src/main/java/com/example/bondoman/repositories/ScanRepository.kt index 414331bd0da6892bb5100b4a81ab9e5d7400b65a..c0de84668cb5d2de36e62eed625968507043ca4b 100644 --- a/app/src/main/java/com/example/bondoman/repositories/ScanRepository.kt +++ b/app/src/main/java/com/example/bondoman/repositories/ScanRepository.kt @@ -17,10 +17,8 @@ class ScanRepository(private val context : Context) { val requestFile = file.asRequestBody("image/jpg".toMediaTypeOrNull()) val body = MultipartBody.Part.createFormData("file", file.name, requestFile) val token = SecurePreferences(context).getToken() - Log.d("Token", "Bearer $token") - val response = Retro().getRetroClientInstance().create(IScanService::class.java).scan("Bearer $token", body) - Log.d("Response", response.toString()) - return response + val response = Retro().getRetroClientInstance().create(IScanService::class.java) + return response.scan("Bearer $token", body) } catch (e: Exception) { Log.e("UploadPhoto", "Error uploading photo", e) throw e // Re-throw if you want to handle it in the calling code diff --git a/app/src/main/java/com/example/bondoman/repositories/TransactionRepository.kt b/app/src/main/java/com/example/bondoman/repositories/TransactionRepository.kt index f6d0e13f6a8398012bd0f97d05ba03fd092afa1d..2ee2b3dbfb4a0412d887d8103e3cce9141ba1f8a 100644 --- a/app/src/main/java/com/example/bondoman/repositories/TransactionRepository.kt +++ b/app/src/main/java/com/example/bondoman/repositories/TransactionRepository.kt @@ -3,10 +3,11 @@ package com.example.bondoman.repositories import androidx.annotation.WorkerThread import com.example.bondoman.entities.Transaction import com.example.bondoman.interfaces.ITransactionDao +import com.example.bondoman.lib.SecurePreferences import kotlinx.coroutines.flow.Flow -class TransactionRepository(private val transactionDao: ITransactionDao) { - val allTransaction : Flow<List<Transaction>> = transactionDao.getAll("testing") +class TransactionRepository(private val transactionDao: ITransactionDao, securePreferences: SecurePreferences) { + val allTransaction : Flow<List<Transaction>> = transactionDao.getAll(securePreferences.getEmail() ?: "") @Suppress("RedundantSuspendModifier") @WorkerThread 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/anim/slide_down.xml b/app/src/main/res/anim/slide_down.xml new file mode 100644 index 0000000000000000000000000000000000000000..dd5f914d5569462d0cd3e7633f937fb00e294322 --- /dev/null +++ b/app/src/main/res/anim/slide_down.xml @@ -0,0 +1,10 @@ +<!-- slide_up.xml --> +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:fillAfter="true" + android:interpolator="@android:anim/accelerate_interpolator"> + + <translate + android:fromYDelta="100%p" + android:toYDelta="0" + android:duration="500" /> +</set> \ No newline at end of file diff --git a/app/src/main/res/anim/slide_morph.xml b/app/src/main/res/anim/slide_morph.xml new file mode 100644 index 0000000000000000000000000000000000000000..96353fd82b58e719905de0262feb6666889a831c --- /dev/null +++ b/app/src/main/res/anim/slide_morph.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <translate + android:fromYDelta="50%p" + android:toYDelta="0" + android:duration="500" /> +</set> \ No newline at end of file diff --git a/app/src/main/res/anim/slide_up.xml b/app/src/main/res/anim/slide_up.xml new file mode 100644 index 0000000000000000000000000000000000000000..22dbcee13e1cfad038b8039dbae7bf6144398c68 --- /dev/null +++ b/app/src/main/res/anim/slide_up.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <translate + android:fromYDelta="100%p" + android:toYDelta="0" + android:duration="500"/> +</set> \ No newline at end of file diff --git a/app/src/main/res/drawable-hdpi/ic_randomize.png b/app/src/main/res/drawable-hdpi/ic_randomize.png new file mode 100644 index 0000000000000000000000000000000000000000..5a4557d24aee299cb5034a713107cb498af72805 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_randomize.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_randomize.png b/app/src/main/res/drawable-mdpi/ic_randomize.png new file mode 100644 index 0000000000000000000000000000000000000000..d6be20a40c785f0ac8642edd924bcd052cda2bce Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_randomize.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_randomize.png b/app/src/main/res/drawable-xhdpi/ic_randomize.png new file mode 100644 index 0000000000000000000000000000000000000000..14c8a161763dac7ceed252db918f5754b575dc71 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_randomize.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_randomize.png b/app/src/main/res/drawable-xxhdpi/ic_randomize.png new file mode 100644 index 0000000000000000000000000000000000000000..2bc6d51a60a40548ae9895c3b7eabb9ca547b204 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_randomize.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_randomize.png b/app/src/main/res/drawable-xxxhdpi/ic_randomize.png new file mode 100644 index 0000000000000000000000000000000000000000..9c17bb1ddc59cf28bde6489e0de7007abf1e5bf1 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_randomize.png differ diff --git a/app/src/main/res/drawable/ic_search.xml b/app/src/main/res/drawable/ic_search.xml new file mode 100644 index 0000000000000000000000000000000000000000..d52a85d7b663e9a892310ff7faccf2bd6157f31e --- /dev/null +++ b/app/src/main/res/drawable/ic_search.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="#000000" + android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zm-6,0C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/> +</vector> diff --git a/app/src/main/res/drawable/icon_randomize.png b/app/src/main/res/drawable/icon_randomize.png new file mode 100644 index 0000000000000000000000000000000000000000..29c662674a66ae5dc44f9ceab1efccb67e160e63 Binary files /dev/null and b/app/src/main/res/drawable/icon_randomize.png differ diff --git a/app/src/main/res/layout/activity_add_transaksi.xml b/app/src/main/res/layout/activity_add_transaksi.xml index a50e5d76a2a9761e2c96317b6ecf47f7790166ec..c609127d78578b71d585cae6904768f93cac3b4f 100644 --- a/app/src/main/res/layout/activity_add_transaksi.xml +++ b/app/src/main/res/layout/activity_add_transaksi.xml @@ -1,98 +1,111 @@ <?xml version="1.0" encoding="utf-8"?> -<LinearLayout + +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="16dp"> - - - <TextView - android:id="@+id/textViewTypeLabel" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="Type" - android:layout_marginBottom="8dp" - android:layout_alignParentTop="true" - android:layout_alignParentStart="true" /> - - <RelativeLayout + <ScrollView + android:id="@+id/scrollable" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@drawable/spinner_enabled" - android:backgroundTint="@color/brown_1" - android:layout_marginBottom="8dp"> - <Spinner - android:id="@+id/spinnerCategory" + android:paddingBottom="50dp"> + <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" - style="@style/Widget.AppCompat.DropDownItem.Spinner" - android:textSize="18sp" - android:paddingVertical="6dp" - android:paddingHorizontal="12dp" - /> - </RelativeLayout> - - - - <TextView - android:id="@+id/textViewTitleLabel" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="Title" - android:layout_marginBottom="8dp" - android:layout_alignParentTop="true" - android:layout_alignParentStart="true" /> - - <EditText - android:id="@+id/editTextTitle" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="@drawable/edittext_enabled" - android:paddingVertical="6dp" - android:paddingHorizontal="12dp" - android:layout_marginBottom="8dp" - android:hint="Title" /> - - <TextView - android:id="@+id/textViewAmountLabel" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="Amount" - android:layout_marginBottom="8dp" - android:layout_alignParentTop="true" - android:layout_alignParentStart="true" /> - - <EditText - android:id="@+id/editTextAmount" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="@drawable/edittext_enabled" - android:inputType="numberDecimal" - android:paddingVertical="6dp" - android:paddingHorizontal="12dp" - android:layout_marginBottom="8dp" - android:hint="Amount" /> - - <TextView - android:id="@+id/textViewLocationLabel" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="Location" - android:layout_marginBottom="8dp" - android:layout_alignParentTop="true" - android:layout_alignParentStart="true" /> + android:orientation="vertical"> + <TextView + android:id="@+id/textViewTypeLabel" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Type" + android:layout_marginBottom="8dp" + android:layout_alignParentTop="true" + android:layout_alignParentStart="true" /> + + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@drawable/spinner_enabled" + android:backgroundTint="@color/brown_1" + android:layout_marginBottom="8dp"> + <Spinner + android:id="@+id/spinnerCategory" + android:layout_width="match_parent" + android:layout_height="wrap_content" + style="@style/Widget.AppCompat.DropDownItem.Spinner" + android:textSize="18sp" + android:paddingVertical="6dp" + android:paddingHorizontal="12dp" + /> + </RelativeLayout> + + + + <TextView + android:id="@+id/textViewTitleLabel" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Title" + android:layout_marginBottom="8dp" + android:layout_alignParentTop="true" + android:layout_alignParentStart="true" /> + + <EditText + android:id="@+id/editTextTitle" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@drawable/edittext_enabled" + android:paddingVertical="6dp" + android:paddingHorizontal="12dp" + android:layout_marginBottom="8dp" + android:hint="Title" /> + + <TextView + android:id="@+id/textViewAmountLabel" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Amount" + android:layout_marginBottom="8dp" + android:layout_alignParentTop="true" + android:layout_alignParentStart="true" /> + + <EditText + android:id="@+id/editTextAmount" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@drawable/edittext_enabled" + android:inputType="numberDecimal" + android:paddingVertical="6dp" + android:paddingHorizontal="12dp" + android:layout_marginBottom="8dp" + android:hint="Amount" /> + + <TextView + android:id="@+id/textViewLocationLabel" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Location" + android:layout_marginBottom="8dp" + android:layout_alignParentTop="true" + android:layout_alignParentStart="true" /> + + <EditText + android:id="@+id/editTextLocation" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@drawable/edittext_enabled" + android:paddingVertical="6dp" + android:paddingHorizontal="12dp" + android:layout_marginBottom="8dp" + android:hint="Location" /> + + </LinearLayout> + + </ScrollView> - <EditText - android:id="@+id/editTextLocation" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="@drawable/edittext_enabled" - android:paddingVertical="6dp" - android:paddingHorizontal="12dp" - android:layout_marginBottom="8dp" - android:hint="Location" /> <Space @@ -108,9 +121,9 @@ android:text="Submit" android:backgroundTint="@color/green" android:textColor="@color/white" + android:layout_gravity="bottom" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintBottom_toBottomOf="parent" /> +</FrameLayout> - -</LinearLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_edit_transaksi.xml b/app/src/main/res/layout/activity_edit_transaksi.xml index f5771c5fe288b785eae7096276ebcffdf41fa5d5..717af90fea4a0ebd04ecc046750dbeef1e2bbd87 100644 --- a/app/src/main/res/layout/activity_edit_transaksi.xml +++ b/app/src/main/res/layout/activity_edit_transaksi.xml @@ -1,146 +1,161 @@ <?xml version="1.0" encoding="utf-8"?> -<LinearLayout +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="16dp"> + <ScrollView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="100dp"> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + <TextView + android:id="@+id/textViewDateLabel" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Date" + android:layout_marginBottom="8dp" + android:layout_alignParentTop="true" + android:layout_alignParentStart="true" /> + + <TextView + android:id="@+id/editTextDate" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:inputType="none" + android:background="@drawable/edittext_disabled" + android:paddingVertical="6dp" + android:paddingHorizontal="12dp" + android:layout_marginBottom="8dp" + android:hint="Select Date" /> + + <TextView + android:id="@+id/textViewTypeLabel" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Type" + android:layout_marginBottom="8dp" + android:layout_alignParentTop="true" + android:layout_alignParentStart="true" /> + + <TextView + android:id="@+id/editTextType" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:inputType="none" + android:background="@drawable/edittext_disabled" + android:paddingVertical="6dp" + android:paddingHorizontal="12dp" + android:layout_marginBottom="8dp" + android:hint="Income" /> + + <TextView + android:id="@+id/textViewTitleLabel" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Title" + android:layout_marginBottom="8dp" + android:layout_alignParentTop="true" + android:layout_alignParentStart="true" /> + + <EditText + android:id="@+id/editTextTitle" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@drawable/edittext_enabled" + android:paddingVertical="6dp" + android:paddingHorizontal="12dp" + android:layout_marginBottom="8dp" + android:hint="Title" /> + + <TextView + android:id="@+id/textViewAmountLabel" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Amount" + android:layout_marginBottom="8dp" + android:layout_alignParentTop="true" + android:layout_alignParentStart="true" /> + + <EditText + android:id="@+id/editTextAmount" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@drawable/edittext_enabled" + android:inputType="numberDecimal" + android:paddingVertical="6dp" + android:paddingHorizontal="12dp" + android:layout_marginBottom="8dp" + android:hint="Amount" /> + + <TextView + android:id="@+id/textViewLocationLabel" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Location" + android:layout_marginBottom="8dp" + android:layout_alignParentTop="true" + android:layout_alignParentStart="true" /> + + <EditText + android:id="@+id/editTextLocation" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@drawable/edittext_enabled" + android:paddingVertical="6dp" + android:paddingHorizontal="12dp" + android:layout_marginBottom="8dp" + android:hint="Location" /> + + <TextView + android:id="@+id/textViewSeeLocationLabel" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="See on Google Maps" + android:layout_marginBottom="8dp" + android:layout_alignParentTop="true" + android:layout_alignParentStart="true" + android:textStyle="italic" + android:layout_gravity="right"/> + + + </LinearLayout> + + </ScrollView> + + + <Space + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1"/> + + <Button + android:id="@+id/buttonDelete" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Delete" + android:backgroundTint="@color/brown_1" + android:textColor="@color/green" + android:layout_gravity="bottom" + android:layout_marginBottom="50dp"/> + + + <Button + android:id="@+id/buttonSubmit" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Save changes" + android:backgroundTint="@color/green" + android:textColor="@color/white" + android:layout_gravity="bottom" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintBottom_toBottomOf="parent" /> + +</FrameLayout> - <TextView - android:id="@+id/textViewDateLabel" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="Date" - android:layout_marginBottom="8dp" - android:layout_alignParentTop="true" - android:layout_alignParentStart="true" /> - - <TextView - android:id="@+id/editTextDate" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:inputType="none" - android:background="@drawable/edittext_disabled" - android:paddingVertical="6dp" - android:paddingHorizontal="12dp" - android:layout_marginBottom="8dp" - android:hint="Select Date" /> - - <TextView - android:id="@+id/textViewTypeLabel" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="Type" - android:layout_marginBottom="8dp" - android:layout_alignParentTop="true" - android:layout_alignParentStart="true" /> - - <TextView - android:id="@+id/editTextType" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:inputType="none" - android:background="@drawable/edittext_disabled" - android:paddingVertical="6dp" - android:paddingHorizontal="12dp" - android:layout_marginBottom="8dp" - android:hint="Income" /> - - <TextView - android:id="@+id/textViewTitleLabel" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="Title" - android:layout_marginBottom="8dp" - android:layout_alignParentTop="true" - android:layout_alignParentStart="true" /> - - <EditText - android:id="@+id/editTextTitle" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="@drawable/edittext_enabled" - android:paddingVertical="6dp" - android:paddingHorizontal="12dp" - android:layout_marginBottom="8dp" - android:hint="Title" /> - - <TextView - android:id="@+id/textViewAmountLabel" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="Amount" - android:layout_marginBottom="8dp" - android:layout_alignParentTop="true" - android:layout_alignParentStart="true" /> - - <EditText - android:id="@+id/editTextAmount" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="@drawable/edittext_enabled" - android:inputType="numberDecimal" - android:paddingVertical="6dp" - android:paddingHorizontal="12dp" - android:layout_marginBottom="8dp" - android:hint="Amount" /> - - <TextView - android:id="@+id/textViewLocationLabel" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="Location" - android:layout_marginBottom="8dp" - android:layout_alignParentTop="true" - android:layout_alignParentStart="true" /> - - <EditText - android:id="@+id/editTextLocation" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="@drawable/edittext_enabled" - android:paddingVertical="6dp" - android:paddingHorizontal="12dp" - android:layout_marginBottom="8dp" - android:hint="Location" /> - - <TextView - android:id="@+id/textViewSeeLocationLabel" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="See on Google Maps" - android:layout_marginBottom="8dp" - android:layout_alignParentTop="true" - android:layout_alignParentStart="true" - android:textStyle="italic" - android:layout_gravity="right"/> - - - <Space - android:layout_width="match_parent" - android:layout_height="0dp" - android:layout_weight="1"/> - - <Button - android:id="@+id/buttonDelete" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:text="Delete" - android:backgroundTint="@color/brown_1" - android:textColor="@color/green" /> - - - <Button - android:id="@+id/buttonSubmit" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:text="Save changes" - android:backgroundTint="@color/green" - android:textColor="@color/white" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintBottom_toBottomOf="parent" /> - - -</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 index 4bf4fb7b3ad9a0a5d0a6cd6e6b96f2d5e47c5945..085884ffeff8088dcd67fb6a8931f290ab9ccdc9 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -6,126 +6,131 @@ android:layout_height="match_parent" tools:context=".activities.LoginActivity" android:paddingHorizontal="20dp" + android:layout_gravity="center" > - - <ImageView - android:id="@+id/imageView" - android:layout_width="266dp" - android:layout_height="94dp" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintHorizontal_bias="0.496" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintVertical_bias="0.262" - app:srcCompat="@drawable/bondoman" - /> - - <androidx.cardview.widget.CardView + <LinearLayout + android:id="@+id/layout" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginHorizontal="40dp" - android:layout_marginEnd="8dp" - app:cardCornerRadius="30dp" - app:cardElevation="4dp" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toBottomOf="@+id/imageView" - app:layout_constraintVertical_bias="0.1" - app:cardBackgroundColor="@color/white"> + android:layout_height="match_parent" + android:orientation="vertical" + android:gravity="center"> + <ImageView + android:id="@+id/imageView" + android:layout_width="266dp" + android:layout_height="94dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + android:layout_marginHorizontal="30dp" + app:srcCompat="@drawable/bondoman" + /> - <LinearLayout + <androidx.cardview.widget.CardView + android:visibility="gone" + android:id="@+id/login_card" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginHorizontal="20dp" - android:layout_marginVertical="30dp" - android:orientation="vertical" - > - <TextView - android:id="@+id/textView" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:fontFamily="@font/montserrat_600" - android:text="Masuk" - android:textAlignment="center" - android:textColor="@color/black" - android:textColorHint="@color/black" - android:textSize="24sp" - android:textStyle="bold" /> + app:cardCornerRadius="30dp" + app:cardElevation="4dp" + android:layout_marginVertical="20dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toBottomOf="@+id/imageView" + app:layout_constraintVertical_bias="0.1" + app:cardBackgroundColor="@color/white"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginHorizontal="20dp" + android:layout_marginVertical="20dp" android:orientation="vertical" - android:layout_marginTop="12dp" > - <TextView + android:id="@+id/textView" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="10dp" - android:fontFamily="@font/montserrat_500" - android:text="Email" + android:fontFamily="@font/montserrat_600" + android:text="Masuk" + android:textAlignment="center" android:textColor="@color/black" - android:textSize="14sp" /> - <EditText - android:layout_width="match_parent" - android:layout_height="40dp" - android:id="@+id/email" - android:background="@drawable/input_bg" - android:layout_marginTop="10dp" - android:paddingHorizontal="15dp" - /> - </LinearLayout> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - android:layout_marginTop="15dp" - > - <TextView - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="10dp" - android:fontFamily="@font/montserrat_500" - android:text="Password" - android:textColor="@color/black" - android:textSize="14sp" /> - <com.google.android.material.textfield.TextInputLayout + android:textColorHint="@color/black" + android:textSize="24sp" + android:textStyle="bold" /> + + <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" - app:passwordToggleEnabled="true" + android:orientation="vertical" + android:layout_marginTop="12dp" > + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="10dp" + android:fontFamily="@font/montserrat_500" + android:text="Email" + android:textColor="@color/black" + android:textSize="14sp" /> <EditText android:layout_width="match_parent" android:layout_height="40dp" - android:id="@+id/password" + android:id="@+id/email" + android:inputType="textEmailAddress" android:background="@drawable/input_bg" android:layout_marginTop="10dp" android:paddingHorizontal="15dp" - android:inputType="textPassword" /> - </com.google.android.material.textfield.TextInputLayout> - - </LinearLayout> - - <Button - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:id="@+id/login_button" - android:fontFamily="@font/montserrat_700" - android:background="@drawable/rounded_button" - android:text="Masuk" - android:layout_marginTop="20dp" - android:layout_gravity="center_horizontal" - android:textColor="@color/white" - android:paddingHorizontal="15dp" - /> - </LinearLayout> + </LinearLayout> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:layout_marginTop="15dp" + > + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="10dp" + android:fontFamily="@font/montserrat_500" + android:text="Password" + android:textColor="@color/black" + android:textSize="14sp" /> + <com.google.android.material.textfield.TextInputLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:passwordToggleEnabled="true" + > + <EditText + android:layout_width="match_parent" + android:layout_height="40dp" + android:id="@+id/password" + android:background="@drawable/input_bg" + android:layout_marginTop="10dp" + android:paddingHorizontal="15dp" + android:inputType="textPassword" + /> + </com.google.android.material.textfield.TextInputLayout> + </LinearLayout> - </androidx.cardview.widget.CardView> + <Button + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:id="@+id/login_button" + android:fontFamily="@font/montserrat_700" + android:background="@drawable/rounded_button" + android:text="Masuk" + android:layout_marginTop="20dp" + android:layout_gravity="center_horizontal" + android:textColor="@color/white" + android:paddingHorizontal="15dp" + /> + </LinearLayout> + </androidx.cardview.widget.CardView> + </LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index f3b90c2bdba1cbe770d7ca7589e7a5cac39cd0cb..be2dba81140f7a73a50ec9f94977ee33dc081953 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -10,15 +10,29 @@ <fragment android:id="@+id/nav_fragment" android:name="androidx.navigation.fragment.NavHostFragment" - android:layout_width="match_parent" + android:layout_width="0dp" android:layout_height="0dp" app:defaultNavHost="true" app:layout_constraintBottom_toTopOf="@+id/bottom_navigation_view" - app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintLeft_toRightOf="@+id/navigation_view" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:navGraph="@navigation/nav_graph"/> + <com.google.android.material.navigation.NavigationView + android:id="@+id/navigation_view" + android:layout_width="60dp" + android:layout_height="match_parent" + android:layout_gravity="left" + app:itemIconTint="@color/menu_color" + app:itemTextColor="@color/green" + android:backgroundTint="@color/brown_1" + app:itemBackground="@color/brown_1" + android:background="?android:attr/windowBackground" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintLeft_toLeftOf="parent" + app:menu="@menu/menu_nav"/> + <com.google.android.material.bottomnavigation.BottomNavigationView android:id="@+id/bottom_navigation_view" android:layout_width="0dp" @@ -28,6 +42,8 @@ app:itemIconTint="@color/menu_color" app:itemTextColor="@color/green" app:itemBackground="@color/brown_1" + android:backgroundTint="@color/brown_1" + app:itemHorizontalTranslationEnabled="false" android:background="?android:attr/windowBackground" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" diff --git a/app/src/main/res/layout/fragment_login.xml b/app/src/main/res/layout/fragment_login.xml deleted file mode 100644 index 251d39abf35242a9e567456828093131d873dc21..0000000000000000000000000000000000000000 --- a/app/src/main/res/layout/fragment_login.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?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=".fragments.LoginFragment"> - - <!-- TODO: Update blank fragment layout --> - - <androidx.constraintlayout.widget.ConstraintLayout - android:layout_width="match_parent" - android:layout_height="match_parent"> - - <TextView - android:layout_width="208dp" - android:layout_height="47dp" - android:text="@string/hello_blank_fragment" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> - </androidx.constraintlayout.widget.ConstraintLayout> - -</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 9e4852d26aebf883e0b805a90eb9872388fb3fa6..38b1d48794ae75ba6aca6ae18494660b78b0429b 100644 --- a/app/src/main/res/layout/fragment_settings.xml +++ b/app/src/main/res/layout/fragment_settings.xml @@ -6,94 +6,110 @@ android:layout_height="match_parent" android:background="@color/bg_color" tools:context=".fragments.SettingsFragment"> - - <androidx.constraintlayout.widget.ConstraintLayout + <ScrollView android:layout_width="match_parent" android:layout_height="match_parent"> + <androidx.constraintlayout.widget.ConstraintLayout + android:layout_width="match_parent" + android:layout_height="match_parent"> - <LinearLayout - android:layout_width="329dp" - android:layout_height="452dp" - android:orientation="vertical" - android:background="@color/bg_color" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintVertical_bias="0.175"> - - <Button - android:id="@+id/save_button" - android:layout_width="match_parent" + <LinearLayout + android:id="@+id/settings_linear_layout" + android:layout_width="329dp" android:layout_height="wrap_content" - android:layout_marginBottom="24dp" - android:background="@drawable/button_background" - android:drawableStart="@drawable/ic_download" - android:drawablePadding="16dp" - android:elevation="24dp" - android:paddingHorizontal="16dp" - android:paddingVertical="16dp" - android:text="@string/save_button_text" - android:textAlignment="viewStart" - android:textAllCaps="false" - android:textSize="17sp" - android:textStyle="bold" /> + android:orientation="vertical" + android:background="@color/bg_color" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="0.175"> - <Button - android:id="@+id/send_button" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginBottom="24dp" - android:background="@drawable/button_background" - android:drawableStart="@drawable/ic_send" - android:drawablePadding="16dp" - android:paddingHorizontal="16dp" - android:paddingVertical="16dp" - android:text="@string/send_button_text" - android:textAlignment="viewStart" - android:textAllCaps="false" - android:textSize="17sp" - android:textStyle="bold" /> + <Button + android:id="@+id/save_button" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="24dp" + android:background="@drawable/button_background" + android:drawableStart="@drawable/ic_download" + android:drawablePadding="16dp" + android:elevation="24dp" + android:paddingHorizontal="16dp" + android:paddingVertical="16dp" + android:text="@string/save_button_text" + android:textAlignment="viewStart" + android:textAllCaps="false" + android:textSize="17sp" + android:textStyle="bold" /> - <Button - android:id="@+id/logout_button" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginBottom="24dp" - android:background="@drawable/button_background" - android:drawableStart="@drawable/ic_logout" - android:drawablePadding="16dp" - android:paddingHorizontal="16dp" - android:paddingVertical="16dp" - android:text="@string/logout_button_text" - android:textAlignment="viewStart" - android:textAllCaps="false" - android:textSize="17sp" - android:textStyle="bold" /> - </LinearLayout> + <Button + android:id="@+id/send_button" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="24dp" + android:background="@drawable/button_background" + android:drawableStart="@drawable/ic_send" + android:drawablePadding="16dp" + android:paddingHorizontal="16dp" + android:paddingVertical="16dp" + android:text="@string/send_button_text" + android:textAlignment="viewStart" + android:textAllCaps="false" + android:textSize="17sp" + android:textStyle="bold" /> + + <Button + android:id="@+id/randomize_button" + 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:drawablePadding="16dp" + android:paddingHorizontal="16dp" + android:paddingVertical="16dp" + android:text="Generate Random Transaction" + android:textAlignment="viewStart" + android:textAllCaps="false" + android:textSize="17sp" + android:textStyle="bold" /> + + <Button + android:id="@+id/logout_button" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="24dp" + android:background="@drawable/button_background" + android:drawableStart="@drawable/ic_logout" + android:drawablePadding="16dp" + android:paddingHorizontal="16dp" + android:paddingVertical="16dp" + android:text="@string/logout_button_text" + android:textAlignment="viewStart" + android:textAllCaps="false" + android:textSize="17sp" + android:textStyle="bold" /> + + <com.airbnb.lottie.LottieAnimationView + android:id="@+id/loadingAnimation" + android:layout_width="335dp" + android:layout_height="95dp" + app:lottie_autoPlay="true" + app:lottie_loop="true" + app:lottie_rawRes="@raw/loading" /> + </LinearLayout> - <com.airbnb.lottie.LottieAnimationView - android:id="@+id/loadingAnimation" - android:layout_width="335dp" - android:layout_height="95dp" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" - app:lottie_autoPlay="true" - app:lottie_loop="true" - app:lottie_rawRes="@raw/loading" /> + <androidx.coordinatorlayout.widget.CoordinatorLayout + android:id="@+id/snackbarContainer" + android:layout_width="309dp" + android:layout_height="197dp" + android:orientation="vertical" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/settings_linear_layout" + app:layout_constraintVertical_bias="0.421" /> + </androidx.constraintlayout.widget.ConstraintLayout> + </ScrollView> - <androidx.coordinatorlayout.widget.CoordinatorLayout - android:id="@+id/snackbarContainer" - android:layout_width="309dp" - android:layout_height="197dp" - android:orientation="vertical" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintHorizontal_bias="0.495" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/loadingAnimation" - app:layout_constraintVertical_bias="0.818" /> - </androidx.constraintlayout.widget.ConstraintLayout> </FrameLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_transaksi.xml b/app/src/main/res/layout/fragment_transaksi.xml index 42df984082f2b5695565457caed71a8d791b7062..0ea9128419aa3941976e030f41e8f864fae2febf 100644 --- a/app/src/main/res/layout/fragment_transaksi.xml +++ b/app/src/main/res/layout/fragment_transaksi.xml @@ -7,11 +7,6 @@ tools:context=".fragments.TransaksiFragment" android:padding="16dp"> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:id="@+id/test" - /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" diff --git a/app/src/main/res/layout/fragment_twibbon.xml b/app/src/main/res/layout/fragment_twibbon.xml index e070c02f2e45bb1e015d20d61f0684f5329b60a4..0a2e513ef3ae923ba1b7f2a939fb0607831d5f8d 100644 --- a/app/src/main/res/layout/fragment_twibbon.xml +++ b/app/src/main/res/layout/fragment_twibbon.xml @@ -5,109 +5,122 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".fragments.TwibbonFragment"> + tools:context=".fragments.TwibbonFragment" + android:layout_gravity="center"> - <LinearLayout + <ScrollView android:layout_width="wrap_content" android:layout_height="wrap_content" - android:orientation="vertical" - app:layout_constraintLeft_toLeftOf="parent" + android:foregroundGravity="center" app:layout_constraintRight_toRightOf="parent" + app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" - android:gravity="center" > + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + android:paddingVertical="30dp" + android:gravity="center" + > - <FrameLayout - android:layout_width="300dp" - android:layout_height="wrap_content" - android:layout_marginBottom="20dp"> <FrameLayout - android:id="@+id/preview" android:layout_width="300dp" - android:layout_height="300dp" - android:layout_gravity="center"> - <androidx.camera.view.PreviewView - android:id="@+id/viewFinder" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:scaleType="fitCenter"/> + android:layout_height="wrap_content" + android:layout_marginBottom="20dp"> + <FrameLayout + android:id="@+id/preview" + android:layout_width="300dp" + android:layout_height="300dp" + android:layout_gravity="center"> + <androidx.camera.view.PreviewView + android:id="@+id/viewFinder" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:scaleType="fitCenter"/> + </FrameLayout> + + <ImageView + android:id="@+id/overlay_image" + android:layout_width="300dp" + android:layout_height="300dp" + android:layout_gravity="center" + android:scaleType="fitCenter" + android:src="@drawable/twibbon_1" /> + <ImageView + android:id="@+id/imageView" + android:layout_width="300dp" + android:layout_height="300dp" + android:visibility="gone" + android:scaleType="centerCrop" + android:layout_gravity="center"/> </FrameLayout> + <HorizontalScrollView + android:id="@+id/horizontalScrollView" + android:layout_width="match_parent" + android:layout_height="wrap_content"> - <ImageView - android:id="@+id/overlay_image" - android:layout_width="300dp" - android:layout_height="300dp" - android:layout_gravity="center" - android:scaleType="fitCenter" - android:src="@drawable/twibbon_1" /> - <ImageView - android:id="@+id/imageView" - android:layout_width="300dp" - android:layout_height="300dp" - android:visibility="gone" - android:scaleType="centerCrop" - android:layout_gravity="center"/> - </FrameLayout> - <HorizontalScrollView - android:id="@+id/horizontalScrollView" - android:layout_width="match_parent" - android:layout_height="wrap_content"> + <LinearLayout + android:id="@+id/buttonContainer" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal"> - <LinearLayout - android:id="@+id/buttonContainer" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal"> + <Button + android:id="@+id/button1" + android:layout_width="80dp" + android:layout_height="80dp" + android:background="@drawable/twibbon_1" + android:layout_marginEnd="10dp"/> - <Button - android:id="@+id/button1" - android:layout_width="80dp" - android:layout_height="80dp" - android:background="@drawable/twibbon_1" - android:layout_marginEnd="10dp"/> + <Button + android:id="@+id/button2" + android:layout_width="80dp" + android:layout_height="80dp" + android:background="@drawable/twibbon_2" + android:layout_marginEnd="10dp"/> - <Button - android:id="@+id/button2" - android:layout_width="80dp" - android:layout_height="80dp" - android:background="@drawable/twibbon_2" - android:layout_marginEnd="10dp"/> + <Button + android:id="@+id/button3" + android:layout_width="80dp" + android:layout_height="80dp" + android:background="@drawable/twibbon_3" + android:layout_marginEnd="10dp"/> - <Button - android:id="@+id/button3" - android:layout_width="80dp" - android:layout_height="80dp" - android:background="@drawable/twibbon_3" - android:layout_marginEnd="10dp"/> + <Button + android:id="@+id/button4" + android:layout_width="80dp" + android:layout_height="80dp" + android:background="@drawable/twibbon_4" + android:layout_marginEnd="10dp"/> + <Button + android:id="@+id/button5" + android:layout_width="80dp" + android:layout_height="80dp" + android:background="@drawable/twibbon_5"/> - <Button - android:id="@+id/button4" - android:layout_width="80dp" - android:layout_height="80dp" - android:background="@drawable/twibbon_4" - android:layout_marginEnd="10dp"/> - <Button - android:id="@+id/button5" - android:layout_width="80dp" - android:layout_height="80dp" - android:background="@drawable/twibbon_5"/> + <!-- Add more buttons as needed --> - <!-- Add more buttons as needed --> + </LinearLayout> + </HorizontalScrollView> - </LinearLayout> - </HorizontalScrollView> + <Button + android:id="@+id/image_capture_button" + android:layout_width="90dp" + android:layout_height="50dp" + android:layout_marginTop="20dp" + android:background="@drawable/button_green" + android:textColor="@color/white" + android:elevation="2dp" + android:text="CAPTURE" + app:layout_constraintTop_toBottomOf="@id/viewFinder"/> - <Button - android:id="@+id/image_capture_button" - android:layout_width="90dp" - android:layout_height="50dp" - android:layout_marginTop="20dp" - android:background="@drawable/button_green" - android:textColor="@color/white" - android:elevation="2dp" - android:text="CAPTURE" - app:layout_constraintTop_toBottomOf="@id/viewFinder"/> + </LinearLayout> - </LinearLayout> + </ScrollView> </androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/loading_upload.xml b/app/src/main/res/layout/loading.xml similarity index 67% rename from app/src/main/res/layout/loading_upload.xml rename to app/src/main/res/layout/loading.xml index b3a7629f21bae074144cb04a9ac4328cd5b0f68a..cd4a2cfa9d47365470d0ddefaac4aa4075863165 100644 --- a/app/src/main/res/layout/loading_upload.xml +++ b/app/src/main/res/layout/loading.xml @@ -11,11 +11,4 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" /> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="Uploading..." - android:layout_marginTop="10dp" - android:textColor="@android:color/white" /> - </LinearLayout> diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000000000000000000000000000000000000..90f958096f81d050d55c085b7c3bad49456f9673 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@mipmap/ic_launcher_adaptive_back"/> + <foreground android:drawable="@mipmap/ic_launcher_adaptive_fore"/> +</adaptive-icon> \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher.xml b/app/src/main/res/mipmap-anydpi/ic_launcher.xml deleted file mode 100644 index 6f3b755bf50c6b03d8714a9c6184705e6a08389f..0000000000000000000000000000000000000000 --- a/app/src/main/res/mipmap-anydpi/ic_launcher.xml +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> - <background android:drawable="@drawable/ic_launcher_background" /> - <foreground android:drawable="@drawable/ic_launcher_foreground" /> - <monochrome android:drawable="@drawable/ic_launcher_foreground" /> -</adaptive-icon> \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml deleted file mode 100644 index 6f3b755bf50c6b03d8714a9c6184705e6a08389f..0000000000000000000000000000000000000000 --- a/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> - <background android:drawable="@drawable/ic_launcher_background" /> - <foreground android:drawable="@drawable/ic_launcher_foreground" /> - <monochrome android:drawable="@drawable/ic_launcher_foreground" /> -</adaptive-icon> \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..c4b29d827ca1033d90d44872f9134c146d605eae Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp deleted file mode 100644 index c209e78ecd372343283f4157dcfd918ec5165bb3..0000000000000000000000000000000000000000 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_back.png b/app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_back.png new file mode 100644 index 0000000000000000000000000000000000000000..19669488f1b2f9321fa4d25c1334bc323c92fbb2 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_back.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_fore.png b/app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_fore.png new file mode 100644 index 0000000000000000000000000000000000000000..a0de360d45608e70289265ebc229028aa7047297 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_fore.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp deleted file mode 100644 index b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9..0000000000000000000000000000000000000000 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..a061eafe1075fd0ce86c875dd61d61375edee560 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp deleted file mode 100644 index 4f0f1d64e58ba64d180ce43ee13bf9a17835fbca..0000000000000000000000000000000000000000 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_back.png b/app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_back.png new file mode 100644 index 0000000000000000000000000000000000000000..75025cfd5ccc1702d5ada42072626e319bca6cbe Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_back.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_fore.png b/app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_fore.png new file mode 100644 index 0000000000000000000000000000000000000000..c9ef358ef5cc02ae9058edd28fe0cb95da624b8c Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_fore.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp deleted file mode 100644 index 62b611da081676d42f6c3f78a2c91e7bcedddedb..0000000000000000000000000000000000000000 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..5cb1e77b57a7749de1c599d29d2925a900f0e4a7 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp deleted file mode 100644 index 948a3070fe34c611c42c0d3ad3013a0dce358be0..0000000000000000000000000000000000000000 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_back.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_back.png new file mode 100644 index 0000000000000000000000000000000000000000..9784f16c882653abeb055d62e5e36fc0fa0da47d Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_back.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png new file mode 100644 index 0000000000000000000000000000000000000000..e0fb2bad6c415c6ef100993417cd6976abeb1dda Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp deleted file mode 100644 index 1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f..0000000000000000000000000000000000000000 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..306bee97676b123152540ca62099e8cab4e35c77 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp deleted file mode 100644 index 28d4b77f9f036a47549d47db79c16788749dca10..0000000000000000000000000000000000000000 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png new file mode 100644 index 0000000000000000000000000000000000000000..04ef206c8607696ac2bb082bb1b46afe648f59f7 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png new file mode 100644 index 0000000000000000000000000000000000000000..af76f857184b1b9875b027d8012f2bd5316ebf22 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp deleted file mode 100644 index 9287f5083623b375139afb391af71cc533a7dd37..0000000000000000000000000000000000000000 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..2b4c57c762057a25c07099f9fc0705b5b70acdae Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp deleted file mode 100644 index aa7d6427e6fa1074b79ccd52ef67ac15c5637e85..0000000000000000000000000000000000000000 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png new file mode 100644 index 0000000000000000000000000000000000000000..66a5487a223c9db9c415a840b4451379ec15b214 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png new file mode 100644 index 0000000000000000000000000000000000000000..d05040e94bdcfe478b88f41ecd5433c5687c7750 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp deleted file mode 100644 index 9126ae37cbc3587421d6889eadd1d91fbf1994d4..0000000000000000000000000000000000000000 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index 0f58d9d89d1ed0969f99d10b31a2628e187e3a66..04865af38fac53187201993dccead8bd2a3bd570 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -3,13 +3,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/nav_graph" - app:startDestination="@id/loginFragment"> + app:startDestination="@id/transaksiFragment"> - <fragment - android:id="@+id/loginFragment" - android:name="com.example.bondoman.fragments.LoginFragment" - android:label="fragment_login" - tools:layout="@layout/fragment_login" /> <fragment android:id="@+id/transaksiFragment" android:name="com.example.bondoman.fragments.TransaksiFragment" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0adeb3f342e1e04275d84857d3acaea4bc68b79f..f76093b3d5e56939e774a24951dd146886cec5b6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2,9 +2,10 @@ <string name="app_name">BondoMan</string> <!-- TODO: Remove or change this placeholder text --> <string name="hello_blank_fragment">Hello blank fragment</string> - <string name="save_button_text">Simpan daftar transaksi</string> - <string name="send_button_text">Kirim daftar transaksi</string> - <string name="logout_button_text">Keluar</string> - <string name="data_transaksi_title">Data Transaksi</string> + <string name="save_button_text">Export transaction list</string> + <string name="send_button_text">Send transaction to email</string> + <string name="logout_button_text">Logout</string> + <string name="data_transaksi_title">Transaction Data</string> + <string name="randomize_buton_text">Randomize transaction</string> <string name="items">Items</string> </resources> \ No newline at end of file diff --git a/app/src/main/res/xml/provider_paths.xml b/app/src/main/res/xml/provider_paths.xml new file mode 100644 index 0000000000000000000000000000000000000000..3197349cc8116bd1767d7cd3f7a27f9105cbeb2e --- /dev/null +++ b/app/src/main/res/xml/provider_paths.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<paths xmlns:android="http://schemas.android.com/apk/res/android"> + <external-path name="external_files" path="." /> +</paths> \ No newline at end of file