____ _ _______ __
/ __ \ | /| / / __/ |/ /
/ /_/ / |/ |/ / _// /
\____/|__/|__/___/_||_/
owenclave / app/src/main/java/io/nekohasekai/sagernet/tasker/TaskerActivity.kt
/******************************************************************************
* Copyright (C) 2022 by nekohasekai *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see . *
* *
******************************************************************************/
package io.nekohasekai.sagernet.tasker
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import androidx.activity.OnBackPressedCallback
import androidx.activity.result.component1
import androidx.activity.result.component2
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.preference.ListPreference
import androidx.preference.PreferenceDataStore
import androidx.preference.PreferenceFragmentCompat
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import io.nekohasekai.sagernet.Key
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.database.preference.OnPreferenceDataStoreChangeListener
import io.nekohasekai.sagernet.ktx.Logs
import io.nekohasekai.sagernet.ktx.runOnDefaultDispatcher
import io.nekohasekai.sagernet.ktx.runOnMainDispatcher
import io.nekohasekai.sagernet.ui.ProfileSelectActivity
import io.nekohasekai.sagernet.ui.ThemedActivity
import io.nekohasekai.sagernet.widget.TaskerProfilePreference
class TaskerActivity : ThemedActivity(R.layout.layout_config_settings),
OnPreferenceDataStoreChangeListener {
companion object {
const val KEY_DIRTY = "dirty"
}
val settings by lazy { TaskerBundle.fromIntent(intent) }
var dirty = false
override val onBackPressedCallback = object : OnBackPressedCallback(enabled = false) {
override fun handleOnBackPressed() {
MaterialAlertDialogBuilder(this@TaskerActivity)
.setTitle(R.string.unsaved_changes_prompt)
.setPositiveButton(android.R.string.ok) { _, _ ->
runOnDefaultDispatcher {
saveAndExit()
}
}
.setNegativeButton(android.R.string.cancel) { _, _ ->
finish()
}
.show()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.settings)) { v, insets ->
val bars = insets.getInsets(
WindowInsetsCompat.Type.systemBars()
or WindowInsetsCompat.Type.displayCutout()
)
v.updatePadding(
left = bars.left,
right = bars.right,
bottom = bars.bottom,
)
insets
}
setSupportActionBar(findViewById(R.id.toolbar))
supportActionBar?.apply {
setTitle(R.string.tasker_settings)
setDisplayHomeAsUpEnabled(true)
setHomeAsUpIndicator(R.drawable.ic_navigation_close)
}
supportFragmentManager.beginTransaction()
.replace(R.id.settings, MyPreferenceFragmentCompat())
.commit()
DataStore.profileCacheStore.registerChangeListener(this)
savedInstanceState?.getBoolean(KEY_DIRTY)?.let {
dirty = it
onBackPressedCallback.isEnabled = it
}
}
lateinit var profile: TaskerProfilePreference
lateinit var action: ListPreference
fun PreferenceFragmentCompat.createPreferences(
savedInstanceState: Bundle?,
rootKey: String?,
) {
addPreferencesFromResource(R.xml.tasker_preferences)
profile = findPreference(Key.TASKER_PROFILE)!!
profile.setOnPreferenceChangeListener { _, newValue ->
if (newValue == "1") {
selectProfileForTasker.launch(Intent(
this@TaskerActivity, ProfileSelectActivity::class.java
).apply {
putExtra(ProfileSelectActivity.EXTRA_SELECTED, settings.profileId)
})
}
true
}
action = findPreference(Key.TASKER_ACTION)!!
profile.isEnabled = action.value == "0"
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putBoolean(KEY_DIRTY, dirty)
}
override fun onPreferenceDataStoreChanged(store: PreferenceDataStore, key: String) {
if (key != Key.PROFILE_DIRTY) {
dirty = true
onBackPressedCallback.isEnabled = true
}
when (key) {
Key.TASKER_ACTION -> {
settings.action = DataStore.taskerAction
profile.isEnabled = settings.action == TaskerBundle.ACTION_START
}
Key.TASKER_PROFILE -> {
if (DataStore.taskerProfile == 0) DataStore.taskerProfileId = -1L
}
Key.TASKER_PROFILE_ID -> {
settings.profileId = DataStore.taskerProfileId
if (settings.profileId > 0L) runOnMainDispatcher {
profile.summaryProvider = profile.summaryProvider
}
}
}
}
val selectProfileForTasker = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { (resultCode, data) ->
if (resultCode == Activity.RESULT_OK) {
val profileId = data!!.getLongExtra(ProfileSelectActivity.EXTRA_PROFILE_ID, -1L)
runOnDefaultDispatcher {
DataStore.taskerProfileId = profileId
}
}
}
fun saveAndExit() {
setResult(RESULT_OK, settings.toIntent())
finish()
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.profile_apply_menu, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.action_apply -> {
runOnDefaultDispatcher {
saveAndExit()
}
true
}
else -> false
}
override fun onSupportNavigateUp(): Boolean {
if (!super.onSupportNavigateUp()) finish()
return true
}
override fun onDestroy() {
DataStore.profileCacheStore.unregisterChangeListener(this)
super.onDestroy()
}
class MyPreferenceFragmentCompat : PreferenceFragmentCompat() {
val activity: TaskerActivity
get() = requireActivity() as TaskerActivity
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
preferenceManager.preferenceDataStore = DataStore.profileCacheStore
try {
activity.apply {
createPreferences(savedInstanceState, rootKey)
}
} catch (e: Exception) {
Logs.w(e)
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
ViewCompat.setOnApplyWindowInsetsListener(listView) { v, insets ->
val bars = insets.getInsets(
WindowInsetsCompat.Type.systemBars()
or WindowInsetsCompat.Type.displayCutout()
)
v.updatePadding(
left = bars.left,
right = bars.right,
bottom = bars.bottom,
)
insets
}
}
}
}