diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000000000000000000000000000000000..6571f9db65ae1cfbb7868c12601f696ff1babd4d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{kt,kts}] +indent_style = tab +indent_size = 4 +ktlint_code_style = ktlint_official +ktlint_standard_package-name = disabled +ktlint_standard_no-wildcard-imports = disabled +ktlint_standard_no-consecutive-comments = disabled +ktlint_standard_if-else-wrapping = disabled \ No newline at end of file diff --git a/.gitignore b/.gitignore index aa724b77071afcbd9bb398053e05adaf7ac9405a..565a54125f541a7e8913d8930845bc8484de6d2b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *.iml .gradle +.idea /local.properties /.idea/caches /.idea/libraries diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 26d33521af10bcc7fd8cea344038eaaeb78d0ef5..0000000000000000000000000000000000000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..0d565a4553059a3c37faab353ade2a08084e1610 --- /dev/null +++ b/README.md @@ -0,0 +1,68 @@ +# BondoMan 🚀 + +Made with 💙 by +|Name|NIM| +|-|-| +|Farhan Nabil Suryono|13521114| +|Johanes Lee|13521148| +|I Putu Bakta Hari Sudewa|13521150| + +## App Description +Bondoman is an Android application allows users to record their transactions. The application provides the following functionalities: +1. **Add Transaction**: Users can add a new transaction by providing the transaction category (income or outcome), title, and amount. Date and location will be automatically recorded by the application. +2. **Edit Transaction**: Users can edit the transaction details, including the title and amount. +3. **Delete Transaction**: Users can delete the transaction. +4. **View Transaction**: Users can view the list of transactions, including the transaction category, title, amount, date, and location. +5. **Scan Receipt**: Users can scan the receipt of the transaction. The application will automatically extract the transaction details from the receipt. +6. **See Chart**: Users can see the chart of their transactions, including the total and the percentage of income and outcome. +7. **Export Data**: Users can export the transaction data to a xlsx file. +8. **Send Report**: Users can send the transaction report to their email. +9. **Twibbon (Bonus)**: Users can make a twibbon for fun. + +## Libraries Used +Bondoman uses the following libraries: +1. **Room**: For local database management. +2. **Moshi**: For JSON serialization and deserialization. +3. **Retrofit**: For network connectivity. +4. **Spotless and Ktlint**: For code formatting and linting (com.diffplug.spotless). +5. **Camera**: For camera management (androidx.camera). +6. **BlurView**: For blur effect (com.github.Dimezis:BlurView). +7. **MPAndroidChart**: For chart visualization (com.github.PhilJay:MPAndroidChart). +8. **Crypto**: For encryption and decryption (androidx.security.crypto). + +## Application Screenshots +The following are the screenshots of the Bondoman application: +1. **Login Screen** + +2. **Settings Screen** + +3. **Transaction List Screen** + +4. **Transaction Detail Screen** + +5. **Transaction Form Screen** + + +6. **Scan-Receipt Screen** + + + +6. **Chart Screen** + +7. **Twibbon Screen** + +8. **Send-Report Display** + + +## Work Division +|Name|NIM|Tasks| +|-|-|-| +|Farhan Nabil Suryono|13521114|Halaman Setting, Halaman Login, Mekanisme Autentikasi, Expiry JWT Service, Save Excel, Randomize Transaction| +|Johanes Lee|13521148|Transactions, chart, connectivity| +|I Putu Bakta Hari Sudewa|13521150|Scan Nota, Twibbon, Intent email| +## Hours of Work +|Name|NIM|Hours| +|-|-|-| +|Farhan Nabil Suryono|13521114|100 hours| +|Johanes Lee|13521148|110 hours| +|I Putu Bakta Hari Sudewa|13521150|100 hours| \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f18ec4b045544917e0b5dcbb596bab326a8725f6..1d995aee9f775ee5bbba4347cbe5ff546c9d76bf 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,48 +1,94 @@ plugins { - alias(libs.plugins.androidApplication) - alias(libs.plugins.jetbrainsKotlinAndroid) + alias(libs.plugins.androidApplication) + alias(libs.plugins.jetbrainsKotlinAndroid) + id("kotlin-parcelize") + id("com.google.devtools.ksp") + id("androidx.navigation.safeargs.kotlin") } + android { - namespace = "com.example.bondoman" - compileSdk = 34 - - defaultConfig { - applicationId = "com.example.bondoman" - minSdk = 32 - targetSdk = 34 - versionCode = 1 - versionName = "1.0" - - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" - ) - } - } - compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 - } - kotlinOptions { - jvmTarget = "1.8" - } + namespace = "com.example.bondoman" + compileSdk = 34 + + defaultConfig { + applicationId = "com.example.bondoman" + minSdk = 29 + targetSdk = 34 + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } + buildFeatures { + viewBinding = true + } } dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) - implementation(libs.androidx.core.ktx) - implementation(libs.androidx.appcompat) - implementation(libs.material) - implementation(libs.androidx.activity) - implementation(libs.androidx.constraintlayout) + implementation(libs.material) + implementation(libs.androidx.activity) + implementation(libs.androidx.constraintlayout) + implementation(libs.androidx.navigation.fragment.ktx) + implementation(libs.androidx.navigation.ui.ktx) + implementation(libs.poi) + implementation(libs.poi.ooxml) testImplementation(libs.junit) - androidTestImplementation(libs.androidx.junit) - androidTestImplementation(libs.androidx.espresso.core) -} \ No newline at end of file + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) + + // Room + implementation(libs.androidx.room.runtime) + ksp(libs.androidx.room.compiler) + + // Room Coroutine Support + implementation(libs.androidx.room.ktx) + + // Room Test Helper + implementation(libs.androidx.room.testing) + + // Camera X + implementation(libs.camera.core) + implementation(libs.camera.camera2) + implementation(libs.camera.lifecycle) + implementation(libs.camera.view) + implementation(libs.camera.extensions) + + // Blur view + implementation("com.github.Dimezis:BlurView:version-2.0.3") + + // MPChart + implementation(libs.mpchart) + + // Retrofit + implementation(libs.retrofit) + implementation(libs.converter.moshi) + + // Moshi + implementation(libs.moshi) + implementation(libs.moshi.kotlin) + ksp(libs.moshi.kotlin.codegen) + + // Crypto + implementation(libs.androidx.security.crypto) +} diff --git a/app/src/androidTest/java/com/example/bondoman/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/example/bondoman/ExampleInstrumentedTest.kt index 4985d8b5c32e726edb0cc805fc4df1f44bfad035..8e590f03af6626e47288c8d3f92630f0147b6c9e 100644 --- a/app/src/androidTest/java/com/example/bondoman/ExampleInstrumentedTest.kt +++ b/app/src/androidTest/java/com/example/bondoman/ExampleInstrumentedTest.kt @@ -1,13 +1,11 @@ package com.example.bondoman -import androidx.test.platform.app.InstrumentationRegistry import androidx.test.ext.junit.runners.AndroidJUnit4 - +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.* import org.junit.Test import org.junit.runner.RunWith -import org.junit.Assert.* - /** * Instrumented test, which will execute on an Android device. * @@ -15,10 +13,10 @@ import org.junit.Assert.* */ @RunWith(AndroidJUnit4::class) class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("com.example.bondoman", appContext.packageName) - } -} \ No newline at end of file + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.example.bondoman", appContext.packageName) + } +} diff --git a/app/src/androidTest/java/com/example/bondoman/databases/TransactionDatabaseTest.kt b/app/src/androidTest/java/com/example/bondoman/databases/TransactionDatabaseTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..ef21ca67babcda9d65b7750eec59140a53783e22 --- /dev/null +++ b/app/src/androidTest/java/com/example/bondoman/databases/TransactionDatabaseTest.kt @@ -0,0 +1,86 @@ +package com.example.bondoman.databases + +import android.content.Context +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.example.bondoman.data.dataaccess.TransactionDao +import com.example.bondoman.data.databases.TransactionDatabase +import com.example.bondoman.data.models.Transaction +import com.example.bondoman.data.models.TransactionCategory +import com.example.bondoman.data.repositories.TransactionRepository +import kotlinx.coroutines.runBlocking +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class TransactionDatabaseTest { + private lateinit var dao: TransactionDao + private lateinit var database: TransactionDatabase + private lateinit var repository: TransactionRepository + + @Before + fun setUp() { + val context: Context = ApplicationProvider.getApplicationContext() + + database = + Room.inMemoryDatabaseBuilder(context, TransactionDatabase::class.java) + .allowMainThreadQueries() + .build() + + dao = database.transactionDao() + repository = TransactionRepository(dao) + } + + @After + @Throws(Exception::class) + fun tearDown() { + database.close() + } + + @Test + @Throws(Exception::class) + fun insertAndGetTransaction() = + runBlocking { + val user = "13521114" + + val transaction1 = + Transaction( + title = "Transaction 1", + owner = user, + category = TransactionCategory.EARNINGS, + amount = 100L, + ) + + val transaction2 = + Transaction( + title = "Transaction 2", + owner = user, + category = TransactionCategory.EARNINGS, + amount = 200L, + ) + + val transaction3 = + Transaction( + title = "Transaction 3", + owner = user, + category = TransactionCategory.EXPENSE, + amount = 100L, + ) + + repository.insert(transaction1, transaction2, transaction3) + + val allTransactions = repository.getAll(user) + + assert(allTransactions.isNotEmpty()) + assert(allTransactions.size == 3) + + val transaction1FromDB = repository.getById(1, user) + assert(transaction1FromDB != null) + assert(transaction1FromDB!!.title == transaction1.title) + assert(transaction1FromDB.category == transaction1.category) + assert(transaction1FromDB.amount == transaction1.amount) + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1860d4717a624588e45c798ffd232912e2b2e515..ca3b8ab7f29aa8932f2d4a142b38381362c34868 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,8 +1,23 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> + + <uses-feature android:name="android.hardware.camera.any" /> + + <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> + <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> + <uses-permission android:name="android.permission.CAMERA" /> + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> + <uses-permission + android:name="android.permission.WRITE_EXTERNAL_STORAGE" + android:maxSdkVersion="28" /> + <uses-permission android:name="android.permission.INTERNET" /> + + <queries> + <package android:name="com.google.android.apps.maps" /> + </queries> <application + android:name=".MainApplication" android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" @@ -10,17 +25,30 @@ android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" - android:theme="@style/Theme.BondoMan" - tools:targetApi="31"> + android:theme="@style/Theme.BondoMan"> + <activity + android:name=".views.activities.MainActivity" + android:exported="true" /> <activity - android:name=".MainActivity" + android:name=".views.activities.LoginActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> + + <service android:name=".services.services.ExpiryService" /> + <provider + android:name="androidx.core.content.FileProvider" + android:authorities="${applicationId}.provider" + android:exported="false" + android:grantUriPermissions="true"> + <meta-data + android:name="android.support.FILE_PROVIDER_PATHS" + android:resource="@xml/file_paths" /> + </provider> </application> -</manifest> \ No newline at end of file + +</manifest> diff --git a/app/src/main/java/com/example/bondoman/MainActivity.kt b/app/src/main/java/com/example/bondoman/MainActivity.kt deleted file mode 100644 index bdfbd09469a07e5d5f199d94722134c7677c0aa4..0000000000000000000000000000000000000000 --- a/app/src/main/java/com/example/bondoman/MainActivity.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.example.bondoman - -import android.os.Bundle -import androidx.activity.enableEdgeToEdge -import androidx.appcompat.app.AppCompatActivity -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat - -class MainActivity : AppCompatActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - enableEdgeToEdge() - setContentView(R.layout.activity_main) - ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets -> - val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) - v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) - insets - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/example/bondoman/MainApplication.kt b/app/src/main/java/com/example/bondoman/MainApplication.kt new file mode 100644 index 0000000000000000000000000000000000000000..5e736d3d5485ad046251c06d10b40ccdcdfd3af1 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/MainApplication.kt @@ -0,0 +1,16 @@ +package com.example.bondoman + +import android.app.Application +import com.example.bondoman.cores.containers.AppContainer +import com.example.bondoman.cores.containers.DefaultAppContainer +import com.example.bondoman.data.databases.TransactionDatabase + +class MainApplication : Application() { + lateinit var container: AppContainer + + override fun onCreate() { + super.onCreate() + val database = TransactionDatabase.getInstance(applicationContext) + container = DefaultAppContainer(database) + } +} diff --git a/app/src/main/java/com/example/bondoman/cores/containers/AppContainer.kt b/app/src/main/java/com/example/bondoman/cores/containers/AppContainer.kt new file mode 100644 index 0000000000000000000000000000000000000000..36eca093cff0f51b2ded4b2321b4dfba4daa0215 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/cores/containers/AppContainer.kt @@ -0,0 +1,7 @@ +package com.example.bondoman.cores.containers + +import com.example.bondoman.data.repositories.TransactionRepository + +interface AppContainer { + val transactionRepository: TransactionRepository +} diff --git a/app/src/main/java/com/example/bondoman/cores/containers/DefaultAppContainer.kt b/app/src/main/java/com/example/bondoman/cores/containers/DefaultAppContainer.kt new file mode 100644 index 0000000000000000000000000000000000000000..e81973fe9b82869bd571984c32e4890e4f67eb69 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/cores/containers/DefaultAppContainer.kt @@ -0,0 +1,14 @@ +package com.example.bondoman.cores.containers + +import com.example.bondoman.data.dataaccess.TransactionDao +import com.example.bondoman.data.databases.TransactionDatabase +import com.example.bondoman.data.repositories.TransactionRepository + +class DefaultAppContainer(database: TransactionDatabase) : AppContainer { + private val transactionDao: TransactionDao by lazy { + database.transactionDao() + } + override val transactionRepository: TransactionRepository by lazy { + TransactionRepository(transactionDao) + } +} diff --git a/app/src/main/java/com/example/bondoman/data/Constants.kt b/app/src/main/java/com/example/bondoman/data/Constants.kt new file mode 100644 index 0000000000000000000000000000000000000000..9db9662e3c30e89b9135f14272b855c123b839f7 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/data/Constants.kt @@ -0,0 +1,10 @@ +package com.example.bondoman.data + +object Constants { + // URL + const val BASE_URL = "https://pbd-backend-2024.vercel.app/" + + // Intents + const val LOGOUT_TIMEOUT_INTENT = "com.example.bondoman.intents.LOGOUT_TIMEOUT" + const val RANDOMIZE_TRANSACTION_INTENT = "com.example.bondoman.intents.RANDOMIZE_TRANSACTION" +} diff --git a/app/src/main/java/com/example/bondoman/data/dataaccess/TransactionDao.kt b/app/src/main/java/com/example/bondoman/data/dataaccess/TransactionDao.kt new file mode 100644 index 0000000000000000000000000000000000000000..73387b7239692c47821fdaf0f15e22e922c92cc6 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/data/dataaccess/TransactionDao.kt @@ -0,0 +1,49 @@ +package com.example.bondoman.data.dataaccess + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.Query +import com.example.bondoman.data.models.CategoryTotal +import com.example.bondoman.data.models.Transaction +import java.util.Date + +@Dao +interface TransactionDao { + @Query("SELECT * FROM transactions WHERE owner = :owner ORDER BY date DESC") + suspend fun getAll(owner: String): List<Transaction> + + @Query("SELECT * FROM transactions WHERE id = :id and owner = :owner") + suspend fun getById( + id: Long, + owner: String, + ): Transaction? + + @Insert + suspend fun insert(vararg transaction: Transaction) + + @Query("UPDATE transactions SET title = :title, amount = :amount, location = :location WHERE id = :id and owner = :owner") + suspend fun update( + id: Long, + owner: String, + title: String, + amount: Long, + location: String?, + ) + + @Query("DELETE FROM transactions WHERE id = :id and owner = :owner") + suspend fun delete( + id: Long, + owner: String, + ) + + @Query( + "SELECT category, TOTAL(amount) AS totalAmount " + + "FROM transactions " + + "WHERE owner = :owner AND date >= :beginDate AND date < :endDate GROUP BY category", + ) + suspend fun getCategoryTotals( + owner: String, + beginDate: Date, + endDate: Date, + ): List<CategoryTotal> +} diff --git a/app/src/main/java/com/example/bondoman/data/databases/TransactionDatabase.kt b/app/src/main/java/com/example/bondoman/data/databases/TransactionDatabase.kt new file mode 100644 index 0000000000000000000000000000000000000000..5342476215aac278a22739538a6e6c61abf43081 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/data/databases/TransactionDatabase.kt @@ -0,0 +1,47 @@ +package com.example.bondoman.data.databases + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase +import androidx.room.TypeConverter +import androidx.room.TypeConverters +import com.example.bondoman.data.dataaccess.TransactionDao +import com.example.bondoman.data.models.Transaction +import java.util.Date + +class DateConverter { + @TypeConverter + fun timestampToDate(value: Long?): Date? { + return value?.let { Date(it) } + } + + @TypeConverter + fun dateToTimestamp(date: Date?): Long? { + return date?.time + } +} + +@Database(entities = [Transaction::class], version = 1, exportSchema = false) +@TypeConverters(DateConverter::class) +abstract class TransactionDatabase : RoomDatabase() { + abstract fun transactionDao(): TransactionDao + + companion object { + @Volatile + private var instance: TransactionDatabase? = null + + fun getInstance(context: Context): TransactionDatabase { + return instance ?: synchronized(this) { + val newInstance = + Room.databaseBuilder( + context.applicationContext, + TransactionDatabase::class.java, + "transaction_database", + ).build() + instance = newInstance + newInstance + } + } + } +} diff --git a/app/src/main/java/com/example/bondoman/data/models/ParcelableBitmap.kt b/app/src/main/java/com/example/bondoman/data/models/ParcelableBitmap.kt new file mode 100644 index 0000000000000000000000000000000000000000..ad581c2cbc0c403f0e1ff01a516225514fe991b2 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/data/models/ParcelableBitmap.kt @@ -0,0 +1,8 @@ +package com.example.bondoman.data.models + +import android.graphics.Bitmap +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +data class ParcelableBitmap(val bitmap: Bitmap?) : Parcelable diff --git a/app/src/main/java/com/example/bondoman/data/models/Transaction.kt b/app/src/main/java/com/example/bondoman/data/models/Transaction.kt new file mode 100644 index 0000000000000000000000000000000000000000..8461d7817e707d41fae530ebf117ebd01204da39 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/data/models/Transaction.kt @@ -0,0 +1,47 @@ +package com.example.bondoman.data.models + +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable +import java.util.Date + +enum class TransactionCategory(val string: String) { + EARNINGS("Income"), + EXPENSE("Outcome"), +} + +data class CategoryTotal( + val category: TransactionCategory, + val totalAmount: Double, +) + +val transactionCategoryMap: Map<String, TransactionCategory> = + mapOf( + "Income" to TransactionCategory.EARNINGS, + "Outcome" to TransactionCategory.EXPENSE, + ) + +@Entity(tableName = "transactions") +data class Transaction( + @PrimaryKey(autoGenerate = true) + val id: Long = 0L, + val title: String, + val owner: String, + val category: TransactionCategory, + val amount: Long, + val date: Date = Date(), + val location: String? = null, +) : Serializable { + init { + require(title.isNotEmpty()) { "Title can't be empty" } + require(title.length <= MAX_TITLE_LENGTH) { "Title can't be more than $MAX_TITLE_LENGTH characters" } + require(amount > 0) { "Amount cannot be zero" } + require(amount <= MAX_AMOUNT) { "Amount cannot exceed $MAX_AMOUNT" } + require(location == null || location.isNotEmpty()) { "Location cannot be blank" } + } + + companion object { + const val MAX_AMOUNT = Long.MAX_VALUE + const val MAX_TITLE_LENGTH = 50 + } +} diff --git a/app/src/main/java/com/example/bondoman/data/repositories/ScanReceiptRepository.kt b/app/src/main/java/com/example/bondoman/data/repositories/ScanReceiptRepository.kt new file mode 100644 index 0000000000000000000000000000000000000000..ccd7f17cb1b9eaed3af73a635289171e2550413c --- /dev/null +++ b/app/src/main/java/com/example/bondoman/data/repositories/ScanReceiptRepository.kt @@ -0,0 +1,12 @@ +package com.example.bondoman.data.repositories + +import com.example.bondoman.networks.responses.ScanReceiptResponseItemsWrapper +import com.example.bondoman.networks.services.ScanReceiptService +import okhttp3.MultipartBody +import retrofit2.Response + +class ScanReceiptRepository(private val scanReceiptService: ScanReceiptService) { + suspend fun scanReceipt(formData: MultipartBody.Part): Response<ScanReceiptResponseItemsWrapper> { + return scanReceiptService.scanReceipt(formData) + } +} diff --git a/app/src/main/java/com/example/bondoman/data/repositories/TransactionRepository.kt b/app/src/main/java/com/example/bondoman/data/repositories/TransactionRepository.kt new file mode 100644 index 0000000000000000000000000000000000000000..d57eeee6701eb04e377d543976d6e03288819f70 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/data/repositories/TransactionRepository.kt @@ -0,0 +1,35 @@ +package com.example.bondoman.data.repositories + +import com.example.bondoman.data.dataaccess.TransactionDao +import com.example.bondoman.data.models.Transaction +import java.util.Date + +class TransactionRepository(private val transactionDao: TransactionDao) { + suspend fun getAll(owner: String) = transactionDao.getAll(owner) + + suspend fun getById( + id: Long, + owner: String, + ) = transactionDao.getById(id, owner) + + suspend fun insert(vararg transaction: Transaction) = transactionDao.insert(*transaction) + + suspend fun update( + id: Long, + owner: String, + title: String, + amount: Long, + location: String?, + ) = transactionDao.update(id, owner, title, amount, location) + + suspend fun delete( + id: Long, + owner: String, + ) = transactionDao.delete(id, owner) + + suspend fun getCategoryTotals( + owner: String, + beginDate: Date, + endDate: Date, + ) = transactionDao.getCategoryTotals(owner, beginDate, endDate) +} diff --git a/app/src/main/java/com/example/bondoman/data/repositories/UserRepository.kt b/app/src/main/java/com/example/bondoman/data/repositories/UserRepository.kt new file mode 100644 index 0000000000000000000000000000000000000000..fb787193e36e282569d09fab4ae7976c35668ce2 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/data/repositories/UserRepository.kt @@ -0,0 +1,20 @@ +package com.example.bondoman.data.repositories + +import com.example.bondoman.networks.requests.LoginRequest +import com.example.bondoman.networks.responses.LoginResponse +import com.example.bondoman.networks.responses.TokenResponse +import com.example.bondoman.networks.services.UserService +import retrofit2.Response + +class UserRepository(private val service: UserService) { + suspend fun login( + email: String, + password: String, + ): Response<LoginResponse> { + return service.login(LoginRequest(email, password)) + } + + suspend fun checkToken(): Response<TokenResponse> { + return service.checkToken() + } +} diff --git a/app/src/main/java/com/example/bondoman/data/utils/PreferencesManager.kt b/app/src/main/java/com/example/bondoman/data/utils/PreferencesManager.kt new file mode 100644 index 0000000000000000000000000000000000000000..30a505b50cff7e20b6c91cbfaf0c740c9d5d77fd --- /dev/null +++ b/app/src/main/java/com/example/bondoman/data/utils/PreferencesManager.kt @@ -0,0 +1,109 @@ +package com.example.bondoman.data.utils + +import android.content.Context +import android.content.SharedPreferences +import androidx.security.crypto.EncryptedSharedPreferences +import androidx.security.crypto.MasterKey + +object PreferencesManager { + private const val PREFS_NAME = "BondomanPreferences" + + fun getSharedPreferences(context: Context): SharedPreferences { + return context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) + } + + fun getEncryptedSharedPreferences(context: Context): SharedPreferences { + val masterKey = + MasterKey.Builder(context) + .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) + .build() + + return EncryptedSharedPreferences.create( + context, + PREFS_NAME, + masterKey, + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM, + ) + } + + fun putString( + context: Context, + key: String, + value: String, + encrypted: Boolean = false, + ) { + val editor = + if (encrypted) { + getEncryptedSharedPreferences(context).edit() + } else { + getSharedPreferences(context).edit() + } + + editor.putString(key, value) + editor.apply() + } + + fun getString( + context: Context, + key: String, + encrypted: Boolean = false, + ): String? { + val sharedPreferences = + if (encrypted) { + getEncryptedSharedPreferences(context) + } else { + getSharedPreferences(context) + } + + return sharedPreferences.getString(key, null) + } + + fun putBoolean( + context: Context, + key: String, + value: Boolean, + encrypted: Boolean = false, + ) { + val editor = + if (encrypted) { + getEncryptedSharedPreferences(context).edit() + } else { + getSharedPreferences(context).edit() + } + + editor.putBoolean(key, value) + editor.apply() + } + + fun getBoolean( + context: Context, + key: String, + encrypted: Boolean = false, + ): Boolean { + val sharedPreferences = + if (encrypted) { + getEncryptedSharedPreferences(context) + } else { + getSharedPreferences(context) + } + + return sharedPreferences.getBoolean(key, false) + } + + fun remove( + context: Context, + key: String, + encrypted: Boolean = false, + ) { + val editor = + if (encrypted) { + getEncryptedSharedPreferences(context).edit() + } else { + getSharedPreferences(context).edit() + } + + editor.remove(key) + editor.apply() + } +} diff --git a/app/src/main/java/com/example/bondoman/data/utils/TransactionParser.kt b/app/src/main/java/com/example/bondoman/data/utils/TransactionParser.kt new file mode 100644 index 0000000000000000000000000000000000000000..cdd35d3140dcb0b3eeb6db4d77967d9e5d016f8d --- /dev/null +++ b/app/src/main/java/com/example/bondoman/data/utils/TransactionParser.kt @@ -0,0 +1,59 @@ +package com.example.bondoman.data.utils + +import com.example.bondoman.data.models.TransactionCategory +import java.text.DecimalFormat +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale + +class TransactionParser { + companion object { + const val DATE_FORMAT = "dd MMM yyyy" + const val DECIMAL_FORMAT_PATTERN = "#,###" + const val INTEGER_SEPARATOR = '.' + } + + private val dateFormat = SimpleDateFormat(DATE_FORMAT, Locale.ENGLISH) + + private fun formatAmountString(integerString: String): String { + val reversedString = integerString.reversed() + val formattedBuilder = StringBuilder() + + for ((index, char) in reversedString.withIndex()) { + formattedBuilder.append(char) + if ((index + 1) % 3 == 0 && index != reversedString.length - 1) { + formattedBuilder.append('.') + } + } + + return formattedBuilder.reverse().toString() + } + + fun getAmountString( + amount: Long, + category: TransactionCategory, + ): String { + val sign = if (category == TransactionCategory.EXPENSE) "-" else "" + val amountFormatted = formatAmountString(amount.toString()) + return "$sign Rp$amountFormatted" + } + + fun getAmountString( + amount: Double, + category: TransactionCategory, + ): String { + val sign = if (category == TransactionCategory.EXPENSE) "-" else "" + + val decimalFormat = DecimalFormat("0.00") + val formattedAmount = decimalFormat.format(amount) + + val parts = formattedAmount.split(".") + val integerPart = parts[0] + val amountFormatted = formatAmountString(integerPart) + return "$sign Rp$amountFormatted" + } + + fun getDateString(date: Date): String { + return dateFormat.format(date) + } +} diff --git a/app/src/main/java/com/example/bondoman/data/viewmodels/login/LoginViewModel.kt b/app/src/main/java/com/example/bondoman/data/viewmodels/login/LoginViewModel.kt new file mode 100644 index 0000000000000000000000000000000000000000..fcd1b31499e2fb7ba30ccb6324ef496cf6dd88a0 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/data/viewmodels/login/LoginViewModel.kt @@ -0,0 +1,76 @@ +package com.example.bondoman.data.viewmodels.login + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.bondoman.data.repositories.UserRepository +import com.example.bondoman.networks.RetrofitClient +import com.example.bondoman.networks.responses.LoginResponse +import com.example.bondoman.networks.responses.TokenResponse +import com.example.bondoman.networks.services.UserService +import kotlinx.coroutines.launch + +class LoginViewModel : ViewModel() { + private val _loginResponse = MutableLiveData<LoginResponse?>() + val loginResponse: LiveData<LoginResponse?> = _loginResponse + + private val _tokenResponse = MutableLiveData<TokenResponse?>() + val tokenResponse: LiveData<TokenResponse?> = _tokenResponse + + fun login( + email: String, + password: String, + ) { + viewModelScope.launch { + try { + val repository = + UserRepository(RetrofitClient.getInstance().create(UserService::class.java)) + val loginResponse = repository.login(email, password) + + if (loginResponse.isSuccessful) { + _loginResponse.value = loginResponse.body() + } else { + _loginResponse.value = + LoginResponse(null, loginResponse.errorBody()?.string() ?: "Unknown error") + } + } catch (e: Exception) { + _loginResponse.value = LoginResponse(null, "Something went wrong") + } + } + } + + fun checkToken(bearerToken: String) { + viewModelScope.launch { + try { + val repository = + UserRepository( + RetrofitClient.getInstanceWithAuth(bearerToken).create(UserService::class.java), + ) + val tokenResponse = repository.checkToken() + + if (tokenResponse.isSuccessful) { + _tokenResponse.value = tokenResponse.body() + } else { + _tokenResponse.value = + TokenResponse( + "", + 0, + 0, + tokenResponse.errorBody()?.string() ?: "Unknown error", + ) + } + } catch (e: Exception) { + _tokenResponse.value = TokenResponse("", 0, 0, e.message ?: "Unknown error") + } + } + } + + fun resetLoginResponse() { + _loginResponse.value = null + } + + fun resetTokenResponse() { + _tokenResponse.value = null + } +} diff --git a/app/src/main/java/com/example/bondoman/data/viewmodels/scanReceipt/ScanReceiptViewModel.kt b/app/src/main/java/com/example/bondoman/data/viewmodels/scanReceipt/ScanReceiptViewModel.kt new file mode 100644 index 0000000000000000000000000000000000000000..edbcaab71cc89a9116e1653fb39a270488605e3a --- /dev/null +++ b/app/src/main/java/com/example/bondoman/data/viewmodels/scanReceipt/ScanReceiptViewModel.kt @@ -0,0 +1,64 @@ +package com.example.bondoman.data.viewmodels.scanReceipt + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.bondoman.data.repositories.ScanReceiptRepository +import com.example.bondoman.networks.RetrofitClient +import com.example.bondoman.networks.responses.ScanReceiptResponseItemsWrapper +import com.example.bondoman.networks.services.ScanReceiptService +import kotlinx.coroutines.launch +import okhttp3.MediaType +import okhttp3.MultipartBody +import okhttp3.RequestBody +import java.io.File +import java.net.UnknownHostException + +class ScanReceiptViewModel : ViewModel() { + private val _scanReceiptResponse = MutableLiveData<ScanReceiptResponseItemsWrapper>() + val scanReceiptResponse = _scanReceiptResponse + + fun scanReceipt( + scanReceiptImageFile: File, + bearerToken: String, + ) { + viewModelScope.launch { + try { + val repository = + ScanReceiptRepository( + RetrofitClient.getInstanceWithAuth(bearerToken) + .create(ScanReceiptService::class.java), + ) + val formData = + MultipartBody.Part.createFormData( + "file", + scanReceiptImageFile.name, + RequestBody.create(MediaType.parse("image/*"), scanReceiptImageFile), + ) + val scanReceiptResponse = repository.scanReceipt(formData) + + if (scanReceiptResponse.isSuccessful) { + _scanReceiptResponse.value = scanReceiptResponse.body() + } else { + val error = + when (scanReceiptResponse.code()) { + 401 -> "Upload receipt failed. Unauthorized." + 413 -> "Upload receipt failed. File too large." + else -> "Upload receipt failed. Unknown Error." + } + _scanReceiptResponse.value = + ScanReceiptResponseItemsWrapper(null, error) + } + } catch (e: UnknownHostException) { + _scanReceiptResponse.value = + ScanReceiptResponseItemsWrapper( + null, + "You're offline. Please check your internet.", + ) + } catch (e: Exception) { + _scanReceiptResponse.value = + ScanReceiptResponseItemsWrapper(null, "Something went wrong. Please try again.") + } + } + } +} diff --git a/app/src/main/java/com/example/bondoman/data/viewmodels/settings/ExportTransactionResponseContainer.kt b/app/src/main/java/com/example/bondoman/data/viewmodels/settings/ExportTransactionResponseContainer.kt new file mode 100644 index 0000000000000000000000000000000000000000..076f025cff64cc7de61204631ab90b1e83592b5e --- /dev/null +++ b/app/src/main/java/com/example/bondoman/data/viewmodels/settings/ExportTransactionResponseContainer.kt @@ -0,0 +1,11 @@ +package com.example.bondoman.data.viewmodels.settings + +enum class ExportTransactionStatus { + SUCCESS, + FAILED, +} + +data class ExportTransactionResponseContainer( + val status: ExportTransactionStatus, + val message: String, +) diff --git a/app/src/main/java/com/example/bondoman/data/viewmodels/settings/SendTransactionResponse.kt b/app/src/main/java/com/example/bondoman/data/viewmodels/settings/SendTransactionResponse.kt new file mode 100644 index 0000000000000000000000000000000000000000..df14b7881a03bd740f30c4143b1b8bd5eb2c842f --- /dev/null +++ b/app/src/main/java/com/example/bondoman/data/viewmodels/settings/SendTransactionResponse.kt @@ -0,0 +1,8 @@ +package com.example.bondoman.data.viewmodels.settings + +import org.apache.poi.ss.usermodel.Workbook + +data class SendTransactionResponse( + val workbook: Workbook, + val nim: String?, +) diff --git a/app/src/main/java/com/example/bondoman/data/viewmodels/settings/SettingViewModel.kt b/app/src/main/java/com/example/bondoman/data/viewmodels/settings/SettingViewModel.kt new file mode 100644 index 0000000000000000000000000000000000000000..a4d2d2a3ad7e13f1e42229f19f0ffd4d1b1889d2 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/data/viewmodels/settings/SettingViewModel.kt @@ -0,0 +1,126 @@ +package com.example.bondoman.data.viewmodels.settings + +import android.app.Application +import android.content.ContentValues +import android.os.Environment +import android.provider.MediaStore +import android.util.Log +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope +import com.example.bondoman.data.databases.TransactionDatabase +import com.example.bondoman.data.models.Transaction +import com.example.bondoman.data.repositories.TransactionRepository +import kotlinx.coroutines.launch +import org.apache.poi.ss.usermodel.Sheet +import org.apache.poi.ss.usermodel.Workbook +import org.apache.poi.xssf.usermodel.XSSFWorkbook +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale + +class SettingViewModel(applicationContext: Application) : AndroidViewModel(applicationContext) { + private val _exportReportResponse = MutableLiveData<ExportTransactionResponseContainer>() + val exportReportResponse = _exportReportResponse + private val _sendTransactionReport = MutableLiveData<SendTransactionResponse>() + val sendTransactionReport = _sendTransactionReport + + fun exportTransactionReport( + nim: String, + save: Boolean = true, + ) { + viewModelScope.launch { + val transactionDB = TransactionDatabase.getInstance(getApplication()) + val transactionRepository = TransactionRepository(transactionDB.transactionDao()) + val transactions = transactionRepository.getAll(nim) + + val workbook = transactionToExcel(transactions) + + if (save) { + try { + createExcel(workbook) + _exportReportResponse.value = + ExportTransactionResponseContainer( + ExportTransactionStatus.SUCCESS, + "Export Report Success", + ) + } catch (e: Exception) { + Log.e("SettingViewModel", e.message ?: "Unknown error") + _exportReportResponse.value = + ExportTransactionResponseContainer( + ExportTransactionStatus.FAILED, + e.message ?: "Unknown error", + ) + } + } else { + _sendTransactionReport.value = SendTransactionResponse(workbook, nim) + } + } + } + + private fun generateFileName(): String { + // Generate File Name based on current date and time + val sdf = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()) + return sdf.format(Date()) + } + + private fun createExcel(workbook: Workbook) { + // Save XLSX File to Downloads/Bondoman + val filename = generateFileName() + + val contentValues = + ContentValues().apply { + put(MediaStore.MediaColumns.DISPLAY_NAME, "$filename.xlsx") + put( + MediaStore.MediaColumns.MIME_TYPE, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + ) + put( + MediaStore.MediaColumns.RELATIVE_PATH, + Environment.DIRECTORY_DOWNLOADS + "/Bondoman", + ) + } + + val uri = + getApplication<Application>().contentResolver.insert( + MediaStore.Downloads.EXTERNAL_CONTENT_URI, + contentValues, + ) + val outputStream = getApplication<Application>().contentResolver.openOutputStream(uri!!) + workbook.write(outputStream) + outputStream?.close() + + workbook.close() + } + + fun transactionToExcel(transactions: List<Transaction>): Workbook { + // Write transactions to XLSX File + val workbook = XSSFWorkbook() + val sheet: Sheet = workbook.createSheet("Transaction Report") + + createHeaderRow(sheet) + + transactions.asReversed().forEachIndexed { index, transaction -> + val row = sheet.createRow(index + 1) + row.createCell(0).setCellValue(transaction.id.toDouble()) + row.createCell(1).setCellValue(transaction.title) + row.createCell(2).setCellValue(transaction.owner) + row.createCell(3).setCellValue(transaction.category.string) + row.createCell(4).setCellValue(transaction.amount.toDouble()) + row.createCell(5).setCellValue(transaction.date.toString()) + row.createCell(6).setCellValue(transaction.location) + } + + return workbook + } + + private fun createHeaderRow(sheet: Sheet) { + // Write Header Row + val headerRow = sheet.createRow(0) + val headerList = listOf("ID", "Title", "Owner", "Category", "Amount", "Date", "Location") + + headerList.forEachIndexed { index, header -> + headerRow.createCell(index).setCellValue(header) + } + } +} diff --git a/app/src/main/java/com/example/bondoman/data/viewmodels/transaction/TransactionViewModel.kt b/app/src/main/java/com/example/bondoman/data/viewmodels/transaction/TransactionViewModel.kt new file mode 100644 index 0000000000000000000000000000000000000000..2341bf8e541e46ad42acef391a083c40f5bc15d4 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/data/viewmodels/transaction/TransactionViewModel.kt @@ -0,0 +1,141 @@ +package com.example.bondoman.data.viewmodels.transaction + +import android.util.Log +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.bondoman.data.models.CategoryTotal +import com.example.bondoman.data.models.Transaction +import com.example.bondoman.data.models.TransactionCategory +import com.example.bondoman.data.repositories.TransactionRepository +import kotlinx.coroutines.launch +import java.util.Date + +class TransactionViewModel( + private val transactionRepository: TransactionRepository, + userNim: String, +) : + ViewModel() { + private val currentUserNim = userNim + private val _currentTransaction = MutableLiveData<Transaction?>() + private val _transactions = MutableLiveData<List<Transaction>>() + private val _categoryTotals = MutableLiveData<List<CategoryTotal>>() + + val currentTransaction: LiveData<Transaction?> = _currentTransaction + val transactions: LiveData<List<Transaction>> = _transactions + val categoryTotals: LiveData<List<CategoryTotal>> = _categoryTotals + + fun setCurrentTransaction(transaction: Transaction) { + _currentTransaction.value = transaction + } + + fun removeCurrentTransaction() { + _currentTransaction.value = null + } + + fun getAllTransaction() { + viewModelScope.launch { + try { + _transactions.value = transactionRepository.getAll(currentUserNim) + } catch (e: Exception) { + Log.e("TransactionViewModel", e.toString()) + } + } + } + + fun insertTransaction( + title: String, + category: TransactionCategory, + amount: Long, + location: String?, + callback: ((Boolean, String) -> Unit)? = null, + ) { + try { + val newTransaction = + Transaction( + title = title, + owner = currentUserNim, + amount = amount, + category = category, + location = location, + ) + + viewModelScope.launch { + try { + transactionRepository.insert(newTransaction) + callback?.invoke( + true, + "Transaction created successfully", + ) // Callback with success + } catch (e: Exception) { + Log.e("TransactionViewModel", e.toString()) + callback?.invoke(false, "Failed to create transaction") + } + } + } catch (e: IllegalArgumentException) { + throw e + } + } + + fun updateCurrentTransaction( + title: String, + amount: Long, + location: String?, + callback: ((Boolean, String) -> Unit)? = null, + ) { + if (_currentTransaction.value == null) { + throw IllegalStateException("There is no current transaction") + } + + viewModelScope.launch { + try { + transactionRepository.update( + _currentTransaction.value!!.id, + currentUserNim, + title, + amount, + location, + ) + + callback?.invoke( + true, + "Transaction updated successfully", + ) // Callback with success + } catch (e: Exception) { + Log.e("TransactionViewModel", e.toString()) + callback?.invoke(false, "Failed to update transaction") + } + } + } + + fun deleteCurrentTransaction(callback: ((Boolean, String) -> Unit)? = null) { + if (_currentTransaction.value == null) { + throw IllegalStateException("There is no current transaction") + } + + viewModelScope.launch { + try { + transactionRepository.delete(_currentTransaction.value!!.id, currentUserNim) + callback?.invoke(true, "Transaction deleted successfully") + } catch (e: Exception) { + Log.e("TransactionViewModel", e.toString()) + callback?.invoke(false, "Failed to delete transaction") + } + } + } + + fun getTransactionTotals( + beginDate: Date, + endDate: Date, + ) { + viewModelScope.launch { + try { + _categoryTotals.value = + transactionRepository.getCategoryTotals(currentUserNim, beginDate, endDate) + } catch (e: Exception) { + Log.e("TransactionViewModel", e.toString()) + } + } + } +} diff --git a/app/src/main/java/com/example/bondoman/data/viewmodels/transaction/TransactionViewModelFactory.kt b/app/src/main/java/com/example/bondoman/data/viewmodels/transaction/TransactionViewModelFactory.kt new file mode 100644 index 0000000000000000000000000000000000000000..8cc8594985ff1244c25bd620420b5e51b1d4e0fa --- /dev/null +++ b/app/src/main/java/com/example/bondoman/data/viewmodels/transaction/TransactionViewModelFactory.kt @@ -0,0 +1,15 @@ +package com.example.bondoman.data.viewmodels.transaction + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.example.bondoman.MainApplication + +class TransactionViewModelFactory( + private val application: MainApplication, + private val userNim: String, +) : ViewModelProvider.Factory { + override fun <T : ViewModel> create(modelClass: Class<T>): T { + @Suppress("UNCHECKED_CAST") + return TransactionViewModel(application.container.transactionRepository, userNim) as T + } +} diff --git a/app/src/main/java/com/example/bondoman/networks/RetrofitClient.kt b/app/src/main/java/com/example/bondoman/networks/RetrofitClient.kt new file mode 100644 index 0000000000000000000000000000000000000000..555f88c47b2ccecffb61d6fc5d5c5d3628d55b25 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/networks/RetrofitClient.kt @@ -0,0 +1,55 @@ +package com.example.bondoman.networks + +import com.example.bondoman.data.Constants +import com.example.bondoman.networks.interceptors.AuthInterceptor +import com.squareup.moshi.Moshi +import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory +import okhttp3.OkHttpClient +import retrofit2.Retrofit +import retrofit2.converter.moshi.MoshiConverterFactory +import java.util.concurrent.TimeUnit + +object RetrofitClient { + private var token = "" + + private val moshi = + Moshi.Builder() + .add(KotlinJsonAdapterFactory()) + .build() + + private var instance: Retrofit? = null + private var instanceWithAuth: Retrofit? = null + + fun getInstance(): Retrofit { + if (instance == null) { + instance = + Retrofit.Builder() + .baseUrl(Constants.BASE_URL) + .addConverterFactory(MoshiConverterFactory.create(moshi)) + .build() + } + + return instance!! + } + + fun getInstanceWithAuth(bearerToken: String): Retrofit { + if (instanceWithAuth == null || token != bearerToken) { + token = bearerToken + + val client = + OkHttpClient.Builder() + .readTimeout(30, TimeUnit.SECONDS) + .addInterceptor(AuthInterceptor(bearerToken)) + .build() + + instanceWithAuth = + Retrofit.Builder() + .baseUrl(Constants.BASE_URL) + .addConverterFactory(MoshiConverterFactory.create(moshi)) + .client(client) + .build() + } + + return instanceWithAuth!! + } +} diff --git a/app/src/main/java/com/example/bondoman/networks/interceptors/AuthInterceptor.kt b/app/src/main/java/com/example/bondoman/networks/interceptors/AuthInterceptor.kt new file mode 100644 index 0000000000000000000000000000000000000000..0059c5c74de9ffd00ba6fcd365c4971de832b252 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/networks/interceptors/AuthInterceptor.kt @@ -0,0 +1,16 @@ +package com.example.bondoman.networks.interceptors + +import okhttp3.Interceptor +import okhttp3.Response + +class AuthInterceptor(private val bearerToken: String) : Interceptor { + override fun intercept(chain: Interceptor.Chain): Response { + val request = chain.request() + val newRequest = + request.newBuilder() + .addHeader("Authorization", "Bearer $bearerToken") + .build() + + return chain.proceed(newRequest) + } +} diff --git a/app/src/main/java/com/example/bondoman/networks/requests/LoginRequest.kt b/app/src/main/java/com/example/bondoman/networks/requests/LoginRequest.kt new file mode 100644 index 0000000000000000000000000000000000000000..524f9eb7262a8d533a4e51b3d5497d73d5e5f770 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/networks/requests/LoginRequest.kt @@ -0,0 +1,12 @@ +package com.example.bondoman.networks.requests + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class LoginRequest( + @Json(name = "email") + var email: String? = null, + @Json(name = "password") + var password: String? = null, +) diff --git a/app/src/main/java/com/example/bondoman/networks/requests/ScanReceiptRequest.kt b/app/src/main/java/com/example/bondoman/networks/requests/ScanReceiptRequest.kt new file mode 100644 index 0000000000000000000000000000000000000000..a4a4f8f2be8f02cba1862b36a236aafc32f34bd1 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/networks/requests/ScanReceiptRequest.kt @@ -0,0 +1,9 @@ +package com.example.bondoman.networks.requests + +import okhttp3.MultipartBody +import retrofit2.http.Part + +data class ScanReceiptRequest( + @Part + val formData: MultipartBody.Part? = null, +) diff --git a/app/src/main/java/com/example/bondoman/networks/responses/LoginResponse.kt b/app/src/main/java/com/example/bondoman/networks/responses/LoginResponse.kt new file mode 100644 index 0000000000000000000000000000000000000000..5c22b9f864157b56da86eceab1390349a08c0b8b --- /dev/null +++ b/app/src/main/java/com/example/bondoman/networks/responses/LoginResponse.kt @@ -0,0 +1,9 @@ +package com.example.bondoman.networks.responses + +import com.squareup.moshi.Json + +data class LoginResponse( + @Json(name = "token") + val token: String?, + val error: String?, +) diff --git a/app/src/main/java/com/example/bondoman/networks/responses/ScanReceiptResponse.kt b/app/src/main/java/com/example/bondoman/networks/responses/ScanReceiptResponse.kt new file mode 100644 index 0000000000000000000000000000000000000000..ff960974c7b1aaa121bc2e4264c0bf2dac771068 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/networks/responses/ScanReceiptResponse.kt @@ -0,0 +1,27 @@ +package com.example.bondoman.networks.responses + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class ScanReceiptResponseItemsWrapper( + @Json(name = "items") + val items: ScanReceiptResponseItems?, + val error: String?, +) + +@JsonClass(generateAdapter = true) +data class ScanReceiptResponseItems( + @Json(name = "items") + val items: List<ScanReceiptResponseItem>, +) + +@JsonClass(generateAdapter = true) +data class ScanReceiptResponseItem( + @Json(name = "name") + val name: String, + @Json(name = "qty") + val quantity: Int, + @Json(name = "price") + val price: Double, +) diff --git a/app/src/main/java/com/example/bondoman/networks/responses/TokenResponse.kt b/app/src/main/java/com/example/bondoman/networks/responses/TokenResponse.kt new file mode 100644 index 0000000000000000000000000000000000000000..1b0c07ecbf9df360efa7e25b688604a62a16751b --- /dev/null +++ b/app/src/main/java/com/example/bondoman/networks/responses/TokenResponse.kt @@ -0,0 +1,13 @@ +package com.example.bondoman.networks.responses + +import com.squareup.moshi.Json + +data class TokenResponse( + @Json(name = "nim") + val nim: String?, + @Json(name = "iat") + val iat: Int?, + @Json(name = "exp") + val exp: Int?, + val error: String? = null, +) diff --git a/app/src/main/java/com/example/bondoman/networks/services/ScanReceiptService.kt b/app/src/main/java/com/example/bondoman/networks/services/ScanReceiptService.kt new file mode 100644 index 0000000000000000000000000000000000000000..6369406a6d9365beaf670de3f368c8ceaff4446f --- /dev/null +++ b/app/src/main/java/com/example/bondoman/networks/services/ScanReceiptService.kt @@ -0,0 +1,16 @@ +package com.example.bondoman.networks.services + +import com.example.bondoman.networks.responses.ScanReceiptResponseItemsWrapper +import okhttp3.MultipartBody +import retrofit2.Response +import retrofit2.http.Multipart +import retrofit2.http.POST +import retrofit2.http.Part + +interface ScanReceiptService { + @Multipart + @POST("/api/bill/upload") + suspend fun scanReceipt( + @Part request: MultipartBody.Part, + ): Response<ScanReceiptResponseItemsWrapper> +} diff --git a/app/src/main/java/com/example/bondoman/networks/services/UserService.kt b/app/src/main/java/com/example/bondoman/networks/services/UserService.kt new file mode 100644 index 0000000000000000000000000000000000000000..184603967597439c4c664752b3fc03d87212d133 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/networks/services/UserService.kt @@ -0,0 +1,18 @@ +package com.example.bondoman.networks.services + +import com.example.bondoman.networks.requests.LoginRequest +import com.example.bondoman.networks.responses.LoginResponse +import com.example.bondoman.networks.responses.TokenResponse +import retrofit2.Response +import retrofit2.http.Body +import retrofit2.http.POST + +interface UserService { + @POST("/api/auth/login") + suspend fun login( + @Body request: LoginRequest, + ): Response<LoginResponse> + + @POST("/api/auth/token") + suspend fun checkToken(): Response<TokenResponse> +} diff --git a/app/src/main/java/com/example/bondoman/services/receivers/ExpiryBroadcastReceiver.kt b/app/src/main/java/com/example/bondoman/services/receivers/ExpiryBroadcastReceiver.kt new file mode 100644 index 0000000000000000000000000000000000000000..579c05d9ca51ec373b82aca96aecd65886b53041 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/services/receivers/ExpiryBroadcastReceiver.kt @@ -0,0 +1,25 @@ +package com.example.bondoman.services.receivers + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import com.example.bondoman.data.Constants +import com.example.bondoman.data.utils.PreferencesManager +import com.example.bondoman.views.activities.LoginActivity + +class ExpiryBroadcastReceiver : BroadcastReceiver() { + override fun onReceive( + context: Context?, + intent: Intent?, + ) { + if (intent?.action == Constants.LOGOUT_TIMEOUT_INTENT) { + PreferencesManager.remove(context!!, "token", true) + PreferencesManager.remove(context, "nim", true) + PreferencesManager.remove(context, "email", true) + + val newIntent = Intent(context, LoginActivity::class.java) + newIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + context.startActivity(newIntent) + } + } +} diff --git a/app/src/main/java/com/example/bondoman/services/services/ExpiryService.kt b/app/src/main/java/com/example/bondoman/services/services/ExpiryService.kt new file mode 100644 index 0000000000000000000000000000000000000000..c74202f0c11f7cddee5b543216c529883e20b2af --- /dev/null +++ b/app/src/main/java/com/example/bondoman/services/services/ExpiryService.kt @@ -0,0 +1,121 @@ +package com.example.bondoman.services.services + +import android.app.Service +import android.content.Intent +import android.os.IBinder +import android.util.Log +import androidx.localbroadcastmanager.content.LocalBroadcastManager +import com.example.bondoman.data.Constants +import com.example.bondoman.data.repositories.UserRepository +import com.example.bondoman.data.utils.PreferencesManager +import com.example.bondoman.networks.RetrofitClient +import com.example.bondoman.networks.services.UserService +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.launch +import java.util.Timer +import java.util.TimerTask + +class ExpiryService : Service() { + private var defaultTimeout: Long = 300000L + private var timer: Timer? = null + + private val supervisorJob = SupervisorJob() + private val coroutineScope = CoroutineScope(Dispatchers.IO + supervisorJob) + + override fun onStartCommand( + intent: Intent?, + flags: Int, + startId: Int, + ): Int { + Log.i("ExpiryService", "Service started") + val timeout = intent?.getLongExtra("timeout_duration", defaultTimeout) ?: defaultTimeout + startTimer(timeout) + return START_STICKY + } + + private fun startTimer(timeout: Long) { + Log.i("ExpiryService", "Starting timer") + Log.d("ExpiryService", "Timeout: $timeout") + timer = Timer() + timer?.schedule( + object : TimerTask() { + override fun run() { + Log.i("ExpiryService", "Timer expired") + checkToken() + stopSelf() + } + }, + timeout, + ) + } + + private fun checkToken() { + val token: String? = PreferencesManager.getString(this, "token", true) + + if (token.isNullOrEmpty()) { + logout() + stopSelf() + } else { + checkTokenJob(token) + } + } + + private fun checkTokenJob(token: String) { + coroutineScope.launch { + try { + val repository = + UserRepository( + RetrofitClient.getInstanceWithAuth(token).create( + UserService::class.java, + ), + ) + + val tokenResponse = repository.checkToken() + + // If not success then sendBroadcast + if (!tokenResponse.isSuccessful || tokenResponse.body() == null) { + logout() + stopSelf() + return@launch + } + + // If expired then sendBroadcast + val currentTime = System.currentTimeMillis() / 1000 + if (tokenResponse.body()?.exp != null && tokenResponse.body()?.exp!! < currentTime) { + logout() + stopSelf() + return@launch + } + + val timeout = + tokenResponse.body()?.exp?.minus(currentTime)?.times(1000) ?: defaultTimeout + startTimer(timeout) + } catch (e: Exception) { + Log.e("ExpiryService", "Error checking token", e) + logout() + stopSelf() + } + } + } + + private fun logout() { + PreferencesManager.remove(this, "token", true) + PreferencesManager.remove(this, "nim", true) + PreferencesManager.remove(this, "email", true) + + Log.i("ExpiryService", "Sending broadcast") + val intent = Intent(Constants.LOGOUT_TIMEOUT_INTENT) + LocalBroadcastManager.getInstance(this).sendBroadcast(intent) + } + + override fun onDestroy() { + timer?.cancel() + super.onDestroy() + } + + override fun onBind(intent: Intent?): IBinder? { + return null + } +} diff --git a/app/src/main/java/com/example/bondoman/views/activities/LoginActivity.kt b/app/src/main/java/com/example/bondoman/views/activities/LoginActivity.kt new file mode 100644 index 0000000000000000000000000000000000000000..fe3ff0c975d81a001a07c34a0417ead3fe7afae2 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/views/activities/LoginActivity.kt @@ -0,0 +1,170 @@ +package com.example.bondoman.views.activities + +import android.content.Intent +import android.content.pm.ActivityInfo +import android.os.Bundle +import android.util.Log +import android.widget.Toast +import androidx.activity.viewModels +import androidx.appcompat.app.AppCompatActivity +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.lifecycle.Observer +import com.example.bondoman.R +import com.example.bondoman.data.utils.PreferencesManager +import com.example.bondoman.data.viewmodels.login.LoginViewModel +import com.example.bondoman.networks.responses.LoginResponse +import com.example.bondoman.networks.responses.TokenResponse +import com.example.bondoman.services.services.ExpiryService +import com.google.android.material.button.MaterialButton +import com.google.android.material.textfield.TextInputEditText + +class LoginActivity : AppCompatActivity() { + private val viewModel: LoginViewModel by viewModels() + private val expiryDuration: Long = 300000L // 5 minutes + // private val expiryDuration: Long = 10000L // 10 seconds for testing + + private lateinit var loginLayout: ConstraintLayout + private lateinit var emailInput: TextInputEditText + private lateinit var passwordInput: TextInputEditText + private lateinit var loginButton: MaterialButton + + @Override + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_login) + + checkLoginStatus() + + loginLayout = findViewById(R.id.login_layout) + emailInput = findViewById(R.id.email_text_input) + passwordInput = findViewById(R.id.password_text_input) + loginButton = findViewById(R.id.login_button) + + // TODO: Remove this block + // For faster testing + emailInput.setText("13521114@std.stei.itb.ac.id") + passwordInput.setText("password_13521114") + + loginButton.setOnClickListener { + onLoginButtonClick() + } + + requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + } + + private fun validateEmail(email: String): Boolean { + val emailRegex = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\$".toRegex() + return emailRegex.matches(email) + } + + private fun onLoginButtonClick() { + val email = emailInput.text.toString() + val password = passwordInput.text.toString() + + if (email.isEmpty()) { + emailInput.error = "Email is required" + return + } + + if (!validateEmail(email)) { + emailInput.error = "Invalid email" + return + } + + if (password.isEmpty()) { + passwordInput.error = "Password is required" + return + } + + viewModel.login(email, password) + + val observer = + Observer<LoginResponse?> { response -> + if (response == null) { + return@Observer + } + + if (response.error != null) { + Toast.makeText(this, response.error, Toast.LENGTH_SHORT).show() + } else { + val nim = email.substringBefore("@") + + PreferencesManager.putString(this, "token", response.token ?: "", true) + PreferencesManager.putString(this, "nim", nim, true) + PreferencesManager.putString(this, "email", email, true) + + // Start ExpiryService + val serviceIntent = Intent(this, ExpiryService::class.java).apply { + putExtra("timeout", expiryDuration) + } + stopService(serviceIntent) + startService(serviceIntent) + + val intent = Intent(this, MainActivity::class.java) + startActivity(intent) + finish() + } + + viewModel.resetLoginResponse() + } + + viewModel.loginResponse.observe(this, observer) + } + + private fun checkLoginStatus() { + val token: String? = PreferencesManager.getString(this, "token", true) + + if (token.isNullOrEmpty()) { + return + } + + viewModel.checkToken(token) + + val observer = + Observer<TokenResponse?> { response -> + if (response == null) { + return@Observer + } + + if (response.error != null) { + Log.e("LoginActivity", response.error) + PreferencesManager.remove(this, "token", true) + PreferencesManager.remove(this, "nim", true) + PreferencesManager.remove(this, "email", true) + viewModel.resetTokenResponse() + return@Observer + } + + val currentTime = System.currentTimeMillis() / 1000 + if (response.exp != null && response.exp < currentTime) { + Log.i("LoginActivity", "Token expired") + PreferencesManager.remove(this, "token", true) + PreferencesManager.remove(this, "nim", true) + PreferencesManager.remove(this, "email", true) + viewModel.resetTokenResponse() + return@Observer + } + + val nim: String = response.nim!! + + PreferencesManager.putString(this, "token", token, true) + PreferencesManager.putString(this, "nim", nim, true) + PreferencesManager.putString(this, "email", "${nim}@std.stei.itb.ac.id", true) + + // Start ExpiryService + val serviceIntent = Intent(this, ExpiryService::class.java).apply { + putExtra("timeout", expiryDuration) + } + stopService(serviceIntent) + startService(serviceIntent) + + val intent = Intent(this, MainActivity::class.java) + startActivity(intent) + finish() + + viewModel.resetTokenResponse() + } + + viewModel.tokenResponse.observe(this, observer) + } +} diff --git a/app/src/main/java/com/example/bondoman/views/activities/MainActivity.kt b/app/src/main/java/com/example/bondoman/views/activities/MainActivity.kt new file mode 100644 index 0000000000000000000000000000000000000000..13ea4db4d1fe8cf4ddc58c0684b2c1dd61fe0da8 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/views/activities/MainActivity.kt @@ -0,0 +1,472 @@ +package com.example.bondoman.views.activities + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.content.pm.ActivityInfo +import android.net.ConnectivityManager +import android.net.LinkProperties +import android.net.Network +import android.net.NetworkCapabilities +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.view.View +import android.view.ViewGroup +import android.widget.ImageButton +import android.widget.TextView +import android.widget.Toast +import androidx.activity.OnBackPressedCallback +import androidx.appcompat.app.AppCompatActivity +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.view.ViewCompat +import androidx.lifecycle.ViewModelProvider +import androidx.localbroadcastmanager.content.LocalBroadcastManager +import androidx.navigation.NavController +import androidx.navigation.fragment.NavHostFragment +import androidx.navigation.ui.setupWithNavController +import com.example.bondoman.MainApplication +import com.example.bondoman.R +import com.example.bondoman.data.Constants +import com.example.bondoman.data.utils.PreferencesManager +import com.example.bondoman.data.viewmodels.transaction.TransactionViewModel +import com.example.bondoman.data.viewmodels.transaction.TransactionViewModelFactory +import com.example.bondoman.databinding.ActivityMainBinding +import com.example.bondoman.services.receivers.ExpiryBroadcastReceiver +import com.example.bondoman.services.services.ExpiryService +import com.example.bondoman.views.utils.interfaces.ParentActivityService +import com.google.android.material.bottomnavigation.BottomNavigationView +import eightbitlab.com.blurview.BlurView + +class MainActivity : AppCompatActivity(), ParentActivityService { + companion object { + private const val DIALOG_BLUR_RADIUS = 1f + private val MAIN_FRAGMENT_IDS = + listOf( + R.id.transactionListFragment, + R.id.scanReceiptFragment, + R.id.graphFragment, + R.id.twibbonFragment, + R.id.settingsFragment, + ) + } + + private lateinit var binding: ActivityMainBinding + private lateinit var mainLayout: ConstraintLayout + private lateinit var backButton: ImageButton + private lateinit var headerText: TextView + private lateinit var dialogParent: BlurView + private lateinit var transactionViewModel: TransactionViewModel + private lateinit var navController: NavController + + // destinations + private val destinationListeners = mutableListOf<(Int) -> Unit>() + private var currentFragmentId: Int? = null + private var isConnectionlost: Boolean = false + private var isOnConnectionLostFragment: Boolean = false + private var isOnBack: Boolean = false + private val connectionRequiredFragmentIds: MutableList<Int> = mutableListOf() + private var hasMovedFromConnectionLostFragment: Boolean = false + private val onBackPressedCallback = + object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + isOnBack = true + navController.popBackStack() + } + } + + private val expiryReceiver = ExpiryBroadcastReceiver() + + var randomizeNextTransaction: Boolean = false + + private val randomizeReceiver = + object : BroadcastReceiver() { + override fun onReceive( + context: Context?, + intent: Intent?, + ) { + if (intent?.action == Constants.RANDOMIZE_TRANSACTION_INTENT) { + randomizeNextTransaction = true + } + } + } + + override fun addDestinationChangedListener(listener: (Int) -> Unit) { + destinationListeners.add(listener) + } + + override fun removeDestinationChangedListener(listener: (Int) -> Unit) { + destinationListeners.remove(listener) + } + + override fun appendLayout(view: View) { + mainLayout.addView(view) + } + + override fun appendLayout( + view: View, + layoutParams: ViewGroup.LayoutParams, + ) { + mainLayout.addView(view, layoutParams) + } + + override fun removeLayout(view: View) { + mainLayout.removeView(view) + } + + override fun getParentLayoutId(): Int { + return R.id.main + } + + override fun showBackButton() { + backButton.visibility = View.VISIBLE + } + + override fun showBackButton(onClick: () -> Unit) { + backButton.visibility = View.VISIBLE + backButton.setOnClickListener { + isOnBack = true + onClick() + } + } + + override fun hideBackButton() { + backButton.visibility = View.GONE + + backButton.setOnClickListener { + isOnBack = true + navController.popBackStack() + } + } + + override fun setHeaderText(text: String) { + headerText.text = text + } + + override fun showDialog( + view: View, + rootView: ViewGroup, + elevation: Float, + ) { + configureDialogBehavior(rootView) + + dialogParent.removeAllViews() + dialogParent.isClickable = true + dialogParent.addView(view) + dialogParent.setBlurEnabled(true) + + dialogParent.elevation = elevation + } + + override fun showDialog( + view: View, + elevation: Float, + ) { + val decorView = window.decorView as ViewGroup + showDialog(view, decorView, elevation) + } + + override fun showDialog(view: View) { + val decorView = window.decorView as ViewGroup + showDialog(view, decorView, 0f) + } + + override fun hideDialog() { + dialogParent.setBlurEnabled(false) + dialogParent.removeAllViews() + dialogParent.isClickable = false + } + + override fun dialogParentId(): Int { + return R.id.dialog_parent + } + + override fun showToast( + message: String, + duration: Int, + ) { + Toast.makeText( + baseContext, + message, + duration, + ).show() + } + + override fun getOnBackPressedCallback(): OnBackPressedCallback { + return onBackPressedCallback + } + + private fun addConnectionRequiredFragment(fragmentId: Int) { + this.connectionRequiredFragmentIds.add(fragmentId) + } + + override fun getEmail(): String { + return PreferencesManager.getString(this, "email", true) ?: "" + } + + override fun getNIM(): String { + return PreferencesManager.getString(this, "nim", true) ?: "" + } + + private fun initializeComponents() { + mainLayout = binding.main + backButton = binding.backButton + headerText = binding.headerText + dialogParent = binding.dialogParent + } + + private fun configureDialogBehavior(rootView: ViewGroup) { + dialogParent + .setupWith(rootView) + .setFrameClearDrawable(rootView.background) + .setBlurRadius(DIALOG_BLUR_RADIUS) + dialogParent.setBlurEnabled(false) + } + + private fun configureNavigation() { + this.addConnectionRequiredFragment(R.id.scanReceiptFragment) + + onBackPressedDispatcher.addCallback(this, onBackPressedCallback) + + val navbar = findViewById<BottomNavigationView>(R.id.navbar) + val navHostFragment = + supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment + navController = navHostFragment.navController + val headerText = findViewById<TextView>(R.id.header_text) + + navbar.setupWithNavController(navController) + + navController.addOnDestinationChangedListener { _, destination, _ -> + headerText.text = destination.label ?: headerText.text + + if (destination.id == R.id.graphFragment) { + requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED + } else { + requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + } + + if (destination.id in MAIN_FRAGMENT_IDS) { + hideBackButton() + } + + destinationListeners.forEach { it.invoke(destination.id) } + + // handle back navigation + // IMPORTANT: must be checked before isConnectionLost condition + if (isOnBack) { + if (isOnConnectionLostFragment) { + isOnConnectionLostFragment = false + navController.popBackStack() + return@addOnDestinationChangedListener + } + + isOnBack = false + } + + // update states + if (destination.id != R.id.connectionLostFragment) { + isOnConnectionLostFragment = false + hasMovedFromConnectionLostFragment = true + } + + // remove old ConnectionLostFragment + if ((isOnConnectionLostFragment || hasMovedFromConnectionLostFragment) && + destination.id == R.id.connectionLostFragment + ) { + navController.popBackStack() + return@addOnDestinationChangedListener + } + + // handle twibbon preview navigation + if ( + (currentFragmentId != R.id.twibbonFragment) && + destination.id == R.id.twibbonPreviewFragment + ) { + // Set the action to go back to TwibbonFragment + navController.popBackStack(R.id.twibbonFragment, false) + currentFragmentId = R.id.twibbonFragment + return@addOnDestinationChangedListener + } + + // handle navigation to add and update page + if ( + (currentFragmentId != R.id.transactionListFragment) && + (destination.id == R.id.transactionAddFragment || destination.id == R.id.transactionUpdateFragment) + ) { + // Set the action to go back to TransactionListFragment + navController.popBackStack(R.id.transactionListFragment, false) + return@addOnDestinationChangedListener + } + + // handle scan fragment navigation + if (currentFragmentId != R.id.scanReceiptFragment && destination.id == R.id.scanReceiptResultFragment) { + navController.popBackStack(R.id.scanReceiptFragment, false) + return@addOnDestinationChangedListener + } + + // handle connection lost fragment + if (isConnectionlost) { + if (destination.id in connectionRequiredFragmentIds) { + hasMovedFromConnectionLostFragment = false + navController.navigate(R.id.action_global_to_ConnectionLostFragment) + return@addOnDestinationChangedListener + } else if (destination.id == R.id.connectionLostFragment) { + isOnConnectionLostFragment = true + hasMovedFromConnectionLostFragment = false + return@addOnDestinationChangedListener + } + } + + // ignore existing connection lost page on stack + if (destination.id == R.id.connectionLostFragment) { + navController.popBackStack() + } + + // handle self destination + else if (currentFragmentId == destination.id) { + if (!isOnConnectionLostFragment) { + return@addOnDestinationChangedListener + } else { + currentFragmentId = destination.id + } + } + + // handle default behaviour + else { + currentFragmentId = destination.id + } + } + + // back button configuration + val backButton = binding.backButton + val isRtl = ViewCompat.getLayoutDirection(backButton) == ViewCompat.LAYOUT_DIRECTION_RTL + if (isRtl) { + backButton.scaleX = -1f + } + hideBackButton() + } + + private fun configureBroadcastReceiver() { + // IntentFilter + val expiryReceiverFilter = IntentFilter(Constants.LOGOUT_TIMEOUT_INTENT) + val randomizeReceiverFilter = + IntentFilter(Constants.RANDOMIZE_TRANSACTION_INTENT) + + // Register Expiry Receiver + LocalBroadcastManager.getInstance(this) + .registerReceiver(expiryReceiver, expiryReceiverFilter) + LocalBroadcastManager.getInstance(this) + .registerReceiver(randomizeReceiver, randomizeReceiverFilter) + } + + private fun monitorConnection() { + val connectivityManager = + getSystemService(ConnectivityManager::class.java) as ConnectivityManager + connectivityManager.registerDefaultNetworkCallback( + object : + ConnectivityManager.NetworkCallback() { + override fun onAvailable(network: Network) { + if (isConnectionlost) { + showToast( + "Connection found", + Toast.LENGTH_SHORT, + ) + } + + Handler(Looper.getMainLooper()).post { + if (isConnectionlost) { + isConnectionlost = false + navController.popBackStack() + } + } + } + + override fun onLost(network: Network) { + Handler(Looper.getMainLooper()).post { + isConnectionlost = true + if (currentFragmentId in connectionRequiredFragmentIds) { + navController.navigate(R.id.action_global_to_ConnectionLostFragment) + } + + showToast( + "Connection lost", + Toast.LENGTH_SHORT, + ) + } + } + + override fun onCapabilitiesChanged( + network: Network, + networkCapabilities: NetworkCapabilities, + ) { + } + + override fun onLinkPropertiesChanged( + network: Network, + linkProperties: LinkProperties, + ) { + } + }, + ) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + if (savedInstanceState != null) { + randomizeNextTransaction = savedInstanceState.getBoolean("randomizeNextTransaction") + } + + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) + + // initialize viewmodel + val factory = TransactionViewModelFactory(application as MainApplication, getNIM()) + transactionViewModel = ViewModelProvider(this, factory)[TransactionViewModel::class.java] + + // initialize fragment id tracking + currentFragmentId = null + + configureBroadcastReceiver() + + initializeComponents() + configureNavigation() + + // monitor network + monitorConnection() + } + + override fun onResume() { + super.onResume() + + // If the token is missing, logout + if (PreferencesManager.getString(this, "token", true) == null) { + // Stop Service + val serviceIntent = Intent(this, ExpiryService::class.java) + stopService(serviceIntent) + + // Remove Shared Preferences + PreferencesManager.remove(this, "token", true) + PreferencesManager.remove(this, "nim", true) + PreferencesManager.remove(this, "email", true) + + // Move to LoginActivity + val intent = Intent(this, LoginActivity::class.java) + startActivity(intent) + finish() + } + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putBoolean("randomizeNextTransaction", randomizeNextTransaction) + } + + override fun onDestroy() { + LocalBroadcastManager.getInstance(this) + .unregisterReceiver(expiryReceiver) + LocalBroadcastManager.getInstance(this).unregisterReceiver(randomizeReceiver) + super.onDestroy() + } +} diff --git a/app/src/main/java/com/example/bondoman/views/adapters/TransactionListAdapter.kt b/app/src/main/java/com/example/bondoman/views/adapters/TransactionListAdapter.kt new file mode 100644 index 0000000000000000000000000000000000000000..1083d8932462f89a32f8a78cf7ec0a85560279e7 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/views/adapters/TransactionListAdapter.kt @@ -0,0 +1,94 @@ +package com.example.bondoman.views.adapters + +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.example.bondoman.R +import com.example.bondoman.data.models.Transaction +import com.example.bondoman.data.models.TransactionCategory +import com.example.bondoman.data.utils.TransactionParser +import com.example.bondoman.databinding.ComponentTransactionCardBinding +import com.example.bondoman.views.utils.interfaces.TransactionClickListener + +class TransactionListAdapter( + private val clickListener: TransactionClickListener, +) : ListAdapter<Transaction, TransactionListAdapter.ViewHolder>(TransactionDiffCallback()) { + inner class ViewHolder(private val binding: ComponentTransactionCardBinding) : + RecyclerView.ViewHolder(binding.root) { + private val iconImageView: ImageView = binding.transactionIconImage + private val dateTextView: TextView = binding.dateText + private val amountTextView: TextView = binding.amountText + private val titleTextView: TextView = binding.titleText + private val locationTextView: TextView = binding.transactionLocationText + private val transactionParser = TransactionParser() + + fun bind(transaction: Transaction) { + iconImageView.setImageResource( + if (transaction.category == TransactionCategory.EARNINGS) { + R.drawable.ic_coins + } else { + R.drawable.ic_shopping_bag_minus + }, + ) + iconImageView.setBackgroundColor( + ContextCompat.getColor( + itemView.context, + if (transaction.category == TransactionCategory.EARNINGS) { + R.color.teal_200 + } else { + R.color.rose_200 + }, + ), + ) + dateTextView.text = transactionParser.getDateString(transaction.date) + amountTextView.text = + transactionParser.getAmountString(transaction.amount, transaction.category) + titleTextView.text = transaction.title + locationTextView.text = transaction.location ?: "Unknown" + + itemView.setOnClickListener { clickListener.onItemClick(transaction) } + } + } + + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int, + ): ViewHolder { + val binding = + ComponentTransactionCardBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false, + ) + return ViewHolder(binding) + } + + override fun onBindViewHolder( + holder: ViewHolder, + position: Int, + ) { + val item = getItem(position) + holder.bind(item) + } +} + +class TransactionDiffCallback : DiffUtil.ItemCallback<Transaction>() { + override fun areItemsTheSame( + oldItem: Transaction, + newItem: Transaction, + ): Boolean { + return oldItem.id == newItem.id + } + + override fun areContentsTheSame( + oldItem: Transaction, + newItem: Transaction, + ): Boolean { + return oldItem == newItem + } +} diff --git a/app/src/main/java/com/example/bondoman/views/components/DateInputComponent.kt b/app/src/main/java/com/example/bondoman/views/components/DateInputComponent.kt new file mode 100644 index 0000000000000000000000000000000000000000..8e744cc8bc7a6e0aacf73ef23d1f7bb73625f46e --- /dev/null +++ b/app/src/main/java/com/example/bondoman/views/components/DateInputComponent.kt @@ -0,0 +1,68 @@ +package com.example.bondoman.views.components + +import android.content.Context +import android.util.AttributeSet +import android.widget.LinearLayout +import androidx.appcompat.app.AppCompatActivity +import com.example.bondoman.R +import com.example.bondoman.views.fragments.DatePickerFragment +import com.google.android.material.button.MaterialButton +import com.google.android.material.textview.MaterialTextView + +class DateInputComponent + @JvmOverloads + constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, + ) : LinearLayout(context, attrs, defStyleAttr) { + companion object { + const val FRAGMENT_TAG = "datePicker" + } + + private val labelTextView: MaterialTextView + private val button: MaterialButton + private var onDatePickedListener: ((year: Int, month: Int, day: Int) -> Unit)? = null + + init { + inflate(context, R.layout.component_date_input, this) + + labelTextView = findViewById(R.id.date_input_component_label) + button = findViewById(R.id.date_input_button) + + // Load attributes + val attributes = context.obtainStyledAttributes(attrs, R.styleable.DateInputComponent) + val labelText = attributes.getString(R.styleable.DateInputComponent_dateInputLabel) ?: "" + attributes.recycle() + + // Set label text and hint + labelTextView.text = labelText + + if (context is AppCompatActivity) { + button.setOnClickListener { + val datePickerFragment = DatePickerFragment() + datePickerFragment.setOnDatePicked { year, month, day -> + setText(year, month, day) + onDatePickedListener?.invoke(year, month, day) + } + datePickerFragment.show(context.supportFragmentManager, FRAGMENT_TAG) + } + } + } + + private fun setText(text: String) { + button.text = text + } + + fun setText( + year: Int, + month: Int, + day: Int, + ) { + setText("$year/${month + 1}/$day") + } + + fun setOnDatePicked(listener: (year: Int, month: Int, day: Int) -> Unit) { + onDatePickedListener = listener + } + } diff --git a/app/src/main/java/com/example/bondoman/views/components/DialogComponent.kt b/app/src/main/java/com/example/bondoman/views/components/DialogComponent.kt new file mode 100644 index 0000000000000000000000000000000000000000..b4a1c104872de91f332f3e3d480804a44a524b2f --- /dev/null +++ b/app/src/main/java/com/example/bondoman/views/components/DialogComponent.kt @@ -0,0 +1,129 @@ +package com.example.bondoman.views.components + +import android.content.Context +import android.util.AttributeSet +import android.widget.LinearLayout +import androidx.core.content.ContextCompat +import com.example.bondoman.R +import com.google.android.material.button.MaterialButton +import com.google.android.material.textview.MaterialTextView + +class DialogComponent + @JvmOverloads + constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, + ) : LinearLayout(context, attrs, defStyleAttr) { + companion object { + enum class ButtonType(val value: Int) { + POSITIVE_BUTTON(1), + NEUTRAL_BUTTON(0), + NEGATIVE_BUTTON(-1), + } + + val buttonTypeMap = + mapOf( + 1 to ButtonType.POSITIVE_BUTTON, + 0 to ButtonType.NEUTRAL_BUTTON, + -1 to ButtonType.NEGATIVE_BUTTON, + ) + + val buttonBackgroundColorMap = + mapOf( + ButtonType.POSITIVE_BUTTON to R.color.teal_200, + ButtonType.NEUTRAL_BUTTON to R.color.zinc_600, + ButtonType.NEGATIVE_BUTTON to R.color.rose_200, + ) + + val buttonTextColorMap = + mapOf( + ButtonType.POSITIVE_BUTTON to R.color.zinc_900, + ButtonType.NEUTRAL_BUTTON to R.color.zinc_300, + ButtonType.NEGATIVE_BUTTON to R.color.zinc_900, + ) + } + + private val textView: MaterialTextView + private val firstButton: MaterialButton + private val secondButton: MaterialButton + + init { + inflate(context, R.layout.component_dialog, this) + + textView = findViewById(R.id.dialog_text) + firstButton = findViewById(R.id.dialog_first_button) + secondButton = findViewById(R.id.dialog_second_button) + + // Load attributes + val attributes = context.obtainStyledAttributes(attrs, R.styleable.DialogComponent) + val text = attributes.getString(R.styleable.DialogComponent_dialogText) ?: "" + val firstButtonTypeOrdinal = + attributes.getInt( + R.styleable.DialogComponent_dialogFirstButtonType, + ButtonType.NEUTRAL_BUTTON.ordinal, + ) + val secondButtonTypeOrdinal = + attributes.getInt( + R.styleable.DialogComponent_dialogSecondButtonType, + ButtonType.POSITIVE_BUTTON.ordinal, + ) + val firstButtonText = + attributes.getString(R.styleable.DialogComponent_firstButtonText) ?: "" + val secondButtonText = + attributes.getString(R.styleable.DialogComponent_secondButtonText) ?: "" + + attributes.recycle() + + // Set label text + setText(text) + + // Set button types + setFirstButtonType(buttonTypeMap[firstButtonTypeOrdinal]!!) + setSecondButtonType(buttonTypeMap[secondButtonTypeOrdinal]!!) + + // set button text + setFirstButtonText(firstButtonText) + setSecondButtonText(secondButtonText) + } + + fun setText(text: String) { + textView.text = text + } + + fun setFirstButtonText(text: String) { + firstButton.text = text + } + + fun setSecondButtonText(text: String) { + secondButton.text = text + } + + fun setFirstButtonOnClickListener(listener: () -> Unit) { + firstButton.setOnClickListener { listener() } + } + + fun setSecondButtonOnClickListener(listener: () -> Unit) { + secondButton.setOnClickListener { listener() } + } + + fun setFirstButtonType(type: ButtonType) { + firstButton.setBackgroundColor( + ContextCompat.getColor( + context, + buttonBackgroundColorMap[type]!!, + ), + ) + firstButton.setTextColor(ContextCompat.getColor(context, buttonTextColorMap[type]!!)) + } + + fun setSecondButtonType(type: ButtonType) { + secondButton.setBackgroundColor( + ContextCompat.getColor( + context, + buttonBackgroundColorMap[type]!!, + ), + ) + secondButton.setTextColor(ContextCompat.getColor(context, buttonTextColorMap[type]!!)) + } + } diff --git a/app/src/main/java/com/example/bondoman/views/components/PageInfoComponent.kt b/app/src/main/java/com/example/bondoman/views/components/PageInfoComponent.kt new file mode 100644 index 0000000000000000000000000000000000000000..bf564242300dc718b58b57c0bfb15e296b544e1b --- /dev/null +++ b/app/src/main/java/com/example/bondoman/views/components/PageInfoComponent.kt @@ -0,0 +1,43 @@ +package com.example.bondoman.views.components + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.widget.LinearLayout +import com.example.bondoman.R +import com.example.bondoman.databinding.ComponentPageInfoBinding + +class PageInfoComponent + @JvmOverloads + constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, + ) : LinearLayout(context, attrs, defStyleAttr) { + private var viewBinding: ComponentPageInfoBinding + + init { + viewBinding = + ComponentPageInfoBinding.inflate(LayoutInflater.from(context), this, true) + + // Load attributes + val attributes = context.obtainStyledAttributes(attrs, R.styleable.PageInfoComponent) + + val text = attributes.getText(R.styleable.PageInfoComponent_pageInfoText) ?: "" + + val src = + attrs?.getAttributeResourceValue( + "http://schemas.android.com/apk/res/android", + "src", + R.drawable.ic_router_off, + ) + + attributes.recycle() + + viewBinding.pageInfoText.text = text + + if (src != null) { + viewBinding.pageInfoIcon.setImageResource(src) + } + } + } diff --git a/app/src/main/java/com/example/bondoman/views/components/SettingButtonComponent.kt b/app/src/main/java/com/example/bondoman/views/components/SettingButtonComponent.kt new file mode 100644 index 0000000000000000000000000000000000000000..b2e8fb4c26c768e30e2f4ec0516de219747db6c0 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/views/components/SettingButtonComponent.kt @@ -0,0 +1,74 @@ +package com.example.bondoman.views.components + +import android.animation.ArgbEvaluator +import android.animation.ObjectAnimator +import android.content.Context +import android.util.AttributeSet +import android.widget.ImageView +import android.widget.LinearLayout +import androidx.constraintlayout.widget.ConstraintLayout +import com.example.bondoman.R +import com.google.android.material.textview.MaterialTextView + +class SettingButtonComponent + @JvmOverloads + constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, + ) : LinearLayout(context, attrs, defStyleAttr) { + private val layout: ConstraintLayout + private val icon: ImageView + private val title: MaterialTextView + private val subtitle: MaterialTextView + + init { + inflate(context, R.layout.component_setting_button, this) + + layout = findViewById(R.id.setting_button_layout) + icon = findViewById(R.id.setting_button_icon) + title = findViewById(R.id.setting_button_title) + subtitle = findViewById(R.id.setting_button_subtitle) + + // Load attributes + val attributes = context.obtainStyledAttributes(attrs, R.styleable.SettingButtonComponent) + val icon = attributes.getDrawable(R.styleable.SettingButtonComponent_settingButtonIcon) + val title = + attributes.getString(R.styleable.SettingButtonComponent_settingButtonTitle) ?: "" + val subtitle = + attributes.getString(R.styleable.SettingButtonComponent_settingButtonSubtitle) ?: "" + + // Set values + this.icon.setImageDrawable(icon) + this.title.text = title + this.subtitle.text = subtitle + + setOnClickListener { } + + attributes.recycle() + } + + override fun setOnClickListener(listener: OnClickListener?) { + super.setOnClickListener { v -> + animateButton() + listener?.onClick(v) + } + } + + private fun animateButton() { + // Change the background color of the layout + layout.setBackgroundColor(resources.getColor(R.color.button_pressed_color, null)) + + // Animate the background color to slowly fade back to the original color for 1s + val fadeBackAnimator = + ObjectAnimator.ofArgb( + layout, + "backgroundColor", + resources.getColor(R.color.button_pressed_color, null), + resources.getColor(R.color.button_default_color, null), + ) + fadeBackAnimator.duration = 500 // 1 second + fadeBackAnimator.setEvaluator(ArgbEvaluator()) + fadeBackAnimator.start() + } + } diff --git a/app/src/main/java/com/example/bondoman/views/components/TextInputComponent.kt b/app/src/main/java/com/example/bondoman/views/components/TextInputComponent.kt new file mode 100644 index 0000000000000000000000000000000000000000..55a3292e29bedbc5a51ad02cd5609c5ec9f22ef3 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/views/components/TextInputComponent.kt @@ -0,0 +1,213 @@ +package com.example.bondoman.views.components + +import android.content.Context +import android.text.Editable +import android.text.InputFilter +import android.text.InputFilter.LengthFilter +import android.text.InputType +import android.text.TextWatcher +import android.text.method.DigitsKeyListener +import android.util.AttributeSet +import android.view.LayoutInflater +import android.widget.LinearLayout +import com.example.bondoman.R +import com.google.android.material.textfield.TextInputEditText +import com.google.android.material.textview.MaterialTextView + +class TextInputComponent + @JvmOverloads + constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, + ) : LinearLayout(context, attrs, defStyleAttr) { + private val labelTextView: MaterialTextView + private val inputEditText: TextInputEditText + private var beforeTextChangedListener: ((text: CharSequence?, start: Int, count: Int, after: Int) -> Unit)? = + null + private var onTextChangedListener: ((text: CharSequence?, start: Int, before: Int, count: Int) -> Unit)? = + null + private var afterTextChangedListener: ((text: Editable?) -> Unit)? = null + + companion object { + enum class Keyboard(val value: Int) { + TEXT(0), + NUMBER(1), + } + } + + init { + LayoutInflater.from(context).inflate(R.layout.component_text_input, this, true) + + labelTextView = findViewById(R.id.text_input_component_label) + inputEditText = findViewById(R.id.text_input_component_field) + + // Load attributes + val attributes = context.obtainStyledAttributes(attrs, R.styleable.TextInputComponent) + val labelText = attributes.getString(R.styleable.TextInputComponent_textInputLabel) ?: "" + val hint = attributes.getString(R.styleable.TextInputComponent_textInputHint) ?: "" + val inputType = + attrs?.getAttributeIntValue( + "http://schemas.android.com/apk/res/android", + "inputType", + InputType.TYPE_CLASS_TEXT, + ) ?: InputType.TYPE_CLASS_TEXT + + val digits = + attrs?.getAttributeValue( + "http://schemas.android.com/apk/res/android", + "digits", + ) + + val maxLength = + attrs?.getAttributeValue( + "http://schemas.android.com/apk/res/android", + "maxLength", + ) + + val keyboardOrdinal = + attributes.getInt( + R.styleable.TextInputComponent_textInputKeyboard, + TextInputComponent.Companion.Keyboard.TEXT.ordinal, + ) + + attributes.recycle() + + // Set attributes + labelTextView.text = labelText + inputEditText.hint = hint + inputEditText.inputType = inputType + + if (digits != null) { + when (keyboardOrdinal) { + Keyboard.TEXT.ordinal -> { + val textWatcher = + object : TextWatcher { + override fun beforeTextChanged( + s: CharSequence?, + start: Int, + count: Int, + after: Int, + ) { + // This method is called before the text is changed + } + + override fun onTextChanged( + s: CharSequence?, + start: Int, + before: Int, + count: Int, + ) { + if (s != null) { + val filteredText = + s.filter { + it in digits + } + + if (filteredText.length < s.length) { + inputEditText.setText(filteredText) + inputEditText.setSelection( + filteredText.length, + ) + } + } + } + + override fun afterTextChanged(s: Editable?) { + // This method is called after the text has changed + } + } + + // Attach the TextWatcher to the EditText + inputEditText.addTextChangedListener(textWatcher) + } + + Keyboard.NUMBER.ordinal -> { + inputEditText.keyListener = DigitsKeyListener.getInstance(digits) + } + } + } + + if (maxLength != null) { + val filterArray = arrayOfNulls<InputFilter>(1) + filterArray[0] = LengthFilter(maxLength.toInt()) + inputEditText.setFilters(filterArray) + } + + if (maxLength != null) { + val filterArray = arrayOfNulls<InputFilter>(1) + filterArray[0] = LengthFilter(maxLength.toInt()) + inputEditText.setFilters(filterArray) + } + + // Set up text watcher + inputEditText.addTextChangedListener( + object : TextWatcher { + override fun beforeTextChanged( + s: CharSequence?, + start: Int, + count: Int, + after: Int, + ) { + beforeTextChangedListener?.invoke(s, start, count, after) + } + + override fun onTextChanged( + s: CharSequence?, + start: Int, + before: Int, + count: Int, + ) { + onTextChangedListener?.invoke(s, start, before, count) + } + + override fun afterTextChanged(s: Editable?) { + afterTextChangedListener?.invoke(s) + } + }, + ) + } + + fun setInputType(type: Int) { + inputEditText.inputType = type + } + + fun setText(text: String) { + inputEditText.setText(text) + } + + fun getText(): String { + return inputEditText.text.toString() + } + + fun disable() { + inputEditText.isCursorVisible = false + inputEditText.isFocusableInTouchMode = false + } + + fun enable() { + inputEditText.isCursorVisible = true + inputEditText.isFocusableInTouchMode = true + } + + // Setter methods for text change listeners + fun setBeforeTextChangedListener(listener: (text: CharSequence?, start: Int, count: Int, after: Int) -> Unit) { + beforeTextChangedListener = listener + } + + fun setOnTextChangedListener(listener: (text: CharSequence?, start: Int, before: Int, count: Int) -> Unit) { + onTextChangedListener = listener + } + + fun setAfterTextChangedListener(listener: (text: Editable?) -> Unit) { + afterTextChangedListener = listener + } + + fun addTextWactcher(textWatcher: TextWatcher) { + inputEditText.addTextChangedListener(textWatcher) + } + + fun removeTextWatcher(textWatcher: TextWatcher) { + inputEditText.removeTextChangedListener(textWatcher) + } + } diff --git a/app/src/main/java/com/example/bondoman/views/components/TransactionDetailComponent.kt b/app/src/main/java/com/example/bondoman/views/components/TransactionDetailComponent.kt new file mode 100644 index 0000000000000000000000000000000000000000..e770dfa9d06d488af831b67c69e92e59990434fe --- /dev/null +++ b/app/src/main/java/com/example/bondoman/views/components/TransactionDetailComponent.kt @@ -0,0 +1,192 @@ +package com.example.bondoman.views.components + +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.text.Html +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.LinearLayout +import androidx.core.content.ContextCompat +import com.example.bondoman.R +import com.example.bondoman.data.models.Transaction +import com.example.bondoman.data.models.TransactionCategory +import com.example.bondoman.data.utils.TransactionParser +import com.example.bondoman.databinding.ComponentTransactionDetailBinding +import com.google.android.material.bottomsheet.BottomSheetBehavior +import eightbitlab.com.blurview.BlurView + + +class TransactionDetailComponent +@JvmOverloads +constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, +) : LinearLayout(context, attrs, defStyleAttr) { + + companion object { + private const val UNKNOWN_LOCATION_VALUE = "Unknown" + private const val GOOGLE_MAP_INTENT_URL = "com.google.android.apps.maps" + } + + private var viewBinding: ComponentTransactionDetailBinding + private var transactionParser: TransactionParser + private var onTransactionRemovedListener: (() -> Unit)? = null + + fun setOnTransactionRemovedListener(listener: (() -> Unit)?) { + this.onTransactionRemovedListener = listener + } + + private var blurView: BlurView + private lateinit var bottomSheetBehavior: BottomSheetBehavior<out View> + + private val blurRadius: Float = 1f + private val blurColor = ContextCompat.getColor(context, R.color.blur) + private val transparentColor = ContextCompat.getColor(context, android.R.color.transparent) + + init { + viewBinding = + ComponentTransactionDetailBinding.inflate(LayoutInflater.from(context), this, true) + transactionParser = TransactionParser() + + blurView = viewBinding.blurView + configureBottomSheetBehaviour() + + viewBinding.transactionDetailLocationText.isClickable = true + viewBinding.transactionDetailLocationText.setOnClickListener { + val locationText = viewBinding.transactionDetailLocationText.text.toString() + + + if (locationText != UNKNOWN_LOCATION_VALUE) { + val gmmIntentUri = + Uri.parse("geo:0,0?q=" + Uri.encode(locationText)) + val mapIntent = Intent(Intent.ACTION_VIEW, gmmIntentUri) + mapIntent.setPackage(GOOGLE_MAP_INTENT_URL) + + if (mapIntent.resolveActivity(context.packageManager) != null) { + context.startActivity(mapIntent) + } + } + } + } + + fun configureBlurEffect(rootView: ViewGroup) { + blurView + .setupWith(rootView) + .setFrameClearDrawable(rootView.background) + .setBlurRadius(blurRadius) + blurView.setBlurEnabled(false) + } + + private fun hideAction() { + blurView.setBlurEnabled(false) + blurView.setBackgroundColor(transparentColor) + blurView.isClickable = false + } + + private fun showAction() { + blurView.setBlurEnabled(true) + blurView.setBackgroundColor(transparentColor) + + blurView.isClickable = true + } + + private fun configureBottomSheetBehaviour() { + val detailSheet = viewBinding.transactionDetailSheet + + bottomSheetBehavior = BottomSheetBehavior.from(detailSheet) + bottomSheetBehavior.skipCollapsed = true + bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN + + viewBinding.transactionDetailSheetButton + .setOnClickListener { + bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN + } + + bottomSheetBehavior.addBottomSheetCallback( + object : BottomSheetBehavior.BottomSheetCallback() { + override fun onStateChanged( + view: View, + newState: Int, + ) { + when (newState) { + BottomSheetBehavior.STATE_HIDDEN -> { + hideAction() + onTransactionRemovedListener?.invoke() + } + + BottomSheetBehavior.STATE_EXPANDED -> { + showAction() + } + // You can handle other states if needed + } + } + + override fun onSlide( + view: View, + offset: Float, + ) { + blurView.setBlurEnabled(false) + blurView.setBackgroundColor(blurColor) + } + }, + ) + + blurView.setOnClickListener { + bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN + } + blurView.isClickable = false + } + + fun setTransaction(transaction: Transaction) { + viewBinding.transactionDetailDate.text = transactionParser.getDateString(transaction.date) + viewBinding.transactionDetailAmount.text = + transactionParser.getAmountString(transaction.amount, transaction.category) + viewBinding.transactionDetailTitle.text = transaction.title + + if (transaction.location == null) { + viewBinding.transactionDetailLocationText.text = UNKNOWN_LOCATION_VALUE + } else { + val htmlContent = "<u>${transaction.location}</u>" + viewBinding.transactionDetailLocationText.text = + Html.fromHtml(htmlContent, Html.FROM_HTML_MODE_LEGACY) + } + + val categoryBackgroundColor = + ContextCompat.getColor( + context, + if (transaction.category == TransactionCategory.EARNINGS) { + R.color.teal_200 + } else { + R.color.rose_200 + }, + ) + + viewBinding.transactionDetailIconImage.setImageResource( + if (transaction.category == TransactionCategory.EARNINGS) { + R.drawable.ic_coins + } else { + R.drawable.ic_shopping_bag_minus + }, + ) + viewBinding.transactionDetailIconImage.setBackgroundColor(categoryBackgroundColor) + + viewBinding.transactionDetailCategoryText.text = transaction.category.string.uppercase() + viewBinding.transactionDetailCategoryText.setBackgroundColor(categoryBackgroundColor) + } + + fun setDeleteListener(listener: () -> Unit) { + viewBinding.trasactionDetailDeleteButton.setOnClickListener { listener() } + } + + fun setUpdateListener(listener: () -> Unit) { + viewBinding.trasactionDetailEditButton.setOnClickListener { listener() } + } + + fun setBottomSheetState(state: Int) { + bottomSheetBehavior.state = state + } +} diff --git a/app/src/main/java/com/example/bondoman/views/decorators/DividerItemDecorator.kt b/app/src/main/java/com/example/bondoman/views/decorators/DividerItemDecorator.kt new file mode 100644 index 0000000000000000000000000000000000000000..a925349c5c55136fc83c0c817830b5804d928fe7 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/views/decorators/DividerItemDecorator.kt @@ -0,0 +1,26 @@ +package com.example.bondoman.views.decorators + +import android.graphics.Canvas +import android.graphics.drawable.Drawable +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.ItemDecoration + +class DividerItemDecorator(private val mDivider: Drawable) : ItemDecoration() { + override fun onDrawOver( + canvas: Canvas, + parent: RecyclerView, + state: RecyclerView.State, + ) { + val dividerLeft = parent.getPaddingLeft() + val dividerRight = parent.width - parent.getPaddingRight() + val childCount = parent.childCount + for (i in 0..childCount - 2) { + val child = parent.getChildAt(i) + val params = child.layoutParams as RecyclerView.LayoutParams + val dividerTop = child.bottom + params.bottomMargin + val dividerBottom = dividerTop + mDivider.intrinsicHeight + mDivider.setBounds(dividerLeft, dividerTop, dividerRight, dividerBottom) + mDivider.draw(canvas) + } + } +} diff --git a/app/src/main/java/com/example/bondoman/views/fragments/ConnectionLostFragment.kt b/app/src/main/java/com/example/bondoman/views/fragments/ConnectionLostFragment.kt new file mode 100644 index 0000000000000000000000000000000000000000..9465ca97f56314b90290b442f50f21bf72189348 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/views/fragments/ConnectionLostFragment.kt @@ -0,0 +1,29 @@ +package com.example.bondoman.views.fragments + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import com.example.bondoman.databinding.FragmentConnectionLostBinding + +class ConnectionLostFragment : Fragment() { + companion object { + @JvmStatic + fun newInstance() = + ConnectionLostFragment().apply { + } + } + + private lateinit var binding: FragmentConnectionLostBinding + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + // Inflate the layout for this fragment + binding = FragmentConnectionLostBinding.inflate(inflater) + return binding.root + } +} diff --git a/app/src/main/java/com/example/bondoman/views/fragments/DatePickerFragment.kt b/app/src/main/java/com/example/bondoman/views/fragments/DatePickerFragment.kt new file mode 100644 index 0000000000000000000000000000000000000000..d11d798476e5d29e39823429c805cf6f7e668173 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/views/fragments/DatePickerFragment.kt @@ -0,0 +1,57 @@ +package com.example.bondoman.views.fragments + +import android.app.DatePickerDialog +import android.app.Dialog +import android.os.Bundle +import android.widget.DatePicker +import androidx.core.content.ContextCompat +import androidx.fragment.app.DialogFragment +import com.example.bondoman.R +import java.util.Calendar + +class DatePickerFragment : DialogFragment(), DatePickerDialog.OnDateSetListener { + private var onDatePickedListener: ((year: Int, month: Int, day: Int) -> Unit)? = null + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + // Use the current date as the default date in the picker. + val c = Calendar.getInstance() + val year = c.get(Calendar.YEAR) + val month = c.get(Calendar.MONTH) + val day = c.get(Calendar.DAY_OF_MONTH) + + // Create a new instance of DatePickerDialog and return it. + val dialog = DatePickerDialog(requireContext(), this, year, month, day) + dialog.show() + + // Change the color of the positive button + val positiveButton = dialog.getButton(DatePickerDialog.BUTTON_POSITIVE) + positiveButton.setTextColor(ContextCompat.getColor(requireContext(), R.color.teal_200)) + + // Change the color of the negative button + val negativeButton = dialog.getButton(DatePickerDialog.BUTTON_NEGATIVE) + negativeButton.setTextColor(ContextCompat.getColor(requireContext(), R.color.zinc_300)) + + // change background + dialog.datePicker.setBackgroundColor( + ContextCompat.getColor( + requireContext(), + R.color.bg_main, + ), + ) + + return dialog + } + + fun setOnDatePicked(listener: (year: Int, month: Int, day: Int) -> Unit) { + onDatePickedListener = listener + } + + override fun onDateSet( + view: DatePicker, + year: Int, + month: Int, + day: Int, + ) { + onDatePickedListener?.invoke(year, month, day) + } +} diff --git a/app/src/main/java/com/example/bondoman/views/fragments/GraphFragment.kt b/app/src/main/java/com/example/bondoman/views/fragments/GraphFragment.kt new file mode 100644 index 0000000000000000000000000000000000000000..e039061067dae4d62bf76c41b01d067656f85c3a --- /dev/null +++ b/app/src/main/java/com/example/bondoman/views/fragments/GraphFragment.kt @@ -0,0 +1,279 @@ +package com.example.bondoman.views.fragments + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import androidx.core.view.isVisible +import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelProvider +import com.example.bondoman.R +import com.example.bondoman.data.models.TransactionCategory +import com.example.bondoman.data.utils.TransactionParser +import com.example.bondoman.data.viewmodels.transaction.TransactionViewModel +import com.example.bondoman.databinding.FragmentGraphBinding +import com.github.mikephil.charting.animation.Easing +import com.github.mikephil.charting.charts.PieChart +import com.github.mikephil.charting.data.PieData +import com.github.mikephil.charting.data.PieDataSet +import com.github.mikephil.charting.data.PieEntry +import com.github.mikephil.charting.formatter.PercentFormatter +import java.util.Arrays +import java.util.Calendar +import java.util.Date + +class GraphFragment : Fragment() { + companion object { + @JvmStatic + fun newInstance() = + GraphFragment().apply { + } + } + + private lateinit var viewBinding: FragmentGraphBinding + private lateinit var pieChart: PieChart + private lateinit var transactionViewModel: TransactionViewModel + + private lateinit var beginDate: Date + private lateinit var endDate: Date + + private val transactionParser = TransactionParser() + + private fun initPieChart() { + // using percentage as values instead of amount + pieChart.setUsePercentValues(true) + + // remove the description label on the lower left corner, default true if not set + pieChart.description.isEnabled = false + pieChart.legend.isEnabled = false + + // enabling the user to rotate the chart, default true + pieChart.isRotationEnabled = false + + // adding friction when rotating the pie chart + pieChart.setDragDecelerationFrictionCoef(0.9f) + // setting the first entry start from right hand side, default starting from top + pieChart.setRotationAngle(0f) + + // highlight the entry when it is tapped, default true if not set + pieChart.isHighlightPerTapEnabled = true + + // label appearance + pieChart.setEntryLabelColor(ContextCompat.getColor(requireContext(), R.color.zinc_900)) + + // adding animation so the entries pop up from 0 degree + pieChart.animateY(1400, Easing.EaseInOutQuad) + + // adding extra offset for avoiding cut-off + pieChart.extraTopOffset = 10f + pieChart.extraBottomOffset = 10f + + pieChart.holeRadius = 30f + pieChart.transparentCircleRadius = 40f + } + + private fun showPieChart(typeAmountMap: MutableMap<String, Double>) { + val pieEntries = ArrayList<PieEntry>() + + // initializing colors for the entries + val colorArray = + Arrays.copyOfRange( + resources.getIntArray(R.array.colors_pie_chart_area), + 0, + typeAmountMap.size, + ) + val colors = ArrayList<Int>() + for (color in colorArray) { + colors.add(color) + } + + // colors for legend + val legendColorArray = + Arrays.copyOfRange( + resources.getIntArray(R.array.colors_pie_chart_area), + 0, + typeAmountMap.size, + ) + + val legendColors = ArrayList<Int>() + for (color in legendColorArray) { + legendColors.add(color) + } + + // input data and fit data into pie chart entry + for (type in typeAmountMap.keys) { + pieEntries.add(PieEntry(typeAmountMap[type]!!.toFloat(), type)) + } + + // collecting the entries + val pieDataSet = PieDataSet(pieEntries, null) + + // providing color list for coloring different entries + pieDataSet.colors = colors + + // set line for values + pieDataSet.yValuePosition = PieDataSet.ValuePosition.OUTSIDE_SLICE + pieDataSet.valueLinePart1Length = 0.7f + pieDataSet.valueLinePart2Length = 0.3f + pieDataSet.valueLinePart1OffsetPercentage = 95f + pieDataSet.valueLineColor = ContextCompat.getColor(requireContext(), R.color.zinc_300) + + // Value text appearance + pieDataSet.yValuePosition = PieDataSet.ValuePosition.OUTSIDE_SLICE + pieDataSet.valueTextColor = ContextCompat.getColor(requireContext(), R.color.zinc_300) + pieDataSet.valueTextSize = 12f + + // grouping the data set from entry to chart + val pieData = PieData(pieDataSet) + pieData.setValueFormatter(PercentFormatter(pieChart)) + + // showing the value of the entries, default true if not set + pieData.setDrawValues(true) + pieChart.setData(pieData) + pieChart.notifyDataSetChanged() + pieChart.invalidate() + } + + private fun setBeginDate( + year: Int, + month: Int, + day: Int, + ) { + val calendar = Calendar.getInstance() + calendar.set(year, month, day, 0, 0, 0) + + beginDate = calendar.time + } + + private fun setEndDate( + year: Int, + month: Int, + day: Int, + ) { + val calendar = Calendar.getInstance() + calendar.set(year, month, day, 0, 0, 0) + calendar.add(Calendar.DAY_OF_YEAR, 1) + + endDate = calendar.time + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + transactionViewModel = + ViewModelProvider(requireActivity())[TransactionViewModel::class.java] + } + + private fun configureObserver() { + transactionViewModel.categoryTotals.observe( + viewLifecycleOwner, + ) { + if (it != null) { + val typeAmountMap: MutableMap<String, Double> = HashMap() + + for (categoryTotal in it) { + typeAmountMap[categoryTotal.category.string] = categoryTotal.totalAmount + } + + for (category in TransactionCategory.entries) { + // Check if the map contains the key for the current category + if (!typeAmountMap.containsKey(category.string)) { + // If the key is missing, assign 0 as the value + typeAmountMap[category.string] = 0.0 + } + } + + viewBinding.incomeValueText.text = + transactionParser.getAmountString( + typeAmountMap[TransactionCategory.EARNINGS.string]!!, + TransactionCategory.EARNINGS, + ) + viewBinding.outcomeValueText.text = + transactionParser.getAmountString( + typeAmountMap[TransactionCategory.EXPENSE.string]!!, + TransactionCategory.EXPENSE, + ) + + if (it.isNotEmpty()) { + showPieChart(typeAmountMap) + viewBinding.graphPieChart.isVisible = true + viewBinding.graphPageInfoNotAvailable.isVisible = false + } else { + viewBinding.graphPageInfoNotAvailable.isVisible = true + viewBinding.graphPieChart.isVisible = false + } + } + } + } + + private fun configureInitialDate() { + val calendar = Calendar.getInstance() + calendar.time = Date() + + // Get the first day of the current month + calendar.set(Calendar.DAY_OF_MONTH, 1) + beginDate = calendar.time + + var year = calendar.get(Calendar.YEAR) + var month = calendar.get(Calendar.MONTH) + var day = calendar.get(Calendar.DAY_OF_MONTH) + setBeginDate(year, month, day) + viewBinding.graphDateBeginInput.setText(year, month, day) + + calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH)) + endDate = calendar.time + + year = calendar.get(Calendar.YEAR) + month = calendar.get(Calendar.MONTH) + day = calendar.get(Calendar.DAY_OF_MONTH) + setEndDate(year, month, day) + viewBinding.graphDateEndInput.setText(year, month, day) + } + + private fun configureDateInput() { + val dateBeginInput = viewBinding.graphDateBeginInput + val dateEndInput = viewBinding.graphDateEndInput + + dateBeginInput.setOnDatePicked { year, month, day -> + setBeginDate(year, month, day) + + transactionViewModel.getTransactionTotals(beginDate, endDate) + } + + dateEndInput.setOnDatePicked { year, month, day -> + setEndDate(year, month, day) + + transactionViewModel.getTransactionTotals(beginDate, endDate) + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + // Inflate the layout for this fragment + viewBinding = FragmentGraphBinding.inflate(inflater, container, false) + + // configure pie chart + pieChart = viewBinding.graphPieChart + initPieChart() + + // configure initial value + viewBinding.incomeValueText.text = "-" + viewBinding.outcomeValueText.text = "-" + + configureObserver() + configureInitialDate() + configureDateInput() + + return viewBinding.root + } + + override fun onStart() { + super.onStart() + + transactionViewModel.getTransactionTotals(beginDate, endDate) + } +} diff --git a/app/src/main/java/com/example/bondoman/views/fragments/ScanReceiptFragment.kt b/app/src/main/java/com/example/bondoman/views/fragments/ScanReceiptFragment.kt new file mode 100644 index 0000000000000000000000000000000000000000..00cb1d344dbf79d87b1ba1808541ac820e0486d3 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/views/fragments/ScanReceiptFragment.kt @@ -0,0 +1,218 @@ +package com.example.bondoman.views.fragments + +import android.content.pm.PackageManager +import android.graphics.Bitmap +import android.graphics.Bitmap.CompressFormat +import android.graphics.BitmapFactory +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.Toast +import androidx.activity.result.PickVisualMediaRequest +import androidx.activity.result.contract.ActivityResultContracts +import androidx.camera.core.CameraSelector +import androidx.camera.core.ImageCapture +import androidx.camera.core.ImageCaptureException +import androidx.camera.core.ImageProxy +import androidx.camera.core.Preview +import androidx.camera.lifecycle.ProcessCameraProvider +import androidx.core.content.ContextCompat +import androidx.fragment.app.Fragment +import androidx.navigation.fragment.findNavController +import com.example.bondoman.data.models.ParcelableBitmap +import com.example.bondoman.databinding.FragmentScanReceiptBinding +import java.io.ByteArrayOutputStream +import java.io.FileNotFoundException + +class ScanReceiptFragment : Fragment() { + private lateinit var viewBinding: FragmentScanReceiptBinding + private var imageCapture: ImageCapture? = null + private val activityResultLauncher = + registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> + var permissionGranted = true + permissions.entries.forEach { + if (it.key in REQUIRED_PERMISSIONS && !it.value) { + permissionGranted = false + } + } + + if (!permissionGranted) { + Toast.makeText( + requireActivity().baseContext, + "Permission request denied", + Toast.LENGTH_SHORT, + ).show() + } else { + startCamera() + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + viewBinding = FragmentScanReceiptBinding.inflate(inflater, container, false) + return viewBinding.root + } + + override fun onViewCreated( + view: View, + savedInstanceState: Bundle?, + ) { + super.onViewCreated(view, savedInstanceState) + + // Pick image from gallery listener + val pickMedia = + registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri -> + if (uri != null) { + viewBinding.scanReceiptLoading.visibility = View.VISIBLE + + val scanReceiptImageBitmap = uriToBitmap(uri) + val action = + ScanReceiptFragmentDirections.actionScanReceiptFragmentToScanReceiptResultFragment( + ParcelableBitmap(scanReceiptImageBitmap), + ) + findNavController().navigate(action) + } else { + Log.d(TAG, "No image selected") + } + } + + viewBinding.scanReceiptButtonPickImageFromGallery.setOnClickListener { + pickMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)) + } + + // Request camera permissions + if (allPermissionsGranted()) { + startCamera() + } else { + requestPermissions() + } + + // Set up the listeners for capture button + viewBinding.receiptCaptureButton.setOnClickListener { + takePhoto() + } + } + + private fun startCamera() { + val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext()) + + cameraProviderFuture.addListener({ + val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get() + + val preview = + Preview.Builder().build().also { + it.setSurfaceProvider(viewBinding.cameraPreview.surfaceProvider) + } + + imageCapture = ImageCapture.Builder().build() + val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA + + try { + cameraProvider.unbindAll() + + cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture) + } catch (exc: Exception) { + Log.e(TAG, "Use case binding failed", exc) + } + }, ContextCompat.getMainExecutor(requireContext())) + } + + private fun takePhoto() { + viewBinding.scanReceiptLoading.visibility = View.VISIBLE + + val imageCapture = imageCapture ?: return + + imageCapture.takePicture( + ContextCompat.getMainExecutor(this.requireContext()), + object : ImageCapture.OnImageCapturedCallback() { + override fun onError(exception: ImageCaptureException) { + super.onError(exception) + Log.e(TAG, "${exception.message}") + } + + override fun onCaptureSuccess(image: ImageProxy) { + super.onCaptureSuccess(image) + + val scanReceiptImageBitmap = imageProxyToBitmap(image) + image.close() + + val action = + ScanReceiptFragmentDirections.actionScanReceiptFragmentToScanReceiptResultFragment( + ParcelableBitmap(scanReceiptImageBitmap), + ) + findNavController().navigate(action) + } + }, + ) + } + + private fun uriToBitmap(uri: Uri): Bitmap? { + return try { + val inputStream = requireContext().contentResolver.openInputStream(uri) + val bitmap = BitmapFactory.decodeStream(inputStream) + inputStream?.close() + compressBitmap(bitmap) + } catch (e: FileNotFoundException) { + Log.e(TAG, "Invalid image uri") + null + } + } + + private fun imageProxyToBitmap(image: ImageProxy): Bitmap? { + val buffer = image.planes[0].buffer + val bytes = ByteArray(buffer.remaining()) + buffer.get(bytes) + + return compressBitmap(BitmapFactory.decodeByteArray(bytes, 0, bytes.size)) + } + + private fun compressBitmap(bitmap: Bitmap): Bitmap? { + val outputStream = ByteArrayOutputStream() + bitmap.compress(CompressFormat.JPEG, 100, outputStream) + var quality = 100 // Starting quality + + while (outputStream.toByteArray().size / 1024 > 1024 * 1 && quality > 0) { + outputStream.reset() // Clear the output stream + quality -= 10 // Reduce quality by 10 + bitmap.compress(CompressFormat.JPEG, quality, outputStream) + } + + return BitmapFactory.decodeByteArray(outputStream.toByteArray(), 0, outputStream.size()) + } + + private fun requestPermissions() { + activityResultLauncher.launch(REQUIRED_PERMISSIONS) + } + + private fun allPermissionsGranted() = + REQUIRED_PERMISSIONS.all { + ContextCompat.checkSelfPermission( + requireActivity().baseContext, + it, + ) == PackageManager.PERMISSION_GRANTED + } + + override fun onDestroy() { + super.onDestroy() + } + + companion object { + private val TAG = "ScanReceipt" + private val REQUIRED_PERMISSIONS = + mutableListOf(android.Manifest.permission.CAMERA).toTypedArray() + + @JvmStatic + fun newInstance() = + ScanReceiptFragment().apply { + arguments = + Bundle().apply { + } + } + } +} diff --git a/app/src/main/java/com/example/bondoman/views/fragments/ScanReceiptResultFragment.kt b/app/src/main/java/com/example/bondoman/views/fragments/ScanReceiptResultFragment.kt new file mode 100644 index 0000000000000000000000000000000000000000..538ac1dfcc5849be10f9dac96f5ce67f147aa0e1 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/views/fragments/ScanReceiptResultFragment.kt @@ -0,0 +1,512 @@ +package com.example.bondoman.views.fragments + +import android.content.Context +import android.content.pm.PackageManager +import android.graphics.Bitmap +import android.location.Geocoder +import android.location.Location +import android.location.LocationListener +import android.location.LocationManager +import android.os.Build +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ProgressBar +import android.widget.Toast +import androidx.activity.result.contract.ActivityResultContracts +import androidx.annotation.RequiresApi +import androidx.core.content.ContextCompat +import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProvider +import androidx.navigation.Navigation +import androidx.navigation.fragment.findNavController +import androidx.navigation.fragment.navArgs +import com.example.bondoman.R +import com.example.bondoman.data.models.TransactionCategory +import com.example.bondoman.data.utils.PreferencesManager +import com.example.bondoman.data.viewmodels.scanReceipt.ScanReceiptViewModel +import com.example.bondoman.data.viewmodels.transaction.TransactionViewModel +import com.example.bondoman.databinding.ComponentScanReceiptItemCardBinding +import com.example.bondoman.databinding.FragmentScanReceiptResultBinding +import com.example.bondoman.networks.responses.ScanReceiptResponseItemsWrapper +import java.io.File +import java.io.FileOutputStream +import java.io.IOException +import java.util.Locale +import java.util.function.Consumer + +class ScanReceiptResultFragment : Fragment() { + private lateinit var viewBinding: FragmentScanReceiptResultBinding + private lateinit var locationManager: LocationManager + private lateinit var transactionViewModel: TransactionViewModel + + private val scanReceiptViewModel: ScanReceiptViewModel by viewModels() + private val args by navArgs<ScanReceiptResultFragmentArgs>() + + private var location: String? = null + private var currentGpsLocation: Location? = null + private var currentNetworkLocation: Location? = null + private var locationTextString = MutableLiveData<String?>(null) + + private val activityResultLauncher = + registerForActivityResult( + ActivityResultContracts.RequestMultiplePermissions(), + ) { permissions -> + when { + permissions.getOrDefault( + android.Manifest.permission.ACCESS_FINE_LOCATION, + false, + ) -> { + // Precise location access granted. + requestLocation() + } + + permissions.getOrDefault( + android.Manifest.permission.ACCESS_COARSE_LOCATION, + false, + ) -> { + // Only approximate location access granted. + requestLocation() + } + + else -> { + Toast.makeText( + this.requireContext(), + "Location permission denied", + Toast.LENGTH_SHORT, + ).show() + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + transactionViewModel = + ViewModelProvider(this.requireActivity())[TransactionViewModel::class.java] + locationManager = + this.requireActivity().getSystemService(Context.LOCATION_SERVICE) as LocationManager + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + viewBinding = FragmentScanReceiptResultBinding.inflate(inflater, container, false) + + viewBinding.scanReceiptImageResult.setImageBitmap(args.ScanReceiptImageBitmap.bitmap) + + val viewWrapper = viewBinding.scanReceiptViewWrapper + + for (i in 0 until viewWrapper.childCount) { + val childView = viewWrapper.getChildAt(i) + if (childView !is ProgressBar) { + childView.visibility = View.GONE + } + } + + viewBinding.scanReceiptLoading.visibility = View.VISIBLE + + locationTextString.observe(requireActivity()) { + viewBinding.scanReceiptFormLocationField.setText(it) + viewBinding.scanReceiptFormButtonSave.isClickable = true + viewBinding.scanReceiptFormButtonSave.setBackgroundColor( + ContextCompat.getColor( + requireContext(), + R.color.teal_200, + ), + ) + } + + return viewBinding.root + } + + override fun onViewCreated( + view: View, + savedInstanceState: Bundle?, + ) { + super.onViewCreated(view, savedInstanceState) + + // POST the image to the server + val scanReceiptImageBitmap = args.ScanReceiptImageBitmap.bitmap + if (scanReceiptImageBitmap != null) { + val file = File.createTempFile("image", ".jpg") + val scanReceiptImageFile = bitmapToFile(scanReceiptImageBitmap, file) + val token: String = PreferencesManager.getString(requireContext(), "token", true) ?: "" + + scanReceiptViewModel.scanReceipt(scanReceiptImageFile, token) + + val observer = + Observer<ScanReceiptResponseItemsWrapper> { response -> + if (response.error != null) { + Toast.makeText(this.requireContext(), response.error, Toast.LENGTH_SHORT) + .show() + Navigation.findNavController(view) + .navigate(R.id.action_scanReceiptResultFragment_to_scanReceiptFragment) + } else { + // Hide loading indicator + viewBinding.scanReceiptLoading.visibility = View.GONE + + val viewWrapper = viewBinding.scanReceiptViewWrapper + + // Storing total amount of the receipt + var totalAmount = 0L + + if (response.items != null) { + val itemsWrapper = viewBinding.scanReceiptItems + for (item in response.items.items) { + val amount = (item.price * item.quantity * 100).toLong() + totalAmount += amount + + val scanReceiptItemViewBinding = + ComponentScanReceiptItemCardBinding.inflate( + LayoutInflater.from(viewBinding.root.context), + viewBinding.root, + false, + ) + val scanReceiptItemView = scanReceiptItemViewBinding.root + + scanReceiptItemViewBinding.scanReceiptItemName.text = item.name + + val itemPriceString = + String.format("%d", amount) + val itemPriceFormatted = formatAmountString(itemPriceString) + + scanReceiptItemViewBinding.scanReceiptItemPrice.text = + String.format("Rp$itemPriceFormatted") + + itemsWrapper.addView(scanReceiptItemView) + } + } + + // Show all child except ProgressBar + for (i in 0 until viewWrapper.childCount) { + val childView = viewWrapper.getChildAt(i) + if (childView !is ProgressBar) { + childView.visibility = View.VISIBLE + } + } + + // Set amount input field value + viewBinding.scanReceiptFormAmount.setText(totalAmount.toString()) + } + } + + scanReceiptViewModel.scanReceiptResponse.observe(viewLifecycleOwner, observer) + } + + // Get current location + changeLocation() + + // Retry scan receipt process + viewBinding.scanReceiptFormButtonRetake.setOnClickListener { + Navigation.findNavController(view) + .navigate(R.id.action_scanReceiptResultFragment_to_scanReceiptFragment) + } + + // Save button listener + viewBinding.scanReceiptFormButtonSave.setOnClickListener { + try { + val title = viewBinding.scanReceiptFormTitle.getText().trim() + val amount = viewBinding.scanReceiptFormAmount.getText().trim().toLong() + val location = viewBinding.scanReceiptFormLocationField.text.toString().trim() + val isDataValid = validateInput(title, amount, location) + + if (isDataValid == null) { + // Create new transaction + transactionViewModel.insertTransaction( + title, + TransactionCategory.EXPENSE, + amount, + location, + ) { _, message -> + Toast.makeText(this.requireContext(), message, Toast.LENGTH_SHORT).show() + findNavController().popBackStack(R.id.scanReceiptFragment, false) + } + } else { + Toast.makeText(this.requireContext(), isDataValid, Toast.LENGTH_SHORT).show() + } + } catch (e: NumberFormatException) { + Toast.makeText( + this.requireContext(), + "Amount must be a positive number", + Toast.LENGTH_SHORT, + ).show() + } + } + } + + private fun validateInput( + title: String, + amount: Long, + location: String, + ): String? { + if (title == "" || title.length > 50) { + return "Title is required" + } + + if (amount < 0) { + return "Amount must be a positive number" + } + + if (location == "") { + return "Location is required" + } + + return null + } + + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + private fun getLocationString( + location: Location, + callback: (String?) -> Unit, + ) { + val geocoder = Geocoder(requireContext(), Locale.getDefault()) + geocoder.getFromLocation( + location.latitude, + location.longitude, + 1, + ) { addresses -> + if (addresses.isNotEmpty()) { + val address = addresses[0] + val locationString = address.getAddressLine(0) + callback(locationString) + } else { + callback(null) + } + } + } + + private fun getLocationString(location: Location): String? { + val geocoder = Geocoder(requireContext(), Locale.getDefault()) + val addresses = + geocoder.getFromLocation( + location.latitude, + location.longitude, + 1, + ) + + return if (!addresses.isNullOrEmpty()) { + val address = addresses[0] + address.getAddressLine(0) + } else { + null + } + } + + private fun requestLocation() { + assert( + ContextCompat.checkSelfPermission( + requireContext(), + android.Manifest.permission.ACCESS_FINE_LOCATION, + ) == PackageManager.PERMISSION_GRANTED || + ContextCompat.checkSelfPermission( + requireContext(), + android.Manifest.permission.ACCESS_COARSE_LOCATION, + ) == PackageManager.PERMISSION_GRANTED, + ) + + viewBinding.scanReceiptFormButtonSave.isClickable = false + viewBinding.scanReceiptFormButtonSave.setBackgroundColor( + ContextCompat.getColor( + requireContext(), + R.color.zinc_300, + ), + ) + + viewBinding.scanReceiptFormLocationField.text = null + viewBinding.scanReceiptFormLocationField.setHint("Getting location…") + currentGpsLocation = null + currentNetworkLocation = null + + val hasGps = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) + val hasNetwork = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) + + if (hasGps) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + locationManager.getCurrentLocation( + LocationManager.GPS_PROVIDER, + null, + requireActivity().mainExecutor, + gpsLocationCallback, + ) + } else { + locationManager.requestSingleUpdate( + LocationManager.GPS_PROVIDER, + gpsLocationListener, + null, + ) + } + } + + if (hasNetwork) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + locationManager.getCurrentLocation( + LocationManager.NETWORK_PROVIDER, + null, + requireActivity().mainExecutor, + networkLocationCallback, + ) + } else { + locationManager.requestSingleUpdate( + LocationManager.NETWORK_PROVIDER, + networkLocationListener, + null, + ) + } + } + + if (!hasGps and !hasNetwork) { + Toast.makeText( + this.requireContext(), + "No provider to get location. Enable GPS", + Toast.LENGTH_SHORT, + ).show() + } + } + + private val gpsLocationCallback = + Consumer<Location> { location -> + if (isAdded && location != null) { + currentGpsLocation = location + changeLocationData() + } + } + + private val networkLocationCallback = + Consumer<Location> { location -> + if (isAdded && location != null) { + currentNetworkLocation = location + changeLocationData() + } + } + + private fun changeLocationData() { + assert(currentGpsLocation != null || currentNetworkLocation != null) + + var currentLocation: Location? = null + + if (currentGpsLocation != null && currentNetworkLocation != null) { + currentLocation = + if (currentGpsLocation!!.accuracy >= currentNetworkLocation!!.accuracy) { + currentGpsLocation + } else { + currentNetworkLocation + } + } else if (currentGpsLocation != null) { + currentLocation = currentGpsLocation + } else if (currentNetworkLocation != null) { + currentLocation = currentNetworkLocation + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + getLocationString(currentLocation!!) { + location = it + locationTextString.postValue(it) + } + } else { + val locationString = getLocationString(currentLocation!!) + location = locationString + locationTextString.value = locationString + } + } + + protected fun changeLocation() { + if ( + ContextCompat.checkSelfPermission( + requireContext(), + android.Manifest.permission.ACCESS_FINE_LOCATION, + ) != PackageManager.PERMISSION_GRANTED + ) { + activityResultLauncher.launch(REQUIRED_PERMISSIONS) + } else { + requestLocation() + } + } + + private val gpsLocationListener: LocationListener = + object : LocationListener { + override fun onLocationChanged(location: Location) { + currentGpsLocation = location + } + + override fun onStatusChanged( + provider: String, + status: Int, + extras: Bundle, + ) { + } + + override fun onProviderEnabled(provider: String) {} + + override fun onProviderDisabled(provider: String) {} + } + + private val networkLocationListener: LocationListener = + object : LocationListener { + override fun onLocationChanged(location: Location) { + currentNetworkLocation = location + } + + override fun onStatusChanged( + provider: String, + status: Int, + extras: Bundle, + ) { + } + + override fun onProviderEnabled(provider: String) {} + + override fun onProviderDisabled(provider: String) {} + } + + private fun formatAmountString(integerString: String): String { + val reversedString = integerString.reversed() + val formattedBuilder = StringBuilder() + + for ((index, char) in reversedString.withIndex()) { + formattedBuilder.append(char) + if ((index + 1) % 3 == 0 && index != reversedString.length - 1) { + formattedBuilder.append('.') + } + } + + return formattedBuilder.reverse().toString() + } + + private fun bitmapToFile( + bitmap: Bitmap, + file: File, + ): File { + return try { + val fileOutputStream = FileOutputStream(file) + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fileOutputStream) + fileOutputStream.flush() + fileOutputStream.close() + file + } catch (e: IOException) { + Log.e(TAG, "${e.message}") + file + } + } + + companion object { + private const val TAG = "ScanReceiptResult" + private val REQUIRED_PERMISSIONS = + mutableListOf( + android.Manifest.permission.ACCESS_FINE_LOCATION, + android.Manifest.permission.ACCESS_COARSE_LOCATION, + ).toTypedArray() + + @JvmStatic + fun newInstance() = + ScanReceiptResultFragment().apply { + } + } +} diff --git a/app/src/main/java/com/example/bondoman/views/fragments/SettingsFragment.kt b/app/src/main/java/com/example/bondoman/views/fragments/SettingsFragment.kt new file mode 100644 index 0000000000000000000000000000000000000000..316ac4310f18393e77f717db4c2ad51be733a158 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/views/fragments/SettingsFragment.kt @@ -0,0 +1,166 @@ +package com.example.bondoman.views.fragments + +import android.content.Intent +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.core.content.FileProvider +import androidx.fragment.app.Fragment +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProvider +import androidx.localbroadcastmanager.content.LocalBroadcastManager +import com.example.bondoman.R +import com.example.bondoman.data.Constants +import com.example.bondoman.data.utils.PreferencesManager +import com.example.bondoman.data.viewmodels.settings.ExportTransactionResponseContainer +import com.example.bondoman.data.viewmodels.settings.ExportTransactionStatus +import com.example.bondoman.data.viewmodels.settings.SendTransactionResponse +import com.example.bondoman.data.viewmodels.settings.SettingViewModel +import com.example.bondoman.services.services.ExpiryService +import com.example.bondoman.views.activities.LoginActivity +import com.example.bondoman.views.components.SettingButtonComponent +import com.example.bondoman.views.utils.interfaces.ParentActivityService +import java.io.File +import java.io.FileOutputStream + +class SettingsFragment : Fragment() { + private lateinit var settingViewModel: SettingViewModel + + private lateinit var exportReportButton: SettingButtonComponent + private lateinit var sendReportButton: SettingButtonComponent + private lateinit var randomizeTransactionButton: SettingButtonComponent + private lateinit var logoutButton: SettingButtonComponent + private lateinit var parentActivityService: ParentActivityService + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View? { + // Inflate the layout for this fragment + val view = inflater.inflate(R.layout.fragment_settings, container, false) + settingViewModel = ViewModelProvider(this)[SettingViewModel::class.java] + + exportReportButton = view.findViewById(R.id.export_report_button) + sendReportButton = view.findViewById(R.id.send_report_button) + randomizeTransactionButton = view.findViewById(R.id.randomize_transaction_button) + logoutButton = view.findViewById(R.id.logout_button) + parentActivityService = requireActivity() as ParentActivityService + + exportReportButton.setOnClickListener { onExportReportButtonClick() } + sendReportButton.setOnClickListener { onSendReportButtonClick() } + randomizeTransactionButton.setOnClickListener { onRandomizeTransactionButtonClick() } + logoutButton.setOnClickListener { onLogoutButtonClick() } + + return view + } + + private fun onExportReportButtonClick() { + settingViewModel.exportTransactionReport(parentActivityService.getNIM()) + + val observer = + Observer<ExportTransactionResponseContainer> { + when (it.status) { + ExportTransactionStatus.SUCCESS -> { + Toast.makeText( + requireActivity(), + "Export Report Success", + Toast.LENGTH_SHORT, + ).show() + } + + ExportTransactionStatus.FAILED -> { + Toast.makeText( + requireActivity(), + "Export Report Failed", + Toast.LENGTH_SHORT, + ).show() + } + } + } + + settingViewModel.exportReportResponse.observe(requireActivity(), observer) + } + + private fun onSendReportButtonClick() { + Toast.makeText(requireActivity(), "Processing report...", Toast.LENGTH_SHORT).show() + + settingViewModel.exportTransactionReport(parentActivityService.getNIM(), false) + + val observer = + Observer<SendTransactionResponse> { response -> + val tempFile = + File.createTempFile( + "TransactionReport", + ".xlsx", + this.requireContext().cacheDir, + ) + FileOutputStream(tempFile).use { + response.workbook.write(it) + } + val reportUri = + FileProvider.getUriForFile( + this.requireContext(), + this.requireContext().applicationContext.packageName + ".provider", + tempFile, + ) + + val emailIntent = Intent(Intent.ACTION_SEND) + emailIntent.type = + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + emailIntent.putExtra( + Intent.EXTRA_EMAIL, + arrayOf(parentActivityService.getEmail()), + ) + emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Your Transaction Report") + emailIntent.putExtra( + Intent.EXTRA_TEXT, + "Your transaction reports is attached with this email. Don't be surprised 😎", + ) + emailIntent.putExtra(Intent.EXTRA_STREAM, reportUri) + emailIntent.setPackage("com.google.android.gm") + + try { + this.requireContext() + .startActivity(emailIntent) + } catch (ex: android.content.ActivityNotFoundException) { + Log.e(TAG, "${ex.message}") + } + + Log.d(TAG, "Workbook: ${response.workbook}") + } + + settingViewModel.sendTransactionReport.observe(this.requireActivity(), observer) + } + + private fun onRandomizeTransactionButtonClick() { + val intent = Intent(Constants.RANDOMIZE_TRANSACTION_INTENT) + LocalBroadcastManager.getInstance(requireActivity()).sendBroadcast(intent) +// Toast.makeText(requireActivity(), "Add Transaction Page has been randomized", Toast.LENGTH_SHORT).show() + } + + private fun onLogoutButtonClick() { + PreferencesManager.remove(requireActivity(), "token", true) + PreferencesManager.remove(requireActivity(), "nim", true) + PreferencesManager.remove(requireActivity(), "email", true) + + val serviceIntent = Intent(requireActivity(), ExpiryService::class.java) + requireActivity().stopService(serviceIntent) + + val intent = Intent(requireActivity(), LoginActivity::class.java) + startActivity(intent) + requireActivity().finish() + } + + companion object { + private const val TAG = "SettingsFragment" + + @JvmStatic + fun newInstance() = + SettingsFragment().apply { + } + } +} diff --git a/app/src/main/java/com/example/bondoman/views/fragments/TransactionAddFragment.kt b/app/src/main/java/com/example/bondoman/views/fragments/TransactionAddFragment.kt new file mode 100644 index 0000000000000000000000000000000000000000..af91a41c894a093a2856ab82473f44e334d62799 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/views/fragments/TransactionAddFragment.kt @@ -0,0 +1,73 @@ +package com.example.bondoman.views.fragments + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.lifecycle.ViewModelProvider +import com.example.bondoman.data.models.TransactionCategory +import com.example.bondoman.data.viewmodels.transaction.TransactionViewModel +import com.example.bondoman.views.utils.interfaces.ParentActivityService +import java.util.Date + +class TransactionAddFragment : TransactionFormFragment() { + companion object { + const val HEADER_TEXT: String = "New Transaction" + + @JvmStatic + fun newInstance() = + TransactionUpdateFragment().apply { + } + } + + private lateinit var transactionViewModel: TransactionViewModel + private lateinit var parentActivityService: ParentActivityService + + override fun getHeaderText(): String { + return HEADER_TEXT + } + + override fun onTransactionSave( + category: TransactionCategory, + date: Date?, + title: String, + amount: Long, + location: String?, + ) { + transactionViewModel.insertTransaction(title, category, amount, location) { _, messsage -> + parentActivityService.showToast(messsage, Toast.LENGTH_SHORT) + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + transactionViewModel = + ViewModelProvider(requireActivity())[TransactionViewModel::class.java] + + parentActivityService = requireActivity() as ParentActivityService + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + // Inflate the layout for this fragment + val view = super.onCreateView(inflater, container, savedInstanceState)!! + + getBinding().transactionFormDate.visibility = View.GONE + + return view + } + + override fun onViewCreated( + view: View, + savedInstanceState: Bundle?, + ) { + super.onViewCreated(view, savedInstanceState) + + changeLocation() + } +} diff --git a/app/src/main/java/com/example/bondoman/views/fragments/TransactionFormFragment.kt b/app/src/main/java/com/example/bondoman/views/fragments/TransactionFormFragment.kt new file mode 100644 index 0000000000000000000000000000000000000000..02035bdbd9e7fa0155e442a9e131d1c9dd0f9034 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/views/fragments/TransactionFormFragment.kt @@ -0,0 +1,510 @@ +package com.example.bondoman.views.fragments + +import android.content.Context +import android.content.pm.PackageManager +import android.location.Geocoder +import android.location.Location +import android.location.LocationListener +import android.location.LocationManager +import android.os.Build +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.AdapterView +import android.widget.ArrayAdapter +import android.widget.Toast +import androidx.activity.result.contract.ActivityResultContracts +import androidx.annotation.RequiresApi +import androidx.core.content.ContextCompat +import androidx.fragment.app.Fragment +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.lifecycleScope +import androidx.navigation.fragment.findNavController +import com.example.bondoman.R +import com.example.bondoman.data.models.TransactionCategory +import com.example.bondoman.data.models.transactionCategoryMap +import com.example.bondoman.data.utils.TransactionParser +import com.example.bondoman.databinding.FragmentTransactionFormBinding +import com.example.bondoman.views.activities.MainActivity +import com.example.bondoman.views.utils.interfaces.ParentActivityService +import com.example.bondoman.views.utils.interfaces.SecondaryFragment +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import java.util.Date +import java.util.Locale +import java.util.function.Consumer + +abstract class TransactionFormFragment : Fragment(), SecondaryFragment { + private inner class ValidationResult(val isValid: Boolean, val message: String?) + + companion object { + private val REQUIRED_PERMISSIONS = + mutableListOf( + android.Manifest.permission.ACCESS_FINE_LOCATION, + android.Manifest.permission.ACCESS_COARSE_LOCATION, + ).toTypedArray() + } + + private lateinit var parentActivityService: ParentActivityService + private lateinit var binding: FragmentTransactionFormBinding + private lateinit var locationManager: LocationManager + + private var category: TransactionCategory? = null + private var title: String = "" + private var date: Date? = null + private var amount: Long? = null + private var location: String? = null + private var currentGpsLocation: Location? = null + private var currentNetworkLocation: Location? = null + + private var locationTextString = MutableLiveData<String?>(null) + + private val transactionParser = TransactionParser() + + private val activityResultLauncher = + registerForActivityResult( + ActivityResultContracts.RequestMultiplePermissions(), + ) { permissions -> + when { + permissions.getOrDefault( + android.Manifest.permission.ACCESS_FINE_LOCATION, + false, + ) -> { + // Precise location access granted. + requestLocation() + } + + permissions.getOrDefault( + android.Manifest.permission.ACCESS_COARSE_LOCATION, + false, + ) -> { + // Only approximate location access granted. + requestLocation() + } + + else -> { + parentActivityService.showToast( + "Permission request denied", + Toast.LENGTH_SHORT, + ) + } + } + } + + private val gpsLocationListener: LocationListener = + object : LocationListener { + override fun onLocationChanged(location: Location) { + currentGpsLocation = location + changeLocationData() + } + + override fun onStatusChanged( + provider: String, + status: Int, + extras: Bundle, + ) { + } + + override fun onProviderEnabled(provider: String) {} + + override fun onProviderDisabled(provider: String) {} + } + + private val networkLocationListener: LocationListener = + object : LocationListener { + override fun onLocationChanged(location: Location) { + currentNetworkLocation = location + changeLocationData() + } + + override fun onStatusChanged( + provider: String, + status: Int, + extras: Bundle, + ) { + } + + override fun onProviderEnabled(provider: String) {} + + override fun onProviderDisabled(provider: String) {} + } + + protected abstract fun onTransactionSave( + category: TransactionCategory, + date: Date?, + title: String, + amount: Long, + location: String?, + ) + + protected fun getBinding(): FragmentTransactionFormBinding { + return binding + } + + protected fun setCategory(category: TransactionCategory) { + this.category = category + binding.transactionFormAutoComplete.setText(category.string) + } + + protected fun setTitle(title: String) { + this.title = title + binding.transactionFormTitle.setText(title) + } + + protected fun setDate(date: Date) { + this.date = date + binding.transactionFormDate.setText(transactionParser.getDateString(date)) + } + + protected fun setAmount(amount: Long) { + this.amount = amount + binding.transactionFormAmount.setText(amount.toString().split(".").first()) + } + + protected fun setLocation(location: String?) { + this.location = location + locationTextString.value = location + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + parentActivityService = requireActivity() as ParentActivityService + locationManager = + requireActivity().getSystemService(Context.LOCATION_SERVICE) as LocationManager + } + + private fun configureDropdown() { + // set category dropdown adapter + val autoCompleteTextView = binding.transactionFormAutoComplete + val categories = resources.getStringArray(R.array.transaction_categories) + val dropdownAdapter = + ArrayAdapter(requireContext(), R.layout.component_dropdown_item, categories) + autoCompleteTextView.setAdapter(dropdownAdapter) + + autoCompleteTextView.setDropDownBackgroundResource(R.color.bg_main) + autoCompleteTextView.onFocusChangeListener = + View.OnFocusChangeListener { _, hasFocus -> + if (!hasFocus) { + val enteredText = autoCompleteTextView.text.toString() + val itemFound = categories.contains(enteredText) + + if (!itemFound) { + autoCompleteTextView.setText("") + } + } + } + } + + private val gpsLocationCallback = + Consumer<Location> { location -> + if (isAdded && location != null) { + currentGpsLocation = location + changeLocationData() + } + } + + private val networkLocationCallback = + Consumer<Location> { location -> + if (isAdded && location != null) { + currentNetworkLocation = location + changeLocationData() + } + } + + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + private fun getLocationString( + location: Location, + callback: (String?) -> Unit, + ) { + val geocoder = Geocoder(requireContext(), Locale.getDefault()) + geocoder.getFromLocation( + location.latitude, + location.longitude, + 1, + ) { addresses -> + if (addresses.isNotEmpty()) { + val address = addresses[0] + val locationString = address.getAddressLine(0) + callback(locationString) + } else { + callback(null) + } + } + } + + private suspend fun getLocationStringSuspend( + location: Location, + callback: (String?) -> Unit, + ) { + withContext(Dispatchers.IO) { + val geocoder = Geocoder(requireContext(), Locale.getDefault()) + val addresses = + geocoder.getFromLocation( + location.latitude, + location.longitude, + 1, + ) + + if (!addresses.isNullOrEmpty()) { + val address = addresses[0] + val locationString = address.getAddressLine(0) + + callback(locationString) + } else { + callback(null) + } + } + } + + private fun changeLocationData() { + assert(currentGpsLocation != null || currentNetworkLocation != null) + + var currentLocation: Location? = null + + if (currentGpsLocation != null && currentNetworkLocation != null) { + currentLocation = + if (currentGpsLocation!!.accuracy >= currentNetworkLocation!!.accuracy) { + currentGpsLocation + } else { + currentNetworkLocation + } + } else if (currentGpsLocation != null) { + currentLocation = currentGpsLocation + } else if (currentNetworkLocation != null) { + currentLocation = currentNetworkLocation + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + getLocationString(currentLocation!!) { + location = it + locationTextString.postValue(it) + } + } else { + lifecycleScope.launch { + getLocationStringSuspend(currentLocation!!) { + location = it + locationTextString.postValue(it) + } + } + } + } + + private fun requestLocation() { + assert( + ContextCompat.checkSelfPermission( + requireContext(), + android.Manifest.permission.ACCESS_FINE_LOCATION, + ) == PackageManager.PERMISSION_GRANTED || + ContextCompat.checkSelfPermission( + requireContext(), + android.Manifest.permission.ACCESS_COARSE_LOCATION, + ) == PackageManager.PERMISSION_GRANTED, + ) + + binding.transactionFormSaveButton.isClickable = false + binding.transactionFormSaveButton.setBackgroundColor( + ContextCompat.getColor( + requireContext(), + R.color.zinc_300, + ), + ) + + binding.transactionFormLocationField.text = null + binding.transactionFormLocationField.setHint("Getting location…") + currentGpsLocation = null + currentNetworkLocation = null + + val hasGps = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) + val hasNetwork = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) + + if (hasGps) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + locationManager.getCurrentLocation( + LocationManager.GPS_PROVIDER, + null, + requireActivity().mainExecutor, + gpsLocationCallback, + ) + } else { + locationManager.requestSingleUpdate( + LocationManager.GPS_PROVIDER, + gpsLocationListener, + null, + ) + } + } + + if (hasNetwork) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + locationManager.getCurrentLocation( + LocationManager.NETWORK_PROVIDER, + null, + requireActivity().mainExecutor, + networkLocationCallback, + ) + } else { + locationManager.requestSingleUpdate( + LocationManager.NETWORK_PROVIDER, + networkLocationListener, + null, + ) + } + } + + if (!hasGps and !hasNetwork) { + parentActivityService.showToast( + "No provider to get location. Enable GPS", + Toast.LENGTH_SHORT, + ) + } + } + + protected fun changeLocation() { + if ( + ContextCompat.checkSelfPermission( + requireContext(), + android.Manifest.permission.ACCESS_FINE_LOCATION, + ) != PackageManager.PERMISSION_GRANTED + ) { + activityResultLauncher.launch(REQUIRED_PERMISSIONS) + } else { + requestLocation() + } + } + + private fun configureInputFields() { + configureDropdown() + + binding.transactionFormAutoComplete.onItemClickListener = + AdapterView.OnItemClickListener { parent, _, position, _ -> + val selectedItem = parent.getItemAtPosition(position).toString() + category = transactionCategoryMap[selectedItem] + } + + binding.transactionFormTitle.setOnTextChangedListener { text, _, _, _ -> + title = text.toString() + } + + // TODO: set text watcher for amount field + binding.transactionFormAmount.setOnTextChangedListener { text, _, _, _ -> + val textString = text.toString() + + amount = + if (textString.isNotEmpty()) { + textString.toLong() + } else { + null + } + } + + binding.transactionFormChangeLocationText.setOnClickListener { + changeLocation() + } + + // set empty location + setLocation(null) + } + + private fun validateInput( + category: TransactionCategory?, + date: Date?, + title: String, + amount: Long?, + location: String?, + ): ValidationResult { + if (category == null) { + return ValidationResult(false, "Please fill the category") + } else if (title.isEmpty()) { + return ValidationResult(false, "Please fill the title") + } else if (amount == null) { + return ValidationResult(false, "Please fill the amount") + } else if (amount == 0L) { + return ValidationResult(false, "Amount can't be zero") + } + + return ValidationResult(true, null) + } + + private fun randomizeTransaction() { + val randomCategory = transactionCategoryMap.keys.random() + val randomTitle = "Transaction " + (0..1000).random() + val randomAmount = (0..10000).random().times(1000L) + val randomLocation = null + + setCategory(transactionCategoryMap[randomCategory]!!) + setTitle(randomTitle) + setAmount(randomAmount) + setLocation(randomLocation) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View? { + // Inflate the layout for this fragment + binding = FragmentTransactionFormBinding.inflate(inflater) + val view = binding.root + + // set header text + parentActivityService.setHeaderText(getHeaderText()) + + // set back button callback + parentActivityService.showBackButton() + + configureInputFields() + + binding.transactionFormSaveButton.setOnClickListener { + val validationResult = validateInput(category, date, title, amount, location) + + if (validationResult.isValid) { + parentActivityService.showToast( + "Saving transaction…", + Toast.LENGTH_SHORT, + ) + + onTransactionSave(category!!, date, title, amount!!, location) + + findNavController().popBackStack(R.id.transactionListFragment, false) + } else { + parentActivityService.showToast( + validationResult.message!!, + Toast.LENGTH_SHORT, + ) + } + } + + // set location text observer + locationTextString.observe(requireActivity()) { + binding.transactionFormLocationField.setText(it) + binding.transactionFormSaveButton.isClickable = true + binding.transactionFormSaveButton.setBackgroundColor( + ContextCompat.getColor( + requireContext(), + R.color.teal_200, + ), + ) + } + + return view + } + + override fun onViewCreated( + view: View, + savedInstanceState: Bundle?, + ) { + super.onViewCreated(view, savedInstanceState) + + // Randomize Transaction + if (activity is MainActivity) { + val mainActivity = activity as MainActivity + if (mainActivity.randomizeNextTransaction) { + randomizeTransaction() + mainActivity.randomizeNextTransaction = false + } + } + } +} diff --git a/app/src/main/java/com/example/bondoman/views/fragments/TransactionListFragment.kt b/app/src/main/java/com/example/bondoman/views/fragments/TransactionListFragment.kt new file mode 100644 index 0000000000000000000000000000000000000000..9366a9b559728f04276b525216141f7b96d481eb --- /dev/null +++ b/app/src/main/java/com/example/bondoman/views/fragments/TransactionListFragment.kt @@ -0,0 +1,199 @@ +package com.example.bondoman.views.fragments + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.core.content.ContextCompat +import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelProvider +import androidx.navigation.fragment.findNavController +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView.ItemDecoration +import com.example.bondoman.R +import com.example.bondoman.data.models.Transaction +import com.example.bondoman.data.viewmodels.transaction.TransactionViewModel +import com.example.bondoman.databinding.FragmentTransactionListBinding +import com.example.bondoman.views.adapters.TransactionListAdapter +import com.example.bondoman.views.components.DialogComponent +import com.example.bondoman.views.components.TransactionDetailComponent +import com.example.bondoman.views.decorators.DividerItemDecorator +import com.example.bondoman.views.utils.interfaces.ParentActivityService +import com.example.bondoman.views.utils.interfaces.TransactionClickListener +import com.google.android.material.bottomsheet.BottomSheetBehavior + +class TransactionListFragment : Fragment(), TransactionClickListener { + private lateinit var adapter: TransactionListAdapter + private lateinit var detailLayout: TransactionDetailComponent + private lateinit var dialog: DialogComponent + private lateinit var parentActivityService: ParentActivityService + private lateinit var transactionViewModel: TransactionViewModel + private lateinit var viewBinding: FragmentTransactionListBinding + private lateinit var currentTransactionObserver: (Transaction?) -> Unit + + private fun configureRecyclerView() { + val recyclerView = viewBinding.transactionListRecyclerView + recyclerView.layoutManager = LinearLayoutManager(requireContext()) + adapter = TransactionListAdapter(this) + recyclerView.adapter = adapter + + transactionViewModel.transactions.observe(viewLifecycleOwner) { + adapter.submitList(it) + viewBinding.transactionListPageInfoWaiting.visibility = View.GONE + + if (it.isEmpty()) { + viewBinding.transactionListPageInfoEmpty.visibility = View.VISIBLE + } else { + viewBinding.transactionListPageInfoEmpty.visibility = View.GONE + } + } + + val dividerItemDecoration: ItemDecoration = + DividerItemDecorator( + ContextCompat.getDrawable( + requireContext(), + R.drawable.shape_recycler_view_divider, + )!!, + ) + + recyclerView.addItemDecoration(dividerItemDecoration) + } + + private fun addDialogComponent( + inflater: LayoutInflater, + container: ViewGroup?, + ) { + val dialog = + inflater.inflate( + R.layout.component_delete_transaction_dialog, + container, + false, + ) as DialogComponent + dialog.setFirstButtonOnClickListener { parentActivityService.hideDialog() } + dialog.setSecondButtonOnClickListener { + parentActivityService.showToast("Deleting transaction ...", Toast.LENGTH_SHORT) + transactionViewModel.deleteCurrentTransaction { status, message -> + parentActivityService.showToast(message, Toast.LENGTH_SHORT) + + if (status) { + transactionViewModel.getAllTransaction() + } + } + parentActivityService.hideDialog() + detailLayout.setBottomSheetState(BottomSheetBehavior.STATE_HIDDEN) + } + this.dialog = dialog + } + + private fun addDetailLayout() { + val decorView = requireActivity().window.decorView as ViewGroup + detailLayout = TransactionDetailComponent(requireContext()) + detailLayout.layoutParams = + ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT, + ) + + detailLayout.configureBlurEffect(decorView) + detailLayout.setOnTransactionRemovedListener { transactionViewModel.removeCurrentTransaction() } + + parentActivityService.appendLayout(detailLayout) + } + + private fun configureDetailData() { + currentTransactionObserver = { + if (it != null) { + detailLayout.setTransaction(it) + } + } + + transactionViewModel.currentTransaction.observe( + viewLifecycleOwner, + currentTransactionObserver, + ) + detailLayout.setUpdateListener { + val action = + TransactionListFragmentDirections.actionTransactionListFragmentToTransactionUpdateFragment( + transactionViewModel.currentTransaction.value!!, + ) + findNavController() + .navigate(action) + } + + detailLayout.setDeleteListener { + parentActivityService.showDialog(dialog, detailLayout, DIALOG_ELEVATION) + } + } + + private fun configureDetailView() { + addDetailLayout() + configureDetailData() + } + + private fun configureAddButton() { + val addButton = viewBinding.transactionListAddButton + addButton.setOnClickListener { + val action = + TransactionListFragmentDirections.actionTransactionListFragmentToTransactionAddFragment() + + findNavController().navigate(action) + } + } + + override fun onItemClick(item: Transaction) { + transactionViewModel.setCurrentTransaction(item) + detailLayout.setBottomSheetState(BottomSheetBehavior.STATE_EXPANDED) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + parentActivityService = requireActivity() as ParentActivityService + transactionViewModel = + ViewModelProvider(requireActivity())[TransactionViewModel::class.java] + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + viewBinding = FragmentTransactionListBinding.inflate(inflater, container, false) + + configureRecyclerView() + + configureDetailView() + + addDialogComponent(inflater, container) + + configureAddButton() + + return viewBinding.root + } + + override fun onStart() { + super.onStart() + transactionViewModel.getAllTransaction() + } + + override fun onDestroyView() { + super.onDestroyView() + + parentActivityService.removeLayout(detailLayout) + parentActivityService.hideDialog() + } + + companion object { + const val DIALOG_ELEVATION = 10f + + @JvmStatic + fun newInstance() = + TransactionListFragment().apply { + arguments = + Bundle().apply { + // add arguments if necessary + } + } + } +} diff --git a/app/src/main/java/com/example/bondoman/views/fragments/TransactionUpdateFragment.kt b/app/src/main/java/com/example/bondoman/views/fragments/TransactionUpdateFragment.kt new file mode 100644 index 0000000000000000000000000000000000000000..cc9981515bf2ae08db3939697f85ee338cdc5cd6 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/views/fragments/TransactionUpdateFragment.kt @@ -0,0 +1,92 @@ +package com.example.bondoman.views.fragments + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.lifecycle.ViewModelProvider +import androidx.navigation.fragment.navArgs +import com.example.bondoman.R +import com.example.bondoman.data.models.TransactionCategory +import com.example.bondoman.data.viewmodels.transaction.TransactionViewModel +import com.example.bondoman.views.utils.interfaces.ParentActivityService +import java.util.Date + +class TransactionUpdateFragment : TransactionFormFragment() { + companion object { + const val HEADER_TEXT: String = "Update Transaction" + + @JvmStatic + fun newInstance() = + TransactionUpdateFragment().apply { + } + } + + private lateinit var parentActivityService: ParentActivityService + private lateinit var transactionViewModel: TransactionViewModel + + private val args by navArgs<TransactionUpdateFragmentArgs>() + + override fun getHeaderText(): String { + return HEADER_TEXT + } + + override fun onTransactionSave( + category: TransactionCategory, + date: Date?, + title: String, + amount: Long, + location: String?, + ) { + transactionViewModel.updateCurrentTransaction( + title, + amount, + location, + ) { _, messsage -> + parentActivityService.showToast(messsage, Toast.LENGTH_SHORT) + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + transactionViewModel = + ViewModelProvider(requireActivity())[TransactionViewModel::class.java] + + parentActivityService = requireActivity() as ParentActivityService + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + // Inflate the layout for this fragment + val view = super.onCreateView(inflater, container, savedInstanceState)!! + + // configure category dropdown + val textInputLayout = getBinding().transactionFormCategoryDropdown + textInputLayout.hint = getString(R.string.hint_category) + textInputLayout.endIconDrawable = null + + val autoCompleteTextView = getBinding().transactionFormAutoComplete + autoCompleteTextView.dropDownHeight = 0 + + // change location hint + getBinding().transactionFormLocationField.hint = "Change location" + + // fill input fields + val currentTransaction = args.CurrentTransaction + setCategory(currentTransaction.category) + setDate(currentTransaction.date) + setTitle(currentTransaction.title) + setAmount(currentTransaction.amount) + setLocation(currentTransaction.location) + + // configure date input + val dateFieldComponent = getBinding().transactionFormDate + dateFieldComponent.disable() + return view + } +} diff --git a/app/src/main/java/com/example/bondoman/views/fragments/TwibbonFragment.kt b/app/src/main/java/com/example/bondoman/views/fragments/TwibbonFragment.kt new file mode 100644 index 0000000000000000000000000000000000000000..3414479cfaf178714f641c5b55d41c3864bb8eed --- /dev/null +++ b/app/src/main/java/com/example/bondoman/views/fragments/TwibbonFragment.kt @@ -0,0 +1,224 @@ +package com.example.bondoman.views.fragments + +import android.content.ContentValues +import android.content.pm.PackageManager +import android.graphics.Bitmap +import android.graphics.Rect +import android.net.Uri +import android.os.Bundle +import android.os.Environment +import android.os.Handler +import android.os.Looper +import android.provider.MediaStore +import android.util.Log +import android.view.LayoutInflater +import android.view.PixelCopy +import android.view.View +import android.view.ViewGroup +import android.view.Window +import android.widget.Toast +import androidx.activity.result.contract.ActivityResultContracts +import androidx.camera.core.CameraSelector +import androidx.camera.core.ImageCapture +import androidx.camera.core.Preview +import androidx.camera.lifecycle.ProcessCameraProvider +import androidx.core.content.ContextCompat +import androidx.fragment.app.Fragment +import androidx.navigation.fragment.findNavController +import com.example.bondoman.data.models.ParcelableBitmap +import com.example.bondoman.databinding.FragmentTwibbonBinding +import java.io.OutputStream + +class TwibbonFragment : Fragment() { + private lateinit var viewBinding: FragmentTwibbonBinding + private var imageCapture: ImageCapture? = null + private var cameraSelector: CameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA + private val activityResultLauncher = + registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> + var permissionGranted = true + permissions.entries.forEach { + if (it.key in REQUIRED_PERMISSIONS && !it.value) { + permissionGranted = false + } + } + + if (!permissionGranted) { + Toast.makeText( + requireActivity().baseContext, + "Permission request denied", + Toast.LENGTH_SHORT, + ).show() + } else { + startCamera() + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + viewBinding = FragmentTwibbonBinding.inflate(inflater, container, false) + return viewBinding.root + } + + override fun onViewCreated( + view: View, + savedInstanceState: Bundle?, + ) { + super.onViewCreated(view, savedInstanceState) + + // Request camera permissions + if (allPermissionsGranted()) { + startCamera() + } else { + requestPermissions() + } + + // Set up the listeners for capture button + viewBinding.selfieCaptureButton.setOnClickListener { + takePhoto() + } + + // Camera rotate + viewBinding.cameraRotateButton.setOnClickListener { + cameraSelector = + if (cameraSelector == CameraSelector.DEFAULT_FRONT_CAMERA) { + CameraSelector.DEFAULT_BACK_CAMERA + } else { + CameraSelector.DEFAULT_FRONT_CAMERA + } + startCamera() + } + } + + private fun startCamera() { + val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext()) + + imageCapture = ImageCapture.Builder().build() + + cameraProviderFuture.addListener({ + // Used to bind the lifecycle of camera to the lifecycle owner + val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get() + + // Preview + val preview = + Preview.Builder().build().also { + it.setSurfaceProvider(viewBinding.cameraPreview.surfaceProvider) + } + + try { + // Unbind use cases before rebinding + cameraProvider.unbindAll() + + // Bind use cases to camera + cameraProvider.bindToLifecycle(this, cameraSelector, imageCapture, preview) + } catch (exc: Exception) { + Log.e(TAG, "Use case binding failed", exc) + } + }, ContextCompat.getMainExecutor(requireContext())) + } + + private fun takePhoto() { + // Get a stable reference of the modifiable image capture use case + val bitmap = viewBinding.cameraPreview.bitmap + + // Get selfie bitmap + viewBinding.selfie.setImageBitmap(bitmap) + viewBinding.selfie.visibility = View.VISIBLE + + Handler(Looper.getMainLooper()).postDelayed({ + captureView(viewBinding.selfie, requireActivity().window) { + viewBinding.selfie.visibility = View.GONE + + val action = + TwibbonFragmentDirections.actionTwibbonFragmentToTwibbonPreviewFragment( + ParcelableBitmap(it), + ) + findNavController() + .navigate(action) + } + }, 500) + } + + private fun saveMediaToStorage(bitmap: Bitmap) { + // Generating a file name + val filename = "${System.currentTimeMillis()}.jpg" + + // Output stream + var fos: OutputStream? = null + + // For devices running android >= Q + // getting the contentResolver + context?.contentResolver?.also { resolver -> + + // Content resolver will process the contentvalues + val contentValues = + ContentValues().apply { + // putting file information in content values + put(MediaStore.MediaColumns.DISPLAY_NAME, filename) + put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg") + put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES) + } + + // Inserting the contentValues to contentResolver and getting the Uri + val imageUri: Uri? = + resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues) + + // Opening an outputstream with the Uri that we got + fos = imageUri?.let { resolver.openOutputStream(it) } + } + + fos?.use { + // Finally writing the bitmap to the output stream that we opened + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, it) + Toast.makeText(requireActivity().baseContext, "SAVED", Toast.LENGTH_SHORT).show() + } + } + + private fun captureView( + view: View, + window: Window, + bitmapCallback: (Bitmap) -> Unit, + ) { + val bitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888) + val location = IntArray(2) + view.getLocationInWindow(location) + PixelCopy.request( + window, + Rect( + location[0], + location[1], + location[0] + view.width, + location[1] + view.height, + ), + bitmap, + { + if (it == PixelCopy.SUCCESS) { + bitmapCallback.invoke(bitmap) + } + }, + Handler(Looper.getMainLooper()), + ) + } + + private fun requestPermissions() { + activityResultLauncher.launch(REQUIRED_PERMISSIONS) + } + + private fun allPermissionsGranted() = + REQUIRED_PERMISSIONS.all { + ContextCompat.checkSelfPermission( + requireActivity().baseContext, + it, + ) == PackageManager.PERMISSION_GRANTED + } + + companion object { + private const val TAG = "Bondoman" + private val REQUIRED_PERMISSIONS = + mutableListOf( + android.Manifest.permission.CAMERA, + ).toTypedArray() + } +} diff --git a/app/src/main/java/com/example/bondoman/views/fragments/TwibbonPreviewFragment.kt b/app/src/main/java/com/example/bondoman/views/fragments/TwibbonPreviewFragment.kt new file mode 100644 index 0000000000000000000000000000000000000000..1e51a4e58e5cfff8ee83f10b1e04b72c994d8a58 --- /dev/null +++ b/app/src/main/java/com/example/bondoman/views/fragments/TwibbonPreviewFragment.kt @@ -0,0 +1,41 @@ +package com.example.bondoman.views.fragments + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.navigation.Navigation +import androidx.navigation.fragment.navArgs +import com.example.bondoman.R +import com.example.bondoman.databinding.FragmentTwibbonPreviewBinding + +class TwibbonPreviewFragment : Fragment() { + private val args by navArgs<TwibbonPreviewFragmentArgs>() + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + val view = FragmentTwibbonPreviewBinding.inflate(inflater, container, false) + view.twibbonResult.setImageBitmap(args.twibbonResult.bitmap) + view.retakeTwibbon.setOnClickListener { + Navigation.findNavController(view.root) + .navigate(R.id.action_twibbonPreviewFragment_to_twibbonFragment) + } + return view.root + } + + companion object { + @JvmStatic + fun newInstance( + param1: String, + param2: String, + ) = TwibbonPreviewFragment().apply { + arguments = + Bundle().apply { + } + } + } +} diff --git a/app/src/main/java/com/example/bondoman/views/utils/filters/CustomInputFilter.kt b/app/src/main/java/com/example/bondoman/views/utils/filters/CustomInputFilter.kt new file mode 100644 index 0000000000000000000000000000000000000000..7d478f5b62d8d1b7d2e1b4607e7b55fc179d829e --- /dev/null +++ b/app/src/main/java/com/example/bondoman/views/utils/filters/CustomInputFilter.kt @@ -0,0 +1,29 @@ +package com.example.bondoman.views.utils.filters + +import android.text.InputFilter +import android.text.Spanned + +class CustomInputFilter(private val allowedCharacters: String) : InputFilter { + override fun filter( + source: CharSequence?, + start: Int, + end: Int, + dest: Spanned?, + dstart: Int, + dend: Int, + ): CharSequence? { + val builder = StringBuilder() + + // Loop through each character in the source + for (i in start until end) { + val c = source?.get(i) + + if (c != null && allowedCharacters.contains(c, ignoreCase = true)) { + builder.append(c) + } + } + + val allCharactersValid = builder.length == end - start + return if (allCharactersValid) null else builder.toString() + } +} diff --git a/app/src/main/java/com/example/bondoman/views/utils/interfaces/ParentActivityService.kt b/app/src/main/java/com/example/bondoman/views/utils/interfaces/ParentActivityService.kt new file mode 100644 index 0000000000000000000000000000000000000000..71b43952dad5fd8ad5f132d71d13277ad9a7ff3c --- /dev/null +++ b/app/src/main/java/com/example/bondoman/views/utils/interfaces/ParentActivityService.kt @@ -0,0 +1,59 @@ +package com.example.bondoman.views.utils.interfaces + +import android.view.View +import android.view.ViewGroup +import android.view.ViewGroup.LayoutParams +import androidx.activity.OnBackPressedCallback + +interface ParentActivityService { + fun appendLayout(view: View) + + fun appendLayout( + view: View, + layoutParams: LayoutParams, + ) + + fun removeLayout(view: View) + + fun getParentLayoutId(): Int + + fun showBackButton() + + fun showBackButton(onClick: () -> Unit) + + fun hideBackButton() + + fun setHeaderText(text: String) + + fun showDialog(view: View) + + fun showDialog( + view: View, + elevation: Float, + ) + + fun showDialog( + view: View, + rootView: ViewGroup, + elevation: Float, + ) + + fun hideDialog() + + fun dialogParentId(): Int + + fun addDestinationChangedListener(listener: (Int) -> Unit) + + fun removeDestinationChangedListener(listener: (Int) -> Unit) + + fun showToast( + message: String, + duration: Int, + ) + + fun getEmail(): String + + fun getNIM(): String + + fun getOnBackPressedCallback(): OnBackPressedCallback +} diff --git a/app/src/main/java/com/example/bondoman/views/utils/interfaces/SecondaryFragment.kt b/app/src/main/java/com/example/bondoman/views/utils/interfaces/SecondaryFragment.kt new file mode 100644 index 0000000000000000000000000000000000000000..c8b77329769bce19094a33f11e7b81be5ad7dedd --- /dev/null +++ b/app/src/main/java/com/example/bondoman/views/utils/interfaces/SecondaryFragment.kt @@ -0,0 +1,5 @@ +package com.example.bondoman.views.utils.interfaces + +interface SecondaryFragment { + fun getHeaderText(): String +} diff --git a/app/src/main/java/com/example/bondoman/views/utils/interfaces/TransactionClickListener.kt b/app/src/main/java/com/example/bondoman/views/utils/interfaces/TransactionClickListener.kt new file mode 100644 index 0000000000000000000000000000000000000000..0ac47129d71524c28fe94839b505def8342e14fc --- /dev/null +++ b/app/src/main/java/com/example/bondoman/views/utils/interfaces/TransactionClickListener.kt @@ -0,0 +1,7 @@ +package com.example.bondoman.views.utils.interfaces + +import com.example.bondoman.data.models.Transaction + +interface TransactionClickListener { + fun onItemClick(item: Transaction) +} diff --git a/app/src/main/res/color/bnv_tab_item.xml b/app/src/main/res/color/bnv_tab_item.xml new file mode 100644 index 0000000000000000000000000000000000000000..2314867616400da02504d581a43b448d20c1f532 --- /dev/null +++ b/app/src/main/res/color/bnv_tab_item.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_checked="true" android:color="@color/teal_200" /> + <item android:color="@color/zinc_300" /> +</selector> \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_dot_grid.xml b/app/src/main/res/drawable/bg_dot_grid.xml new file mode 100644 index 0000000000000000000000000000000000000000..469d417702adca1cdf54b2911404726075f4b793 --- /dev/null +++ b/app/src/main/res/drawable/bg_dot_grid.xml @@ -0,0 +1,2347 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="390dp" + android:height="844dp" + android:viewportWidth="390" + android:viewportHeight="844"> + <group> + <clip-path + android:pathData="M0,0h390v844h-390z"/> + <path + android:pathData="M0.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + </group> +</vector> diff --git a/app/src/main/res/drawable/bg_full_border.xml b/app/src/main/res/drawable/bg_full_border.xml new file mode 100644 index 0000000000000000000000000000000000000000..800c1e3e6a3285ae9d80eb3db5f3274af92bd9aa --- /dev/null +++ b/app/src/main/res/drawable/bg_full_border.xml @@ -0,0 +1,32 @@ +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + +<item> + <shape android:shape="rectangle"> + <solid android:color="?attr/borderColor" /> + <corners + android:radius="?attr/borderRadius" + android:topLeftRadius="?attr/borderTopLeftRadius" + android:topRightRadius="?attr/borderTopRightRadius" + android:bottomLeftRadius="?attr/borderBottomLeftRadius" + android:bottomRightRadius="?attr/borderBottomRightRadius" /> + </shape> +</item> + +<item + android:start="?attr/borderStartWidth" + android:end="?attr/borderEndWidth" + android:top="?attr/borderTopWidth" + android:left="?attr/borderLeftWidth" + android:right="?attr/borderRightWidth" + android:bottom="?attr/borderBottomWidth"> + <shape android:shape="rectangle"> + <solid android:color="?attr/backgroundColor" /> + <corners + android:radius="?attr/borderRadius" + android:topLeftRadius="?attr/borderTopLeftRadius" + android:topRightRadius="?attr/borderTopRightRadius" + android:bottomLeftRadius="?attr/borderBottomLeftRadius" + android:bottomRightRadius="?attr/borderBottomRightRadius"/> + </shape> +</item> +</layer-list> \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_arrow_left.xml b/app/src/main/res/drawable/ic_arrow_left.xml new file mode 100644 index 0000000000000000000000000000000000000000..330055ca6c408bd955e21b18e97cef0902b4fc82 --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_left.xml @@ -0,0 +1,27 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M5,12l14,0" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M5,12l6,6" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M5,12l6,-6" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> +</vector> diff --git a/app/src/main/res/drawable/ic_arrows_shuffle.xml b/app/src/main/res/drawable/ic_arrows_shuffle.xml new file mode 100644 index 0000000000000000000000000000000000000000..b0dfc9d7c438d478acdf0a70444f41c4e08b9f8d --- /dev/null +++ b/app/src/main/res/drawable/ic_arrows_shuffle.xml @@ -0,0 +1,34 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M18,4l3,3l-3,3" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M18,20l3,-3l-3,-3" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M3,7h3a5,5 0,0 1,5 5a5,5 0,0 0,5 5h5" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M21,7h-5a4.978,4.978 0,0 0,-3 1m-4,8a4.984,4.984 0,0 1,-3 1h-3" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> +</vector> diff --git a/app/src/main/res/drawable/ic_camera.xml b/app/src/main/res/drawable/ic_camera.xml new file mode 100644 index 0000000000000000000000000000000000000000..1e1fb93f66e801788b3bcae15381411eb3e15e60 --- /dev/null +++ b/app/src/main/res/drawable/ic_camera.xml @@ -0,0 +1,20 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M5,7h1a2,2 0,0 0,2 -2a1,1 0,0 1,1 -1h6a1,1 0,0 1,1 1a2,2 0,0 0,2 2h1a2,2 0,0 1,2 2v9a2,2 0,0 1,-2 2h-14a2,2 0,0 1,-2 -2v-9a2,2 0,0 1,2 -2" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M9,13a3,3 0,1 0,6 0a3,3 0,0 0,-6 0" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> +</vector> diff --git a/app/src/main/res/drawable/ic_camera_rotate.xml b/app/src/main/res/drawable/ic_camera_rotate.xml new file mode 100644 index 0000000000000000000000000000000000000000..0be85fb9b4c1dc3f4ca4c2715077303f3d18989c --- /dev/null +++ b/app/src/main/res/drawable/ic_camera_rotate.xml @@ -0,0 +1,30 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt" + android:viewportWidth="24" + android:viewportHeight="24" + android:width="24dp" + android:height="24dp"> + <path + android:pathData="M5 7h1a2 2 0 0 0 2 -2a1 1 0 0 1 1 -1h6a1 1 0 0 1 1 1a2 2 0 0 0 2 2h1a2 2 0 0 1 2 2v9a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2v-9a2 2 0 0 1 2 -2" + android:strokeColor="@color/white" + android:strokeWidth="2" + android:strokeLineCap="round" + android:strokeLineJoin="round" /> + <path + android:pathData="M11.245 15.904a3 3 0 0 0 3.755 -2.904m-2.25 -2.905a3 3 0 0 0 -3.75 2.905" + android:strokeColor="@color/white" + android:strokeWidth="2" + android:strokeLineCap="round" + android:strokeLineJoin="round" /> + <path + android:pathData="M14 13h2v2" + android:strokeColor="@color/white" + android:strokeWidth="2" + android:strokeLineCap="round" + android:strokeLineJoin="round" /> + <path + android:pathData="M10 13h-2v-2" + android:strokeColor="@color/white" + android:strokeWidth="2" + android:strokeLineCap="round" + android:strokeLineJoin="round" /> +</vector> \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_camera_selfie.xml b/app/src/main/res/drawable/ic_camera_selfie.xml new file mode 100644 index 0000000000000000000000000000000000000000..bbe665a8a09e28d66bbc12ca467a80e7538315b6 --- /dev/null +++ b/app/src/main/res/drawable/ic_camera_selfie.xml @@ -0,0 +1,34 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M5,7h1a2,2 0,0 0,2 -2a1,1 0,0 1,1 -1h6a1,1 0,0 1,1 1a2,2 0,0 0,2 2h1a2,2 0,0 1,2 2v9a2,2 0,0 1,-2 2h-14a2,2 0,0 1,-2 -2v-9a2,2 0,0 1,2 -2" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M9.5,15a3.5,3.5 0,0 0,5 0" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M15,11l0.01,0" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M9,11l0.01,0" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> +</vector> diff --git a/app/src/main/res/drawable/ic_chart_pie.xml b/app/src/main/res/drawable/ic_chart_pie.xml new file mode 100644 index 0000000000000000000000000000000000000000..2e1f14f51bf11d682912d9925ba0476d91dbdb30 --- /dev/null +++ b/app/src/main/res/drawable/ic_chart_pie.xml @@ -0,0 +1,20 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M10,3.2a9,9 0,1 0,10.8 10.8a1,1 0,0 0,-1 -1h-6.8a2,2 0,0 1,-2 -2v-7a0.9,0.9 0,0 0,-1 -0.8" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M15,3.5a9,9 0,0 1,5.5 5.5h-4.5a1,1 0,0 1,-1 -1v-4.5" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> +</vector> diff --git a/app/src/main/res/drawable/ic_chevron_down.xml b/app/src/main/res/drawable/ic_chevron_down.xml new file mode 100644 index 0000000000000000000000000000000000000000..cf5498c7cae7dead918f3e299afade6eadcea4c9 --- /dev/null +++ b/app/src/main/res/drawable/ic_chevron_down.xml @@ -0,0 +1,13 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M6,9l6,6l6,-6" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> +</vector> diff --git a/app/src/main/res/drawable/ic_coins.xml b/app/src/main/res/drawable/ic_coins.xml new file mode 100644 index 0000000000000000000000000000000000000000..daac8aaacce2dce5bb39f745e1902c63e8e6bcf9 --- /dev/null +++ b/app/src/main/res/drawable/ic_coins.xml @@ -0,0 +1,41 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M9,14c0,1.657 2.686,3 6,3s6,-1.343 6,-3s-2.686,-3 -6,-3s-6,1.343 -6,3z" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M9,14v4c0,1.656 2.686,3 6,3s6,-1.344 6,-3v-4" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M3,6c0,1.072 1.144,2.062 3,2.598s4.144,0.536 6,0c1.856,-0.536 3,-1.526 3,-2.598c0,-1.072 -1.144,-2.062 -3,-2.598s-4.144,-0.536 -6,0c-1.856,0.536 -3,1.526 -3,2.598z" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M3,6v10c0,0.888 0.772,1.45 2,2" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M3,11c0,0.888 0.772,1.45 2,2" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> +</vector> diff --git a/app/src/main/res/drawable/ic_device_floppy.xml b/app/src/main/res/drawable/ic_device_floppy.xml new file mode 100644 index 0000000000000000000000000000000000000000..9fd33d9f47bddd273639765070a404ab63884c68 --- /dev/null +++ b/app/src/main/res/drawable/ic_device_floppy.xml @@ -0,0 +1,27 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M6,4h10l4,4v10a2,2 0,0 1,-2 2h-12a2,2 0,0 1,-2 -2v-12a2,2 0,0 1,2 -2" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M12,14m-2,0a2,2 0,1 0,4 0a2,2 0,1 0,-4 0" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M14,4l0,4l-6,0l0,-4" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> +</vector> diff --git a/app/src/main/res/drawable/ic_edit.xml b/app/src/main/res/drawable/ic_edit.xml new file mode 100644 index 0000000000000000000000000000000000000000..1a5a4cf2a34e4e5f4cf87e1277d1083f49e5596f --- /dev/null +++ b/app/src/main/res/drawable/ic_edit.xml @@ -0,0 +1,27 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M7,7h-1a2,2 0,0 0,-2 2v9a2,2 0,0 0,2 2h9a2,2 0,0 0,2 -2v-1" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M20.385,6.585a2.1,2.1 0,0 0,-2.97 -2.97l-8.415,8.385v3h3l8.385,-8.415z" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M16,5l3,3" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> +</vector> diff --git a/app/src/main/res/drawable/ic_file_export.xml b/app/src/main/res/drawable/ic_file_export.xml new file mode 100644 index 0000000000000000000000000000000000000000..0f5d9a42c1d2eabc33b53f0449d9353288d85204 --- /dev/null +++ b/app/src/main/res/drawable/ic_file_export.xml @@ -0,0 +1,20 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M14,3v4a1,1 0,0 0,1 1h4" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M11.5,21h-4.5a2,2 0,0 1,-2 -2v-14a2,2 0,0 1,2 -2h7l5,5v5m-5,6h7m-3,-3l3,3l-3,3" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> +</vector> diff --git a/app/src/main/res/drawable/ic_hourglass_high.xml b/app/src/main/res/drawable/ic_hourglass_high.xml new file mode 100644 index 0000000000000000000000000000000000000000..3042deb349c6cd4a16afe9897bc3203bcf4a1c49 --- /dev/null +++ b/app/src/main/res/drawable/ic_hourglass_high.xml @@ -0,0 +1,27 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M6.5,7h11" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M6,20v-2a6,6 0,1 1,12 0v2a1,1 0,0 1,-1 1h-10a1,1 0,0 1,-1 -1z" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M6,4v2a6,6 0,1 0,12 0v-2a1,1 0,0 0,-1 -1h-10a1,1 0,0 0,-1 1z" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> +</vector> diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml index 07d5da9cbf141911847041df5d7b87f0dd5ef9d4..f1a8c2117b97dad3387fa8f08285ab57181e6062 100644 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -1,170 +1,2347 @@ -<?xml version="1.0" encoding="utf-8"?> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="108dp" android:height="108dp" android:viewportWidth="108" android:viewportHeight="108"> - <path - android:fillColor="#3DDC84" - android:pathData="M0,0h108v108h-108z" /> - <path - android:fillColor="#00000000" - android:pathData="M9,0L9,108" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M19,0L19,108" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M29,0L29,108" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M39,0L39,108" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M49,0L49,108" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M59,0L59,108" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M69,0L69,108" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M79,0L79,108" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M89,0L89,108" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M99,0L99,108" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M0,9L108,9" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M0,19L108,19" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M0,29L108,29" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M0,39L108,39" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M0,49L108,49" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M0,59L108,59" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M0,69L108,69" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M0,79L108,79" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M0,89L108,89" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M0,99L108,99" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M19,29L89,29" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M19,39L89,39" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M19,49L89,49" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M19,59L89,59" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M19,69L89,69" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M19,79L89,79" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M29,19L29,89" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M39,19L39,89" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M49,19L49,89" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M59,19L59,89" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M69,19L69,89" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> - <path - android:fillColor="#00000000" - android:pathData="M79,19L79,89" - android:strokeWidth="0.8" - android:strokeColor="#33FFFFFF" /> + <group> + <clip-path + android:pathData="M0,0h390v844h-390z"/> + <path + android:pathData="M0.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,0.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,21.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,42.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,63.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,84.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,105.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,126.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,147.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,168.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,189.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,210.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,231.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,252.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,273.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,294.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,315.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,336.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,357.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,378.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,399.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,420.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,441.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,462.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,483.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,504.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,525.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,546.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,567.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,588.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,609.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,630.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,651.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,672.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,693.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,714.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,735.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,756.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,777.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,798.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,819.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M0.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M21.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M42.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M63.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M84.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M105.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M126.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M147.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M168.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M189.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M210.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M231.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M252.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M273.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M294.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M315.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M336.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M357.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + <path + android:pathData="M378.5,840.5m-0.5,0a0.5,0.5 0,1 1,1 0a0.5,0.5 0,1 1,-1 0" + android:fillColor="#71717A"/> + </group> </vector> diff --git a/app/src/main/res/drawable/ic_library_photo.xml b/app/src/main/res/drawable/ic_library_photo.xml new file mode 100644 index 0000000000000000000000000000000000000000..065c78edfe6226bac33912723d9d143569050f6e --- /dev/null +++ b/app/src/main/res/drawable/ic_library_photo.xml @@ -0,0 +1,41 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M7,3m0,2.667a2.667,2.667 0,0 1,2.667 -2.667h8.666a2.667,2.667 0,0 1,2.667 2.667v8.666a2.667,2.667 0,0 1,-2.667 2.667h-8.666a2.667,2.667 0,0 1,-2.667 -2.667z" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M4.012,7.26a2.005,2.005 0,0 0,-1.012 1.737v10c0,1.1 0.9,2 2,2h10c0.75,0 1.158,-0.385 1.5,-1" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M17,7h0.01" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M7,13l3.644,-3.644a1.21,1.21 0,0 1,1.712 0l3.644,3.644" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M15,12l1.644,-1.644a1.21,1.21 0,0 1,1.712 0l2.644,2.644" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> +</vector> diff --git a/app/src/main/res/drawable/ic_logout.xml b/app/src/main/res/drawable/ic_logout.xml new file mode 100644 index 0000000000000000000000000000000000000000..cbfb43d8f6e52c2f97efdc1c6f041448b06c27fd --- /dev/null +++ b/app/src/main/res/drawable/ic_logout.xml @@ -0,0 +1,27 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M10,8v-2a2,2 0,0 1,2 -2h7a2,2 0,0 1,2 2v12a2,2 0,0 1,-2 2h-7a2,2 0,0 1,-2 -2v-2" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M15,12h-12l3,-3" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M6,15l-3,-3" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> +</vector> diff --git a/app/src/main/res/drawable/ic_map_pin.xml b/app/src/main/res/drawable/ic_map_pin.xml new file mode 100644 index 0000000000000000000000000000000000000000..1947e4a0464dce49de67c1893dced1a6bdada11a --- /dev/null +++ b/app/src/main/res/drawable/ic_map_pin.xml @@ -0,0 +1,20 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M9,11a3,3 0,1 0,6 0a3,3 0,0 0,-6 0" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M17.657,16.657l-4.243,4.243a2,2 0,0 1,-2.827 0l-4.244,-4.243a8,8 0,1 1,11.314 0z" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> +</vector> diff --git a/app/src/main/res/drawable/ic_minus.xml b/app/src/main/res/drawable/ic_minus.xml new file mode 100644 index 0000000000000000000000000000000000000000..3bab21bf899a34556319b00a2fb113e21dd773ed --- /dev/null +++ b/app/src/main/res/drawable/ic_minus.xml @@ -0,0 +1,13 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M5,12l14,0" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> +</vector> diff --git a/app/src/main/res/drawable/ic_outline_warning.xml b/app/src/main/res/drawable/ic_outline_warning.xml new file mode 100644 index 0000000000000000000000000000000000000000..548274ae75dedb4d9eb32e6b883271213cd6439d --- /dev/null +++ b/app/src/main/res/drawable/ic_outline_warning.xml @@ -0,0 +1,5 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="@color/rose_200" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp"> + + <path android:fillColor="@android:color/white" android:pathData="M1,21h22L12,2 1,21zM13,18h-2v-2h2v2zM13,14h-2v-4h2v4z"/> + +</vector> diff --git a/app/src/main/res/drawable/ic_plus.xml b/app/src/main/res/drawable/ic_plus.xml new file mode 100644 index 0000000000000000000000000000000000000000..6d879a7d36ce82f7bd38d68c05c7d046c84ed76c --- /dev/null +++ b/app/src/main/res/drawable/ic_plus.xml @@ -0,0 +1,20 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12,5l0,14" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M5,12l14,0" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> +</vector> diff --git a/app/src/main/res/drawable/ic_receipt.xml b/app/src/main/res/drawable/ic_receipt.xml new file mode 100644 index 0000000000000000000000000000000000000000..ec61dddfa6f229c3a91fdea9d17f81b3840f5390 --- /dev/null +++ b/app/src/main/res/drawable/ic_receipt.xml @@ -0,0 +1,13 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M5,21v-16a2,2 0,0 1,2 -2h10a2,2 0,0 1,2 2v16l-3,-2l-2,2l-2,-2l-2,2l-2,-2l-3,2m4,-14h6m-6,4h6m-2,4h2" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> +</vector> diff --git a/app/src/main/res/drawable/ic_rotate_clockwise.xml b/app/src/main/res/drawable/ic_rotate_clockwise.xml new file mode 100644 index 0000000000000000000000000000000000000000..e96dee86c1357cc88726e8a89ed1c3d740d2a858 --- /dev/null +++ b/app/src/main/res/drawable/ic_rotate_clockwise.xml @@ -0,0 +1,13 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M4.05,11a8,8 0,1 1,0.5 4m-0.5,5v-5h5" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> +</vector> diff --git a/app/src/main/res/drawable/ic_router_off.xml b/app/src/main/res/drawable/ic_router_off.xml new file mode 100644 index 0000000000000000000000000000000000000000..ddff828271777fd172a2863fdbd00e8438020677 --- /dev/null +++ b/app/src/main/res/drawable/ic_router_off.xml @@ -0,0 +1,48 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M17,13h2a2,2 0,0 1,2 2v2m-0.588,3.417c-0.362,0.36 -0.861,0.583 -1.412,0.583h-14a2,2 0,0 1,-2 -2v-4a2,2 0,0 1,2 -2h8" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M17,17v0.01" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M13,17v0.01" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M12.226,8.2a4,4 0,0 1,6.024 0.55" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M9.445,5.407a8,8 0,0 1,12.055 1.093" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M3,3l18,18" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> +</vector> diff --git a/app/src/main/res/drawable/ic_scan.xml b/app/src/main/res/drawable/ic_scan.xml new file mode 100644 index 0000000000000000000000000000000000000000..a057ed6c7840a93e27bc59bf013f4c585d8ec828 --- /dev/null +++ b/app/src/main/res/drawable/ic_scan.xml @@ -0,0 +1,41 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M4,7v-1a2,2 0,0 1,2 -2h2" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M4,17v1a2,2 0,0 0,2 2h2" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M16,4h2a2,2 0,0 1,2 2v1" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M16,20h2a2,2 0,0 0,2 -2v-1" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M5,12l14,0" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> +</vector> diff --git a/app/src/main/res/drawable/ic_send.xml b/app/src/main/res/drawable/ic_send.xml new file mode 100644 index 0000000000000000000000000000000000000000..8020df522bf09046ba7fd313b7d3a60ef55876e5 --- /dev/null +++ b/app/src/main/res/drawable/ic_send.xml @@ -0,0 +1,20 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M10,14l11,-11" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M21,3l-6.5,18a0.55,0.55 0,0 1,-1 0l-3.5,-7l-7,-3.5a0.55,0.55 0,0 1,0 -1l18,-6.5" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> +</vector> diff --git a/app/src/main/res/drawable/ic_settings.xml b/app/src/main/res/drawable/ic_settings.xml new file mode 100644 index 0000000000000000000000000000000000000000..31e74e834d0a9bda1e60c088c197c4c2e9bc6b72 --- /dev/null +++ b/app/src/main/res/drawable/ic_settings.xml @@ -0,0 +1,20 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M10.325,4.317c0.426,-1.756 2.924,-1.756 3.35,0a1.724,1.724 0,0 0,2.573 1.066c1.543,-0.94 3.31,0.826 2.37,2.37a1.724,1.724 0,0 0,1.065 2.572c1.756,0.426 1.756,2.924 0,3.35a1.724,1.724 0,0 0,-1.066 2.573c0.94,1.543 -0.826,3.31 -2.37,2.37a1.724,1.724 0,0 0,-2.572 1.065c-0.426,1.756 -2.924,1.756 -3.35,0a1.724,1.724 0,0 0,-2.573 -1.066c-1.543,0.94 -3.31,-0.826 -2.37,-2.37a1.724,1.724 0,0 0,-1.065 -2.572c-1.756,-0.426 -1.756,-2.924 0,-3.35a1.724,1.724 0,0 0,1.066 -2.573c-0.94,-1.543 0.826,-3.31 2.37,-2.37c1,0.608 2.296,0.07 2.572,-1.065z" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M9,12a3,3 0,1 0,6 0a3,3 0,0 0,-6 0" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> +</vector> diff --git a/app/src/main/res/drawable/ic_shopping_bag_minus.xml b/app/src/main/res/drawable/ic_shopping_bag_minus.xml new file mode 100644 index 0000000000000000000000000000000000000000..7234614980055e9bb5593760a33cfed52a54e0b3 --- /dev/null +++ b/app/src/main/res/drawable/ic_shopping_bag_minus.xml @@ -0,0 +1,27 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M12.5,21h-3.926a3,3 0,0 1,-2.965 -2.544l-1.255,-8.152a2,2 0,0 1,1.977 -2.304h11.339a2,2 0,0 1,1.977 2.304l-0.73,4.744" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M9,11v-5a3,3 0,0 1,6 0v5" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M16,19h6" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> +</vector> diff --git a/app/src/main/res/drawable/ic_trash_x.xml b/app/src/main/res/drawable/ic_trash_x.xml new file mode 100644 index 0000000000000000000000000000000000000000..ad38327da36eeecc16c5a3ad72a3b2c716f47cbf --- /dev/null +++ b/app/src/main/res/drawable/ic_trash_x.xml @@ -0,0 +1,34 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M4,7h16" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M5,7l1,12a2,2 0,0 0,2 2h8a2,2 0,0 0,2 -2l1,-12" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M9,7v-3a1,1 0,0 1,1 -1h4a1,1 0,0 1,1 1v3" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> + <path + android:pathData="M10,12l4,4m0,-4l-4,4" + android:strokeLineJoin="round" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="@color/white" + android:strokeLineCap="round"/> +</vector> diff --git a/app/src/main/res/drawable/shape_date_input_divider.xml b/app/src/main/res/drawable/shape_date_input_divider.xml new file mode 100644 index 0000000000000000000000000000000000000000..d431a7e3fe17bc455d40f2acf40d521e8c62ac2e --- /dev/null +++ b/app/src/main/res/drawable/shape_date_input_divider.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <solid android:color="@android:color/transparent" /> + <size android:height="2dp"/> +</shape> diff --git a/app/src/main/res/drawable/shape_form_divider.xml b/app/src/main/res/drawable/shape_form_divider.xml new file mode 100644 index 0000000000000000000000000000000000000000..77b918bd22e3c33be5ed3da8a4f2a3a0979b1590 --- /dev/null +++ b/app/src/main/res/drawable/shape_form_divider.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <solid android:color="@android:color/transparent" /> + <size android:height="20dp"/> +</shape> \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_recycler_view_divider.xml b/app/src/main/res/drawable/shape_recycler_view_divider.xml new file mode 100644 index 0000000000000000000000000000000000000000..cda49de23368d92ff8a3ac587f6a4451aef14535 --- /dev/null +++ b/app/src/main/res/drawable/shape_recycler_view_divider.xml @@ -0,0 +1,5 @@ +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid android:color="@color/zinc_700"/> + <size android:height="1dp"/> +</shape> \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_round_corners.xml b/app/src/main/res/drawable/shape_round_corners.xml new file mode 100644 index 0000000000000000000000000000000000000000..ff75ea001b5c3fb49f7dec05d55e2573ddd389b1 --- /dev/null +++ b/app/src/main/res/drawable/shape_round_corners.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <corners android:radius="5dp" /> + <solid android:color="@color/zinc_800"/> +</shape> diff --git a/app/src/main/res/drawable/shape_text_input_cursor.xml b/app/src/main/res/drawable/shape_text_input_cursor.xml new file mode 100644 index 0000000000000000000000000000000000000000..bdd9b372c1e83d17b7bcc4a67fc9af59c035169b --- /dev/null +++ b/app/src/main/res/drawable/shape_text_input_cursor.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" > + <size android:width="1dp" /> + <solid android:color="@color/teal_200"/> +</shape> \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_text_input_divider.xml b/app/src/main/res/drawable/shape_text_input_divider.xml new file mode 100644 index 0000000000000000000000000000000000000000..200b43e0371796b89de29f4e9a4859f2dd07950d --- /dev/null +++ b/app/src/main/res/drawable/shape_text_input_divider.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <solid android:color="@android:color/transparent" /> + <size android:height="5dp"/> +</shape> \ No newline at end of file diff --git a/app/src/main/res/drawable/twibbon.png b/app/src/main/res/drawable/twibbon.png new file mode 100644 index 0000000000000000000000000000000000000000..7cb57f3e7dcb779a44e037200e9b69128742dd57 Binary files /dev/null and b/app/src/main/res/drawable/twibbon.png differ diff --git a/app/src/main/res/font/inter.xml b/app/src/main/res/font/inter.xml new file mode 100644 index 0000000000000000000000000000000000000000..71311f0a995421988fa6cdeb43ec1774346368d9 --- /dev/null +++ b/app/src/main/res/font/inter.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<font-family xmlns:app="http://schemas.android.com/apk/res-auto" + app:fontProviderAuthority="com.google.android.gms.fonts" + app:fontProviderPackage="com.google.android.gms" + app:fontProviderQuery="Inter" + app:fontProviderCerts="@array/com_google_android_gms_fonts_certs"> +</font-family> diff --git a/app/src/main/res/font/inter_bold.xml b/app/src/main/res/font/inter_bold.xml new file mode 100644 index 0000000000000000000000000000000000000000..fc40af7997622bac4316bdaf40173e36b2866af0 --- /dev/null +++ b/app/src/main/res/font/inter_bold.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<font-family xmlns:app="http://schemas.android.com/apk/res-auto" + app:fontProviderAuthority="com.google.android.gms.fonts" + app:fontProviderPackage="com.google.android.gms" + app:fontProviderQuery="name=Inter&weight=700" + app:fontProviderCerts="@array/com_google_android_gms_fonts_certs"> +</font-family> diff --git a/app/src/main/res/font/inter_medium.xml b/app/src/main/res/font/inter_medium.xml new file mode 100644 index 0000000000000000000000000000000000000000..70a4c867bec901832195d21fa87c7158c491cb52 --- /dev/null +++ b/app/src/main/res/font/inter_medium.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<font-family xmlns:app="http://schemas.android.com/apk/res-auto" + app:fontProviderAuthority="com.google.android.gms.fonts" + app:fontProviderPackage="com.google.android.gms" + app:fontProviderQuery="name=Inter&weight=500" + app:fontProviderCerts="@array/com_google_android_gms_fonts_certs"> +</font-family> diff --git a/app/src/main/res/font/inter_thin.xml b/app/src/main/res/font/inter_thin.xml new file mode 100644 index 0000000000000000000000000000000000000000..aeaeebade9007d76a69f2fd7ea2d0777db3036b6 --- /dev/null +++ b/app/src/main/res/font/inter_thin.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<font-family xmlns:app="http://schemas.android.com/apk/res-auto" + app:fontProviderAuthority="com.google.android.gms.fonts" + app:fontProviderPackage="com.google.android.gms" + app:fontProviderQuery="name=Inter&weight=100" + app:fontProviderCerts="@array/com_google_android_gms_fonts_certs"> +</font-family> diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml new file mode 100644 index 0000000000000000000000000000000000000000..f9097bfa1027d483ee30aaea5876ab83d4d170db --- /dev/null +++ b/app/src/main/res/layout/activity_login.xml @@ -0,0 +1,86 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/login_layout" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/bg_main" + tools:context=".views.activities.LoginActivity"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@drawable/bg_dot_grid" + tools:layout_editor_absoluteX="0dp" + tools:layout_editor_absoluteY="-27dp"> + + <com.google.android.material.textfield.TextInputEditText + android:id="@+id/email_text_input" + android:layout_width="317dp" + android:layout_height="48dp" + android:layout_marginBottom="20dp" + android:background="@drawable/bg_full_border" + android:hint="@string/placeholder_email_form" + android:padding="10dp" + android:textAppearance="@style/TextAppearance.Login.TextInput" + android:textColor="@color/zinc_300" + android:textColorHint="@color/zinc_500" + android:textCursorDrawable="@drawable/shape_text_input_cursor" + android:theme="@style/transactionFormFieldBackground" + app:layout_constraintBottom_toTopOf="@+id/password_text_input_layout" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" /> + + <com.google.android.material.textfield.TextInputLayout + android:id="@+id/password_text_input_layout" + android:layout_width="317dp" + android:layout_height="48dp" + android:textAppearance="@style/TextAppearance.Login.TextInput" + android:textCursorDrawable="@drawable/shape_text_input_cursor" + android:theme="@style/transactionFormFieldBackground" + app:boxStrokeWidth="0dp" + app:boxStrokeWidthFocused="0dp" + app:hintEnabled="false" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:passwordToggleEnabled="true"> + + <com.google.android.material.textfield.TextInputEditText + android:id="@+id/password_text_input" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@drawable/bg_full_border" + android:hint="@string/placeholder_password_form" + android:paddingStart="10dp" + android:paddingTop="10dp" + android:paddingEnd="0dp" + android:textColor="@color/zinc_300" + android:textColorHint="@color/zinc_500" + android:textCursorDrawable="@drawable/shape_text_input_cursor" + android:theme="@style/transactionFormFieldBackground" /> + </com.google.android.material.textfield.TextInputLayout> + + <com.google.android.material.button.MaterialButton + android:id="@+id/login_button" + android:layout_width="317dp" + android:layout_height="48dp" + android:layout_marginTop="20dp" + android:backgroundTint="@color/teal_200" + android:gravity="center" + android:insetTop="0dp" + android:insetBottom="0dp" + android:outlineSpotShadowColor="@android:color/transparent" + android:text="@string/text_login_button" + android:textAppearance="@style/TextAppearance.Login.LoginButton" + android:textColor="@color/zinc_900" + app:cornerRadius="5dp" + app:iconGravity="textStart" + app:iconTint="@color/zinc_900" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/password_text_input_layout" /> + </androidx.constraintlayout.widget.ConstraintLayout> +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 86a5d977951ccdfedf6e21f7fe07f1ac54b3b2ce..9d643d617cc56076fc61161834b60e9404572743 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -5,15 +5,85 @@ android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".MainActivity"> + android:background="@color/bg_main" + tools:context=".views.activities.MainActivity"> + + <ImageButton + android:id="@+id/back_button" + android:layout_width="30dp" + android:layout_height="30dp" + android:layout_marginStart="20dp" + android:layout_marginEnd="20dp" + android:background="@android:color/transparent" + android:contentDescription="@string/desc_back_button" + android:scaleType="fitCenter" + android:src="@drawable/ic_arrow_left" + android:visibility="gone" + app:layout_constraintBottom_toTopOf="@id/main_guideline1" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:tint="@color/zinc_300" /> <TextView + android:id="@+id/header_text" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="Hello World!" - app:layout_constraintBottom_toBottomOf="parent" + android:layout_marginStart="15dp" + android:text="@string/dummy_page_title" + android:theme="@style/TextAppearance.Header" + app:layout_constraintBottom_toTopOf="@id/main_guideline1" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" + app:layout_constraintHorizontal_bias="0" + app:layout_constraintStart_toEndOf="@id/back_button" app:layout_constraintTop_toTopOf="parent" /> -</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file + <androidx.fragment.app.FragmentContainerView + android:id="@+id/fragment_container" + android:name="androidx.navigation.fragment.NavHostFragment" + android:layout_width="match_parent" + android:layout_height="0dp" + android:visibility="visible" + app:defaultNavHost="true" + app:layout_constraintBottom_toTopOf="@id/navbar_container" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintTop_toBottomOf="@id/main_guideline1" + app:navGraph="@navigation/nav_graph" /> + + + <LinearLayout + android:id="@+id/navbar_container" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent"> + + <com.google.android.material.bottomnavigation.BottomNavigationView + android:id="@+id/navbar" + android:layout_width="match_parent" + android:layout_height="75dp" + android:background="@drawable/bg_full_border" + android:theme="@style/zinc700TopBorderBackground" + app:itemActiveIndicatorStyle="@android:color/transparent" + app:itemBackground="@android:color/transparent" + app:itemIconTint="@color/bnv_tab_item" + app:itemTextColor="@color/bnv_tab_item" + app:labelVisibilityMode="unlabeled" + app:menu="@menu/menu_main" /> + </LinearLayout> + + <eightbitlab.com.blurview.BlurView + android:id="@+id/dialog_parent" + android:layout_width="match_parent" + android:layout_height="match_parent" + app:blurOverlayColor="@color/blur" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/main_guideline1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + app:layout_constraintGuide_begin="80dp" /> + +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/layout/component_blur.xml b/app/src/main/res/layout/component_blur.xml new file mode 100644 index 0000000000000000000000000000000000000000..37408b9fe54a67cb321a6c5633fb13b8b0be7609 --- /dev/null +++ b/app/src/main/res/layout/component_blur.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<eightbitlab.com.blurview.BlurView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/blur_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + app:blurOverlayColor="@color/blur"> +</eightbitlab.com.blurview.BlurView> \ No newline at end of file diff --git a/app/src/main/res/layout/component_date_input.xml b/app/src/main/res/layout/component_date_input.xml new file mode 100644 index 0000000000000000000000000000000000000000..3fac846827b040d7121cf31f321d909b0572fadd --- /dev/null +++ b/app/src/main/res/layout/component_date_input.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:background="@color/bg_main" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:orientation="vertical" + android:divider="@drawable/shape_date_input_divider" + android:showDividers="middle"> + <TextView + android:id="@+id/date_input_component_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.TransactionForm.Input" + android:text="@string/dummy_text_input_label"/> + <com.google.android.material.button.MaterialButton + android:id="@+id/date_input_button" + android:backgroundTint="@color/zinc_700" + android:layout_width="match_parent" + android:layout_height="39dp" + android:insetBottom="0dp" + android:insetTop="0dp" + android:paddingHorizontal="10dp" + android:text="@string/placeholder_date_picker" + android:textAppearance="@style/TextAppearance.TransactionForm.Input" + android:textColor="@color/zinc_300" + app:cornerRadius="5dp" + android:textAlignment="textStart" /> +</LinearLayout> diff --git a/app/src/main/res/layout/component_delete_transaction_dialog.xml b/app/src/main/res/layout/component_delete_transaction_dialog.xml new file mode 100644 index 0000000000000000000000000000000000000000..efd3e8c365092e7f78e5e04d6899b88f4daa38aa --- /dev/null +++ b/app/src/main/res/layout/component_delete_transaction_dialog.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<com.example.bondoman.views.components.DialogComponent 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" + app:dialogFirstButtonType="neutral" + app:dialogSecondButtonType="negative" + app:dialogText="@string/text_delete_transaction_dialog" + app:firstButtonText="@string/text_first_button_delete_transaction_dialog" + app:secondButtonText="@string/text_second_button_delete_transaction_dialog"></com.example.bondoman.views.components.DialogComponent> diff --git a/app/src/main/res/layout/component_dialog.xml b/app/src/main/res/layout/component_dialog.xml new file mode 100644 index 0000000000000000000000000000000000000000..0823e4a495dbae8cce19df0ea76c2536e51e4712 --- /dev/null +++ b/app/src/main/res/layout/component_dialog.xml @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <androidx.constraintlayout.widget.ConstraintLayout + android:theme="@style/dialogBackground" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginHorizontal="20dp" + android:padding="20dp" + android:background="@drawable/bg_full_border" + app:layout_constraintWidth_max="350dp" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toTopOf="parent"> + <TextView + android:id="@+id/dialog_text" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:text="@string/dummy_dialog_text" + android:textAppearance="@style/TextAppearance.Dialog" + android:textColor="@color/white" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toTopOf="@id/dialog_guideline" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toRightOf="parent"/> + <com.google.android.material.button.MaterialButton + android:id="@+id/dialog_first_button" + android:layout_marginRight="5dp" + android:layout_width="0dp" + android:layout_height="39dp" + android:insetTop="0dp" + android:insetBottom="0dp" + android:backgroundTint="@color/zinc_600" + android:text="@string/dummy_dialog_first_button" + android:textAppearance="@style/TextAppearance.Dialog" + android:paddingHorizontal="6dp" + app:cornerRadius="5dp" + app:layout_constraintVertical_bias="1" + app:layout_constraintTop_toBottomOf="@id/dialog_guideline" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toLeftOf="@id/dialog_second_button" + app:layout_constraintBottom_toBottomOf="parent"/> + <com.google.android.material.button.MaterialButton + android:id="@+id/dialog_second_button" + android:layout_marginLeft="5dp" + android:layout_width="0dp" + android:layout_height="39dp" + android:insetTop="0dp" + android:insetBottom="0dp" + android:backgroundTint="@color/teal_200" + android:text="@string/dummy_dialog_second_button" + android:textAppearance="@style/TextAppearance.Dialog" + android:textColor="@color/zinc_900" + android:paddingHorizontal="6dp" + app:cornerRadius="5dp" + app:layout_constraintVertical_bias="1" + app:layout_constraintTop_toBottomOf="@id/dialog_guideline" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintLeft_toRightOf="@id/dialog_first_button" + app:layout_constraintBottom_toBottomOf="parent"/> + <androidx.constraintlayout.widget.Guideline + android:id="@+id/dialog_guideline" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + app:layout_constraintGuide_end="59dp"/> + </androidx.constraintlayout.widget.ConstraintLayout> +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/layout/component_dropdown_item.xml b/app/src/main/res/layout/component_dropdown_item.xml new file mode 100644 index 0000000000000000000000000000000000000000..a7d4c0cca40d4f3c3e7820955f7cfd0d6595ac80 --- /dev/null +++ b/app/src/main/res/layout/component_dropdown_item.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/textViewFeelings" + android:layout_width="match_parent" + android:layout_height="39dp" + android:background="@color/bg_main" + android:gravity="center_vertical" + android:paddingHorizontal="10dp" + android:text="@string/dummy_dropdown_item" + android:theme="@style/TextAppearance.TransactionForm.Input" /> diff --git a/app/src/main/res/layout/component_page_info.xml b/app/src/main/res/layout/component_page_info.xml new file mode 100644 index 0000000000000000000000000000000000000000..7d0be165e43610b211319945ba9a422ce882c51d --- /dev/null +++ b/app/src/main/res/layout/component_page_info.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/bg_main"> + + <ImageView + android:id="@+id/page_info_icon" + android:layout_width="80dp" + android:layout_height="80dp" + android:contentDescription="@string/desc_page_info_icon" + android:src="@drawable/ic_router_off" + app:layout_constraintBottom_toTopOf="@id/page_info_guideline" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="1" + app:tint="@color/zinc_300" /> + + <TextView + android:id="@+id/page_info_text" + android:layout_width="256dp" + android:layout_height="wrap_content" + android:layout_marginTop="20dp" + android:text="@string/dummy_page_info_text" + android:textAlignment="center" + android:theme="@style/TextAppearance.PageInfo" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/page_info_guideline" + app:layout_constraintVertical_bias="0" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/page_info_guideline" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + app:layout_constraintGuide_percent="0.5" /> +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/layout/component_scan_receipt_item_card.xml b/app/src/main/res/layout/component_scan_receipt_item_card.xml new file mode 100644 index 0000000000000000000000000000000000000000..c5e3ac173a9285d01b4ed07597288c85f7d96350 --- /dev/null +++ b/app/src/main/res/layout/component_scan_receipt_item_card.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingVertical="10dp"> + + <TextView + android:id="@+id/scan_receipt_item_name" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:ellipsize="end" + android:maxLines="1" + android:textSize="16sp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintWidth_percent="0.50" /> + + <TextView + android:id="@+id/scan_receipt_item_price" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:ellipsize="end" + android:maxLines="1" + android:textAlignment="textEnd" + android:textSize="16sp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintWidth_percent="0.40" /> + +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/layout/component_setting_button.xml b/app/src/main/res/layout/component_setting_button.xml new file mode 100644 index 0000000000000000000000000000000000000000..6377113128a4569312606c430535cda24f223941 --- /dev/null +++ b/app/src/main/res/layout/component_setting_button.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/setting_button_layout" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:padding="20dp" + android:theme="@style/TextAppearance.SettingButton.Subtitle"> + + <ImageView + android:id="@+id/setting_button_icon" + android:layout_width="30dp" + android:layout_height="30dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:srcCompat="@drawable/ic_file_export" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/setting_button_guideline" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + app:layout_constraintGuide_begin="40dp" /> + + <TextView + android:id="@+id/setting_button_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:ellipsize="end" + android:maxLines="1" + android:text="@string/dummy_setting_button_title" + android:theme="@style/TextAppearance.SettingButton.Title" + app:layout_constraintStart_toStartOf="@+id/setting_button_guideline" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@+id/setting_button_subtitle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="2dp" + android:ellipsize="end" + android:maxLines="2" + android:text="@string/dummy_setting_button_subtitle" + android:theme="@style/TextAppearance.SettingButton.Subtitle" + app:layout_constraintStart_toStartOf="@+id/setting_button_guideline" + app:layout_constraintTop_toBottomOf="@+id/setting_button_title" /> + +</androidx.constraintlayout.widget.ConstraintLayout> + + diff --git a/app/src/main/res/layout/component_text_input.xml b/app/src/main/res/layout/component_text_input.xml new file mode 100644 index 0000000000000000000000000000000000000000..76a089a87d7e7542f16ae73783f2a650870eab9d --- /dev/null +++ b/app/src/main/res/layout/component_text_input.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:divider="@drawable/shape_text_input_divider" + android:orientation="vertical" + android:showDividers="middle"> + + <TextView + android:id="@+id/text_input_component_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/dummy_text_input_label" + android:textAppearance="@style/TextAppearance.TransactionForm.Input" /> + + <com.google.android.material.textfield.TextInputEditText + android:id="@+id/text_input_component_field" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@drawable/bg_full_border" + android:hint="@string/dummy_input_text_hint" + android:inputType="text" + android:padding="10dp" + android:textAppearance="@style/TextAppearance.TransactionForm.Input" + android:textColor="@color/zinc_300" + android:textColorHint="@color/zinc_500" + android:textCursorDrawable="@drawable/shape_text_input_cursor" + android:theme="@style/transactionFormFieldBackground" /> +</LinearLayout> diff --git a/app/src/main/res/layout/component_toast.xml b/app/src/main/res/layout/component_toast.xml new file mode 100644 index 0000000000000000000000000000000000000000..f18cc094bd321a3684053dbf31c5a0f4a013ca58 --- /dev/null +++ b/app/src/main/res/layout/component_toast.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/toast" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="@drawable/shape_round_corners" + android:gravity="center" + android:orientation="horizontal" + android:padding="10dp"> + + + <ImageView + android:id="@+id/toast_icon" + android:layout_width="20dp" + android:layout_height="20dp" + android:layout_marginEnd="10dp" + android:contentDescription="@string/toast_icon" + android:src="@drawable/ic_outline_warning" + app:tint="@color/rose_200" /> + + <TextView + android:id="@+id/toast_message" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textColor="@color/zinc_300" /> + + +</LinearLayout> diff --git a/app/src/main/res/layout/component_transaction_card.xml b/app/src/main/res/layout/component_transaction_card.xml new file mode 100644 index 0000000000000000000000000000000000000000..ac2340e68faab3e9641409dfd1caf02e9e377708 --- /dev/null +++ b/app/src/main/res/layout/component_transaction_card.xml @@ -0,0 +1,118 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="120dp" + android:layout_marginBottom="1dp" + android:background="@color/bg_main" + android:padding="20dp"> + + <androidx.cardview.widget.CardView + android:id="@+id/transaction_icon_card" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:cardCornerRadius="10dp" + app:cardElevation="0dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintTop_toTopOf="parent"> + + <ImageView + android:id="@+id/transaction_icon_image" + android:layout_width="40dp" + android:layout_height="40dp" + android:background="@color/rose_200" + android:contentDescription="@string/desc_transaction_icon" + android:padding="5dp" + android:scaleType="fitCenter" + android:src="@drawable/ic_shopping_bag_minus" + app:tint="@color/zinc_700" /> + </androidx.cardview.widget.CardView> + + <TextView + android:id="@+id/date_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/dummy_transaction_date" + android:theme="@style/TextAppearance.Transaction.Date" + app:layout_constraintBottom_toTopOf="@id/transaction_card_guideline2" + app:layout_constraintLeft_toRightOf="@id/transaction_card_guideline1" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@+id/amount_text" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginLeft="20dp" + android:ellipsize="end" + android:maxLines="1" + android:text="@string/dummy_transaction_amount" + android:textAlignment="textEnd" + android:theme="@style/TextAppearance.Transaction.Amount" + app:layout_constraintBottom_toTopOf="@id/transaction_card_guideline2" + app:layout_constraintHorizontal_bias="1" + app:layout_constraintLeft_toRightOf="@id/date_text" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@+id/title_text" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:ellipsize="end" + android:maxLines="1" + android:text="@string/dummy_transaction_title" + android:theme="@style/TextAppearance.Transaction.Title" + app:layout_constraintBottom_toTopOf="@id/transaction_card_guideline3" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0" + app:layout_constraintStart_toEndOf="@id/transaction_card_guideline1" + app:layout_constraintTop_toBottomOf="@id/transaction_card_guideline2" /> + + <ImageView + android:id="@+id/transaction_location_icon_image" + android:layout_width="18dp" + android:layout_height="18dp" + android:contentDescription="@string/desc_location_icon" + android:scaleType="fitCenter" + android:src="@drawable/ic_map_pin" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toEndOf="@id/transaction_card_guideline1" + app:layout_constraintTop_toBottomOf="@id/transaction_card_guideline3" + app:tint="@color/zinc_400" /> + + <TextView + android:id="@+id/transaction_location_text" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="5dp" + android:ellipsize="end" + android:maxLines="1" + android:text="@string/dummy_transaction_location" + android:theme="@style/TextAppearance.Transaction.Location" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/transaction_location_icon_image" + app:layout_constraintTop_toBottomOf="@id/transaction_card_guideline3" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/transaction_card_guideline1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + app:layout_constraintGuide_begin="60dp" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/transaction_card_guideline2" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + app:layout_constraintGuide_percent="0.33" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/transaction_card_guideline3" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + app:layout_constraintGuide_percent="0.66" /> +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/layout/component_transaction_detail.xml b/app/src/main/res/layout/component_transaction_detail.xml new file mode 100644 index 0000000000000000000000000000000000000000..b0722d52fb231eaee48174df21c9992170bae70f --- /dev/null +++ b/app/src/main/res/layout/component_transaction_detail.xml @@ -0,0 +1,238 @@ +<?xml version="1.0" encoding="utf-8"?> +<eightbitlab.com.blurview.BlurView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/blur_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + app:blurOverlayColor="@color/blur"> + + <androidx.coordinatorlayout.widget.CoordinatorLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@android:color/transparent"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:id="@+id/transaction_detail_sheet" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@drawable/bg_full_border" + android:minHeight="167dp" + android:theme="@style/transactionDetailTopBorderBackground" + app:behavior_hideable="true" + app:layout_behavior="@string/bottom_sheet_behavior"> + + <androidx.appcompat.widget.AppCompatButton + android:id="@+id/transaction_detail_sheet_button" + android:layout_width="53dp" + android:layout_height="6dp" + android:layout_marginTop="20dp" + android:background="@drawable/bg_full_border" + android:minWidth="0dp" + android:minHeight="0dp" + android:theme="@style/transactionDetailSwipeButtonBackground" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <androidx.cardview.widget.CardView + android:id="@+id/transaction_detail_icon_card" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:cardCornerRadius="10dp" + app:cardElevation="0dp" + app:layout_constraintLeft_toRightOf="@id/transaction_detail_guideline5" + app:layout_constraintRight_toLeftOf="@id/transaction_detail_guideline6" + app:layout_constraintTop_toBottomOf="@id/transaction_detail_guideline1"> + + <ImageView + android:id="@+id/transaction_detail_icon_image" + android:layout_width="40dp" + android:layout_height="40dp" + android:background="@color/rose_200" + android:contentDescription="@string/desc_transaction_icon" + android:padding="5dp" + android:scaleType="fitCenter" + android:src="@drawable/ic_shopping_bag_minus" + app:tint="@color/zinc_700" /> + </androidx.cardview.widget.CardView> + + <androidx.cardview.widget.CardView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:cardCornerRadius="4dp" + app:layout_constraintRight_toLeftOf="@id/transaction_detail_guideline8" + app:layout_constraintTop_toBottomOf="@id/transaction_detail_guideline1"> + + <TextView + android:id="@+id/transaction_detail_category_text" + android:layout_width="wrap_content" + android:layout_height="19dp" + android:background="@color/rose_200" + android:gravity="center" + android:paddingHorizontal="6dp" + android:text="@string/dummy_transaction_category" + android:theme="@style/TextAppearance.TransactionDetail.Category" /> + </androidx.cardview.widget.CardView> + + <TextView + android:id="@+id/transaction_detail_date" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:text="@string/dummy_transaction_date" + android:theme="@style/TextAppearance.Transaction.Date" + app:layout_constraintBottom_toTopOf="@id/transaction_detail_guideline2" + app:layout_constraintLeft_toRightOf="@id/transaction_detail_guideline7" + app:layout_constraintTop_toBottomOf="@id/transaction_detail_guideline1" /> + + <TextView + android:id="@+id/transaction_detail_title" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginTop="10dp" + android:text="@string/dummy_transaction_title" + android:theme="@style/TextAppearance.Transaction.Title" + app:layout_constraintEnd_toStartOf="@id/transaction_detail_guideline8" + app:layout_constraintStart_toEndOf="@id/transaction_detail_guideline7" + app:layout_constraintTop_toBottomOf="@id/transaction_detail_guideline2" /> + + <TextView + android:id="@+id/transaction_detail_amount" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginVertical="10dp" + android:ellipsize="end" + android:maxLines="1" + android:text="@string/dummy_transaction_amount" + android:theme="@style/TextAppearance.Transaction.Amount" + app:layout_constraintBottom_toTopOf="@id/transaction_detail_guideline9" + app:layout_constraintEnd_toStartOf="@id/transaction_detail_guideline8" + app:layout_constraintStart_toEndOf="@id/transaction_detail_guideline7" + app:layout_constraintTop_toBottomOf="@id/transaction_detail_title" /> + + <ImageView + android:id="@+id/transaction_detail_location_icon_image" + android:layout_width="18dp" + android:layout_height="18dp" + android:contentDescription="@string/desc_location_icon" + android:scaleType="fitCenter" + android:src="@drawable/ic_map_pin" + app:layout_constraintBottom_toTopOf="@id/transaction_detail_guideline3" + app:layout_constraintStart_toEndOf="@id/transaction_detail_guideline7" + app:layout_constraintTop_toBottomOf="@id/transaction_detail_guideline9" + app:tint="@color/zinc_400" /> + + <TextView + android:id="@+id/transaction_detail_location_text" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="5dp" + android:ellipsize="end" + android:maxLines="1" + android:text="@string/dummy_transaction_location" + android:theme="@style/TextAppearance.Transaction.Location" + app:layout_constraintBottom_toTopOf="@id/transaction_detail_guideline3" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/transaction_detail_location_icon_image" + app:layout_constraintTop_toBottomOf="@id/transaction_detail_guideline9" /> + + <ImageButton + android:id="@+id/trasaction_detail_delete_button" + android:layout_width="44dp" + android:layout_height="44dp" + android:background="@drawable/bg_full_border" + android:contentDescription="@string/desc_delete_transaction" + android:padding="10dp" + android:scaleType="fitCenter" + android:src="@drawable/ic_trash_x" + android:theme="@style/deleteTransactionButtonBackground" + app:layout_constraintLeft_toRightOf="@id/transaction_detail_guideline5" + app:layout_constraintRight_toLeftOf="@id/transaction_detail_guideline6" + app:layout_constraintTop_toBottomOf="@id/transaction_detail_guideline4" + app:tint="@color/rose_200" /> + + <com.google.android.material.button.MaterialButton + android:id="@+id/trasaction_detail_edit_button" + android:layout_width="0dp" + android:layout_height="44dp" + android:backgroundTint="@color/teal_200" + android:gravity="center" + android:insetTop="0dp" + android:insetBottom="0dp" + android:outlineSpotShadowColor="@android:color/transparent" + android:text="@string/text_edit_button" + android:textAppearance="@style/TextAppearance.TransactionDetail.EditButton" + android:textColor="@color/zinc_900" + app:cornerRadius="5dp" + app:icon="@drawable/ic_edit" + app:iconGravity="textStart" + app:iconTint="@color/zinc_900" + app:layout_constraintLeft_toRightOf="@id/transaction_detail_guideline7" + app:layout_constraintRight_toLeftOf="@id/transaction_detail_guideline8" + app:layout_constraintTop_toBottomOf="@id/transaction_detail_guideline4" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/transaction_detail_guideline1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + app:layout_constraintGuide_begin="53dp" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/transaction_detail_guideline2" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + app:layout_constraintGuide_begin="72dp" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/transaction_detail_guideline9" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + app:layout_constraintGuide_end="111dp" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/transaction_detail_guideline3" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + app:layout_constraintGuide_end="93dp" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/transaction_detail_guideline4" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + app:layout_constraintGuide_end="66dp" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/transaction_detail_guideline5" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + app:layout_constraintGuide_begin="20dp" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/transaction_detail_guideline6" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + app:layout_constraintGuide_begin="64dp" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/transaction_detail_guideline7" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + app:layout_constraintGuide_begin="74dp" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/transaction_detail_guideline8" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + app:layout_constraintGuide_end="20dp" /> + </androidx.constraintlayout.widget.ConstraintLayout> + </androidx.coordinatorlayout.widget.CoordinatorLayout> +</eightbitlab.com.blurview.BlurView> diff --git a/app/src/main/res/layout/fragment_connection_lost.xml b/app/src/main/res/layout/fragment_connection_lost.xml new file mode 100644 index 0000000000000000000000000000000000000000..d6c8b33a02f7787ace5ff6ebd51d9561f9379d9f --- /dev/null +++ b/app/src/main/res/layout/fragment_connection_lost.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<com.example.bondoman.views.components.PageInfoComponent xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/graph_page_info_not_available" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:src="@drawable/ic_router_off" + app:pageInfoText="@string/text_page_info_connection_lost" /> diff --git a/app/src/main/res/layout/fragment_graph.xml b/app/src/main/res/layout/fragment_graph.xml new file mode 100644 index 0000000000000000000000000000000000000000..b6697d50be0cca80d4c013b620622d21c5756380 --- /dev/null +++ b/app/src/main/res/layout/fragment_graph.xml @@ -0,0 +1,167 @@ +<?xml version="1.0" encoding="utf-8"?> +<ScrollView 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"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@color/bg_main" + android:padding="20dp" + tools:context=".views.fragments.GraphFragment"> + + <com.example.bondoman.views.components.DateInputComponent + android:id="@+id/graph_date_begin_input" + android:layout_width="0dp" + android:layout_height="wrap_content" + app:dateInputLabel="@string/text_date_begin_label" + app:layout_constraintBottom_toTopOf="@id/graph_guideline_1" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toLeftOf="@id/graph_minus_icon" + app:layout_constraintTop_toTopOf="parent" /> + + <ImageView + android:id="@+id/graph_minus_icon" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_marginHorizontal="10dp" + android:contentDescription="@string/desc_separator_icon" + android:src="@drawable/ic_minus" + app:layout_constraintBottom_toTopOf="@id/graph_guideline_1" + app:layout_constraintLeft_toRightOf="@id/graph_date_begin_input" + app:layout_constraintRight_toLeftOf="@id/graph_date_end_input" + app:layout_constraintTop_toTopOf="parent" + app:tint="@color/zinc_300" /> + + <com.example.bondoman.views.components.DateInputComponent + android:id="@+id/graph_date_end_input" + android:layout_width="0dp" + android:layout_height="wrap_content" + app:dateInputLabel="@string/text_date_end_label" + app:layout_constraintBottom_toTopOf="@id/graph_guideline_1" + app:layout_constraintLeft_toRightOf="@id/graph_minus_icon" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <com.github.mikephil.charting.charts.PieChart + android:id="@+id/graph_pie_chart" + android:layout_width="0dp" + android:layout_height="0dp" + android:layout_marginVertical="50dp" + app:layout_constraintBottom_toTopOf="@id/graph_guideline_3" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintTop_toBottomOf="@id/graph_guideline_2" /> + + <com.example.bondoman.views.components.PageInfoComponent + android:id="@+id/graph_page_info_not_available" + android:layout_width="0dp" + android:layout_height="0dp" + android:src="@drawable/ic_chart_pie" + app:layout_constraintBottom_toTopOf="@id/graph_guideline_3" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintTop_toBottomOf="@id/graph_guideline_2" + app:pageInfoText="@string/text_page_info_graph_not_available" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="19dp" + android:background="@drawable/bg_full_border" + android:gravity="center" + android:paddingHorizontal="6dp" + android:paddingVertical="2dp" + android:text="@string/text_income_transaction_category" + android:textAppearance="@style/TextAppearance.TransactionDetail.Category" + android:theme="@style/incomeCategoryBackground" + app:layout_constraintBottom_toTopOf="@id/graph_guideline_4" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintTop_toBottomOf="@id/graph_guideline_3" /> + + <TextView + android:id="@+id/income_value_text" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:ellipsize="end" + android:maxLines="1" + android:text="@string/dummy_transaction_amount" + android:textAlignment="viewEnd" + android:textAppearance="@style/TextAppearance.Graph.Amount" + app:layout_constraintBottom_toTopOf="@id/graph_guideline_4" + app:layout_constraintLeft_toRightOf="@id/graph_guideline_6" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintTop_toBottomOf="@id/graph_guideline_3" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="19dp" + android:background="@drawable/bg_full_border" + android:gravity="center" + android:paddingHorizontal="6dp" + android:paddingVertical="2dp" + android:text="@string/text_outcome_transaction_category" + android:textAppearance="@style/TextAppearance.TransactionDetail.Category" + android:theme="@style/outcomeCategoryBackground" + app:layout_constraintBottom_toTopOf="@id/graph_guideline_5" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintTop_toBottomOf="@id/graph_guideline_4" /> + + <TextView + android:id="@+id/outcome_value_text" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:ellipsize="end" + android:maxLines="1" + android:text="@string/dummy_transaction_amount" + android:textAlignment="viewEnd" + android:textAppearance="@style/TextAppearance.Graph.Amount" + app:layout_constraintBottom_toTopOf="@id/graph_guideline_5" + app:layout_constraintLeft_toRightOf="@id/graph_guideline_6" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintTop_toBottomOf="@id/graph_guideline_4" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/graph_guideline_1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + app:layout_constraintGuide_begin="80dp" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/graph_guideline_2" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + app:layout_constraintGuide_begin="100dp" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/graph_guideline_3" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + app:layout_constraintGuide_begin="450dp" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/graph_guideline_4" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + app:layout_constraintGuide_begin="509dp" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/graph_guideline_5" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + app:layout_constraintGuide_begin="568dp" /> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/graph_guideline_6" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + app:layout_constraintGuide_begin="95dp" /> + </androidx.constraintlayout.widget.ConstraintLayout> +</ScrollView> diff --git a/app/src/main/res/layout/fragment_scan_receipt.xml b/app/src/main/res/layout/fragment_scan_receipt.xml new file mode 100644 index 0000000000000000000000000000000000000000..3afeffbdc42dad8e19f1368d0b46b11984f19009 --- /dev/null +++ b/app/src/main/res/layout/fragment_scan_receipt.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context=".views.fragments.ScanReceiptFragment"> + + <androidx.camera.view.PreviewView + android:id="@+id/camera_preview" + android:layout_width="match_parent" + android:layout_height="0dp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHeight_percent="0.85" + app:layout_constraintHorizontal_bias="0.0" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> + + </androidx.camera.view.PreviewView> + + <ProgressBar + android:id="@+id/scan_receipt_loading" + style="?android:attr/progressBarStyle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="invisible" + app:layout_constraintBottom_toBottomOf="@+id/camera_preview" + app:layout_constraintEnd_toEndOf="@+id/camera_preview" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="@+id/camera_preview" /> + + <ImageButton + android:id="@+id/receipt_capture_button" + android:layout_width="50dp" + android:layout_height="50dp" + android:background="@drawable/bg_full_border" + android:contentDescription="@string/capture" + android:elevation="2dp" + android:src="@drawable/ic_camera" + android:theme="@style/teal200RoundBackground" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/camera_preview" + app:tint="@color/zinc_800" /> + + <ImageButton + android:id="@+id/scan_receipt_button_pick_image_from_gallery" + android:layout_width="30dp" + android:layout_height="30dp" + android:layout_marginStart="20dp" + android:background="@drawable/bg_full_border" + android:contentDescription="@string/gallery" + android:elevation="2dp" + android:scaleType="fitCenter" + android:src="@drawable/ic_library_photo" + android:theme="@style/transparentRoundBackground" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/camera_preview" + app:tint="@color/zinc_300" /> + +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/layout/fragment_scan_receipt_result.xml b/app/src/main/res/layout/fragment_scan_receipt_result.xml new file mode 100644 index 0000000000000000000000000000000000000000..53cf12f2d1f85cd6d6172c7861dbd4e5d0132620 --- /dev/null +++ b/app/src/main/res/layout/fragment_scan_receipt_result.xml @@ -0,0 +1,162 @@ +<?xml version="1.0" encoding="utf-8"?> +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:fillViewport="true" + tools:context=".views.fragments.ScanReceiptResultFragment"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:id="@+id/scan_receipt_view_wrapper" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingHorizontal="15dp" + android:paddingBottom="20dp"> + + <com.google.android.material.imageview.ShapeableImageView + android:id="@+id/scan_receipt_image_result" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:adjustViewBounds="true" + android:contentDescription="@string/scan_receipt_image_result" + android:scaleType="centerCrop" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="1.0" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:shapeAppearanceOverlay="@style/imageRoundedCorner" /> + + <ProgressBar + android:id="@+id/scan_receipt_loading" + style="?android:attr/progressBarStyle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="gone" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <LinearLayout + android:id="@+id/scan_receipt_items" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="20dp" + android:divider="@drawable/shape_recycler_view_divider" + android:orientation="vertical" + android:showDividers="middle" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/scan_receipt_image_result" /> + + + <com.example.bondoman.views.components.TextInputComponent + android:id="@+id/scan_receipt_form_title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="20dp" + android:maxLength="50" + app:layout_constraintTop_toBottomOf="@id/scan_receipt_items" + app:textInputHint="@string/hint_transaction_form_title" + app:textInputLabel="Title" /> + + <com.example.bondoman.views.components.TextInputComponent + android:id="@+id/scan_receipt_form_amount" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="20dp" + android:inputType="number" + android:maxLength="18" + app:layout_constraintTop_toBottomOf="@id/scan_receipt_form_title" + app:textInputHint="@string/hint_transaction_form_amount" + app:textInputLabel="Amount (Rp)" /> + + <LinearLayout + android:id="@+id/scan_receipt_form_change_location" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="20dp" + android:divider="@drawable/shape_text_input_divider" + android:orientation="vertical" + android:showDividers="middle" + app:layout_constraintTop_toBottomOf="@id/scan_receipt_form_amount"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/label_transaction_form_location" + android:textAppearance="@style/TextAppearance.TransactionForm.Input" /> + + <com.google.android.material.textfield.TextInputEditText + android:id="@+id/scan_receipt_form_location_field" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@drawable/bg_full_border" + android:cursorVisible="false" + android:focusableInTouchMode="false" + android:hint="@string/hint_transaction_form_location" + android:imeOptions="actionDone" + android:inputType="text" + android:padding="10dp" + android:textAppearance="@style/TextAppearance.TransactionForm.Input" + android:textColor="@color/zinc_300" + android:textColorHint="@color/zinc_500" + android:theme="@style/transactionFormFieldBackground" /> + + <TextView + android:id="@+id/scan_receipt_form_change_location_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="end" + android:clickable="true" + android:text="@string/text_change_location" + android:theme="@style/TextAppearance.TransactionForm.ChangeLocationText" /> + </LinearLayout> + + <com.google.android.material.button.MaterialButton + android:id="@+id/scan_receipt_form_button_retake" + android:layout_width="0dp" + android:layout_height="44dp" + android:layout_marginTop="20dp" + android:backgroundTint="@color/zinc_600" + android:gravity="center" + android:insetTop="0dp" + android:insetBottom="0dp" + android:outlineSpotShadowColor="@android:color/transparent" + android:text="@string/scan_receipt_form_button_retry_label" + android:textAppearance="@style/TextAppearance.TransactionDetail.EditButton" + android:textColor="@color/zinc_300" + app:cornerRadius="5dp" + app:icon="@drawable/ic_rotate_clockwise" + app:iconGravity="textStart" + app:iconTint="@color/zinc_300" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintTop_toBottomOf="@id/scan_receipt_form_change_location" + app:layout_constraintWidth_percent=".48" /> + + <com.google.android.material.button.MaterialButton + android:id="@+id/scan_receipt_form_button_save" + android:layout_width="0dp" + android:layout_height="44dp" + android:layout_marginTop="20dp" + android:backgroundTint="@color/teal_200" + android:gravity="center" + android:insetTop="0dp" + android:insetBottom="0dp" + android:outlineSpotShadowColor="@android:color/transparent" + android:text="@string/scan_receipt_form_button_save_label" + android:textAppearance="@style/TextAppearance.TransactionDetail.EditButton" + android:textColor="@color/zinc_900" + app:cornerRadius="5dp" + app:icon="@drawable/ic_device_floppy" + app:iconGravity="textStart" + app:iconTint="@color/zinc_900" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintTop_toBottomOf="@id/scan_receipt_form_change_location" + app:layout_constraintWidth_percent=".48" /> + + </androidx.constraintlayout.widget.ConstraintLayout> + + +</ScrollView> diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml new file mode 100644 index 0000000000000000000000000000000000000000..ade46d95ae936bf6b97053106330e0f1c14ad5da --- /dev/null +++ b/app/src/main/res/layout/fragment_settings.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + tools:context=".views.fragments.SettingsFragment"> + + <com.example.bondoman.views.components.SettingButtonComponent + android:id="@+id/export_report_button" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:settingButtonIcon="@drawable/ic_file_export" + app:settingButtonSubtitle="Save an xlsx file containing your transaction report in your device" + app:settingButtonTitle="Export report"></com.example.bondoman.views.components.SettingButtonComponent> + + <View + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="@color/zinc_700" /> + + <com.example.bondoman.views.components.SettingButtonComponent + android:id="@+id/send_report_button" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:settingButtonIcon="@drawable/ic_send" + app:settingButtonSubtitle="Send an xlsx file containing your transaction report to your email" + app:settingButtonTitle="Send report as a file"></com.example.bondoman.views.components.SettingButtonComponent> + + <View + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="@color/zinc_700" /> + + <com.example.bondoman.views.components.SettingButtonComponent + android:id="@+id/randomize_transaction_button" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:settingButtonIcon="@drawable/ic_arrows_shuffle" + app:settingButtonSubtitle="Send a broadcast to create transaction fragment and automatically filled the title and amount fields" + app:settingButtonTitle="Randomize transaction"></com.example.bondoman.views.components.SettingButtonComponent> + + <View + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="@color/zinc_700" /> + + <com.example.bondoman.views.components.SettingButtonComponent + android:id="@+id/logout_button" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:settingButtonIcon="@drawable/ic_logout" + app:settingButtonSubtitle="Logout from the application" + app:settingButtonTitle="Logout"></com.example.bondoman.views.components.SettingButtonComponent> +</LinearLayout> diff --git a/app/src/main/res/layout/fragment_transaction_form.xml b/app/src/main/res/layout/fragment_transaction_form.xml new file mode 100644 index 0000000000000000000000000000000000000000..f9a587057853e61dcae2c0dfd843a55eb092efd2 --- /dev/null +++ b/app/src/main/res/layout/fragment_transaction_form.xml @@ -0,0 +1,117 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:divider="@drawable/shape_form_divider" + android:orientation="vertical" + android:padding="20dp" + android:showDividers="middle"> + + <com.google.android.material.textfield.TextInputLayout + android:id="@+id/transaction_form_category_dropdown" + style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox.ExposedDropdownMenu" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@drawable/bg_full_border" + android:textColorHint="@color/zinc_300" + android:theme="@style/transactionFormFieldBackground" + app:boxStrokeColor="@color/teal_200" + app:cursorColor="@color/teal_200" + app:endIconDrawable="@drawable/ic_chevron_down" + app:endIconTint="@color/zinc_300" + app:hintTextColor="@color/teal_200"> + + <AutoCompleteTextView + android:id="@+id/transaction_form_auto_complete" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@android:color/transparent" + android:hint="@string/dummy_category_dropdown_placeholder" + android:inputType="none" + android:minHeight="44dp" + android:textAppearance="@style/TextAppearance.TransactionForm.Input" + android:textColor="@color/zinc_300" /> + </com.google.android.material.textfield.TextInputLayout> + + <com.example.bondoman.views.components.TextInputComponent + android:id="@+id/transaction_form_date" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:textInputLabel="Date" /> + + <com.example.bondoman.views.components.TextInputComponent + android:id="@+id/transaction_form_title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:digits="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 " + android:inputType="text" + android:maxLength="50" + app:textInputHint="@string/hint_transaction_form_title" + app:textInputLabel="Title" /> + + <com.example.bondoman.views.components.TextInputComponent + android:id="@+id/transaction_form_amount" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:inputType="number" + android:maxLength="18" + app:textInputHint="@string/hint_transaction_form_amount" + app:textInputLabel="Amount (Rp)" /> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:divider="@drawable/shape_text_input_divider" + android:orientation="vertical" + android:showDividers="middle"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/label_transaction_form_location" + android:textAppearance="@style/TextAppearance.TransactionForm.Input" /> + + <com.google.android.material.textfield.TextInputEditText + android:id="@+id/transaction_form_location_field" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@drawable/bg_full_border" + android:cursorVisible="false" + android:focusableInTouchMode="false" + android:hint="@string/hint_transaction_form_location" + android:imeOptions="actionDone" + android:inputType="text" + android:padding="10dp" + android:textAppearance="@style/TextAppearance.TransactionForm.Input" + android:textColor="@color/zinc_300" + android:textColorHint="@color/zinc_500" + android:theme="@style/transactionFormFieldBackground" /> + + <TextView + android:id="@+id/transaction_form_change_location_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="end" + android:clickable="true" + android:text="@string/text_change_location" + android:theme="@style/TextAppearance.TransactionForm.ChangeLocationText" /> + </LinearLayout> + + <com.google.android.material.button.MaterialButton + android:id="@+id/transaction_form_save_button" + android:layout_width="match_parent" + android:layout_height="44dp" + android:backgroundTint="@color/teal_200" + android:gravity="center" + android:insetTop="0dp" + android:insetBottom="0dp" + android:outlineSpotShadowColor="@android:color/transparent" + android:text="@string/text_save_button" + android:textAppearance="@style/TextAppearance.TransactionDetail.EditButton" + android:textColor="@color/zinc_900" + app:cornerRadius="5dp" + app:icon="@drawable/ic_device_floppy" + app:iconGravity="textStart" + app:iconTint="@color/zinc_900" /> +</LinearLayout> diff --git a/app/src/main/res/layout/fragment_transaction_list.xml b/app/src/main/res/layout/fragment_transaction_list.xml new file mode 100644 index 0000000000000000000000000000000000000000..147a6128aecbbb970e8a2d10267b64eb0d9d1ff3 --- /dev/null +++ b/app/src/main/res/layout/fragment_transaction_list.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@color/bg_main"> + + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/transaction_list_recycler_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:clipToPadding="false" + android:paddingTop="20dp" /> + + <com.example.bondoman.views.components.PageInfoComponent + android:id="@+id/transaction_list_page_info_waiting" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:src="@drawable/ic_hourglass_high" + app:pageInfoText="@string/text_page_info_getting_transactions" /> + + <com.example.bondoman.views.components.PageInfoComponent + android:id="@+id/transaction_list_page_info_empty" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:src="@drawable/ic_plus" + android:visibility="gone" + app:pageInfoText="@string/text_page_info_empty_transaction" /> + + <ImageButton + android:id="@+id/transaction_list_add_button" + android:layout_width="49dp" + android:layout_height="49dp" + android:layout_margin="20dp" + android:background="@drawable/bg_full_border" + android:contentDescription="@string/desc_add_transaction" + android:src="@drawable/ic_plus" + android:theme="@style/teal200RoundBackground" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintRight_toRightOf="parent" + app:tint="@color/zinc_800" /> +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/layout/fragment_twibbon.xml b/app/src/main/res/layout/fragment_twibbon.xml new file mode 100644 index 0000000000000000000000000000000000000000..380939fe17e1c24eb5f358e8e47ee72f86abe28a --- /dev/null +++ b/app/src/main/res/layout/fragment_twibbon.xml @@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context=".views.fragments.TwibbonFragment"> + + <androidx.camera.view.PreviewView + android:id="@+id/camera_preview" + android:layout_width="match_parent" + android:layout_height="0dp" + app:layout_constraintDimensionRatio="1:1" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <ImageButton + android:id="@+id/selfie_capture_button" + android:layout_width="80dp" + android:layout_height="80dp" + android:adjustViewBounds="true" + android:background="@drawable/bg_full_border" + android:contentDescription="@string/capture" + android:elevation="2dp" + android:padding="20dp" + android:scaleType="fitCenter" + android:src="@drawable/ic_camera" + android:theme="@style/teal200RoundBackground" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/camera_preview" + app:tint="@color/zinc_800" /> + + + <ImageButton + android:id="@+id/camera_rotate_button" + android:layout_width="80dp" + android:layout_height="80dp" + android:layout_marginEnd="5dp" + android:adjustViewBounds="true" + android:background="@drawable/bg_full_border" + android:contentDescription="@string/camera_rotate" + android:elevation="2dp" + android:padding="20dp" + android:scaleType="fitCenter" + android:src="@drawable/ic_camera_rotate" + android:theme="@style/transparentRoundBackground" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toBottomOf="@id/camera_preview" + app:tint="@color/zinc_300" /> + + <ImageView + android:id="@+id/selfie" + android:layout_width="match_parent" + android:layout_height="0dp" + android:contentDescription="@string/camera_preview_capture" + android:scaleType="centerCrop" + android:visibility="gone" + app:layout_constraintDimensionRatio="1:1" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:visibility="visible" /> + + <ImageView + android:id="@+id/image_overlay" + android:layout_width="match_parent" + android:layout_height="0dp" + android:adjustViewBounds="true" + android:contentDescription="@string/twibbon_image_overlay" + android:scaleType="centerCrop" + android:src="@drawable/twibbon" + app:layout_constraintDimensionRatio="1:1" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/layout/fragment_twibbon_preview.xml b/app/src/main/res/layout/fragment_twibbon_preview.xml new file mode 100644 index 0000000000000000000000000000000000000000..aed87e56e3a4139d42fd641d5b6b81a3a4c00994 --- /dev/null +++ b/app/src/main/res/layout/fragment_twibbon_preview.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:padding="15dp" + tools:context=".views.fragments.TwibbonPreviewFragment"> + + <com.google.android.material.imageview.ShapeableImageView + android:id="@+id/twibbon_result" + android:layout_width="match_parent" + android:layout_height="0dp" + android:adjustViewBounds="true" + android:contentDescription="@string/twibbon_image_overlay" + android:scaleType="centerCrop" + app:layout_constraintDimensionRatio="1:1" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:shapeAppearanceOverlay="@style/imageRoundedCorner" /> + + <com.google.android.material.button.MaterialButton + android:id="@+id/retake_twibbon" + android:layout_width="match_parent" + android:layout_height="44dp" + android:layout_marginTop="20dp" + android:backgroundTint="@color/teal_200" + android:gravity="center" + android:insetTop="0dp" + android:insetBottom="0dp" + android:outlineSpotShadowColor="@android:color/transparent" + android:text="@string/text_retake_button" + android:textAppearance="@style/TextAppearance.TransactionDetail.EditButton" + android:textColor="@color/zinc_900" + app:cornerRadius="5dp" + app:icon="@drawable/ic_rotate_clockwise" + app:iconGravity="textStart" + app:iconTint="@color/zinc_900" + app:layout_constraintLeft_toRightOf="parent" + app:layout_constraintRight_toLeftOf="parent" + app:layout_constraintTop_toBottomOf="@id/twibbon_result" /> + +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml new file mode 100644 index 0000000000000000000000000000000000000000..25d1a48cdc09fb28cceece24f1a4645b634ee91b --- /dev/null +++ b/app/src/main/res/menu/menu_main.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:id="@+id/transactionListFragment" + android:title="@string/menu_title_transactions" + android:icon="@drawable/ic_receipt"/> + <item + android:id="@+id/scanReceiptFragment" + android:title="@string/menu_title_scan" + android:icon="@drawable/ic_scan"/> + <item + android:id="@+id/graphFragment" + android:title="@string/menu_title_charts" + android:icon="@drawable/ic_chart_pie"/> + <item + android:id="@+id/twibbonFragment" + android:title="@string/menu_title_camera" + android:icon="@drawable/ic_camera_selfie"/> + <item + android:id="@+id/settingsFragment" + android:title="@string/menu_title_settings" + android:icon="@drawable/ic_settings"/> +</menu> diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml new file mode 100644 index 0000000000000000000000000000000000000000..0ac66761eff02a75236b33cf43ef112705768a6a --- /dev/null +++ b/app/src/main/res/navigation/nav_graph.xml @@ -0,0 +1,101 @@ +<?xml version="1.0" encoding="utf-8"?> +<navigation xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/bondoman_nav" + app:startDestination="@id/settingsFragment"> + + + <fragment + android:id="@+id/twibbonPreviewFragment" + android:name="com.example.bondoman.views.fragments.TwibbonPreviewFragment" + android:label="@string/menu_title_camera" + tools:layout="@layout/fragment_twibbon_preview"> + <action + android:id="@+id/action_twibbonPreviewFragment_to_twibbonFragment" + app:destination="@id/twibbonFragment" /> + <argument + android:name="twibbonResult" + app:argType="com.example.bondoman.data.models.ParcelableBitmap" /> + </fragment> + <fragment + android:id="@+id/transactionListFragment" + android:name="com.example.bondoman.views.fragments.TransactionListFragment" + android:label="@string/menu_title_transactions" + tools:layout="@layout/fragment_transaction_list"> + <action + android:id="@+id/action_transactionListFragment_to_transactionAddFragment" + app:destination="@id/transactionAddFragment" /> + <action + android:id="@+id/action_transactionListFragment_to_transactionUpdateFragment" + app:destination="@id/transactionUpdateFragment" /> + </fragment> + <fragment + android:id="@+id/transactionAddFragment" + android:name="com.example.bondoman.views.fragments.TransactionAddFragment" + android:label="@string/menu_title_new_transaction" + tools:layout="@layout/fragment_transaction_form"> + <action + android:id="@+id/action_transactionAddFragment_to_transactionListFragment" + app:destination="@id/transactionListFragment" /> + </fragment> + <fragment + android:id="@+id/transactionUpdateFragment" + android:name="com.example.bondoman.views.fragments.TransactionUpdateFragment" + android:label="@string/menu_title_edit_transaction" + tools:layout="@layout/fragment_transaction_form"> + <action + android:id="@+id/action_transactionUpdateFragment_to_transactionListFragment" + app:destination="@id/transactionListFragment" /> + <argument + android:name="CurrentTransaction" + app:argType="com.example.bondoman.data.models.Transaction" /> + </fragment> + <fragment + android:id="@+id/settingsFragment" + android:name="com.example.bondoman.views.fragments.SettingsFragment" + android:label="@string/menu_title_settings" + tools:layout="@layout/fragment_settings" /> + <fragment + android:id="@+id/twibbonFragment" + android:name="com.example.bondoman.views.fragments.TwibbonFragment" + android:label="@string/menu_title_camera" + tools:layout="@layout/fragment_twibbon"> + <action + android:id="@+id/action_twibbonFragment_to_twibbonPreviewFragment" + app:destination="@id/twibbonPreviewFragment" /> + </fragment> + <fragment + android:id="@+id/scanReceiptFragment" + android:name="com.example.bondoman.views.fragments.ScanReceiptFragment" + android:label="@string/menu_title_scan" + tools:layout="@layout/fragment_scan_receipt"> + <action + android:id="@+id/action_scanReceiptFragment_to_scanReceiptResultFragment" + app:destination="@id/scanReceiptResultFragment" /> + </fragment> + <fragment + android:id="@+id/graphFragment" + android:name="com.example.bondoman.views.fragments.GraphFragment" + android:label="@string/menu_title_charts" + tools:layout="@layout/fragment_graph" /> + <fragment + android:id="@+id/connectionLostFragment" + android:name="com.example.bondoman.views.fragments.ConnectionLostFragment" + tools:layout="@layout/fragment_connection_lost" /> + <fragment + android:id="@+id/scanReceiptResultFragment" + android:name="com.example.bondoman.views.fragments.ScanReceiptResultFragment" + android:label="@string/menu_title_scan_receipt_result" + tools:layout="@layout/fragment_scan_receipt_result"> + <action + android:id="@+id/action_scanReceiptResultFragment_to_scanReceiptFragment" + app:destination="@id/scanReceiptFragment" /> + <argument + android:name="ScanReceiptImageBitmap" + app:argType="com.example.bondoman.data.models.ParcelableBitmap" /> + </fragment> + <action + android:id="@+id/action_global_to_ConnectionLostFragment" + app:destination="@id/connectionLostFragment" /> +</navigation> diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml index 8111560848797c9c2dba96310f97f8d0415f575f..d8a92044a685de8530ff45dd70183c667fa7b796 100644 --- a/app/src/main/res/values-night/themes.xml +++ b/app/src/main/res/values-night/themes.xml @@ -1,7 +1,24 @@ <resources xmlns:tools="http://schemas.android.com/tools"> - <!-- Base application theme. --> - <style name="Base.Theme.BondoMan" parent="Theme.Material3.DayNight.NoActionBar"> - <!-- Customize your dark theme here. --> - <!-- <item name="colorPrimary">@color/my_dark_primary</item> --> + + <style name="Base.Theme.BondoMan" parent="Theme.MaterialComponents.DayNight.NoActionBar"> + <item name="android:statusBarColor" tools:targetApi="l">@color/bg_main</item> + <item name="android:datePickerDialogTheme">@style/DatePickerDialogTheme</item> + <item name="android:windowBackground">@drawable/bg_dot_grid</item> </style> -</resources> \ No newline at end of file + + <style name="DatePickerDialogTheme" parent="android:Theme.Material.Dialog"> + <item name="android:datePickerStyle">@style/DatePickerStyle</item> + <item name="android:textColorSecondaryInverse">@color/zinc_300</item> + <item name="android:textColorSecondary">@color/zinc_300</item> + <item name="android:textColorPrimaryInverse">@color/teal_200</item> + <item name="android:textColorPrimary">@color/zinc_300</item> + <item name="android:colorAccent">@color/zinc_700</item> + </style> + + <style name="DatePickerStyle" parent="@android:style/Widget.Material.Light.DatePicker"> + <item name="android:headerBackground">@color/bg_main</item> <!-- header background color --> + + </style> + + <style name="Theme.BondoMan" parent="Base.Theme.BondoMan" /> +</resources> diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml new file mode 100644 index 0000000000000000000000000000000000000000..6e9bd6e926f94f53f7fbf007339868f3050686c3 --- /dev/null +++ b/app/src/main/res/values/attrs.xml @@ -0,0 +1,57 @@ +<resources> + <declare-styleable name="FullBorderBackground"> + <attr name="borderColor" format="color" /> + <attr name="backgroundColor" format="color" /> + <attr name="borderStartWidth" format="dimension" /> + <attr name="borderEndWidth" format="dimension" /> + <attr name="borderTopWidth" format="dimension" /> + <attr name="borderRightWidth" format="dimension" /> + <attr name="borderBottomWidth" format="dimension" /> + <attr name="borderLeftWidth" format="dimension" /> + <attr name="borderRadius" format="dimension" /> + <attr name="borderTopLeftRadius" format="dimension" /> + <attr name="borderTopRightRadius" format="dimension" /> + <attr name="borderBottomLeftRadius" format="dimension" /> + <attr name="borderBottomRightRadius" format="dimension" /> + </declare-styleable> + <declare-styleable name="TextInputComponent"> + <attr name="textInputLabel" format="string" /> + <attr name="textInputHint" format="string" /> + <attr name="textInputType" format="string" /> + <attr name="textInputKeyboard" format="enum"> + <enum name="text" value="0" /> + <enum name="number" value="1" /> + </attr> + <attr name="android:inputType" /> + <attr name="android:digits" /> + <attr name="android:maxLength" /> + </declare-styleable> + <declare-styleable name="DateInputComponent"> + <attr name="dateInputLabel" format="string" /> + </declare-styleable> + <declare-styleable name="DialogComponent"> + <attr name="dialogText" format="string" /> + <attr name="firstButtonText" format="string" /> + <attr name="secondButtonText" format="string" /> + <attr name="dialogFirstButtonType" format="enum"> + <enum name="positive" value="1" /> + <enum name="neutral" value="0" /> + <enum name="negative" value="-1" /> + </attr> + <attr name="dialogSecondButtonType" format="enum"> + <enum name="positive" value="1" /> + <enum name="neutral" value="0" /> + <enum name="negative" value="-1" /> + </attr> + </declare-styleable> + <declare-styleable name="SettingButtonComponent"> + <attr name="settingButtonIcon" format="reference" /> + <attr name="settingButtonTitle" format="string" /> + <attr name="settingButtonSubtitle" format="string" /> + </declare-styleable> + <declare-styleable name="PageInfoComponent"> + <attr name="android:src" /> + <attr name="android:text" /> + <attr name="pageInfoText" format="string" /> + </declare-styleable> +</resources> diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index c8524cd961d27b6695e755c6ef2d4d58cf38431e..7b31d963a6e29795e601e13ee24b97303028df24 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -2,4 +2,24 @@ <resources> <color name="black">#FF000000</color> <color name="white">#FFFFFFFF</color> -</resources> \ No newline at end of file + <color name="bg_main">#2C2C2C</color> + <color name="blur">#B82C2C2C</color> + <color name="zinc_50">#FAFAFA</color> + <color name="zinc_200">#E4E4E7</color> + <color name="zinc_300">#D4D4D8</color> + <color name="zinc_400">#A1A1AA</color> + <color name="zinc_500">#71717A</color> + <color name="zinc_600">#52525B</color> + <color name="zinc_700">#3F3F46</color> + <color name="zinc_800">#27272A</color> + <color name="zinc_900">#18181B</color> + <color name="teal_200">#99F6E4</color> + <color name="rose_200">#FECDD3</color> + <color name="rose_200_11_percent">#1CFECDD3</color> + <array name="colors_pie_chart_area"> + <item>@color/zinc_700</item> + <item>@color/zinc_300</item> + </array> + <color name="button_default_color">#2C2C2C</color> + <color name="button_pressed_color">#525252</color> +</resources> diff --git a/app/src/main/res/values/font_certs.xml b/app/src/main/res/values/font_certs.xml new file mode 100644 index 0000000000000000000000000000000000000000..d2226ac01c057907ca1fc71f1d3390d3deea9ca1 --- /dev/null +++ b/app/src/main/res/values/font_certs.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <array name="com_google_android_gms_fonts_certs"> + <item>@array/com_google_android_gms_fonts_certs_dev</item> + <item>@array/com_google_android_gms_fonts_certs_prod</item> + </array> + <string-array name="com_google_android_gms_fonts_certs_dev"> + <item> + MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs= + </item> + </string-array> + <string-array name="com_google_android_gms_fonts_certs_prod"> + <item> + MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK + </item> + </string-array> +</resources> diff --git a/app/src/main/res/values/preloaded_fonts.xml b/app/src/main/res/values/preloaded_fonts.xml new file mode 100644 index 0000000000000000000000000000000000000000..88e05d7fa3a9f9e3db19dbda8fef0fb5b0b4d2eb --- /dev/null +++ b/app/src/main/res/values/preloaded_fonts.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <array name="preloaded_fonts" translatable="false"> + <item>@font/inter</item> + <item>@font/inter_bold</item> + <item>@font/inter_medium</item> + <item>@font/inter_thin</item> + </array> +</resources> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a2f82a3383e61c6c547033b6d0cb420f9be0298f..2280d207b2256e20bfa9d14f6d71723da3365807 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,77 @@ <resources> <string name="app_name">BondoMan</string> -</resources> \ No newline at end of file + <!-- TODO: Remove or change this placeholder text --> + <string name="hello_blank_fragment">Hello blank fragment</string> + <string name="desc_back_button">back</string> + <string name="menu_title_camera">Twibbon</string> + <string name="menu_title_scan">Scan Receipt</string> + <string name="menu_title_charts">Graph</string> + <string name="menu_title_transactions">Transactions</string> + <string name="menu_title_settings">Settings</string> + <string name="menu_title_new_transaction">New Transaction</string> + <string name="menu_title_edit_transaction">Edit Transaction</string> + <string name="menu_title_scan_receipt_result">Scan Receipt</string> + <string name="desc_transaction_icon">Transaction Icon</string> + <string name="desc_location_icon">location_icon</string> + <string name="desc_add_transaction">Add Transaction</string> + <string name="dummy_transaction_date">30 Mar 2026</string> + <string name="dummy_transaction_amount">-Rp9.000.000.000.000.000</string> + <string name="dummy_transaction_title">Ethereum to the earth’s core</string> + <string name="dummy_transaction_location"><u>Tokyo, Japan</u></string> + <string name="text_button_swipe_vertical">swipe vertically</string> + <string name="desc_delete_transaction">Delete transaction</string> + <string name="dummy_page_title">Title</string> + <string name="text_edit_button">Edit</string> + <string name="dummy_transaction_category">OUTCOME</string> + <string name="dummy_text_input_label">Label</string> + <string name="dummy_input_text_hint">Hint</string> + <string name="label_transaction_form_location">Location</string> + <string name="hint_transaction_form_location">Allow location access</string> + <string name="text_change_location">Change location</string> + <string name="text_save_button">Save</string> + <string name="dummy_dropdown_item">Item</string> + <string name="dummy_category_dropdown_placeholder">Select category</string> + <string name="capture">Capture receipt</string> + <string name="gallery">Gallery</string> + <string name="hint_category">Category</string> + <string name="text_income_transaction_category">INCOME</string> + <string name="text_outcome_transaction_category">OUTCOME</string> + <string name="placeholder_date_picker">Pick a date</string> + <string name="desc_separator_icon">separator</string> + <string name="text_date_begin_label">From</string> + <string name="text_date_end_label">Until</string> + <string-array name="transaction_categories"> + <item>Income</item> + <item>Outcome</item> + </string-array> + <string name="text_transaction_category">OUTCOME</string> + <string name="twibbon_image_overlay">Twibbon image overlay</string> + <string name="camera_rotate">Camera rotate</string> + <string name="camera_preview_capture">Camera preview capture</string> + <string name="dummy_dialog_text">This is just an <b>example</b>. Please customize this text.</string> + <string name="dummy_dialog_second_button">Retry</string> + <string name="dummy_dialog_first_button">Cancel</string> + <string name="text_delete_transaction_dialog">Are you sure to <b>delete</b> this transaction? This action cannot be undone</string> + <string name="text_first_button_delete_transaction_dialog">Cancel</string> + <string name="text_second_button_delete_transaction_dialog">Delete</string> + <string name="dummy_setting_button_title">Title</string> + <string name="dummy_setting_button_subtitle">Subtitle</string> + <string name="text_login_button">Login</string> + <string name="placeholder_password_form">Password</string> + <string name="placeholder_email_form">Email</string> + <string name="text_retake_button">Retake</string> + <string name="hint_transaction_form_title">Enter title</string> + <string name="hint_transaction_form_amount">Enter amount in Rupiah</string> + <string name="desc_page_info_icon">Information icon</string> + <string name="dummy_page_info_text">This is just an <b>example</b>. Please customize this text.</string> + <string name="text_page_info_getting_transactions">Getting transactions…</string> + <string name="text_page_info_empty_transaction">Transaction is <b>empty</b>. Please add new transaction(s)</string> + <string name="text_page_info_graph_not_available">Pie chart is <b>not</b> available for current data</string> + <string name="text_page_info_connection_lost">You\'re <b>offline</b>. Please check your internet connection</string> + <string name="menu_title_connection_lost">Connection Lost</string> + <string name="scan_receipt_image_result">Scan receipt image result</string> + <string name="scan_receipt_form_button_retry_label">Retry</string> + <string name="scan_receipt_form_button_save_label">Save</string> + <string name="toast_icon">Toast icon</string> +</resources> + diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 4f3ed1ef0dab9c06604f9b50bd1015baf4608f0f..149614af19c95135b1f41c619446cf780b0bbc45 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,9 +1,266 @@ <resources xmlns:tools="http://schemas.android.com/tools"> - <!-- Base application theme. --> - <style name="Base.Theme.BondoMan" parent="Theme.Material3.DayNight.NoActionBar"> - <!-- Customize your light theme here. --> - <!-- <item name="colorPrimary">@color/my_light_primary</item> --> + + <style name="Base.Theme.BondoMan" parent="Theme.MaterialComponents.DayNight.NoActionBar"> + <item name="android:statusBarColor" tools:targetApi="l">@color/bg_main</item> + <item name="android:datePickerDialogTheme">@style/DatePickerDialogTheme</item> + <item name="android:windowBackground">@drawable/bg_dot_grid</item> + </style> + + <style name="DatePickerDialogTheme" parent="android:Theme.Material.Dialog"> + <item name="android:datePickerStyle">@style/DatePickerStyle</item> + <item name="android:textColorSecondaryInverse">@color/zinc_300</item> + <item name="android:textColorSecondary">@color/zinc_300</item> + <item name="android:textColorPrimaryInverse">@color/teal_200</item> + <item name="android:textColorPrimary">@color/zinc_300</item> + <item name="android:colorAccent">@color/zinc_700</item> + </style> + + <style name="DatePickerStyle" parent="@android:style/Widget.Material.Light.DatePicker"> + <item name="android:headerBackground">@color/bg_main</item> <!-- header background color --> + </style> <style name="Theme.BondoMan" parent="Base.Theme.BondoMan" /> -</resources> \ No newline at end of file + + <style name="TextAppearance.Header" parent="@android:style/TextAppearance"> + <item name="android:textSize">20sp</item> + <item name="android:fontFamily">@font/inter_bold</item> + <item name="android:typeface">normal</item> + <item name="android:textColor">@color/zinc_50</item> + <item name="android:textColorHighlight">@color/zinc_50</item> + <item name="android:textColorHint">@color/zinc_50</item> + <item name="android:textColorLink">@color/zinc_50</item> + </style> + + <style name="TextAppearance.TransactionDetail.Category" parent="@android:style/TextAppearance"> + <item name="android:textSize">12sp</item> + <item name="android:fontFamily">@font/inter_bold</item> + <item name="android:typeface">normal</item> + <item name="android:textColor">@color/zinc_900</item> + <item name="android:textColorHighlight">@color/zinc_900</item> + <item name="android:textColorHint">@color/zinc_900</item> + <item name="android:textColorLink">@color/zinc_900</item> + </style> + + <style name="TextAppearance.Transaction.Date" parent="@android:style/TextAppearance"> + <item name="android:textSize">12sp</item> + <item name="android:fontFamily">@font/inter</item> + <item name="android:typeface">normal</item> + <item name="android:textColor">@color/zinc_500</item> + <item name="android:textColorHighlight">@color/zinc_500</item> + <item name="android:textColorHint">@color/zinc_500</item> + <item name="android:textColorLink">@color/zinc_500</item> + </style> + + <style name="TextAppearance.Transaction.Amount" parent="@android:style/TextAppearance"> + <item name="android:textSize">16sp</item> + <item name="android:fontFamily">@font/inter_bold</item> + <item name="android:typeface">normal</item> + <item name="android:textColor">@color/zinc_300</item> + <item name="android:textColorHighlight">@color/zinc_300</item> + <item name="android:textColorHint">@color/zinc_300</item> + <item name="android:textColorLink">@color/zinc_300</item> + </style> + + <style name="TextAppearance.Transaction.Title" parent="@android:style/TextAppearance"> + <item name="android:textSize">16sp</item> + <item name="android:fontFamily">@font/inter</item> + <item name="android:typeface">normal</item> + <item name="android:textColor">@color/zinc_300</item> + <item name="android:textColorHighlight">@color/zinc_300</item> + <item name="android:textColorHint">@color/zinc_300</item> + <item name="android:textColorLink">@color/zinc_300</item> + </style> + + <style name="TextAppearance.Transaction.Location" parent="@android:style/TextAppearance"> + <item name="android:textSize">12sp</item> + <item name="android:fontFamily">@font/inter</item> + <item name="android:typeface">normal</item> + <item name="android:textColor">@color/zinc_400</item> + <item name="android:textColorHighlight">@color/zinc_400</item> + <item name="android:textColorHint">@color/zinc_400</item> + <item name="android:textColorLink">@color/zinc_400</item> + </style> + + <style name="TextAppearance.TransactionDetail.EditButton" parent="@android:style/TextAppearance"> + <item name="android:textSize">16sp</item> + <item name="android:fontFamily">@font/inter</item> + <item name="android:typeface">normal</item> + <item name="android:textColor">@color/zinc_900</item> + <item name="android:textColorHighlight">@color/zinc_900</item> + <item name="android:textColorHint">@color/zinc_900</item> + <item name="android:textColorLink">@color/zinc_900</item> + </style> + + <style name="TextAppearance.TransactionForm.Input" parent="@android:style/TextAppearance"> + <item name="android:textSize">16sp</item> + <item name="android:fontFamily">@font/inter</item> + <item name="android:typeface">normal</item> + <item name="android:textColor">@color/zinc_300</item> + <item name="android:textColorHighlight">@color/zinc_300</item> + <item name="android:textColorHint">@color/zinc_300</item> + <item name="android:textColorLink">@color/zinc_300</item> + </style> + + <style name="TextAppearance.TransactionForm.ChangeLocationText" parent="@android:style/TextAppearance"> + <item name="android:textSize">14sp</item> + <item name="android:fontFamily">@font/inter</item> + <item name="android:typeface">normal</item> + <item name="android:textColor">@color/teal_200</item> + <item name="android:textColorHighlight">@color/teal_200</item> + <item name="android:textColorHint">@color/teal_200</item> + <item name="android:textColorLink">@color/teal_200</item> + </style> + + <style name="TextAppearance.Graph.Amount" parent="@android:style/TextAppearance"> + <item name="android:textSize">16sp</item> + <item name="android:fontFamily">@font/inter</item> + <item name="android:typeface">normal</item> + <item name="android:textColor">@color/zinc_300</item> + <item name="android:textColorHighlight">@color/zinc_300</item> + <item name="android:textColorHint">@color/zinc_300</item> + <item name="android:textColorLink">@color/zinc_300</item> + </style> + + <style name="TextAppearance.Dialog" parent="@android:style/TextAppearance"> + <item name="android:textSize">16sp</item> + <item name="android:fontFamily">@font/inter</item> + <item name="android:typeface">normal</item> + </style> + + <style name="TextAppearance.SettingButton.Title" parent="@android:style/TextAppearance"> + <item name="android:textSize">16sp</item> + <item name="android:fontFamily">@font/inter</item> + <item name="android:typeface">normal</item> + <item name="android:textColor">@color/zinc_300</item> + <item name="android:textColorHighlight">@color/zinc_300</item> + <item name="android:textColorHint">@color/zinc_300</item> + <item name="android:textColorLink">@color/zinc_300</item> + </style> + + <style name="TextAppearance.SettingButton.Subtitle" parent="@android:style/TextAppearance"> + <item name="android:textSize">12sp</item> + <item name="android:fontFamily">@font/inter</item> + <item name="android:typeface">normal</item> + <item name="android:textColor">@color/zinc_500</item> + <item name="android:textColorHighlight">@color/zinc_500</item> + <item name="android:textColorHint">@color/zinc_500</item> + <item name="android:textColorLink">@color/zinc_500</item> + </style> + + <style name="TextAppearance.Login.LoginButton" parent="@android:style/TextAppearance"> + <item name="android:textSize">16sp</item> + <item name="android:fontFamily">@font/inter</item> + <item name="android:typeface">normal</item> + <item name="android:textColor">@color/zinc_900</item> + <item name="android:textColorHighlight">@color/zinc_900</item> + <item name="android:textColorHint">@color/zinc_900</item> + <item name="android:textColorLink">@color/zinc_900</item> + </style> + + <style name="TextAppearance.Login.TextInput" parent="@android:style/TextAppearance"> + <item name="android:textSize">16sp</item> + <item name="android:fontFamily">@font/inter</item> + <item name="android:typeface">normal</item> + <item name="android:textColor">@color/zinc_300</item> + <item name="android:textColorHighlight">@color/zinc_300</item> + <item name="android:textColorHint">@color/zinc_300</item> + <item name="android:textColorLink">@color/zinc_300</item> + </style> + + <style name="TextAppearance.PageInfo" parent="@android:style/TextAppearance"> + <item name="android:textSize">16sp</item> + <item name="android:fontFamily">@font/inter</item> + <item name="android:typeface">normal</item> + <item name="android:textColor">@color/white</item> + <item name="android:textColorHighlight">@color/white</item> + <item name="android:textColorHint">@color/white</item> + <item name="android:textColorLink">@color/white</item> + </style> + + <style name="transactionDetailSwipeButtonBackground"> + <item name="backgroundColor">@color/zinc_200</item> + <item name="borderRadius">100dp</item> + <item name="borderTopRightRadius">100dp</item> + <item name="borderTopLeftRadius">100dp</item> + </style> + + <style name="deleteTransactionButtonBackground"> + <item name="backgroundColor">@color/rose_200_11_percent</item> + <item name="borderRadius">5dp</item> + <item name="borderTopRightRadius">5dp</item> + <item name="borderTopLeftRadius">5dp</item> + </style> + + <style name="editTransactionButtonBackground"> + <item name="backgroundColor">@color/teal_200</item> + <item name="borderRadius">5dp</item> + <item name="borderTopRightRadius">5dp</item> + <item name="borderTopLeftRadius">5dp</item> + </style> + + <style name="zinc700TopBorderBackground"> + <item name="borderColor">@color/zinc_700</item> + <item name="backgroundColor">@color/bg_main</item> + <item name="borderTopWidth">1dp</item> + </style> + + <style name="transactionDetailTopBorderBackground"> + <item name="borderColor">@color/zinc_700</item> + <item name="backgroundColor">@color/bg_main</item> + <item name="borderTopWidth">1dp</item> + <item name="borderTopLeftRadius">20dp</item> + <item name="borderTopRightRadius">20dp</item> + </style> + + <style name="transactionFormFieldBackground"> + <item name="backgroundColor">@color/zinc_700</item> + <item name="borderTopLeftRadius">5dp</item> + <item name="borderTopRightRadius">5dp</item> + <item name="borderBottomLeftRadius">5dp</item> + <item name="borderBottomRightRadius">5dp</item> + </style> + + <style name="teal200RoundBackground"> + <item name="borderColor">@android:color/transparent</item> + <item name="backgroundColor">@color/teal_200</item> + <item name="borderRadius">100dp</item> + </style> + + <style name="transparentRoundBackground"> + <item name="borderColor">@android:color/transparent</item> + <item name="backgroundColor">@android:color/transparent</item> + <item name="borderRadius">100dp</item> + </style> + + <style name="swipeButtonBackground"> + <item name="borderColor">@android:color/transparent</item> + <item name="backgroundColor">@color/zinc_200</item> + <item name="borderRadius">5dp</item> + </style> + + <style name="incomeCategoryBackground"> + <item name="backgroundColor">@color/teal_200</item> + <item name="borderRadius">4dp</item> + </style> + + <style name="outcomeCategoryBackground"> + <item name="backgroundColor">@color/rose_200</item> + <item name="borderRadius">4dp</item> + </style> + + <style name="dialogBackground"> + <item name="backgroundColor">@color/bg_main</item> + <item name="borderRadius">5dp</item> + <item name="borderTopWidth">1dp</item> + <item name="borderBottomWidth">1dp</item> + <item name="borderLeftWidth">1dp</item> + <item name="borderRightWidth">1dp</item> + <item name="borderColor">@color/zinc_700</item> + </style> + + <style name="imageRoundedCorner"> + <item name="cornerFamily">rounded</item> + <item name="cornerSize">5dp</item> + </style> +</resources> diff --git a/app/src/main/res/xml/file_paths.xml b/app/src/main/res/xml/file_paths.xml new file mode 100644 index 0000000000000000000000000000000000000000..c782c28367916ea755be7875a055e154307d3976 --- /dev/null +++ b/app/src/main/res/xml/file_paths.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<paths> + <cache-path + name="cache" + path="/" /> +</paths> diff --git a/app/src/test/java/com/example/bondoman/ExampleUnitTest.kt b/app/src/test/java/com/example/bondoman/ExampleUnitTest.kt index a6f826f1f5e9fc7d6a7db3ca07ce4af0ff53e201..e913a2ac8b2a164d5a2fff29c2fe38bb4c5a4d16 100644 --- a/app/src/test/java/com/example/bondoman/ExampleUnitTest.kt +++ b/app/src/test/java/com/example/bondoman/ExampleUnitTest.kt @@ -1,8 +1,7 @@ package com.example.bondoman -import org.junit.Test - import org.junit.Assert.* +import org.junit.Test /** * Example local unit test, which will execute on the development machine (host). @@ -10,8 +9,8 @@ import org.junit.Assert.* * See [testing documentation](http://d.android.com/tools/testing). */ class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} \ No newline at end of file + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} diff --git a/build.gradle.kts b/build.gradle.kts index a0985efc88dec705956b74f5d6e9ac23c8daebb8..aeb38aed6d0f9ebb9a32f1427931f8aa5d961393 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,23 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { - alias(libs.plugins.androidApplication) apply false - alias(libs.plugins.jetbrainsKotlinAndroid) apply false -} \ No newline at end of file + alias(libs.plugins.androidApplication) apply false + alias(libs.plugins.jetbrainsKotlinAndroid) apply false + id("com.google.devtools.ksp") version "1.9.23-1.0.19" apply false + id("com.diffplug.spotless") version "6.25.0" apply false +} + +buildscript { + repositories { + google() + } + dependencies { + val nav_version = "2.7.7" + classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version") + } +} + +subprojects { + afterEvaluate { + project.apply("../spotless.gradle") + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fcbb7bc1f3daf7421cb6ab161471a41c07a7058f..503587f539271e281005e3835ce3e2c24c404c9a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,17 +1,33 @@ [versions] agp = "8.3.0" -kotlin = "1.9.0" +kotlin = "1.9.23" coreKtx = "1.12.0" junit = "4.13.2" junitVersion = "1.1.5" espressoCore = "3.5.1" appcompat = "1.6.1" material = "1.11.0" -activity = "1.8.0" +activity = "1.8.2" constraintlayout = "2.1.4" +moshi = "1.15.1" +retrofit = "2.9.0" +roomRuntime = "2.6.1" +camera = "1.3.2" +mpChart = "v3.1.0" +navigationFragmentKtx = "2.7.7" +navigationUiKtx = "2.7.7" +securityCrypto = "1.1.0-alpha06" +poi = "5.2.5" +poiOoxml = "5.2.5" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } +androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomRuntime" } +androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "roomRuntime" } +androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomRuntime" } +androidx-room-testing = { module = "androidx.room:room-testing", version.ref = "roomRuntime" } +androidx-security-crypto = { module = "androidx.security:security-crypto", version.ref = "securityCrypto" } +converter-moshi = { module = "com.squareup.retrofit2:converter-moshi", version.ref = "retrofit" } junit = { group = "junit", name = "junit", version.ref = "junit" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } @@ -19,6 +35,20 @@ androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version material = { group = "com.google.android.material", name = "material", version.ref = "material" } androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" } androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } +camera-core = { module = "androidx.camera:camera-core", version.ref = "camera" } +camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "camera" } +camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "camera" } +camera-view = { module = "androidx.camera:camera-view", version.ref = "camera" } +camera-extensions = { module = "androidx.camera:camera-extensions", version.ref = "camera" } +moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" } +moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "moshi" } +moshi-kotlin-codegen = { module = "com.squareup.moshi:moshi-kotlin-codegen", version.ref = "moshi" } +mpchart = { module = "com.github.PhilJay:MPAndroidChart", version.ref = "mpChart" } +androidx-navigation-fragment-ktx = { group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref = "navigationFragmentKtx" } +androidx-navigation-ui-ktx = { group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "navigationUiKtx" } +retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" } +poi = { group = "org.apache.poi", name = "poi", version.ref = "poi" } +poi-ooxml = { group = "org.apache.poi", name = "poi-ooxml", version.ref = "poiOoxml" } [plugins] androidApplication = { id = "com.android.application", version.ref = "agp" } diff --git a/screenshot/chart.jpg b/screenshot/chart.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3ae6018a7421e4c1d97b70c0759aff1d0c9bdffa Binary files /dev/null and b/screenshot/chart.jpg differ diff --git a/screenshot/login.jpg b/screenshot/login.jpg new file mode 100644 index 0000000000000000000000000000000000000000..404564adfb303db3a40892f9bd93aa223a489a5d Binary files /dev/null and b/screenshot/login.jpg differ diff --git a/screenshot/report.jpg b/screenshot/report.jpg new file mode 100644 index 0000000000000000000000000000000000000000..196513c7c23335b9374a60100f6d5a0b09ed6610 Binary files /dev/null and b/screenshot/report.jpg differ diff --git a/screenshot/scan_camera.jpg b/screenshot/scan_camera.jpg new file mode 100644 index 0000000000000000000000000000000000000000..70775420b1c29a441b1ecf93b791d6565280a9d4 Binary files /dev/null and b/screenshot/scan_camera.jpg differ diff --git a/screenshot/scan_gallery.jpg b/screenshot/scan_gallery.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5a64d7ded8a674423c7b22f1d772e5afea440531 Binary files /dev/null and b/screenshot/scan_gallery.jpg differ diff --git a/screenshot/scan_result.jpg b/screenshot/scan_result.jpg new file mode 100644 index 0000000000000000000000000000000000000000..169708cb0c7709d413ace712cb2d944f2d405068 Binary files /dev/null and b/screenshot/scan_result.jpg differ diff --git a/screenshot/settings.jpg b/screenshot/settings.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3dacd4007bf82c3b68887750c12fbae79b102df6 Binary files /dev/null and b/screenshot/settings.jpg differ diff --git a/screenshot/transaction_add.jpg b/screenshot/transaction_add.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8ffbeee73ad2383b72f5fd1859febe5096125e14 Binary files /dev/null and b/screenshot/transaction_add.jpg differ diff --git a/screenshot/transaction_delete.jpg b/screenshot/transaction_delete.jpg new file mode 100644 index 0000000000000000000000000000000000000000..aa27e34cf28de4ff31a2d3fed9bc2723ab234f31 Binary files /dev/null and b/screenshot/transaction_delete.jpg differ diff --git a/screenshot/transaction_detail.jpg b/screenshot/transaction_detail.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8b37c1c03220bc6dde0e681df581b13089c42749 Binary files /dev/null and b/screenshot/transaction_detail.jpg differ diff --git a/screenshot/transaction_edit.jpg b/screenshot/transaction_edit.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7c77496ce0d2b4e4e8ac84d9f44b7953d08d4a22 Binary files /dev/null and b/screenshot/transaction_edit.jpg differ diff --git a/screenshot/transaction_list.jpg b/screenshot/transaction_list.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7b4e2cfb175c30c71d8ae36a9c0c1e0d39f462aa Binary files /dev/null and b/screenshot/transaction_list.jpg differ diff --git a/screenshot/twibbon.jpg b/screenshot/twibbon.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3707df75157c8421771dbdd8acd1324a8840dac3 Binary files /dev/null and b/screenshot/twibbon.jpg differ diff --git a/settings.gradle.kts b/settings.gradle.kts index bf387d847d706681072661455ec001e0ab3532fb..a386a0be8c379383d672012fe4e0a5abd515d4c2 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,24 +1,24 @@ pluginManagement { - repositories { - google { - content { - includeGroupByRegex("com\\.android.*") - includeGroupByRegex("com\\.google.*") - includeGroupByRegex("androidx.*") - } - } - mavenCentral() - gradlePluginPortal() - } + repositories { + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + } + } + mavenCentral() + gradlePluginPortal() + } } dependencyResolutionManagement { - repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) - repositories { - google() - mavenCentral() - } + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + maven("https://jitpack.io") + } } rootProject.name = "BondoMan" include(":app") - \ No newline at end of file diff --git a/spotless.gradle b/spotless.gradle new file mode 100644 index 0000000000000000000000000000000000000000..be072c2c72deff5bd06872c092acdc1dbdc1f8d7 --- /dev/null +++ b/spotless.gradle @@ -0,0 +1,8 @@ +apply plugin: "com.diffplug.spotless" +spotless { + kotlin { + target "**/*.kt" + targetExclude "**/opencv/**" + ktlint("1.1.1") + } +} \ No newline at end of file