Refactor: refactor design structure

This commit is contained in:
kr328 2021-09-12 13:01:08 +08:00
parent 603cec113c
commit 4a8b6b2704
9 changed files with 75 additions and 688 deletions

View File

@ -70,10 +70,6 @@ subprojects {
versionName = buildVersionName
versionCode = buildVersionCode
if (!isApp) {
consumerProguardFiles("consumer-rules.pro")
}
resValue("string", "release_name", "v$buildVersionName")
resValue("integer", "release_code", "$buildVersionCode")
@ -82,6 +78,10 @@ subprojects {
abiFilters("arm64-v8a", "armeabi-v7a", "x86", "x86_64")
}
}
if (!isApp) {
consumerProguardFiles("consumer-rules.pro")
}
}
if (isApp) {
@ -108,6 +108,8 @@ subprojects {
dimension = defaultDimension
versionNameSuffix = ".foss"
buildConfigField("boolean", "PREMIUM", "Boolean.parseBoolean(\"false\")")
if (isApp) {
applicationIdSuffix = ".foss"
}
@ -116,6 +118,8 @@ subprojects {
dimension = defaultDimension
versionNameSuffix = ".premium"
buildConfigField("boolean", "PREMIUM", "Boolean.parseBoolean(\"true\")")
val trackFile = rootProject.file("track.properties")
if (trackFile.exists()) {
val track = Properties().apply {

View File

@ -1,399 +0,0 @@
package com.github.kr328.clash.design
import android.content.Context
import android.view.View
import com.github.kr328.clash.core.model.ConfigurationOverride
import com.github.kr328.clash.core.model.LogMessage
import com.github.kr328.clash.core.model.TunnelState
import com.github.kr328.clash.design.adapter.SideloadProviderAdapter
import com.github.kr328.clash.design.databinding.DesignSettingsOverideBinding
import com.github.kr328.clash.design.databinding.DialogPreferenceListBinding
import com.github.kr328.clash.design.dialog.FullScreenDialog
import com.github.kr328.clash.design.model.AppInfo
import com.github.kr328.clash.design.preference.*
import com.github.kr328.clash.design.util.*
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
import kotlin.coroutines.resume
class OverrideSettingsDesign(
context: Context,
configuration: ConfigurationOverride
) : Design<OverrideSettingsDesign.Request>(context) {
enum class Request {
ResetOverride, EditSideloadGeoip
}
private val binding = DesignSettingsOverideBinding
.inflate(context.layoutInflater, context.root, false)
override val root: View
get() = binding.root
suspend fun requestResetConfirm(): Boolean {
return suspendCancellableCoroutine { ctx ->
val dialog = MaterialAlertDialogBuilder(context)
.setTitle(R.string.reset_override_settings)
.setMessage(R.string.reset_override_settings_message)
.setPositiveButton(R.string.ok) { _, _ -> ctx.resume(true) }
.setNegativeButton(R.string.cancel) { _, _ -> }
.show()
dialog.setOnDismissListener {
if (!ctx.isCompleted)
ctx.resume(false)
}
ctx.invokeOnCancellation {
dialog.dismiss()
}
}
}
suspend fun requestSelectSideload(initial: String, apps: List<AppInfo>): String =
withContext(Dispatchers.Main) {
suspendCancellableCoroutine { ctx ->
val binding = DialogPreferenceListBinding
.inflate(context.layoutInflater, context.root, false)
val adapter = SideloadProviderAdapter(context, apps, initial)
val dialog = FullScreenDialog(context)
dialog.setContentView(binding.root)
binding.surface = dialog.surface
binding.titleView.text = context.getString(R.string.sideload_geoip)
binding.newView.visibility = View.INVISIBLE
binding.mainList.applyLinearAdapter(context, adapter)
binding.resetView.setOnClickListener {
ctx.resume("")
dialog.dismiss()
}
binding.cancelView.setOnClickListener {
dialog.dismiss()
}
binding.okView.setOnClickListener {
ctx.resume(adapter.selectedPackageName)
dialog.dismiss()
}
dialog.setOnDismissListener {
if (!ctx.isCompleted)
ctx.resume(initial)
}
dialog.show()
}
}
init {
binding.self = this
binding.activityBarLayout.applyFrom(context)
binding.scrollRoot.bindAppBarElevation(binding.activityBarLayout)
val booleanValues: Array<Boolean?> = arrayOf(
null,
true,
false
)
val booleanValuesText: Array<Int> = arrayOf(
R.string.dont_modify,
R.string.enabled,
R.string.disabled
)
val screen = preferenceScreen(context) {
category(R.string.general)
editableText(
value = configuration::httpPort,
adapter = NullableTextAdapter.Port,
title = R.string.http_port,
placeholder = R.string.dont_modify,
empty = R.string.disabled,
)
editableText(
value = configuration::socksPort,
adapter = NullableTextAdapter.Port,
title = R.string.socks_port,
placeholder = R.string.dont_modify,
empty = R.string.disabled,
)
editableText(
value = configuration::redirectPort,
adapter = NullableTextAdapter.Port,
title = R.string.redirect_port,
placeholder = R.string.dont_modify,
empty = R.string.disabled,
)
editableText(
value = configuration::tproxyPort,
adapter = NullableTextAdapter.Port,
title = R.string.tproxy_port,
placeholder = R.string.dont_modify,
empty = R.string.disabled,
)
editableText(
value = configuration::mixedPort,
adapter = NullableTextAdapter.Port,
title = R.string.mixed_port,
placeholder = R.string.dont_modify,
empty = R.string.disabled,
)
editableTextList(
value = configuration::authentication,
adapter = TextAdapter.String,
title = R.string.authentication,
placeholder = R.string.dont_modify,
)
selectableList(
value = configuration::allowLan,
values = booleanValues,
valuesText = booleanValuesText,
title = R.string.allow_lan,
)
selectableList(
value = configuration::ipv6,
values = booleanValues,
valuesText = booleanValuesText,
title = R.string.ipv6,
)
editableText(
value = configuration::bindAddress,
adapter = NullableTextAdapter.String,
title = R.string.bind_address,
placeholder = R.string.dont_modify,
empty = R.string.default_
)
selectableList(
value = configuration::mode,
values = arrayOf(
null,
TunnelState.Mode.Direct,
TunnelState.Mode.Global,
TunnelState.Mode.Rule,
),
valuesText = arrayOf(
R.string.dont_modify,
R.string.direct_mode,
R.string.global_mode,
R.string.rule_mode,
),
title = R.string.mode
)
selectableList(
value = configuration::logLevel,
values = arrayOf(
null,
LogMessage.Level.Info,
LogMessage.Level.Warning,
LogMessage.Level.Error,
LogMessage.Level.Debug,
LogMessage.Level.Silent,
),
valuesText = arrayOf(
R.string.dont_modify,
R.string.info,
R.string.warning,
R.string.error,
R.string.debug,
R.string.silent,
),
title = R.string.log_level,
)
editableTextMap(
value = configuration::hosts,
keyAdapter = TextAdapter.String,
valueAdapter = TextAdapter.String,
title = R.string.hosts,
placeholder = R.string.dont_modify,
)
clickable(
title = R.string.sideload_geoip,
summary = R.string.sideload_geoip_summary
) {
clicked {
requests.trySend(Request.EditSideloadGeoip)
}
}
category(R.string.dns)
val dnsDependencies: MutableList<Preference> = mutableListOf()
val dns = selectableList(
value = configuration.dns::enable,
values = arrayOf(
null,
true,
false
),
valuesText = arrayOf(
R.string.dont_modify,
R.string.force_enable,
R.string.use_built_in,
),
title = R.string.strategy
) {
listener = OnChangedListener {
if (configuration.dns.enable == false) {
dnsDependencies.forEach {
it.enabled = false
}
} else {
dnsDependencies.forEach {
it.enabled = true
}
}
}
}
editableText(
value = configuration.dns::listen,
adapter = NullableTextAdapter.String,
title = R.string.listen,
placeholder = R.string.dont_modify,
empty = R.string.disabled,
configure = dnsDependencies::add,
)
selectableList(
value = configuration.app::appendSystemDns,
values = booleanValues,
valuesText = booleanValuesText,
title = R.string.append_system_dns,
configure = dnsDependencies::add,
)
selectableList(
value = configuration.dns::ipv6,
values = booleanValues,
valuesText = booleanValuesText,
title = R.string.ipv6,
configure = dnsDependencies::add,
)
selectableList(
value = configuration.dns::useHosts,
values = booleanValues,
valuesText = booleanValuesText,
title = R.string.use_hosts,
configure = dnsDependencies::add,
)
selectableList(
value = configuration.dns::enhancedMode,
values = arrayOf(
null,
ConfigurationOverride.DnsEnhancedMode.None,
ConfigurationOverride.DnsEnhancedMode.FakeIp,
ConfigurationOverride.DnsEnhancedMode.Mapping
),
valuesText = arrayOf(
R.string.dont_modify,
R.string.disabled,
R.string.fakeip,
R.string.mapping
),
title = R.string.enhanced_mode,
configure = dnsDependencies::add,
)
editableTextList(
value = configuration.dns::nameServer,
adapter = TextAdapter.String,
title = R.string.name_server,
placeholder = R.string.dont_modify,
configure = dnsDependencies::add,
)
editableTextList(
value = configuration.dns::fallback,
adapter = TextAdapter.String,
title = R.string.fallback,
placeholder = R.string.dont_modify,
configure = dnsDependencies::add,
)
editableTextList(
value = configuration.dns::defaultServer,
adapter = TextAdapter.String,
title = R.string.default_name_server,
placeholder = R.string.dont_modify,
configure = dnsDependencies::add,
)
editableTextList(
value = configuration.dns::fakeIpFilter,
adapter = TextAdapter.String,
title = R.string.fakeip_filter,
placeholder = R.string.dont_modify,
configure = dnsDependencies::add,
)
selectableList(
value = configuration.dns.fallbackFilter::geoIp,
values = booleanValues,
valuesText = booleanValuesText,
title = R.string.geoip_fallback,
configure = dnsDependencies::add,
)
editableTextList(
value = configuration.dns.fallbackFilter::domain,
adapter = TextAdapter.String,
title = R.string.domain_fallback,
placeholder = R.string.dont_modify,
configure = dnsDependencies::add,
)
editableTextList(
value = configuration.dns.fallbackFilter::ipcidr,
adapter = TextAdapter.String,
title = R.string.ipcidr_fallback,
placeholder = R.string.dont_modify,
configure = dnsDependencies::add,
)
editableTextMap(
value = configuration.dns::nameserverPolicy,
keyAdapter = TextAdapter.String,
valueAdapter = TextAdapter.String,
title = R.string.name_server_policy,
placeholder = R.string.dont_modify,
configure = dnsDependencies::add,
)
dns.listener?.onChanged()
}
binding.content.addView(screen.root)
}
fun requestClear() {
requests.trySend(Request.ResetOverride)
}
}

View File

@ -1,113 +0,0 @@
package com.github.kr328.clash.design.component
import android.content.Context
import android.view.MenuItem
import android.view.View
import androidx.appcompat.widget.PopupMenu
import com.github.kr328.clash.core.model.ProxySort
import com.github.kr328.clash.core.model.TunnelState
import com.github.kr328.clash.design.ProxyDesign
import com.github.kr328.clash.design.R
import com.github.kr328.clash.design.store.UiStore
import kotlinx.coroutines.channels.Channel
class ProxyMenu(
context: Context,
menuView: View,
mode: TunnelState.Mode?,
private val uiStore: UiStore,
private val requests: Channel<ProxyDesign.Request>,
private val updateConfig: () -> Unit,
) : PopupMenu.OnMenuItemClickListener {
private val menu = PopupMenu(context, menuView)
fun show() {
menu.show()
}
override fun onMenuItemClick(item: MenuItem): Boolean {
item.isChecked = !item.isChecked
when (item.itemId) {
R.id.not_selectable -> {
uiStore.proxyExcludeNotSelectable = item.isChecked
requests.trySend(ProxyDesign.Request.ReLaunch)
}
R.id.single -> {
uiStore.proxySingleLine = true
updateConfig()
requests.trySend(ProxyDesign.Request.ReloadAll)
}
R.id.multiple -> {
uiStore.proxySingleLine = false
updateConfig()
requests.trySend(ProxyDesign.Request.ReloadAll)
}
R.id.default_ -> {
uiStore.proxySort = ProxySort.Default
requests.trySend(ProxyDesign.Request.ReloadAll)
}
R.id.name -> {
uiStore.proxySort = ProxySort.Title
requests.trySend(ProxyDesign.Request.ReloadAll)
}
R.id.delay -> {
uiStore.proxySort = ProxySort.Delay
requests.trySend(ProxyDesign.Request.ReloadAll)
}
R.id.dont_modify -> {
requests.trySend(ProxyDesign.Request.PatchMode(null))
}
R.id.direct_mode -> {
requests.trySend(ProxyDesign.Request.PatchMode(TunnelState.Mode.Direct))
}
R.id.global_mode -> {
requests.trySend(ProxyDesign.Request.PatchMode(TunnelState.Mode.Global))
}
R.id.rule_mode -> {
requests.trySend(ProxyDesign.Request.PatchMode(TunnelState.Mode.Rule))
}
else -> return false
}
return true
}
init {
menu.menuInflater.inflate(R.menu.menu_proxy, menu.menu)
menu.menu.apply {
findItem(R.id.not_selectable).isChecked = uiStore.proxyExcludeNotSelectable
if (uiStore.proxySingleLine) {
findItem(R.id.single).isChecked = true
} else {
findItem(R.id.multiple).isChecked = true
}
when (uiStore.proxySort) {
ProxySort.Default -> findItem(R.id.default_).isChecked = true
ProxySort.Title -> findItem(R.id.name).isChecked = true
ProxySort.Delay -> findItem(R.id.delay).isChecked = true
}
when (mode) {
null -> findItem(R.id.dont_modify).isChecked = true
TunnelState.Mode.Direct -> findItem(R.id.direct_mode).isChecked = true
TunnelState.Mode.Global -> findItem(R.id.global_mode).isChecked = true
TunnelState.Mode.Rule -> findItem(R.id.rule_mode).isChecked = true
TunnelState.Mode.Script -> throw IllegalStateException("invalid mode")
}
}
menu.setOnMenuItemClickListener(this)
}
}

View File

@ -1,56 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:title="@string/filter">
<menu>
<item
android:id="@+id/not_selectable"
android:checkable="true"
android:title="@string/not_selectable" />
</menu>
</item>
<item android:title="@string/mode">
<menu>
<group android:checkableBehavior="single">
<item
android:id="@+id/dont_modify"
android:title="@string/dont_modify" />
<item
android:id="@+id/direct_mode"
android:title="@string/direct_mode" />
<item
android:id="@+id/global_mode"
android:title="@string/global_mode" />
<item
android:id="@+id/rule_mode"
android:title="@string/rule_mode" />
</group>
</menu>
</item>
<item android:title="@string/layout">
<menu>
<group android:checkableBehavior="single">
<item
android:id="@+id/single"
android:title="@string/single" />
<item
android:id="@+id/multiple"
android:title="@string/multiple" />
</group>
</menu>
</item>
<item android:title="@string/sort">
<menu>
<group android:checkableBehavior="single">
<item
android:id="@+id/default_"
android:title="@string/default_" />
<item
android:id="@+id/name"
android:title="@string/name" />
<item
android:id="@+id/delay"
android:title="@string/delay" />
</group>
</menu>
</item>
</menu>

View File

@ -47,6 +47,17 @@ class HelpDesign(
category(R.string.feedback)
if (BuildConfig.PREMIUM) {
clickable(
title = R.string.google_play,
summary = R.string.google_play_url
) {
clicked {
openLink(Uri.parse(context.getString(R.string.google_play_url)))
}
}
}
clickable(
title = R.string.github_issues,
summary = R.string.github_issues_url
@ -56,23 +67,25 @@ class HelpDesign(
}
}
category(R.string.sources)
if (!BuildConfig.PREMIUM) {
category(R.string.sources)
clickable(
title = R.string.clash_for_android,
summary = R.string.github_url
) {
clicked {
openLink(Uri.parse(context.getString(R.string.github_url)))
clickable(
title = R.string.clash_for_android,
summary = R.string.github_url
) {
clicked {
openLink(Uri.parse(context.getString(R.string.github_url)))
}
}
}
clickable(
title = R.string.clash_core,
summary = R.string.clash_core_url
) {
clicked {
openLink(Uri.parse(context.getString(R.string.clash_core_url)))
clickable(
title = R.string.clash_core,
summary = R.string.clash_core_url
) {
clicked {
openLink(Uri.parse(context.getString(R.string.clash_core_url)))
}
}
}

View File

@ -185,24 +185,43 @@ class OverrideSettingsDesign(
empty = R.string.default_
)
selectableList(
value = configuration::mode,
values = arrayOf(
null,
TunnelState.Mode.Direct,
TunnelState.Mode.Global,
TunnelState.Mode.Rule,
TunnelState.Mode.Script
),
valuesText = arrayOf(
R.string.dont_modify,
R.string.direct_mode,
R.string.global_mode,
R.string.rule_mode,
R.string.script_mode
),
title = R.string.mode
)
if (BuildConfig.PREMIUM) {
selectableList(
value = configuration::mode,
values = arrayOf(
null,
TunnelState.Mode.Direct,
TunnelState.Mode.Global,
TunnelState.Mode.Rule,
TunnelState.Mode.Script
),
valuesText = arrayOf(
R.string.dont_modify,
R.string.direct_mode,
R.string.global_mode,
R.string.rule_mode,
R.string.script_mode
),
title = R.string.mode
)
} else {
selectableList(
value = configuration::mode,
values = arrayOf(
null,
TunnelState.Mode.Direct,
TunnelState.Mode.Global,
TunnelState.Mode.Rule
),
valuesText = arrayOf(
R.string.dont_modify,
R.string.direct_mode,
R.string.global_mode,
R.string.rule_mode
),
title = R.string.mode
)
}
selectableList(
value = configuration::logLevel,

View File

@ -6,6 +6,7 @@ import android.view.View
import androidx.appcompat.widget.PopupMenu
import com.github.kr328.clash.core.model.ProxySort
import com.github.kr328.clash.core.model.TunnelState
import com.github.kr328.clash.design.BuildConfig
import com.github.kr328.clash.design.ProxyDesign
import com.github.kr328.clash.design.R
import com.github.kr328.clash.design.store.UiStore
@ -88,6 +89,8 @@ class ProxyMenu(
menu.menuInflater.inflate(R.menu.menu_proxy, menu.menu)
menu.menu.apply {
findItem(R.id.script_mode).isVisible = BuildConfig.PREMIUM
findItem(R.id.not_selectable).isChecked = uiStore.proxyExcludeNotSelectable
if (uiStore.proxySingleLine) {

View File

@ -1,84 +0,0 @@
package com.github.kr328.clash.design
import android.content.Context
import android.net.Uri
import android.view.View
import com.github.kr328.clash.common.compat.preferredLocale
import com.github.kr328.clash.design.databinding.DesignSettingsCommonBinding
import com.github.kr328.clash.design.preference.category
import com.github.kr328.clash.design.preference.clickable
import com.github.kr328.clash.design.preference.preferenceScreen
import com.github.kr328.clash.design.preference.tips
import com.github.kr328.clash.design.util.applyFrom
import com.github.kr328.clash.design.util.bindAppBarElevation
import com.github.kr328.clash.design.util.layoutInflater
import com.github.kr328.clash.design.util.root
class HelpDesign(
context: Context,
openLink: (Uri) -> Unit,
) : Design<Unit>(context) {
private val binding = DesignSettingsCommonBinding
.inflate(context.layoutInflater, context.root, false)
override val root: View
get() = binding.root
init {
binding.surface = surface
binding.activityBarLayout.applyFrom(context)
binding.scrollRoot.bindAppBarElevation(binding.activityBarLayout)
val screen = preferenceScreen(context) {
tips(R.string.tips_help)
category(R.string.document)
clickable(
title = R.string.clash_wiki,
summary = R.string.clash_wiki_url
) {
clicked {
openLink(Uri.parse(context.getString(R.string.clash_wiki_url)))
}
}
category(R.string.feedback)
clickable(
title = R.string.github_issues,
summary = R.string.github_issues_url
) {
clicked {
openLink(Uri.parse(context.getString(R.string.github_issues_url)))
}
}
clickable(
title = R.string.google_play,
summary = R.string.google_play_url
) {
clicked {
openLink(Uri.parse(context.getString(R.string.google_play_url)))
}
}
if (context.resources.configuration.preferredLocale.language == "zh") {
category(R.string.donate)
clickable(
title = R.string.donate,
summary = R.string.donate_url
) {
clicked {
openLink(Uri.parse(context.getString(R.string.donate_url)))
}
}
}
}
binding.content.addView(screen.root)
}
}