diff --git a/app/src/main/java/com/denizd/substitutionplan/database/SettingsRepository.kt b/app/src/main/java/com/denizd/substitutionplan/database/SettingsRepository.kt index 8b7a498..d1eeae3 100644 --- a/app/src/main/java/com/denizd/substitutionplan/database/SettingsRepository.kt +++ b/app/src/main/java/com/denizd/substitutionplan/database/SettingsRepository.kt @@ -1,16 +1,20 @@ package com.denizd.substitutionplan.database +import android.annotation.TargetApi import android.app.Application import android.database.Cursor import android.media.RingtoneManager import androidx.preference.PreferenceManager +import com.denizd.substitutionplan.data.Caller import com.denizd.substitutionplan.data.SubstUtil import com.denizd.substitutionplan.models.Ringtone internal class SettingsRepository(private val application: Application) { private val prefs = PreferenceManager.getDefaultSharedPreferences(application) - val notificationChannel = SubstUtil.getNotificationChannel(application, prefs) + + @TargetApi(26) + fun getNotificationChannel() = SubstUtil.getNotificationChannel(application, prefs) val ringtones: List by lazy { lateinit var ringtoneCursor: Cursor @@ -56,5 +60,12 @@ internal class SettingsRepository(private val application: Application) { prefs.edit().putString(key, value).apply() } + fun clearTimes() { + prefs.edit().putString("timeNew", "").putString("newFoodTime", "").apply() + } + fun forceRefresh(): Pair { + clearTimes() + return SubstRepository(application).fetchDataOnline(Caller.SETTINGS) + } } \ No newline at end of file diff --git a/app/src/main/java/com/denizd/substitutionplan/fragments/SettingsFragment.kt b/app/src/main/java/com/denizd/substitutionplan/fragments/SettingsFragment.kt index 05282ee..e6b7a62 100644 --- a/app/src/main/java/com/denizd/substitutionplan/fragments/SettingsFragment.kt +++ b/app/src/main/java/com/denizd/substitutionplan/fragments/SettingsFragment.kt @@ -17,6 +17,7 @@ import android.widget.* import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatDelegate import androidx.browser.customtabs.CustomTabsIntent +import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProviders @@ -32,6 +33,7 @@ import com.denizd.substitutionplan.models.Colour import com.denizd.substitutionplan.viewmodels.SettingsViewModel import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.button.MaterialButton +import com.google.android.material.snackbar.Snackbar import com.google.firebase.messaging.FirebaseMessaging import kotlin.collections.ArrayList @@ -51,6 +53,7 @@ internal class SettingsFragment : Fragment(), View.OnClickListener, View.OnLongC private lateinit var binding: ContentSettingsBinding private lateinit var viewModel: SettingsViewModel + private lateinit var snackBarContainer: CoordinatorLayout override fun onAttach(context: Context) { super.onAttach(context) @@ -66,6 +69,7 @@ internal class SettingsFragment : Fragment(), View.OnClickListener, View.OnLongC super.onViewCreated(view, savedInstanceState) viewModel = ViewModelProviders.of(this)[SettingsViewModel::class.java] + snackBarContainer = view.rootView.findViewById(R.id.coordination) // TODO replace findViewById with ViewBinding binding.apply { val thisFragment = this@SettingsFragment @@ -92,7 +96,6 @@ internal class SettingsFragment : Fragment(), View.OnClickListener, View.OnLongC // Set on long click listeners buttonCustomiseColours.setOnLongClickListener(thisFragment) - buttonCoursesHelp.setOnLongClickListener(thisFragment) buttonOrderHelp.setOnLongClickListener(thisFragment) buttonForcedRefresh.setOnLongClickListener(thisFragment) @@ -203,14 +206,13 @@ internal class SettingsFragment : Fragment(), View.OnClickListener, View.OnLongC R.id.button_courses_help -> createDialog(getString(R.string.courses_help_dialog_title), getString(R.string.courses_help_dialog_text)) R.id.button_order_help -> createDialog(getString(R.string.ordering_systems_dialog_title), getString(R.string.ordering_systems_dialog_text)) R.id.button_forced_refresh -> { -// val repo = SubstRepository(activity?.application!!) -// repo.putAndApplyString("timeNew", "") -// repo.putAndApplyString("newFoodTime", "") -// repo.fetchDataOnline(Caller.SETTINGS) - // TODO needs async - // TODO put in viewModel - // TODO create viewModel - // TODO remove TODOs + viewModel.forceRefresh { result, error -> + val snackBar = Snackbar.make(snackBarContainer, result, Snackbar.LENGTH_LONG) + if (error) { + snackBar.setBackgroundTint(ContextCompat.getColor(requireContext(), R.color.colorError)) + } + snackBar.show() + } } R.id.button_visit_website -> { try { @@ -244,29 +246,12 @@ internal class SettingsFragment : Fragment(), View.OnClickListener, View.OnLongC customiseColoursButtonLongPressed = !customiseColoursButtonLongPressed true } - R.id.button_courses_help -> { - val link = Uri.parse("https://www.youtube.com/watch?v=Jc2xfYuLWgE") // 'Freak' - makeToast(String(Character.toChars(0x2764))) - try { - val intent = Intent(Intent.ACTION_VIEW, link) - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK).setPackage("com.google.android.youtube") - startActivity(intent) - } catch (e: ActivityNotFoundException) { - try { - customTabsIntent.launchUrl(mContext, link) - } catch (e: ActivityNotFoundException) { - makeToast(getString(R.string.youtube_not_found)) - } - } - true - } R.id.button_order_help -> { debugMenu() true } R.id.button_forced_refresh -> { - viewModel.setAndApplyString("timeNew", "") - viewModel.setAndApplyString("newFoodTime", "") + viewModel.clearTimes() makeToast(mContext.getString(R.string.force_refresh_cleared_times)) true } @@ -332,6 +317,8 @@ internal class SettingsFragment : Fragment(), View.OnClickListener, View.OnLongC } // Functions for handling dialog creation + // TODO put this in view model layer + // TODO possibly replace with DialogFragment private fun createDialog(title: String, text: String) { val alertDialog = AlertDialog.Builder(mContext) val dialogView = View.inflate(mContext, R.layout.simple_dialog, null) @@ -359,7 +346,7 @@ internal class SettingsFragment : Fragment(), View.OnClickListener, View.OnLongC private fun createRingtoneDialog() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - viewModel.notificationChannel + viewModel.getNotificationChannel() val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS).apply { putExtra(Settings.EXTRA_APP_PACKAGE, mContext.applicationContext.packageName) putExtra(Settings.EXTRA_CHANNEL_ID, "general") @@ -602,14 +589,15 @@ internal class SettingsFragment : Fragment(), View.OnClickListener, View.OnLongC // Below this point follow string literals that I didn't bother putting in /res/values/strings // TODO check if this is displayed properly - private val licences = """Libraries: - • jsoup HTML parser © 2009-2018 Jonathan Hedley, licensed under the open source MIT Licence - Font: - • Manrope © 2018-2019 Michael Sharanda, licensed under the SIL Open Font Licence 1.1 - Icons: - • bqlqn - • fjstudio - • Freepik - • Smashicons - • © 2013-2019 Freepik Company S.L., licensed under Creative Commons BY 3.0""".trimIndent() + private val licences = """ + Libraries: + • jsoup HTML parser © 2009-2018 Jonathan Hedley, licensed under the open source MIT Licence + + Font: + • Manrope © 2018-2019 Michael Sharanda, licensed under the SIL Open Font Licence 1.1 + + Icons: + • Authors: bqlqn, fjstudio, Freepik, Smashicons + • © 2013-2019 Freepik Company S.L., licensed under Creative Commons BY 3.0 + """.trimIndent() } \ No newline at end of file diff --git a/app/src/main/java/com/denizd/substitutionplan/viewmodels/FoodViewModel.kt b/app/src/main/java/com/denizd/substitutionplan/viewmodels/FoodViewModel.kt index e75e912..5157a47 100644 --- a/app/src/main/java/com/denizd/substitutionplan/viewmodels/FoodViewModel.kt +++ b/app/src/main/java/com/denizd/substitutionplan/viewmodels/FoodViewModel.kt @@ -6,6 +6,7 @@ import androidx.lifecycle.LiveData import com.denizd.substitutionplan.data.Caller import com.denizd.substitutionplan.database.SubstRepository import com.denizd.substitutionplan.models.Food +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.async import kotlinx.coroutines.launch @@ -23,9 +24,9 @@ internal class FoodViewModel(application: Application) : AndroidViewModel(applic fun refresh(updateUi: (result: String, error: Boolean) -> Unit) { GlobalScope.launch { - val task = GlobalScope.async { repo.fetchDataOnline(Caller.FOODMENU) } + val task = async { repo.fetchDataOnline(Caller.FOODMENU) } val result = task.await() - updateUi(result.first, result.second) + launch(Dispatchers.Main) { updateUi(result.first, result.second) } } } } diff --git a/app/src/main/java/com/denizd/substitutionplan/viewmodels/SettingsViewModel.kt b/app/src/main/java/com/denizd/substitutionplan/viewmodels/SettingsViewModel.kt index 220874c..b7dbe73 100644 --- a/app/src/main/java/com/denizd/substitutionplan/viewmodels/SettingsViewModel.kt +++ b/app/src/main/java/com/denizd/substitutionplan/viewmodels/SettingsViewModel.kt @@ -1,10 +1,11 @@ package com.denizd.substitutionplan.viewmodels +import android.annotation.TargetApi import android.app.Application import androidx.lifecycle.AndroidViewModel -import com.denizd.substitutionplan.data.SubstUtil import com.denizd.substitutionplan.database.SettingsRepository import com.denizd.substitutionplan.models.Ringtone +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.async import kotlinx.coroutines.launch @@ -25,7 +26,7 @@ internal class SettingsViewModel(application: Application) : AndroidViewModel(ap get() = repo.getBool("autoRefresh") set(it) = repo.setAndApplyBool("autoRefresh", it) var shouldUseAppSorting: Boolean - get() = repo.getBool("app_specific_sorting") + get() = repo.getBool("app_specific_sorting", true) set(it) = repo.setAndApplyBool("app_specific_sorting", it) @@ -43,16 +44,17 @@ internal class SettingsViewModel(application: Application) : AndroidViewModel(ap get() = repo.getInt("themeInt") set(it) = repo.setAndApplyInt("themeInt", it) - val notificationChannel = repo.notificationChannel + @TargetApi(26) + fun getNotificationChannel() = repo.getNotificationChannel() lateinit var ringtones: List val ringtonesInitialised = ::ringtones.isInitialized fun getRingtones(updateUi: () -> Unit) { GlobalScope.launch { - val task = GlobalScope.async { repo.ringtones } + val task = async { repo.ringtones } ringtones = task.await() - updateUi() + launch(Dispatchers.Main) { updateUi() } } } @@ -62,4 +64,11 @@ internal class SettingsViewModel(application: Application) : AndroidViewModel(ap fun setAndApplyBool(key: String, value: Boolean) = repo.setAndApplyBool(key, value) fun getString(key: String, default: String = "", ifNull: String = "") = repo.getString(key, default) ?: ifNull fun setAndApplyString(key: String, value: String) = repo.setAndApplyString(key, value) + + fun clearTimes() = repo.clearTimes() + fun forceRefresh(updateUi: (result: String, error: Boolean) -> Unit) = GlobalScope.launch { + val task = async { repo.forceRefresh() } + val result = task.await() + launch(Dispatchers.Main) { updateUi(result.first, result.second) } + } } \ No newline at end of file diff --git a/app/src/main/java/com/denizd/substitutionplan/viewmodels/SubstViewModel.kt b/app/src/main/java/com/denizd/substitutionplan/viewmodels/SubstViewModel.kt index a79ebaf..6cb5b5f 100644 --- a/app/src/main/java/com/denizd/substitutionplan/viewmodels/SubstViewModel.kt +++ b/app/src/main/java/com/denizd/substitutionplan/viewmodels/SubstViewModel.kt @@ -4,6 +4,7 @@ import android.app.Application import android.content.res.Configuration import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData +import androidx.lifecycle.viewModelScope import com.denizd.substitutionplan.data.Caller import com.denizd.substitutionplan.database.SubstRepository import com.denizd.substitutionplan.models.Substitution @@ -37,11 +38,9 @@ internal class SubstViewModel(application: Application) : AndroidViewModel(appli return repo.getGridColumnCount(config) } - fun refresh(updateUi: (result: String, error: Boolean) -> Unit) { - GlobalScope.launch { - val task = GlobalScope.async { repo.fetchDataOnline(Caller.SUBSTITUTION) } - val result = task.await() - updateUi(result.first, result.second) - } + fun refresh(updateUi: (result: String, error: Boolean) -> Unit) = GlobalScope.launch { + val task = async { repo.fetchDataOnline(Caller.SUBSTITUTION) } + val result = task.await() + launch(Dispatchers.Main) { updateUi(result.first, result.second) } } }