diff --git a/app/build.gradle b/app/build.gradle
index e11c4ebc..9bd0e662 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -5,7 +5,6 @@ apply plugin: 'com.starter.easylauncher'
apply plugin: "com.github.ben-manes.versions"
android {
- kotlinOptions.useIR = true
compileSdkVersion(Dependencies.compileSdk)
defaultConfig {
applicationId "xyz.hisname.fireflyiii"
@@ -75,7 +74,10 @@ android {
}
}
- configurations {all*.exclude group: 'com.android.support'}
+ configurations {
+ all*.exclude group: 'com.android.support'
+ all*.exclude group: 'com.google.guava', module: 'listenablefuture'
+ }
androidResources {
ignoreAssetsPattern 'NOTICE.txt'
}
@@ -99,7 +101,7 @@ dependencies {
implementation Dependencies.googleMaterialIcons
implementation Dependencies.fontAwesome
- coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
+ coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.2.0'
implementation Dependencies.toasty
implementation Dependencies.chart
@@ -117,4 +119,6 @@ dependencies {
// Project Lib
implementation project(path: ":languagepack")
-}
\ No newline at end of file
+}
+
+build.dependsOn(['checkClasspath'])
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c73d8109..bcf18224 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -23,6 +23,7 @@
+
@@ -49,10 +50,14 @@
android:label="@string/app_name"
android:supportsRtl="true"
android:networkSecurityConfig="@xml/network_security_config"
- android:theme="@style/AppTheme.StartUpTheme">
+ android:theme="@style/AppTheme.StartUpTheme"
+ android:localeConfig="@xml/locales_config"
+ tools:ignore="UnusedAttribute">
-
+
@@ -72,7 +77,9 @@
-
+
@@ -81,7 +88,9 @@
android:resource="@xml/balance_widget_info" />
-
+
@@ -91,7 +100,9 @@
-
+
@@ -102,7 +113,8 @@
+ android:theme="@style/AppTheme"
+ android:exported="true">
@@ -149,13 +161,17 @@
-
+
-
+
@@ -194,7 +210,8 @@
android:name=".service.TransactionTilesService"
android:icon="@drawable/ic_refresh"
android:label="@string/transaction"
- android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
+ android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
+ android:exported="true">
@@ -233,6 +250,16 @@
android:exported="true">
-->
+
+
+
+
+
diff --git a/app/src/main/java/xyz/hisname/fireflyiii/CustomApp.kt b/app/src/main/java/xyz/hisname/fireflyiii/CustomApp.kt
index 15cf6cde..ff7a9627 100644
--- a/app/src/main/java/xyz/hisname/fireflyiii/CustomApp.kt
+++ b/app/src/main/java/xyz/hisname/fireflyiii/CustomApp.kt
@@ -40,7 +40,7 @@ class CustomApp: Application() {
initAcra {
reportFormat = StringFormat.KEY_VALUE_LIST
buildConfigClass = BuildConfig::class.java
- reportContent = arrayOf(ReportField.REPORT_ID, ReportField.APP_VERSION_NAME,
+ reportContent = listOf(ReportField.REPORT_ID, ReportField.APP_VERSION_NAME,
ReportField.PHONE_MODEL, ReportField.BRAND, ReportField.PRODUCT, ReportField.ANDROID_VERSION,
ReportField.BUILD_CONFIG, ReportField.STACK_TRACE, ReportField.LOGCAT)
mailSender {
diff --git a/app/src/main/java/xyz/hisname/fireflyiii/data/local/pref/AppPref.kt b/app/src/main/java/xyz/hisname/fireflyiii/data/local/pref/AppPref.kt
index 068d5fb4..0970d685 100644
--- a/app/src/main/java/xyz/hisname/fireflyiii/data/local/pref/AppPref.kt
+++ b/app/src/main/java/xyz/hisname/fireflyiii/data/local/pref/AppPref.kt
@@ -52,10 +52,6 @@ class AppPref(private val sharedPref: SharedPreferences): PreferenceHelper {
get() = sharedPref.getString("cert_value", "") ?: ""
set(value) = sharedPref.edit { putString("cert_value", value) }
- override var languagePref: String
- get() = sharedPref.getString("language_pref", "") ?: "en"
- set(value) = sharedPref.edit{ putString("language_pref", value)}
-
override var nightModeEnabled: Boolean
get() = sharedPref.getBoolean("night_mode", false)
set(value) = sharedPref.edit { putBoolean("night_mode", value) }
diff --git a/app/src/main/java/xyz/hisname/fireflyiii/data/local/pref/PreferenceHelper.kt b/app/src/main/java/xyz/hisname/fireflyiii/data/local/pref/PreferenceHelper.kt
index b9b5089f..cf5c621f 100644
--- a/app/src/main/java/xyz/hisname/fireflyiii/data/local/pref/PreferenceHelper.kt
+++ b/app/src/main/java/xyz/hisname/fireflyiii/data/local/pref/PreferenceHelper.kt
@@ -29,7 +29,6 @@ interface PreferenceHelper {
var serverVersion: String
var userOs: String
var certValue: String
- var languagePref: String
var nightModeEnabled: Boolean
var isKeyguardEnabled: Boolean
var isCurrencyThumbnailEnabled: Boolean
diff --git a/app/src/main/java/xyz/hisname/fireflyiii/ui/base/BaseActivity.kt b/app/src/main/java/xyz/hisname/fireflyiii/ui/base/BaseActivity.kt
index 61b672c5..84bf0bc4 100644
--- a/app/src/main/java/xyz/hisname/fireflyiii/ui/base/BaseActivity.kt
+++ b/app/src/main/java/xyz/hisname/fireflyiii/ui/base/BaseActivity.kt
@@ -30,7 +30,6 @@ import xyz.hisname.fireflyiii.repository.GlobalViewModel
import xyz.hisname.fireflyiii.util.extension.getCompatColor
import xyz.hisname.fireflyiii.util.extension.getViewModel
import xyz.hisname.fireflyiii.util.getUniqueHash
-import xyz.hisname.languagepack.LanguageChanger
@SuppressLint("Registered")
open class BaseActivity: AppCompatActivity() {
@@ -55,10 +54,6 @@ open class BaseActivity: AppCompatActivity() {
}
}
- override fun attachBaseContext(newBase: Context) {
- super.attachBaseContext(LanguageChanger.init(newBase, sharedPref(newBase).languagePref))
- }
-
override fun applyOverrideConfiguration(overrideConfiguration: Configuration) {
val uiMode = overrideConfiguration.uiMode
overrideConfiguration.setTo(baseContext.resources.configuration)
diff --git a/app/src/main/java/xyz/hisname/fireflyiii/ui/base/BaseAddObjectFragment.kt b/app/src/main/java/xyz/hisname/fireflyiii/ui/base/BaseAddObjectFragment.kt
index 4e4c36f2..18b351ff 100644
--- a/app/src/main/java/xyz/hisname/fireflyiii/ui/base/BaseAddObjectFragment.kt
+++ b/app/src/main/java/xyz/hisname/fireflyiii/ui/base/BaseAddObjectFragment.kt
@@ -27,7 +27,7 @@ import androidx.core.view.isVisible
import xyz.hisname.fireflyiii.util.animation.BakedBezierInterpolator
import kotlin.math.max
-abstract class BaseAddObjectFragment: BaseFragment() {
+abstract class BaseAddObjectFragment : BaseFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@@ -35,21 +35,21 @@ abstract class BaseAddObjectFragment: BaseFragment() {
setWidgets()
}
- protected fun unReveal(rootView: View){
+ protected fun unReveal(rootView: View) {
val x = rootView.width / 2
val y = rootView.height / 2
val finalRadius = (max(rootView.width, rootView.height) * 1.1).toFloat()
- val circularReveal= ViewAnimationUtils.createCircularReveal(
- rootView, x, y,finalRadius, 0f)
+ val circularReveal = ViewAnimationUtils.createCircularReveal(
+ rootView, x, y, finalRadius, 0f
+ )
circularReveal.duration = 400
circularReveal.interpolator = BakedBezierInterpolator.FADE_OUT_CURVE
- circularReveal.addListener(object : AnimatorListenerAdapter(){
- override fun onAnimationEnd(animation: Animator?) {
+ circularReveal.addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
super.onAnimationEnd(animation)
try {
parentFragmentManager.popBackStack()
- } catch(illegal: IllegalStateException){
-
+ } catch (_: IllegalStateException) {
}
rootView.isVisible = false
fragmentContainer.isVisible = true
diff --git a/app/src/main/java/xyz/hisname/fireflyiii/ui/bills/details/BillDetailsFragment.kt b/app/src/main/java/xyz/hisname/fireflyiii/ui/bills/details/BillDetailsFragment.kt
index a4fc33f6..58def8fa 100644
--- a/app/src/main/java/xyz/hisname/fireflyiii/ui/bills/details/BillDetailsFragment.kt
+++ b/app/src/main/java/xyz/hisname/fireflyiii/ui/bills/details/BillDetailsFragment.kt
@@ -34,11 +34,11 @@ import androidx.lifecycle.asLiveData
import androidx.paging.LoadState
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
-import com.kizitonwose.calendarview.CalendarView
-import com.kizitonwose.calendarview.model.CalendarDay
-import com.kizitonwose.calendarview.model.DayOwner
-import com.kizitonwose.calendarview.ui.DayBinder
-import com.kizitonwose.calendarview.ui.ViewContainer
+import com.kizitonwose.calendar.core.CalendarDay
+import com.kizitonwose.calendar.core.DayPosition
+import com.kizitonwose.calendar.view.CalendarView
+import com.kizitonwose.calendar.view.MonthDayBinder
+import com.kizitonwose.calendar.view.ViewContainer
import xyz.hisname.fireflyiii.R
import xyz.hisname.fireflyiii.databinding.CalendarDayBinding
import xyz.hisname.fireflyiii.databinding.DetailsCardBinding
@@ -149,15 +149,15 @@ class BillDetailsFragment: BaseDetailFragment() {
val onDayText = CalendarDayBinding.bind(view).dayText
}
- binding.payDatesCalendarView.dayBinder = object: DayBinder{
- override fun bind(container: DayViewContainer, day: CalendarDay) {
- container.day = day
+ binding.payDatesCalendarView.dayBinder = object: MonthDayBinder{
+ override fun bind(container: DayViewContainer, data: CalendarDay) {
+ container.day = data
val textView = container.onDayText
- textView.text = day.date.dayOfMonth.toString()
- if (day.owner == DayOwner.THIS_MONTH) {
+ textView.text = data.date.dayOfMonth.toString()
+ if (data.position == DayPosition.MonthDate) {
if (selectedPayDays.isNotEmpty()){
- selectedPayDays.forEach { data ->
- if(data == day.date){
+ selectedPayDays.forEach { day ->
+ if(day == data.date){
textView.setBackgroundColor(getCompatColor(R.color.md_red_400))
} else {
textView.setTextColor(setDayNightTheme())
@@ -208,21 +208,21 @@ class BillDetailsFragment: BaseDetailFragment() {
}
}
- binding.paidDatesCalendarView.dayBinder = object: DayBinder{
- override fun bind(container: DayViewContainer, day: CalendarDay) {
- container.day = day
+ binding.paidDatesCalendarView.dayBinder = object: MonthDayBinder{
+ override fun bind(container: DayViewContainer, data: CalendarDay) {
+ container.day = data
val textView = container.onDayText
val divider = container.legendDivider
- textView.text = day.date.dayOfMonth.toString()
- if (day.owner == DayOwner.THIS_MONTH) {
+ textView.text = data.date.dayOfMonth.toString()
+ if (data.position == DayPosition.MonthDate) {
if (selectedPaidDays.isNotEmpty()){
- selectedPaidDays.forEach { data ->
- if(data == day.date){
+ selectedPaidDays.forEach { day ->
+ if(day == data.date){
divider.setBackgroundColor(getCompatColor(R.color.md_green_500))
}
}
}
- if(selectedDate == day.date){
+ if(selectedDate == data.date){
textView.setBackgroundColor(getCompatColor(R.color.md_green_500))
}
textView.setTextColor(setDayNightTheme())
@@ -251,7 +251,7 @@ class BillDetailsFragment: BaseDetailFragment() {
calendarView.setup(startMonth, endMonth,
WeekFields.of(Locale.getDefault()).firstDayOfWeek)
calendarView.scrollToMonth(currentMonth)
- calendarView.updateMonthConfiguration()
+ calendarView.updateMonthData()
}
private fun progressCircle(){
diff --git a/app/src/main/java/xyz/hisname/fireflyiii/ui/settings/SettingsFragment.kt b/app/src/main/java/xyz/hisname/fireflyiii/ui/settings/SettingsFragment.kt
index bf6ef505..b0742896 100644
--- a/app/src/main/java/xyz/hisname/fireflyiii/ui/settings/SettingsFragment.kt
+++ b/app/src/main/java/xyz/hisname/fireflyiii/ui/settings/SettingsFragment.kt
@@ -23,11 +23,13 @@ import android.os.Build
import android.os.Bundle
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
+import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.widget.Toolbar
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricManager.BIOMETRIC_SUCCESS
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.app.ActivityCompat
+import androidx.core.os.LocaleListCompat
import xyz.hisname.fireflyiii.R
import androidx.fragment.app.commit
import androidx.preference.CheckBoxPreference
@@ -40,13 +42,14 @@ import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.iconics.utils.*
import xyz.hisname.fireflyiii.data.local.pref.AppPref
import xyz.hisname.fireflyiii.util.biometric.KeyguardUtil
-import xyz.hisname.languagepack.LanguageChanger
+import xyz.hisname.languagepack.BuildConfig.TRANSLATION_ARRAY
import java.io.File
+import java.util.Locale
-class SettingsFragment: BaseSettings() {
+class SettingsFragment : BaseSettings() {
- private lateinit var chooseFolder: ActivityResultLauncher
+ private lateinit var chooseFolder: ActivityResultLauncher
private lateinit var userDownloadDirectoryPref: Preference
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
@@ -63,18 +66,30 @@ class SettingsFragment: BaseSettings() {
userDefinedDirectory()
}
- private fun setLanguagePref(){
+ private fun setLanguagePref() {
val languagePref = findPreference("language_pref") as ListPreference
- languagePref.value = AppPref(sharedPref).languagePref
+ val availableLocales = TRANSLATION_ARRAY.map { langCode ->
+ val hasCountry = langCode.contains("-")
+ if (hasCountry)
+ langCode.split("-").let { Locale(it[0], it[1]) }
+ else
+ Locale.forLanguageTag(langCode)
+ }
+ languagePref.entries = availableLocales.map { locale ->
+ locale.getDisplayName(locale)
+ .replaceFirstChar { if (it.isLowerCase()) it.titlecase(locale) else it.toString() }
+ }.toTypedArray()
+ languagePref.entryValues = TRANSLATION_ARRAY
+
+ val localesList = AppCompatDelegate.getApplicationLocales()
+ val locales = (0 until localesList.size()).mapNotNull { localesList.get(it) }
+ val currentLanguage = locales.find { availableLocales.contains(it) } ?: Locale.ENGLISH
+
+ languagePref.value = currentLanguage.language
languagePref.setOnPreferenceChangeListener { _, newValue ->
- AppPref(sharedPref).languagePref = newValue.toString()
- LanguageChanger.init(requireContext(), AppPref(sharedPref).languagePref)
- val coordinatorLayout = requireActivity().findViewById(R.id.coordinatorlayout)
- Snackbar.make(coordinatorLayout, "Restart to apply changes", Snackbar.LENGTH_INDEFINITE)
- .setAction(android.R.string.ok){
- ActivityCompat.recreate(requireActivity())
- }
- .show()
+ val appLocale: LocaleListCompat = LocaleListCompat.forLanguageTags(newValue.toString())
+ AppCompatDelegate.setApplicationLocales(appLocale)
+
true
}
languagePref.icon = IconicsDrawable(requireContext()).apply {
@@ -84,7 +99,7 @@ class SettingsFragment: BaseSettings() {
}
}
- private fun setAccountSection(){
+ private fun setAccountSection() {
val accountOptions = findPreference("account_options") as Preference
accountOptions.setOnPreferenceClickListener {
parentFragmentManager.commit {
@@ -101,7 +116,7 @@ class SettingsFragment: BaseSettings() {
}
}
- private fun setTransactionSection(){
+ private fun setTransactionSection() {
val transactionSettings = findPreference("transaction_settings") as Preference
transactionSettings.setOnPreferenceClickListener {
parentFragmentManager.commit {
@@ -119,17 +134,18 @@ class SettingsFragment: BaseSettings() {
}
}
- private fun setNightModeSection(){
+ private fun setNightModeSection() {
val nightModePref = findPreference("night_mode") as CheckBoxPreference
nightModePref.setOnPreferenceChangeListener { preference, newValue ->
val nightMode = newValue as Boolean
AppPref(sharedPref).nightModeEnabled = nightMode
- val coordinatorLayout = requireActivity().findViewById(R.id.coordinatorlayout)
+ val coordinatorLayout =
+ requireActivity().findViewById(R.id.coordinatorlayout)
Snackbar.make(coordinatorLayout, "Restart to apply changes", Snackbar.LENGTH_INDEFINITE)
- .setAction(android.R.string.ok){
- ActivityCompat.recreate(requireActivity())
- }
- .show()
+ .setAction(android.R.string.ok) {
+ ActivityCompat.recreate(requireActivity())
+ }
+ .show()
true
}
nightModePref.icon = IconicsDrawable(requireContext()).apply {
@@ -139,8 +155,9 @@ class SettingsFragment: BaseSettings() {
}
}
- private fun setThumbnail(){
- val thumbnailPref = findPreference("currencyThumbnail") as CheckBoxPreference
+ private fun setThumbnail() {
+ val thumbnailPref =
+ findPreference("currencyThumbnail") as CheckBoxPreference
thumbnailPref.icon = IconicsDrawable(requireContext()).apply {
icon = GoogleMaterial.Icon.gmd_attach_money
sizeDp = 24
@@ -153,7 +170,7 @@ class SettingsFragment: BaseSettings() {
}
}
- private fun setTutorial(){
+ private fun setTutorial() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val tutorialSetting = findPreference("tutorial_setting") as Preference
tutorialSetting.isVisible = true
@@ -169,7 +186,7 @@ class SettingsFragment: BaseSettings() {
}
}
- private fun setDeveloperOption(){
+ private fun setDeveloperOption() {
val developerSettings = findPreference("developer_options") as Preference
developerSettings.setOnPreferenceClickListener {
parentFragmentManager.commit {
@@ -188,23 +205,29 @@ class SettingsFragment: BaseSettings() {
override fun onResume() {
super.onResume()
- requireActivity().findViewById(R.id.activity_toolbar).title = resources.getString(R.string.settings)
+ requireActivity().findViewById(R.id.activity_toolbar).title =
+ resources.getString(R.string.settings)
}
- private fun setIconColor(): Int{
- return if(globalViewModel.isDark){
+ private fun setIconColor(): Int {
+ return if (globalViewModel.isDark) {
R.color.md_white_1000
} else {
R.color.md_black_1000
}
}
- private fun setPinCode(){
+ private fun setPinCode() {
val keyguardPref = findPreference("keyguard") as Preference
- if(!KeyguardUtil(requireActivity()).isDeviceKeyguardEnabled() || BiometricManager.from(requireContext()).canAuthenticate(
- BiometricManager.Authenticators.BIOMETRIC_WEAK or BiometricManager.Authenticators.DEVICE_CREDENTIAL) != BIOMETRIC_SUCCESS){
+ if (!KeyguardUtil(requireActivity()).isDeviceKeyguardEnabled() || BiometricManager.from(
+ requireContext()
+ ).canAuthenticate(
+ BiometricManager.Authenticators.BIOMETRIC_WEAK or BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ ) != BIOMETRIC_SUCCESS
+ ) {
keyguardPref.isSelectable = false
- keyguardPref.summary = "Please enable pin / password / biometrics in your device settings"
+ keyguardPref.summary =
+ "Please enable pin / password / biometrics in your device settings"
}
keyguardPref.icon = IconicsDrawable(requireContext()).apply {
icon = GoogleMaterial.Icon.gmd_lock
@@ -218,7 +241,7 @@ class SettingsFragment: BaseSettings() {
}
}
- private fun deleteItems(){
+ private fun deleteItems() {
val deleteData = findPreference("delete_data") as Preference
deleteData.icon = IconicsDrawable(requireContext()).apply {
icon = GoogleMaterial.Icon.gmd_delete_forever
@@ -235,15 +258,16 @@ class SettingsFragment: BaseSettings() {
}
}
- private fun userDefinedDirectory(){
- userDownloadDirectoryPref = findPreference("userDefinedDownloadDirectory") as Preference
+ private fun userDefinedDirectory() {
+ userDownloadDirectoryPref =
+ findPreference("userDefinedDownloadDirectory") as Preference
val userPref = AppPref(sharedPref).userDefinedDownloadDirectory
userDownloadDirectoryPref.icon = IconicsDrawable(requireContext()).apply {
icon = GoogleMaterial.Icon.gmd_file_download
sizeDp = 24
colorRes = setIconColor()
}
- val userDirectory = if(userPref.isEmpty()){
+ val userDirectory = if (userPref.isEmpty()) {
File(requireContext().getExternalFilesDir(null).toString()).toString()
} else {
userPref
@@ -257,11 +281,12 @@ class SettingsFragment: BaseSettings() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- chooseFolder = registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { folderChoosen ->
- if(folderChoosen != null){
- AppPref(sharedPref).userDefinedDownloadDirectory = folderChoosen.toString()
- userDownloadDirectoryPref.summary = folderChoosen.toString()
+ chooseFolder =
+ registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { folderChoosen ->
+ if (folderChoosen != null) {
+ AppPref(sharedPref).userDefinedDownloadDirectory = folderChoosen.toString()
+ userDownloadDirectoryPref.summary = folderChoosen.toString()
+ }
}
- }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/hisname/fireflyiii/ui/tags/MapsFragment.kt b/app/src/main/java/xyz/hisname/fireflyiii/ui/tags/MapsFragment.kt
index 31346ca6..e5764b90 100644
--- a/app/src/main/java/xyz/hisname/fireflyiii/ui/tags/MapsFragment.kt
+++ b/app/src/main/java/xyz/hisname/fireflyiii/ui/tags/MapsFragment.kt
@@ -22,7 +22,6 @@ import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
-import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import android.os.Bundle
@@ -50,18 +49,22 @@ import org.osmdroid.events.MapEventsReceiver
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
import org.osmdroid.util.GeoPoint
import org.osmdroid.views.CustomZoomButtonsController
-import org.osmdroid.views.overlay.*
+import org.osmdroid.views.overlay.MapEventsOverlay
+import org.osmdroid.views.overlay.Marker
import xyz.hisname.fireflyiii.BuildConfig
import xyz.hisname.fireflyiii.R
import xyz.hisname.fireflyiii.databinding.FragmentMapBinding
import xyz.hisname.fireflyiii.repository.models.nominatim.LocationSearchModel
import xyz.hisname.fireflyiii.ui.base.BaseFragment
-import xyz.hisname.fireflyiii.util.extension.*
import xyz.hisname.fireflyiii.util.extension.getViewModel
+import xyz.hisname.fireflyiii.util.extension.hideKeyboard
+import xyz.hisname.fireflyiii.util.extension.toastInfo
import xyz.hisname.fireflyiii.util.getUniqueHash
+import xyz.hisname.fireflyiii.util.isPermissionGranted
+import xyz.hisname.fireflyiii.util.launchLocationPermissionsRequest
import java.io.File
-class MapsFragment: BaseFragment() {
+class MapsFragment : BaseFragment() {
private val locationService by lazy { requireContext().getSystemService(Context.LOCATION_SERVICE) as LocationManager }
private val mapsViewModel by lazy { getViewModel(MapsViewModel::class.java) }
@@ -69,26 +72,40 @@ class MapsFragment: BaseFragment() {
private val latitudeBundle by lazy { arguments?.getString("latitude") }
private lateinit var startMarker: Marker
private lateinit var cloneLocationList: List
- private lateinit var gpsPermission: ActivityResultLauncher
+ private lateinit var gpsPermission: ActivityResultLauncher>
private var fragmentMapBinding: FragmentMapBinding? = null
private val binding get() = fragmentMapBinding!!
private val mapController by lazy { binding.maps.controller }
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
fragmentMapBinding = FragmentMapBinding.inflate(inflater, container, false)
- val view = binding.root
- return view
+ return binding.root
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- gpsPermission = registerForActivityResult(ActivityResultContracts.RequestPermission()) { success ->
- if(success) {
- if (ContextCompat.checkSelfPermission(requireContext(),
- Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
+ gpsPermission = registerForActivityResult(
+ ActivityResultContracts.RequestMultiplePermissions()
+ ) { success ->
+ if (success.any()) {
+ if (isPermissionGranted(Manifest.permission.ACCESS_COARSE_LOCATION)) {
toastInfo("Waiting for location...")
- locationService.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0L, 0f, locationListener)
- locationService.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0L, 0f, locationListener)
+ locationService.requestLocationUpdates(
+ LocationManager.GPS_PROVIDER,
+ 0L,
+ 0f,
+ locationListener,
+ )
+ locationService.requestLocationUpdates(
+ LocationManager.NETWORK_PROVIDER,
+ 0L,
+ 0f,
+ locationListener,
+ )
}
}
}
@@ -96,15 +113,26 @@ class MapsFragment: BaseFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- Configuration.getInstance().load(requireContext(), requireContext().getSharedPreferences(
- requireContext().getUniqueHash().toString() + "-user-preferences", Context.MODE_PRIVATE))
+ Configuration.getInstance().load(
+ requireContext(),
+ requireContext()
+ .getSharedPreferences(
+ requireContext().getUniqueHash() + "-user-preferences",
+ Context.MODE_PRIVATE
+ )
+ )
Configuration.getInstance().userAgentValue = BuildConfig.APPLICATION_ID
Configuration.getInstance().osmdroidBasePath = requireContext().filesDir
- Configuration.getInstance().osmdroidTileCache = File(requireContext().filesDir.toString() + "/tiles")
+ Configuration.getInstance().osmdroidTileCache =
+ File(requireContext().filesDir.toString() + "/tiles")
startMarker = Marker(binding.maps)
- if(!latitudeBundle.isNullOrEmpty() && !longitudeBundle.isNullOrEmpty()){
- setMap(GeoPoint(latitudeBundle?.toDouble() ?: 37.276675,
- longitudeBundle?.toDouble() ?: -115.798936))
+ if (!latitudeBundle.isNullOrEmpty() && !longitudeBundle.isNullOrEmpty()) {
+ setMap(
+ GeoPoint(
+ latitudeBundle?.toDouble() ?: 37.276675,
+ longitudeBundle?.toDouble() ?: -115.798936
+ )
+ )
} else {
isGpsEnabled()
}
@@ -125,7 +153,7 @@ class MapsFragment: BaseFragment() {
}
}
- private fun setMap(location: GeoPoint){
+ private fun setMap(location: GeoPoint) {
startMarker.position = location
startMarker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM)
binding.maps.setMultiTouchControls(true)
@@ -141,17 +169,17 @@ class MapsFragment: BaseFragment() {
mapController.setZoom(18.0)
}
- private fun searchLocation(){
- binding.mapSearch.setOnKeyListener { v, keyCode, event ->
- if(event.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER){
+ private fun searchLocation() {
+ binding.mapSearch.setOnKeyListener { _, keyCode, event ->
+ if (event.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER) {
location(binding.mapSearch.text.toString())
hideKeyboard()
}
false
}
- binding.mapSearch.addTextChangedListener(object : TextWatcher{
+ binding.mapSearch.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(editable: Editable) {
- if(editable.isNotBlank()) {
+ if (editable.isNotBlank()) {
location(editable.toString())
}
}
@@ -162,22 +190,23 @@ class MapsFragment: BaseFragment() {
override fun onTextChanged(charSequence: CharSequence, p1: Int, p2: Int, p3: Int) {}
})
- binding.mapSearch.onItemClickListener = AdapterView.OnItemClickListener { adapterView, view, i, l ->
+ binding.mapSearch.onItemClickListener = AdapterView.OnItemClickListener { _, _, i, _ ->
setMap(GeoPoint(cloneLocationList[i].lat, cloneLocationList[i].lon))
}
}
- private fun location(query: String){
- mapsViewModel.getLocationFromQuery(query).observe(viewLifecycleOwner){ data ->
- if(data.isNotEmpty()){
- val adapter = ArrayAdapter(requireContext(), android.R.layout.select_dialog_item, data)
+ private fun location(query: String) {
+ mapsViewModel.getLocationFromQuery(query).observe(viewLifecycleOwner) { data ->
+ if (data.isNotEmpty()) {
+ val adapter =
+ ArrayAdapter(requireContext(), android.R.layout.select_dialog_item, data)
binding.mapSearch.setAdapter(adapter)
}
}
}
- private fun setMapClick(){
- val mapReceiver = object : MapEventsReceiver{
+ private fun setMapClick() {
+ val mapReceiver = object : MapEventsReceiver {
override fun longPressHelper(geoPoint: GeoPoint): Boolean {
setMap(geoPoint)
return true
@@ -191,65 +220,81 @@ class MapsFragment: BaseFragment() {
binding.maps.overlays.add(MapEventsOverlay(mapReceiver))
}
- private fun setFab(){
+ private fun setFab() {
binding.fabMap.setImageDrawable(IconicsDrawable(requireContext()).apply {
icon = GoogleMaterial.Icon.gmd_my_location
colorRes = R.color.md_black_1000
sizeDp = 16
})
binding.fabMap.setOnClickListener {
- if(ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_FINE_LOCATION)
- != PackageManager.PERMISSION_GRANTED){
- if (ActivityCompat.shouldShowRequestPermissionRationale(requireActivity(),
- Manifest.permission.ACCESS_FINE_LOCATION)){
+ if (!isPermissionGranted(Manifest.permission.ACCESS_COARSE_LOCATION)) {
+ if (ActivityCompat.shouldShowRequestPermissionRationale(
+ requireActivity(),
+ Manifest.permission.ACCESS_COARSE_LOCATION
+ )
+ ) {
AlertDialog.Builder(requireActivity())
- .setTitle("Grant access to location data?")
- .setMessage("Choosing coordinates data is simple when location data permission is granted. " +
- "Otherwise you may have to manually search for your location")
- .setPositiveButton("OK"){_,_ ->
- gpsPermission.launch(Manifest.permission.ACCESS_FINE_LOCATION)
- }
- .setNegativeButton("No"){ _,_ ->
- toastInfo("Alright...")
- }
- .show()
- } else {
- gpsPermission.launch(Manifest.permission.ACCESS_FINE_LOCATION)
- }
+ .setTitle("Grant access to location data?")
+ .setMessage(
+ "Choosing coordinates data is simple when location data permission is granted. " +
+ "Otherwise you may have to manually search for your location"
+ )
+ .setPositiveButton("OK") { _, _ ->
+ gpsPermission.launchLocationPermissionsRequest()
+ }
+ .setNegativeButton("No") { _, _ ->
+ toastInfo("Alright...")
+ }
+ .show()
+ } else
+ gpsPermission.launchLocationPermissionsRequest()
} else {
- locationService.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0L, 0f, locationListener)
- locationService.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0L, 0f, locationListener)
+ locationService.requestLocationUpdates(
+ LocationManager.GPS_PROVIDER,
+ 0L,
+ 0f,
+ locationListener
+ )
+ locationService.requestLocationUpdates(
+ LocationManager.NETWORK_PROVIDER,
+ 0L,
+ 0f,
+ locationListener
+ )
}
}
}
- private val locationListener: LocationListener = object : LocationListener {
- override fun onLocationChanged(location: Location) {
- setMap(GeoPoint(location.latitude, location.longitude))
- }
- override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {}
- override fun onProviderEnabled(provider: String) {}
- override fun onProviderDisabled(provider: String) {}
+ private val locationListener: LocationListener = LocationListener { location ->
+ setMap(GeoPoint(location.latitude, location.longitude))
}
- private fun isGpsEnabled(){
- if(!locationService.isProviderEnabled(LocationManager.GPS_PROVIDER)){
+ private fun isGpsEnabled() {
+ if (!locationService.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
AlertDialog.Builder(requireActivity())
- .setMessage("For a better experience turn on device's location")
- .setPositiveButton("Sure"){_, _ ->
- requireActivity().startActivity(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS))
- }
- .setNegativeButton("No"){ _, _ ->
- toastInfo("Alright...Using Network data instead.")
- }
- .show()
+ .setMessage("For a better experience turn on device's location")
+ .setPositiveButton("Sure") { _, _ ->
+ requireActivity().startActivity(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS))
+ }
+ .setNegativeButton("No") { _, _ ->
+ toastInfo("Alright...Using Network data instead.")
+ }
+ .show()
} else {
- if (ContextCompat.checkSelfPermission(requireContext(),
- Manifest.permission.ACCESS_FINE_LOCATION)
- == PackageManager.PERMISSION_GRANTED) {
- toastInfo("Acquiring current location...")
- locationService.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0L, 0f, locationListener)
- locationService.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0L, 0f, locationListener)
+ if (isPermissionGranted(Manifest.permission.ACCESS_COARSE_LOCATION)) {
+ toastInfo("Acquiring current location...")
+ locationService.requestLocationUpdates(
+ LocationManager.GPS_PROVIDER,
+ 0L,
+ 0f,
+ locationListener
+ )
+ locationService.requestLocationUpdates(
+ LocationManager.NETWORK_PROVIDER,
+ 0L,
+ 0f,
+ locationListener
+ )
} else {
setMap(GeoPoint(37.276675, -115.798936))
}
@@ -264,9 +309,7 @@ class MapsFragment: BaseFragment() {
override fun onPause() {
super.onPause()
binding.maps.onPause()
- if (ContextCompat.checkSelfPermission(requireContext(),
- Manifest.permission.ACCESS_FINE_LOCATION)
- == PackageManager.PERMISSION_GRANTED) {
+ if (isPermissionGranted(Manifest.permission.ACCESS_COARSE_LOCATION)) {
locationService.removeUpdates(locationListener)
}
}
diff --git a/app/src/main/java/xyz/hisname/fireflyiii/ui/transaction/TransactionSeparatorAdapter.kt b/app/src/main/java/xyz/hisname/fireflyiii/ui/transaction/TransactionSeparatorAdapter.kt
index b3246f97..bad96761 100644
--- a/app/src/main/java/xyz/hisname/fireflyiii/ui/transaction/TransactionSeparatorAdapter.kt
+++ b/app/src/main/java/xyz/hisname/fireflyiii/ui/transaction/TransactionSeparatorAdapter.kt
@@ -102,6 +102,7 @@ class TransactionSeparatorAdapter(private val clickListener:(Transactions) -> Un
}
binding.listItem.setOnClickListener {clickListener(separator.transaction)}
}
+ else -> null
}
}
}
diff --git a/app/src/main/java/xyz/hisname/fireflyiii/ui/transaction/list/TransactionFragment.kt b/app/src/main/java/xyz/hisname/fireflyiii/ui/transaction/list/TransactionFragment.kt
index 5992b1c9..c3789377 100644
--- a/app/src/main/java/xyz/hisname/fireflyiii/ui/transaction/list/TransactionFragment.kt
+++ b/app/src/main/java/xyz/hisname/fireflyiii/ui/transaction/list/TransactionFragment.kt
@@ -44,10 +44,10 @@ import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.datepicker.MaterialDatePicker
-import com.kizitonwose.calendarview.model.CalendarDay
-import com.kizitonwose.calendarview.model.DayOwner
-import com.kizitonwose.calendarview.ui.DayBinder
-import com.kizitonwose.calendarview.ui.ViewContainer
+import com.kizitonwose.calendar.core.CalendarDay
+import com.kizitonwose.calendar.core.DayPosition
+import com.kizitonwose.calendar.view.MonthDayBinder
+import com.kizitonwose.calendar.view.ViewContainer
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.fontawesome.FontAwesome
import com.mikepenz.iconics.utils.sizeDp
@@ -168,7 +168,7 @@ class TransactionFragment: BaseFragment(){
view.setOnClickListener {
selectedDates.clear()
dateRange.clear()
- if (day.owner == DayOwner.THIS_MONTH) {
+ if (day.position == DayPosition.MonthDate) {
selectedDate = day.date
selectedDates.clear()
binding.transactionCalendar.notifyCalendarChanged()
@@ -221,25 +221,25 @@ class TransactionFragment: BaseFragment(){
}
}
}
- binding.transactionCalendar.dayBinder = object: DayBinder{
- override fun bind(container: DayViewContainer, day: CalendarDay) {
- container.day = day
+ binding.transactionCalendar.dayBinder = object: MonthDayBinder{
+ override fun bind(container: DayViewContainer, data: CalendarDay) {
+ container.day = data
val textView = container.onDayText
- textView.text = day.date.dayOfMonth.toString()
+ textView.text = data.date.dayOfMonth.toString()
setShowCase(textView)
- if (day.owner == DayOwner.THIS_MONTH) {
+ if (data.position == DayPosition.MonthDate) {
when {
- selectedDates.contains(day.date) -> {
+ selectedDates.contains(data.date) -> {
if (selectedDates.size != 2){
dateRange.clear()
}
textView.setTextColor(setDayNightTheme())
textView.setBackgroundResource(R.drawable.today_bg)
}
- dateRange.contains(day.date) -> {
+ dateRange.contains(data.date) -> {
textView.setBackgroundResource(R.drawable.today_bg)
}
- selectedDate == day.date -> {
+ selectedDate == data.date -> {
if(selectedDate == today){
textView.setTextColor(getCompatColor(R.color.md_red_800))
} else {
@@ -269,10 +269,9 @@ class TransactionFragment: BaseFragment(){
val currentMonth = YearMonth.now()
val startMonth = currentMonth.minusYears(80)
val endMonth = currentMonth.plusYears(20)
- binding.transactionCalendar.setupAsync(startMonth, endMonth, DayOfWeek.SUNDAY){
- binding.transactionCalendar.scrollToMonth(currentMonth)
- binding.transactionCalendar.updateMonthConfiguration()
- }
+ binding.transactionCalendar.setup(startMonth, endMonth, DayOfWeek.SUNDAY)
+ binding.transactionCalendar.scrollToMonth(currentMonth)
+ binding.transactionCalendar.updateMonthData()
}
private fun dragAndDrop(){
diff --git a/app/src/main/java/xyz/hisname/fireflyiii/util/Permissions.kt b/app/src/main/java/xyz/hisname/fireflyiii/util/Permissions.kt
new file mode 100644
index 00000000..97853254
--- /dev/null
+++ b/app/src/main/java/xyz/hisname/fireflyiii/util/Permissions.kt
@@ -0,0 +1,40 @@
+package xyz.hisname.fireflyiii.util
+
+import android.Manifest
+import android.content.Context
+import android.content.pm.PackageManager
+import androidx.activity.result.ActivityResultLauncher
+import androidx.core.content.ContextCompat
+import androidx.fragment.app.Fragment
+
+/**
+ * Checks if the given permission has been granted.
+ * @author Arnau Mora
+ * @since 20221124
+ */
+fun Context.isPermissionGranted(permission: String) =
+ ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED
+
+/**
+ * Alias for [Fragment.requireContext].[Context.isPermissionGranted].
+ * Checks if the given permission has been granted.
+ * @author Arnau Mora
+ * @since 20221124
+ * @throws IllegalStateException If not currently associated with a context.
+ */
+@Throws(IllegalStateException::class)
+fun Fragment.isPermissionGranted(permission: String) =
+ requireContext().isPermissionGranted(permission)
+
+/**
+ * Runs [ActivityResultLauncher.launch] with the [Manifest.permission.ACCESS_COARSE_LOCATION] and
+ * [Manifest.permission.ACCESS_FINE_LOCATION] permissions.
+ * @author Arnau Mora
+ * @since 20221124
+ */
+fun ActivityResultLauncher>.launchLocationPermissionsRequest() = launch(
+ arrayOf(
+ Manifest.permission.ACCESS_COARSE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ )
+)
diff --git a/app/src/main/res/layout/fragment_bill_details.xml b/app/src/main/res/layout/fragment_bill_details.xml
index b40e9866..29a3a8fe 100644
--- a/app/src/main/res/layout/fragment_bill_details.xml
+++ b/app/src/main/res/layout/fragment_bill_details.xml
@@ -134,14 +134,14 @@
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp" />
-
+ app:cv_scrollPaged="true" />
@@ -208,14 +208,14 @@
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp" />
-
+ app:cv_scrollPaged="true" />
-
diff --git a/app/src/main/res/values/array.xml b/app/src/main/res/values/array.xml
index c8f63f55..2018fcfc 100644
--- a/app/src/main/res/values/array.xml
+++ b/app/src/main/res/values/array.xml
@@ -40,32 +40,6 @@
- 60
-
- - English
- - Deutsch
- - 正體中文
- - 简体中文
- - Français
- - Italiano
- - Nederlands
- - Español
- - Русский
- - Português do Brasil
-
-
-
- - en
- - de
- - zh-rTW
- - zh-rCN
- - fr
- - it
- - nl
- - es
- - ru
- - pt-rBR
-
-
- Connected
- Un-Metered
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index b8dc1f12..156c6107 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -57,7 +57,7 @@
- false
- true
- - @style/MaterialDrawer.DrawerArrowStyle
+