Improve per proxy app selector
This commit is contained in:
@@ -37,7 +37,6 @@
|
|||||||
android:name=".ui.MainActivity"
|
android:name=".ui.MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:theme="@style/AppTheme.NoActionBar"
|
|
||||||
android:launchMode="singleTask">
|
android:launchMode="singleTask">
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
@@ -120,6 +119,9 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name="io.nekohasekai.sfa.ui.profileoverride.PerAppProxyActivity"
|
android:name="io.nekohasekai.sfa.ui.profileoverride.PerAppProxyActivity"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
<activity
|
||||||
|
android:name="io.nekohasekai.sfa.ui.profileoverride.PerAppProxyActivity0"
|
||||||
|
android:exported="false" />
|
||||||
<activity
|
<activity
|
||||||
android:name="io.nekohasekai.sfa.ui.debug.DebugActivity"
|
android:name="io.nekohasekai.sfa.ui.debug.DebugActivity"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package io.nekohasekai.sfa
|
|||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
|
import android.content.ClipboardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.IntentFilter
|
import android.content.IntentFilter
|
||||||
@@ -48,6 +49,7 @@ class Application : Application() {
|
|||||||
val powerManager by lazy { application.getSystemService<PowerManager>()!! }
|
val powerManager by lazy { application.getSystemService<PowerManager>()!! }
|
||||||
val notificationManager by lazy { application.getSystemService<NotificationManager>()!! }
|
val notificationManager by lazy { application.getSystemService<NotificationManager>()!! }
|
||||||
val wifiManager by lazy { application.getSystemService<WifiManager>()!! }
|
val wifiManager by lazy { application.getSystemService<WifiManager>()!! }
|
||||||
|
val clipboard by lazy { application.getSystemService<ClipboardManager>()!! }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import io.nekohasekai.sfa.database.Settings
|
import io.nekohasekai.sfa.database.Settings
|
||||||
import io.nekohasekai.sfa.ui.profileoverride.PerAppProxyActivity
|
import io.nekohasekai.sfa.ui.profileoverride.PerAppProxyActivity0
|
||||||
|
|
||||||
class AppChangeReceiver : BroadcastReceiver() {
|
class AppChangeReceiver : BroadcastReceiver() {
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ class AppChangeReceiver : BroadcastReceiver() {
|
|||||||
Log.d(TAG, "missing package name in intent")
|
Log.d(TAG, "missing package name in intent")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val isChinaApp = PerAppProxyActivity.scanChinaApps(listOf(packageName)).isNotEmpty()
|
val isChinaApp = PerAppProxyActivity0.scanChinaPackage(packageName)
|
||||||
Log.d(TAG, "scan china app result for $packageName: $isChinaApp")
|
Log.d(TAG, "scan china app result for $packageName: $isChinaApp")
|
||||||
if ((perAppProxyUpdateOnChange == Settings.PER_APP_PROXY_INCLUDE) xor !isChinaApp) {
|
if ((perAppProxyUpdateOnChange == Settings.PER_APP_PROXY_INCLUDE) xor !isChinaApp) {
|
||||||
Settings.perAppProxyList += packageName
|
Settings.perAppProxyList += packageName
|
||||||
|
|||||||
12
app/src/main/java/io/nekohasekai/sfa/ktx/Clips.kt
Normal file
12
app/src/main/java/io/nekohasekai/sfa/ktx/Clips.kt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package io.nekohasekai.sfa.ktx
|
||||||
|
|
||||||
|
import android.content.ClipData
|
||||||
|
import io.nekohasekai.sfa.Application
|
||||||
|
|
||||||
|
var clipboardText: String?
|
||||||
|
get() = Application.clipboard.primaryClip?.getItemAt(0)?.text?.toString()
|
||||||
|
set(plainText) {
|
||||||
|
if (plainText != null) {
|
||||||
|
Application.clipboard.setPrimaryClip(ClipData.newPlainText(null, plainText))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
package io.nekohasekai.sfa.ktx
|
package io.nekohasekai.sfa.ktx
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
import android.content.ActivityNotFoundException
|
import android.content.ActivityNotFoundException
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import io.nekohasekai.sfa.R
|
import io.nekohasekai.sfa.R
|
||||||
import io.nekohasekai.sfa.ui.shared.AbstractActivity
|
|
||||||
|
|
||||||
fun AbstractActivity.startFilesForResult(
|
fun Activity.startFilesForResult(
|
||||||
launcher: ActivityResultLauncher<String>, input: String
|
launcher: ActivityResultLauncher<String>, input: String
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ import java.io.File
|
|||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.LinkedList
|
import java.util.LinkedList
|
||||||
|
|
||||||
class MainActivity : AbstractActivity(),
|
class MainActivity : AbstractActivity<ActivityMainBinding>(),
|
||||||
PreferenceFragmentCompat.OnPreferenceStartFragmentCallback,
|
PreferenceFragmentCompat.OnPreferenceStartFragmentCallback,
|
||||||
ServiceConnection.Callback {
|
ServiceConnection.Callback {
|
||||||
|
|
||||||
@@ -61,7 +61,6 @@ class MainActivity : AbstractActivity(),
|
|||||||
private const val TAG = "MainActivity"
|
private const val TAG = "MainActivity"
|
||||||
}
|
}
|
||||||
|
|
||||||
internal lateinit var binding: ActivityMainBinding
|
|
||||||
private val connection = ServiceConnection(this, this)
|
private val connection = ServiceConnection(this, this)
|
||||||
|
|
||||||
val logList = LinkedList<String>()
|
val logList = LinkedList<String>()
|
||||||
@@ -71,10 +70,6 @@ class MainActivity : AbstractActivity(),
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
|
||||||
setContentView(binding.root)
|
|
||||||
setSupportActionBar(binding.toolbar)
|
|
||||||
|
|
||||||
val navController = findNavController(R.id.nav_host_fragment_activity_my)
|
val navController = findNavController(R.id.nav_host_fragment_activity_my)
|
||||||
navController.setGraph(R.navigation.mobile_navigation)
|
navController.setGraph(R.navigation.mobile_navigation)
|
||||||
navController.navigate(R.id.navigation_dashboard)
|
navController.navigate(R.id.navigation_dashboard)
|
||||||
@@ -103,6 +98,7 @@ class MainActivity : AbstractActivity(),
|
|||||||
navDestination: NavDestination,
|
navDestination: NavDestination,
|
||||||
bundle: Bundle?
|
bundle: Bundle?
|
||||||
) {
|
) {
|
||||||
|
val binding = binding ?: return
|
||||||
val destinationId = navDestination.id
|
val destinationId = navDestination.id
|
||||||
binding.dashboardTabContainer.isVisible = destinationId == R.id.navigation_dashboard
|
binding.dashboardTabContainer.isVisible = destinationId == R.id.navigation_dashboard
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,19 +6,12 @@ import io.nekohasekai.sfa.R
|
|||||||
import io.nekohasekai.sfa.databinding.ActivityDebugBinding
|
import io.nekohasekai.sfa.databinding.ActivityDebugBinding
|
||||||
import io.nekohasekai.sfa.ui.shared.AbstractActivity
|
import io.nekohasekai.sfa.ui.shared.AbstractActivity
|
||||||
|
|
||||||
class DebugActivity : AbstractActivity() {
|
class DebugActivity : AbstractActivity<ActivityDebugBinding>() {
|
||||||
|
|
||||||
private var binding: ActivityDebugBinding? = null
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
setTitle(R.string.title_debug)
|
setTitle(R.string.title_debug)
|
||||||
val binding = ActivityDebugBinding.inflate(layoutInflater)
|
|
||||||
this.binding = binding
|
|
||||||
setContentView(binding.root)
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
|
|
||||||
binding.scanVPNButton.setOnClickListener {
|
binding.scanVPNButton.setOnClickListener {
|
||||||
startActivity(Intent(this, VPNScanActivity::class.java))
|
startActivity(Intent(this, VPNScanActivity::class.java))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,19 +25,14 @@ import java.io.File
|
|||||||
import java.util.zip.ZipFile
|
import java.util.zip.ZipFile
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
class VPNScanActivity : AbstractActivity() {
|
class VPNScanActivity : AbstractActivity<ActivityVpnScanBinding>() {
|
||||||
|
|
||||||
private var binding: ActivityVpnScanBinding? = null
|
|
||||||
private var adapter: Adapter? = null
|
private var adapter: Adapter? = null
|
||||||
private val appInfoList = mutableListOf<AppInfo>()
|
private val appInfoList = mutableListOf<AppInfo>()
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
setTitle(R.string.title_scan_vpn)
|
setTitle(R.string.title_scan_vpn)
|
||||||
val binding = ActivityVpnScanBinding.inflate(layoutInflater)
|
|
||||||
this.binding = binding
|
|
||||||
setContentView(binding.root)
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
binding.scanVPNResult.adapter = Adapter().also {
|
binding.scanVPNResult.adapter = Adapter().also {
|
||||||
adapter = it
|
adapter = it
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,11 +79,11 @@ class DashboardFragment : Fragment(R.layout.fragment_dashboard) {
|
|||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
val activity = activity ?: return
|
val activityBinding = activity?.binding ?: return
|
||||||
val binding = binding ?: return
|
val binding = binding ?: return
|
||||||
if (mediator != null) return
|
if (mediator != null) return
|
||||||
mediator = TabLayoutMediator(
|
mediator = TabLayoutMediator(
|
||||||
activity.binding.dashboardTabLayout,
|
activityBinding.dashboardTabLayout,
|
||||||
binding.dashboardPager
|
binding.dashboardPager
|
||||||
) { tab, position ->
|
) { tab, position ->
|
||||||
tab.setText(Page.values()[position].titleRes)
|
tab.setText(Page.values()[position].titleRes)
|
||||||
@@ -93,20 +93,21 @@ class DashboardFragment : Fragment(R.layout.fragment_dashboard) {
|
|||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
mediator?.detach()
|
mediator?.detach()
|
||||||
|
mediator = null
|
||||||
binding = null
|
binding = null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun enablePager() {
|
private fun enablePager() {
|
||||||
val activity = activity ?: return
|
val activity = activity ?: return
|
||||||
val binding = binding ?: return
|
val binding = binding ?: return
|
||||||
activity.binding.dashboardTabLayout.isVisible = true
|
activity.binding?.dashboardTabLayout?.isVisible = true
|
||||||
binding.dashboardPager.isUserInputEnabled = true
|
binding.dashboardPager.isUserInputEnabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun disablePager() {
|
private fun disablePager() {
|
||||||
val activity = activity ?: return
|
val activity = activity ?: return
|
||||||
val binding = binding ?: return
|
val binding = binding ?: return
|
||||||
activity.binding.dashboardTabLayout.isVisible = false
|
activity.binding?.dashboardTabLayout?.isVisible = false
|
||||||
binding.dashboardPager.isUserInputEnabled = false
|
binding.dashboardPager.isUserInputEnabled = false
|
||||||
binding.dashboardPager.setCurrentItem(0, false)
|
binding.dashboardPager.setCurrentItem(0, false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import io.nekohasekai.sfa.databinding.ActivityEditProfileBinding
|
|||||||
import io.nekohasekai.sfa.ktx.addTextChangedListener
|
import io.nekohasekai.sfa.ktx.addTextChangedListener
|
||||||
import io.nekohasekai.sfa.ktx.errorDialogBuilder
|
import io.nekohasekai.sfa.ktx.errorDialogBuilder
|
||||||
import io.nekohasekai.sfa.ktx.setSimpleItems
|
import io.nekohasekai.sfa.ktx.setSimpleItems
|
||||||
import io.nekohasekai.sfa.ktx.shareProfile
|
|
||||||
import io.nekohasekai.sfa.ktx.text
|
import io.nekohasekai.sfa.ktx.text
|
||||||
import io.nekohasekai.sfa.ui.shared.AbstractActivity
|
import io.nekohasekai.sfa.ui.shared.AbstractActivity
|
||||||
import io.nekohasekai.sfa.utils.HTTPClient
|
import io.nekohasekai.sfa.utils.HTTPClient
|
||||||
@@ -28,20 +27,13 @@ import java.io.File
|
|||||||
import java.text.DateFormat
|
import java.text.DateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
||||||
class EditProfileActivity : AbstractActivity() {
|
class EditProfileActivity : AbstractActivity<ActivityEditProfileBinding>() {
|
||||||
|
|
||||||
private var binding: ActivityEditProfileBinding? = null
|
private lateinit var profile: Profile
|
||||||
private var _profile: Profile? = null
|
|
||||||
private val profile get() = _profile!!
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
setTitle(R.string.title_edit_profile)
|
setTitle(R.string.title_edit_profile)
|
||||||
val binding = ActivityEditProfileBinding.inflate(layoutInflater)
|
|
||||||
this.binding = binding
|
|
||||||
setContentView(binding.root)
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
|
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
runCatching {
|
runCatching {
|
||||||
loadProfile()
|
loadProfile()
|
||||||
@@ -55,18 +47,11 @@ class EditProfileActivity : AbstractActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
binding = null
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun loadProfile() {
|
private suspend fun loadProfile() {
|
||||||
val binding = binding ?: return
|
|
||||||
delay(200L)
|
delay(200L)
|
||||||
|
|
||||||
val profileId = intent.getLongExtra("profile_id", -1L)
|
val profileId = intent.getLongExtra("profile_id", -1L)
|
||||||
if (profileId == -1L) error("invalid arguments")
|
if (profileId == -1L) error("invalid arguments")
|
||||||
_profile = ProfileManager.get(profileId) ?: error("invalid arguments")
|
profile = ProfileManager.get(profileId) ?: error("invalid arguments")
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
binding.name.text = profile.name
|
binding.name.text = profile.name
|
||||||
binding.name.addTextChangedListener {
|
binding.name.addTextChangedListener {
|
||||||
@@ -203,16 +188,4 @@ class EditProfileActivity : AbstractActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun shareProfile(button: View) {
|
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
|
||||||
try {
|
|
||||||
shareProfile(profile)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
errorDialogBuilder(e).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -21,28 +21,17 @@ import kotlinx.coroutines.launch
|
|||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class EditProfileContentActivity : AbstractActivity() {
|
class EditProfileContentActivity : AbstractActivity<ActivityEditProfileContentBinding>() {
|
||||||
|
|
||||||
private var binding: ActivityEditProfileContentBinding? = null
|
private var profile: Profile? = null
|
||||||
private var _profile: Profile? = null
|
|
||||||
private val profile get() = _profile!!
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
setTitle(R.string.title_edit_configuration)
|
setTitle(R.string.title_edit_configuration)
|
||||||
val binding = ActivityEditProfileContentBinding.inflate(layoutInflater)
|
|
||||||
this.binding = binding
|
|
||||||
setContentView(binding.root)
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
binding.editor.language = JsonLanguage()
|
binding.editor.language = JsonLanguage()
|
||||||
loadConfiguration()
|
loadConfiguration()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
binding = null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadConfiguration() {
|
private fun loadConfiguration() {
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
runCatching {
|
runCatching {
|
||||||
@@ -120,7 +109,8 @@ class EditProfileContentActivity : AbstractActivity() {
|
|||||||
|
|
||||||
val profileId = intent.getLongExtra("profile_id", -1L)
|
val profileId = intent.getLongExtra("profile_id", -1L)
|
||||||
if (profileId == -1L) error("invalid arguments")
|
if (profileId == -1L) error("invalid arguments")
|
||||||
_profile = ProfileManager.get(profileId) ?: error("invalid arguments")
|
val profile = ProfileManager.get(profileId) ?: error("invalid arguments")
|
||||||
|
this.profile = profile
|
||||||
val content = File(profile.typed.path).readText()
|
val content = File(profile.typed.path).readText()
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
binding.editor.setTextContent(content)
|
binding.editor.setTextContent(content)
|
||||||
|
|||||||
@@ -28,13 +28,12 @@ import java.io.File
|
|||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
||||||
class NewProfileActivity : AbstractActivity() {
|
class NewProfileActivity : AbstractActivity<ActivityAddProfileBinding>() {
|
||||||
enum class FileSource(val formatted: String) {
|
enum class FileSource(val formatted: String) {
|
||||||
CreateNew("Create New"),
|
CreateNew("Create New"),
|
||||||
Import("Import");
|
Import("Import");
|
||||||
}
|
}
|
||||||
|
|
||||||
private var binding: ActivityAddProfileBinding? = null
|
|
||||||
private val importFile =
|
private val importFile =
|
||||||
registerForActivityResult(ActivityResultContracts.GetContent()) { fileURI ->
|
registerForActivityResult(ActivityResultContracts.GetContent()) { fileURI ->
|
||||||
val binding = binding ?: return@registerForActivityResult
|
val binding = binding ?: return@registerForActivityResult
|
||||||
@@ -47,10 +46,6 @@ class NewProfileActivity : AbstractActivity() {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
setTitle(R.string.title_new_profile)
|
setTitle(R.string.title_new_profile)
|
||||||
val binding = ActivityAddProfileBinding.inflate(layoutInflater)
|
|
||||||
this.binding = binding
|
|
||||||
setContentView(binding.root)
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
|
|
||||||
intent.getStringExtra("importName")?.also { importName ->
|
intent.getStringExtra("importName")?.also { importName ->
|
||||||
intent.getStringExtra("importURL")?.also { importURL ->
|
intent.getStringExtra("importURL")?.also { importURL ->
|
||||||
@@ -100,11 +95,6 @@ class NewProfileActivity : AbstractActivity() {
|
|||||||
binding.autoUpdateInterval.addTextChangedListener(this::updateAutoUpdateInterval)
|
binding.autoUpdateInterval.addTextChangedListener(this::updateAutoUpdateInterval)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
binding = null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createProfile(view: View) {
|
private fun createProfile(view: View) {
|
||||||
val binding = binding ?: return
|
val binding = binding ?: return
|
||||||
if (binding.name.showErrorIfEmpty()) {
|
if (binding.name.showErrorIfEmpty()) {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package io.nekohasekai.sfa.ui.profileoverride
|
|||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.content.ClipData
|
|
||||||
import android.content.ClipboardManager
|
import android.content.ClipboardManager
|
||||||
import android.content.pm.ApplicationInfo
|
import android.content.pm.ApplicationInfo
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
@@ -20,7 +19,6 @@ import android.widget.TextView
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import androidx.core.view.isGone
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
@@ -29,6 +27,7 @@ import io.nekohasekai.sfa.R
|
|||||||
import io.nekohasekai.sfa.database.Settings
|
import io.nekohasekai.sfa.database.Settings
|
||||||
import io.nekohasekai.sfa.databinding.ActivityPerAppProxyBinding
|
import io.nekohasekai.sfa.databinding.ActivityPerAppProxyBinding
|
||||||
import io.nekohasekai.sfa.databinding.ViewAppListItemBinding
|
import io.nekohasekai.sfa.databinding.ViewAppListItemBinding
|
||||||
|
import io.nekohasekai.sfa.ktx.clipboardText
|
||||||
import io.nekohasekai.sfa.ui.shared.AbstractActivity
|
import io.nekohasekai.sfa.ui.shared.AbstractActivity
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -37,10 +36,9 @@ import org.jf.dexlib2.dexbacked.DexBackedDexFile
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.zip.ZipFile
|
import java.util.zip.ZipFile
|
||||||
|
|
||||||
class PerAppProxyActivity : AbstractActivity() {
|
class PerAppProxyActivity : AbstractActivity<ActivityPerAppProxyBinding>() {
|
||||||
|
|
||||||
|
|
||||||
private lateinit var binding: ActivityPerAppProxyBinding
|
|
||||||
private lateinit var adapter: AppListAdapter
|
private lateinit var adapter: AppListAdapter
|
||||||
|
|
||||||
private val perAppProxyList = mutableSetOf<String>()
|
private val perAppProxyList = mutableSetOf<String>()
|
||||||
@@ -52,10 +50,8 @@ class PerAppProxyActivity : AbstractActivity() {
|
|||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
setTitle(R.string.title_per_app_proxy)
|
setTitle(R.string.title_per_app_proxy)
|
||||||
binding = ActivityPerAppProxyBinding.inflate(layoutInflater)
|
|
||||||
setContentView(binding.root)
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
|
|
||||||
val proxyMode = Settings.perAppProxyMode
|
val proxyMode = Settings.perAppProxyMode
|
||||||
if (proxyMode == Settings.PER_APP_PROXY_INCLUDE) {
|
if (proxyMode == Settings.PER_APP_PROXY_INCLUDE) {
|
||||||
@@ -87,8 +83,8 @@ class PerAppProxyActivity : AbstractActivity() {
|
|||||||
|
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
private fun loadAppList() {
|
private fun loadAppList() {
|
||||||
binding.recyclerViewAppList.isGone = true
|
// binding.recyclerViewAppList.isGone = true
|
||||||
binding.layoutProgress.isGone = false
|
// binding.layoutProgress.isGone = false
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val list = withContext(Dispatchers.IO) {
|
val list = withContext(Dispatchers.IO) {
|
||||||
@@ -142,8 +138,8 @@ class PerAppProxyActivity : AbstractActivity() {
|
|||||||
adapter.notifyDataSetChanged()
|
adapter.notifyDataSetChanged()
|
||||||
|
|
||||||
binding.recyclerViewAppList.scrollToPosition(0)
|
binding.recyclerViewAppList.scrollToPosition(0)
|
||||||
binding.layoutProgress.isGone = true
|
// binding.layoutProgress.isGone = true
|
||||||
binding.recyclerViewAppList.isGone = false
|
// binding.recyclerViewAppList.isGone = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,7 +179,7 @@ class PerAppProxyActivity : AbstractActivity() {
|
|||||||
|
|
||||||
R.id.action_import -> {
|
R.id.action_import -> {
|
||||||
MaterialAlertDialogBuilder(this)
|
MaterialAlertDialogBuilder(this)
|
||||||
.setTitle(R.string.menu_import_from_clipboard)
|
.setTitle(R.string.per_app_proxy_import)
|
||||||
.setMessage(R.string.message_import_from_clipboard)
|
.setMessage(R.string.message_import_from_clipboard)
|
||||||
.setPositiveButton(R.string.ok) { _, _ ->
|
.setPositiveButton(R.string.ok) { _, _ ->
|
||||||
importFromClipboard()
|
importFromClipboard()
|
||||||
@@ -247,9 +243,7 @@ class PerAppProxyActivity : AbstractActivity() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
val content = perAppProxyList.joinToString("\n")
|
val content = perAppProxyList.joinToString("\n")
|
||||||
val clipboardManager = getSystemService<ClipboardManager>()!!
|
clipboardText = content
|
||||||
val clip = ClipData.newPlainText(null, content)
|
|
||||||
clipboardManager.setPrimaryClip(clip)
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||||
Toast.makeText(this, R.string.toast_copied_to_clipboard, Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, R.string.toast_copied_to_clipboard, Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
@@ -477,14 +471,14 @@ class PerAppProxyActivity : AbstractActivity() {
|
|||||||
) : RecyclerView.ViewHolder(binding.root) {
|
) : RecyclerView.ViewHolder(binding.root) {
|
||||||
|
|
||||||
fun bind(item: AppItem) {
|
fun bind(item: AppItem) {
|
||||||
binding.imageAppIcon.setImageDrawable(item.icon)
|
binding.appIcon.setImageDrawable(item.icon)
|
||||||
binding.textAppName.text = item.name
|
binding.applicationLabel.text = item.name
|
||||||
binding.textAppPackageName.text = item.packageName
|
binding.packageName.text = item.packageName
|
||||||
binding.checkboxAppSelected.isChecked = item.selected
|
binding.selected.isChecked = item.selected
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bindCheck(item: AppItem) {
|
fun bindCheck(item: AppItem) {
|
||||||
binding.checkboxAppSelected.isChecked = item.selected
|
binding.selected.isChecked = item.selected
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,669 @@
|
|||||||
|
package io.nekohasekai.sfa.ui.profileoverride
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.pm.ApplicationInfo
|
||||||
|
import android.content.pm.PackageInfo
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.Gravity
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.appcompat.widget.PopupMenu
|
||||||
|
import androidx.appcompat.widget.SearchView
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import io.nekohasekai.sfa.Application
|
||||||
|
import io.nekohasekai.sfa.R
|
||||||
|
import io.nekohasekai.sfa.database.Settings
|
||||||
|
import io.nekohasekai.sfa.databinding.ActivityPerAppProxy0Binding
|
||||||
|
import io.nekohasekai.sfa.databinding.DialogProgressbarBinding
|
||||||
|
import io.nekohasekai.sfa.databinding.ViewAppListItemBinding
|
||||||
|
import io.nekohasekai.sfa.ktx.clipboardText
|
||||||
|
import io.nekohasekai.sfa.ui.shared.AbstractActivity
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.jf.dexlib2.dexbacked.DexBackedDexFile
|
||||||
|
import java.io.File
|
||||||
|
import java.util.zip.ZipFile
|
||||||
|
|
||||||
|
class PerAppProxyActivity0 : AbstractActivity<ActivityPerAppProxy0Binding>() {
|
||||||
|
enum class SortMode {
|
||||||
|
NAME, PACKAGE_NAME, UID, INSTALL_TIME, UPDATE_TIME,
|
||||||
|
}
|
||||||
|
|
||||||
|
private var proxyMode = Settings.PER_APP_PROXY_INCLUDE
|
||||||
|
private var sortMode = SortMode.NAME
|
||||||
|
private var sortReverse = false
|
||||||
|
private var hideSystemApps = false
|
||||||
|
private var hideOfflineApps = true
|
||||||
|
private var hideDisabledApps = true
|
||||||
|
|
||||||
|
inner class PackageCache(
|
||||||
|
private val packageInfo: PackageInfo,
|
||||||
|
) {
|
||||||
|
|
||||||
|
val packageName: String get() = packageInfo.packageName
|
||||||
|
|
||||||
|
val uid get() = packageInfo.applicationInfo.uid
|
||||||
|
|
||||||
|
val installTime get() = packageInfo.firstInstallTime
|
||||||
|
val updateTime get() = packageInfo.lastUpdateTime
|
||||||
|
val isSystem get() = packageInfo.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM == 1
|
||||||
|
val isOffline get() = packageInfo.requestedPermissions?.contains(Manifest.permission.INTERNET) != true
|
||||||
|
val isDisabled get() = packageInfo.applicationInfo.flags and ApplicationInfo.FLAG_INSTALLED == 0
|
||||||
|
|
||||||
|
val applicationIcon by lazy {
|
||||||
|
packageInfo.applicationInfo.loadIcon(packageManager)
|
||||||
|
}
|
||||||
|
|
||||||
|
val applicationLabel by lazy {
|
||||||
|
packageInfo.applicationInfo.loadLabel(packageManager).toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var adapter: ApplicationAdapter
|
||||||
|
private var packages = listOf<PackageCache>()
|
||||||
|
private var displayPackages = listOf<PackageCache>()
|
||||||
|
private var currentPackages = listOf<PackageCache>()
|
||||||
|
private var selectedUIDs = mutableSetOf<Int>()
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
setTitle(R.string.title_per_app_proxy)
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
proxyMode = if (Settings.perAppProxyMode == Settings.PER_APP_PROXY_EXCLUDE) {
|
||||||
|
Settings.PER_APP_PROXY_EXCLUDE
|
||||||
|
} else {
|
||||||
|
Settings.PER_APP_PROXY_INCLUDE
|
||||||
|
}
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
if (proxyMode != Settings.PER_APP_PROXY_EXCLUDE) {
|
||||||
|
binding.perAppProxyMode.setText(R.string.per_app_proxy_mode_include_description)
|
||||||
|
} else {
|
||||||
|
binding.perAppProxyMode.setText(R.string.per_app_proxy_mode_exclude_description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reloadApplicationList()
|
||||||
|
filterApplicationList()
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
adapter = ApplicationAdapter(displayPackages)
|
||||||
|
binding.appList.adapter = adapter
|
||||||
|
delay(500L)
|
||||||
|
binding.progress.isVisible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun reloadApplicationList() {
|
||||||
|
val packageManagerFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
PackageManager.GET_PERMISSIONS or PackageManager.MATCH_UNINSTALLED_PACKAGES
|
||||||
|
} else {
|
||||||
|
@Suppress("DEPRECATION") PackageManager.GET_PERMISSIONS or PackageManager.GET_UNINSTALLED_PACKAGES
|
||||||
|
}
|
||||||
|
val installedPackages = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
packageManager.getInstalledPackages(
|
||||||
|
PackageManager.PackageInfoFlags.of(
|
||||||
|
packageManagerFlags.toLong()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
@Suppress("DEPRECATION") packageManager.getInstalledPackages(packageManagerFlags)
|
||||||
|
}
|
||||||
|
val packages = mutableListOf<PackageCache>()
|
||||||
|
for (packageInfo in installedPackages) {
|
||||||
|
if (packageInfo.packageName == packageName) continue
|
||||||
|
packages.add(PackageCache(packageInfo))
|
||||||
|
}
|
||||||
|
val selectedPackageNames = Settings.perAppProxyList.toMutableSet()
|
||||||
|
val selectedUIDs = mutableSetOf<Int>()
|
||||||
|
for (packageCache in packages) {
|
||||||
|
if (selectedPackageNames.contains(packageCache.packageName)) {
|
||||||
|
selectedUIDs.add(packageCache.uid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.packages = packages
|
||||||
|
this.selectedUIDs = selectedUIDs
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun filterApplicationList(selectedUIDs: Set<Int> = this.selectedUIDs) {
|
||||||
|
val displayPackages = mutableListOf<PackageCache>()
|
||||||
|
for (packageCache in packages) {
|
||||||
|
if (hideSystemApps && packageCache.isSystem) continue
|
||||||
|
if (hideOfflineApps && packageCache.isOffline) continue
|
||||||
|
if (hideDisabledApps && packageCache.isDisabled) continue
|
||||||
|
displayPackages.add(packageCache)
|
||||||
|
}
|
||||||
|
displayPackages.sortWith(compareBy<PackageCache> {
|
||||||
|
!selectedUIDs.contains(it.uid)
|
||||||
|
}.let {
|
||||||
|
if (!sortReverse) it.thenBy {
|
||||||
|
when (sortMode) {
|
||||||
|
SortMode.NAME -> it.applicationLabel
|
||||||
|
SortMode.PACKAGE_NAME -> it.packageName
|
||||||
|
SortMode.UID -> it.uid
|
||||||
|
SortMode.INSTALL_TIME -> it.installTime
|
||||||
|
SortMode.UPDATE_TIME -> it.updateTime
|
||||||
|
}
|
||||||
|
} else it.thenByDescending {
|
||||||
|
when (sortMode) {
|
||||||
|
SortMode.NAME -> it.applicationLabel
|
||||||
|
SortMode.PACKAGE_NAME -> it.packageName
|
||||||
|
SortMode.UID -> it.uid
|
||||||
|
SortMode.INSTALL_TIME -> it.installTime
|
||||||
|
SortMode.UPDATE_TIME -> it.updateTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.displayPackages = displayPackages
|
||||||
|
this.currentPackages = displayPackages
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateApplicationSelection(packageCache: PackageCache, selected: Boolean) {
|
||||||
|
val performed = if (selected) {
|
||||||
|
selectedUIDs.add(packageCache.uid)
|
||||||
|
} else {
|
||||||
|
selectedUIDs.remove(packageCache.uid)
|
||||||
|
}
|
||||||
|
if (!performed) return
|
||||||
|
currentPackages.forEachIndexed { index, it ->
|
||||||
|
if (it.uid == packageCache.uid) {
|
||||||
|
adapter.notifyItemChanged(index, PayloadUpdateSelection(selected))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
saveSelectedApplications()
|
||||||
|
}
|
||||||
|
|
||||||
|
data class PayloadUpdateSelection(val selected: Boolean)
|
||||||
|
|
||||||
|
inner class ApplicationAdapter(private var applicationList: List<PackageCache>) :
|
||||||
|
RecyclerView.Adapter<ApplicationViewHolder>() {
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
fun setApplicationList(applicationList: List<PackageCache>) {
|
||||||
|
this.applicationList = applicationList
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(
|
||||||
|
parent: ViewGroup, viewType: Int
|
||||||
|
): ApplicationViewHolder {
|
||||||
|
return ApplicationViewHolder(
|
||||||
|
ViewAppListItemBinding.inflate(
|
||||||
|
LayoutInflater.from(parent.context), parent, false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int {
|
||||||
|
return applicationList.size
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(
|
||||||
|
holder: ApplicationViewHolder, position: Int
|
||||||
|
) {
|
||||||
|
holder.bind(applicationList[position])
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(
|
||||||
|
holder: ApplicationViewHolder, position: Int, payloads: MutableList<Any>
|
||||||
|
) {
|
||||||
|
if (payloads.isEmpty()) {
|
||||||
|
onBindViewHolder(holder, position)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
payloads.forEach {
|
||||||
|
when (it) {
|
||||||
|
is PayloadUpdateSelection -> holder.updateSelection(it.selected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class ApplicationViewHolder(
|
||||||
|
private val binding: ViewAppListItemBinding
|
||||||
|
) : RecyclerView.ViewHolder(binding.root) {
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
|
fun bind(packageCache: PackageCache) {
|
||||||
|
binding.appIcon.setImageDrawable(packageCache.applicationIcon)
|
||||||
|
binding.applicationLabel.text = packageCache.applicationLabel
|
||||||
|
binding.packageName.text = "${packageCache.packageName} (${packageCache.uid})"
|
||||||
|
binding.selected.isChecked = selectedUIDs.contains(packageCache.uid)
|
||||||
|
binding.root.setOnClickListener {
|
||||||
|
updateApplicationSelection(packageCache, !binding.selected.isChecked)
|
||||||
|
}
|
||||||
|
binding.root.setOnLongClickListener {
|
||||||
|
val popup = PopupMenu(it.context, it)
|
||||||
|
popup.setForceShowIcon(true)
|
||||||
|
popup.gravity = Gravity.END
|
||||||
|
popup.menuInflater.inflate(R.menu.app_menu, popup.menu)
|
||||||
|
popup.setOnMenuItemClickListener {
|
||||||
|
when (it.itemId) {
|
||||||
|
R.id.action_copy_application_label -> {
|
||||||
|
clipboardText = packageCache.applicationLabel
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
R.id.action_copy_package_name -> {
|
||||||
|
clipboardText = packageCache.packageName
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
R.id.action_copy_uid -> {
|
||||||
|
clipboardText = packageCache.uid.toString()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
popup.show()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateSelection(selected: Boolean) {
|
||||||
|
binding.selected.isChecked = selected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun searchApplications(searchText: String) {
|
||||||
|
currentPackages = if (searchText.isEmpty()) {
|
||||||
|
displayPackages
|
||||||
|
} else {
|
||||||
|
displayPackages.filter {
|
||||||
|
it.applicationLabel.contains(
|
||||||
|
searchText, ignoreCase = true
|
||||||
|
) || it.packageName.contains(
|
||||||
|
searchText, ignoreCase = true
|
||||||
|
) || it.uid.toString().contains(searchText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
adapter.setApplicationList(currentPackages)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||||
|
menuInflater.inflate(R.menu.per_app_menu0, menu)
|
||||||
|
|
||||||
|
if (menu != null) {
|
||||||
|
val searchView = menu.findItem(R.id.action_search).actionView as SearchView
|
||||||
|
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
||||||
|
override fun onQueryTextSubmit(query: String): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onQueryTextChange(newText: String): Boolean {
|
||||||
|
searchApplications(newText)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
searchView.setOnCloseListener {
|
||||||
|
searchApplications("")
|
||||||
|
true
|
||||||
|
}
|
||||||
|
when (proxyMode) {
|
||||||
|
Settings.PER_APP_PROXY_INCLUDE -> {
|
||||||
|
menu.findItem(R.id.action_mode_include).isChecked = true
|
||||||
|
}
|
||||||
|
|
||||||
|
Settings.PER_APP_PROXY_EXCLUDE -> {
|
||||||
|
menu.findItem(R.id.action_mode_exclude).isChecked = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
when (sortMode) {
|
||||||
|
SortMode.NAME -> {
|
||||||
|
menu.findItem(R.id.action_sort_by_name).isChecked = true
|
||||||
|
}
|
||||||
|
|
||||||
|
SortMode.PACKAGE_NAME -> {
|
||||||
|
menu.findItem(R.id.action_sort_by_package_name).isChecked = true
|
||||||
|
}
|
||||||
|
|
||||||
|
SortMode.UID -> {
|
||||||
|
menu.findItem(R.id.action_sort_by_uid).isChecked = true
|
||||||
|
}
|
||||||
|
|
||||||
|
SortMode.INSTALL_TIME -> {
|
||||||
|
menu.findItem(R.id.action_sort_by_install_time).isChecked = true
|
||||||
|
}
|
||||||
|
|
||||||
|
SortMode.UPDATE_TIME -> {
|
||||||
|
menu.findItem(R.id.action_sort_by_update_time).isChecked = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
menu.findItem(R.id.action_sort_reverse).isChecked = sortReverse
|
||||||
|
menu.findItem(R.id.action_hide_system_apps).isChecked = hideSystemApps
|
||||||
|
menu.findItem(R.id.action_hide_offline_apps).isChecked = hideOfflineApps
|
||||||
|
menu.findItem(R.id.action_hide_disabled_apps).isChecked = hideDisabledApps
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.onCreateOptionsMenu(menu)
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
when (item.itemId) {
|
||||||
|
R.id.action_mode_include -> {
|
||||||
|
item.isChecked = true
|
||||||
|
proxyMode = Settings.PER_APP_PROXY_INCLUDE
|
||||||
|
binding.perAppProxyMode.setText(R.string.per_app_proxy_mode_include_description)
|
||||||
|
lifecycleScope.launch {
|
||||||
|
Settings.perAppProxyMode = Settings.PER_APP_PROXY_INCLUDE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
R.id.action_mode_exclude -> {
|
||||||
|
item.isChecked = true
|
||||||
|
proxyMode = Settings.PER_APP_PROXY_EXCLUDE
|
||||||
|
binding.perAppProxyMode.setText(R.string.per_app_proxy_mode_exclude_description)
|
||||||
|
lifecycleScope.launch {
|
||||||
|
Settings.perAppProxyMode = Settings.PER_APP_PROXY_EXCLUDE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
R.id.action_sort_by_name -> {
|
||||||
|
item.isChecked = true
|
||||||
|
sortMode = SortMode.NAME
|
||||||
|
filterApplicationList()
|
||||||
|
adapter.setApplicationList(currentPackages)
|
||||||
|
}
|
||||||
|
|
||||||
|
R.id.action_sort_by_package_name -> {
|
||||||
|
item.isChecked = true
|
||||||
|
sortMode = SortMode.PACKAGE_NAME
|
||||||
|
filterApplicationList()
|
||||||
|
adapter.setApplicationList(currentPackages)
|
||||||
|
}
|
||||||
|
|
||||||
|
R.id.action_sort_by_uid -> {
|
||||||
|
item.isChecked = true
|
||||||
|
sortMode = SortMode.UID
|
||||||
|
filterApplicationList()
|
||||||
|
adapter.setApplicationList(currentPackages)
|
||||||
|
}
|
||||||
|
|
||||||
|
R.id.action_sort_by_install_time -> {
|
||||||
|
item.isChecked = true
|
||||||
|
sortMode = SortMode.INSTALL_TIME
|
||||||
|
filterApplicationList()
|
||||||
|
adapter.setApplicationList(currentPackages)
|
||||||
|
}
|
||||||
|
|
||||||
|
R.id.action_sort_by_update_time -> {
|
||||||
|
item.isChecked = true
|
||||||
|
sortMode = SortMode.UPDATE_TIME
|
||||||
|
filterApplicationList()
|
||||||
|
adapter.setApplicationList(currentPackages)
|
||||||
|
}
|
||||||
|
|
||||||
|
R.id.action_sort_reverse -> {
|
||||||
|
item.isChecked = !item.isChecked
|
||||||
|
sortReverse = item.isChecked
|
||||||
|
filterApplicationList()
|
||||||
|
adapter.setApplicationList(currentPackages)
|
||||||
|
}
|
||||||
|
|
||||||
|
R.id.action_hide_system_apps -> {
|
||||||
|
item.isChecked = !item.isChecked
|
||||||
|
hideSystemApps = item.isChecked
|
||||||
|
filterApplicationList()
|
||||||
|
adapter.setApplicationList(currentPackages)
|
||||||
|
}
|
||||||
|
|
||||||
|
R.id.action_hide_offline_apps -> {
|
||||||
|
item.isChecked = !item.isChecked
|
||||||
|
hideOfflineApps = item.isChecked
|
||||||
|
filterApplicationList()
|
||||||
|
adapter.setApplicationList(currentPackages)
|
||||||
|
}
|
||||||
|
|
||||||
|
R.id.action_hide_disabled_apps -> {
|
||||||
|
item.isChecked = !item.isChecked
|
||||||
|
hideDisabledApps = item.isChecked
|
||||||
|
filterApplicationList()
|
||||||
|
adapter.setApplicationList(currentPackages)
|
||||||
|
}
|
||||||
|
|
||||||
|
R.id.action_select_all -> {
|
||||||
|
val selectedUIDs = mutableSetOf<Int>()
|
||||||
|
for (packageCache in packages) {
|
||||||
|
selectedUIDs.add(packageCache.uid)
|
||||||
|
}
|
||||||
|
this.selectedUIDs = selectedUIDs
|
||||||
|
saveSelectedApplications()
|
||||||
|
}
|
||||||
|
|
||||||
|
R.id.action_deselect_all -> {
|
||||||
|
selectedUIDs = mutableSetOf()
|
||||||
|
saveSelectedApplications()
|
||||||
|
}
|
||||||
|
|
||||||
|
R.id.action_export -> {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val packageList = mutableListOf<String>()
|
||||||
|
for (packageCache in packages) {
|
||||||
|
if (selectedUIDs.contains(packageCache.uid)) {
|
||||||
|
packageList.add(packageCache.packageName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clipboardText = packageList.joinToString("\n")
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
Toast.makeText(
|
||||||
|
this@PerAppProxyActivity0,
|
||||||
|
R.string.toast_copied_to_clipboard,
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
R.id.action_import -> {
|
||||||
|
val packageNames = clipboardText?.split("\n")?.distinct()
|
||||||
|
?.takeIf { it.isNotEmpty() && it[0].isNotEmpty() }
|
||||||
|
if (packageNames.isNullOrEmpty()) {
|
||||||
|
Toast.makeText(
|
||||||
|
this@PerAppProxyActivity0,
|
||||||
|
R.string.toast_clipboard_empty,
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
val selectedUIDs = mutableSetOf<Int>()
|
||||||
|
for (packageCache in packages) {
|
||||||
|
if (packageNames.contains(packageCache.packageName)) {
|
||||||
|
selectedUIDs.add(packageCache.uid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lifecycleScope.launch {
|
||||||
|
postSaveSelectedApplications(selectedUIDs)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
Toast.makeText(
|
||||||
|
this@PerAppProxyActivity0,
|
||||||
|
R.string.toast_imported_from_clipboard,
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
R.id.action_scan_china_apps -> {
|
||||||
|
scanChinaApps()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
private fun scanChinaApps() {
|
||||||
|
val binding = DialogProgressbarBinding.inflate(layoutInflater)
|
||||||
|
binding.progress.max = currentPackages.size
|
||||||
|
binding.message.setText(R.string.message_scanning)
|
||||||
|
val progress = MaterialAlertDialogBuilder(
|
||||||
|
this, com.google.android.material.R.style.Theme_MaterialComponents_Dialog
|
||||||
|
).setView(binding.root).setCancelable(false).create()
|
||||||
|
progress.show()
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val foundApps = withContext(Dispatchers.IO) {
|
||||||
|
mutableMapOf<String, PackageCache>().also { foundApps ->
|
||||||
|
currentPackages.forEachIndexed { index, it ->
|
||||||
|
if (scanChinaPackage(it.packageName)) {
|
||||||
|
foundApps[it.packageName] = it
|
||||||
|
}
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
binding.progress.progress = index + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
progress.dismiss()
|
||||||
|
if (foundApps.isEmpty()) {
|
||||||
|
MaterialAlertDialogBuilder(this@PerAppProxyActivity0).setTitle(R.string.title_scan_result)
|
||||||
|
.setMessage(R.string.message_scan_app_no_apps_found)
|
||||||
|
.setPositiveButton(R.string.ok, null).show()
|
||||||
|
return@withContext
|
||||||
|
}
|
||||||
|
val dialogContent =
|
||||||
|
getString(R.string.message_scan_app_found) + "\n\n" + foundApps.entries.joinToString(
|
||||||
|
"\n"
|
||||||
|
) {
|
||||||
|
"${it.value.applicationLabel} (${it.key})"
|
||||||
|
}
|
||||||
|
MaterialAlertDialogBuilder(this@PerAppProxyActivity0).setTitle(R.string.title_scan_result)
|
||||||
|
.setMessage(dialogContent)
|
||||||
|
.setPositiveButton(R.string.action_select) { dialog, _ ->
|
||||||
|
dialog.dismiss()
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val selectedUIDs = selectedUIDs.toMutableSet()
|
||||||
|
foundApps.values.forEach {
|
||||||
|
selectedUIDs.add(it.uid)
|
||||||
|
}
|
||||||
|
postSaveSelectedApplications(selectedUIDs)
|
||||||
|
}
|
||||||
|
}.setNegativeButton(R.string.action_deselect) { dialog, _ ->
|
||||||
|
dialog.dismiss()
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val selectedUIDs = selectedUIDs.toMutableSet()
|
||||||
|
foundApps.values.forEach {
|
||||||
|
selectedUIDs.remove(it.uid)
|
||||||
|
}
|
||||||
|
postSaveSelectedApplications(selectedUIDs)
|
||||||
|
}
|
||||||
|
}.setNeutralButton(android.R.string.cancel, null).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
private suspend fun postSaveSelectedApplications(newUIDs: MutableSet<Int>) {
|
||||||
|
filterApplicationList(newUIDs)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
selectedUIDs = newUIDs
|
||||||
|
adapter.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
val packageList = selectedUIDs.mapNotNull { uid ->
|
||||||
|
packages.find { it.uid == uid }?.packageName
|
||||||
|
}
|
||||||
|
Settings.perAppProxyList = packageList.toSet()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveSelectedApplications() {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val packageList = selectedUIDs.mapNotNull { uid ->
|
||||||
|
packages.find { it.uid == uid }?.packageName
|
||||||
|
}
|
||||||
|
Settings.perAppProxyList = packageList.toSet()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private val chinaAppPrefixList by lazy {
|
||||||
|
runCatching {
|
||||||
|
Application.application.assets.open("prefix-china-apps.txt").reader().readLines()
|
||||||
|
}.getOrNull() ?: emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val chinaAppRegex by lazy {
|
||||||
|
("(" + chinaAppPrefixList.joinToString("|").replace(".", "\\.") + ").*").toRegex()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun scanChinaPackage(packageName: String): Boolean {
|
||||||
|
val packageManagerFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
PackageManager.MATCH_UNINSTALLED_PACKAGES or PackageManager.GET_ACTIVITIES or PackageManager.GET_SERVICES or PackageManager.GET_RECEIVERS or PackageManager.GET_PROVIDERS
|
||||||
|
} else {
|
||||||
|
@Suppress("DEPRECATION") PackageManager.GET_UNINSTALLED_PACKAGES or PackageManager.GET_ACTIVITIES or PackageManager.GET_SERVICES or PackageManager.GET_RECEIVERS or PackageManager.GET_PROVIDERS
|
||||||
|
}
|
||||||
|
if (packageName.matches(chinaAppRegex)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
val packageInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
Application.packageManager.getPackageInfo(
|
||||||
|
packageName,
|
||||||
|
PackageManager.PackageInfoFlags.of(packageManagerFlags.toLong())
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
@Suppress("DEPRECATION") Application.packageManager.getPackageInfo(
|
||||||
|
packageName, packageManagerFlags
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (packageInfo.services?.find { it.name.matches(chinaAppRegex) } != null || packageInfo.activities?.find {
|
||||||
|
it.name.matches(
|
||||||
|
chinaAppRegex
|
||||||
|
)
|
||||||
|
} != null || packageInfo.receivers?.find { it.name.matches(chinaAppRegex) } != null || packageInfo.providers?.find {
|
||||||
|
it.name.matches(
|
||||||
|
chinaAppRegex
|
||||||
|
)
|
||||||
|
} != null) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
ZipFile(File(packageInfo.applicationInfo.publicSourceDir)).use {
|
||||||
|
for (packageEntry in it.entries()) {
|
||||||
|
if (!(packageEntry.name.startsWith("classes") && packageEntry.name.endsWith(
|
||||||
|
".dex"
|
||||||
|
))
|
||||||
|
) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (packageEntry.size > 15000000) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
val input = it.getInputStream(packageEntry).buffered()
|
||||||
|
val dexFile = try {
|
||||||
|
DexBackedDexFile.fromInputStream(null, input)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for (clazz in dexFile.classes) {
|
||||||
|
val clazzName =
|
||||||
|
clazz.type.substring(1, clazz.type.length - 1).replace("/", ".")
|
||||||
|
.replace("$", ".")
|
||||||
|
if (clazzName.matches(chinaAppRegex)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (ignored: Exception) {
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -15,17 +15,13 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
class ProfileOverrideActivity : AbstractActivity() {
|
class ProfileOverrideActivity :
|
||||||
|
AbstractActivity<ActivityConfigOverrideBinding>() {
|
||||||
private lateinit var binding: ActivityConfigOverrideBinding
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setTitle(R.string.title_profile_override)
|
|
||||||
binding = ActivityConfigOverrideBinding.inflate(layoutInflater)
|
|
||||||
setContentView(binding.root)
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
|
|
||||||
|
setTitle(R.string.title_profile_override)
|
||||||
binding.switchPerAppProxy.isChecked = Settings.perAppProxyEnabled
|
binding.switchPerAppProxy.isChecked = Settings.perAppProxyEnabled
|
||||||
binding.switchPerAppProxy.setOnCheckedChangeListener { _, isChecked ->
|
binding.switchPerAppProxy.setOnCheckedChangeListener { _, isChecked ->
|
||||||
Settings.perAppProxyEnabled = isChecked
|
Settings.perAppProxyEnabled = isChecked
|
||||||
@@ -42,7 +38,7 @@ class ProfileOverrideActivity : AbstractActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
binding.configureAppListButton.setOnClickListener {
|
binding.configureAppListButton.setOnClickListener {
|
||||||
startActivity(Intent(this, PerAppProxyActivity::class.java))
|
startActivity(Intent(this, PerAppProxyActivity0::class.java))
|
||||||
}
|
}
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
reloadSettings()
|
reloadSettings()
|
||||||
|
|||||||
@@ -1,16 +1,26 @@
|
|||||||
package io.nekohasekai.sfa.ui.shared
|
package io.nekohasekai.sfa.ui.shared
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.appcompat.content.res.AppCompatResources
|
import androidx.appcompat.content.res.AppCompatResources
|
||||||
|
import androidx.viewbinding.ViewBinding
|
||||||
|
import com.google.android.material.appbar.MaterialToolbar
|
||||||
import com.google.android.material.color.DynamicColors
|
import com.google.android.material.color.DynamicColors
|
||||||
import io.nekohasekai.sfa.R
|
import io.nekohasekai.sfa.R
|
||||||
import io.nekohasekai.sfa.ktx.getAttrColor
|
import io.nekohasekai.sfa.ktx.getAttrColor
|
||||||
|
import io.nekohasekai.sfa.ui.MainActivity
|
||||||
import io.nekohasekai.sfa.utils.MIUIUtils
|
import io.nekohasekai.sfa.utils.MIUIUtils
|
||||||
|
import java.lang.reflect.ParameterizedType
|
||||||
|
|
||||||
|
abstract class AbstractActivity<Binding : ViewBinding>() :
|
||||||
|
AppCompatActivity() {
|
||||||
|
|
||||||
|
private var _binding: Binding? = null
|
||||||
|
internal val binding get() = _binding!!
|
||||||
|
|
||||||
abstract class AbstractActivity : AppCompatActivity() {
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
@@ -21,17 +31,28 @@ abstract class AbstractActivity : AppCompatActivity() {
|
|||||||
window.statusBarColor = colorSurfaceContainer
|
window.statusBarColor = colorSurfaceContainer
|
||||||
window.navigationBarColor = colorSurfaceContainer
|
window.navigationBarColor = colorSurfaceContainer
|
||||||
|
|
||||||
|
_binding = createBindingInstance(layoutInflater).also {
|
||||||
|
setContentView(it.root)
|
||||||
|
}
|
||||||
|
|
||||||
|
findViewById<MaterialToolbar>(R.id.toolbar)?.also {
|
||||||
|
setSupportActionBar(it)
|
||||||
|
}
|
||||||
|
|
||||||
// MIUI overrides colorSurfaceContainer to colorSurface without below flags
|
// MIUI overrides colorSurfaceContainer to colorSurface without below flags
|
||||||
@Suppress("DEPRECATION") if (MIUIUtils.isMIUI) {
|
@Suppress("DEPRECATION") if (MIUIUtils.isMIUI) {
|
||||||
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
|
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
|
||||||
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
|
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
|
||||||
}
|
}
|
||||||
|
|
||||||
supportActionBar?.setHomeAsUpIndicator(AppCompatResources.getDrawable(
|
if (this !is MainActivity) {
|
||||||
this@AbstractActivity, R.drawable.ic_arrow_back_24
|
supportActionBar?.setHomeAsUpIndicator(AppCompatResources.getDrawable(
|
||||||
)!!.apply {
|
this@AbstractActivity, R.drawable.ic_arrow_back_24
|
||||||
setTint(getAttrColor(com.google.android.material.R.attr.colorOnSurface))
|
)!!.apply {
|
||||||
})
|
setTint(getAttrColor(com.google.android.material.R.attr.colorOnSurface))
|
||||||
|
})
|
||||||
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
@@ -44,4 +65,14 @@ abstract class AbstractActivity : AppCompatActivity() {
|
|||||||
return super.onOptionsItemSelected(item)
|
return super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
private fun createBindingInstance(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
): Binding {
|
||||||
|
val vbType = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0]
|
||||||
|
val vbClass = vbType as Class<Binding>
|
||||||
|
val method = vbClass.getMethod("inflate", LayoutInflater::class.java)
|
||||||
|
return method.invoke(null, inflater) as Binding
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,175 +1,185 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_height="wrap_content">
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<LinearLayout
|
<include layout="@layout/view_appbar" />
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical">
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
|
|
||||||
<com.google.android.material.progressindicator.LinearProgressIndicator
|
|
||||||
android:id="@+id/progressView"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:indeterminate="true"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical">
|
||||||
android:clipToPadding="false"
|
|
||||||
android:clipChildren="false"
|
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||||
android:id="@+id/name"
|
android:id="@+id/progressView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:hint="@string/profile_name">
|
android:indeterminate="true"
|
||||||
|
android:visibility="gone" />
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
|
||||||
android:id="@+id/type"
|
|
||||||
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:hint="@string/profile_type">
|
|
||||||
|
|
||||||
<AutoCompleteTextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:inputType="none"
|
|
||||||
android:text="@string/profile_type_local"
|
|
||||||
app:simpleItems="@array/profile_type" />
|
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/localFields"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical">
|
android:clipChildren="false"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/fileSourceMenu"
|
android:id="@+id/name"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/profile_name">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/type"
|
||||||
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
|
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:hint="@string/profile_source">
|
android:hint="@string/profile_type">
|
||||||
|
|
||||||
<AutoCompleteTextView
|
<AutoCompleteTextView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:inputType="none"
|
android:inputType="none"
|
||||||
android:text="@string/profile_source_create_new"
|
android:text="@string/profile_type_local"
|
||||||
app:simpleItems="@array/profile_source" />
|
app:simpleItems="@array/profile_type" />
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/localFields"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/fileSourceMenu"
|
||||||
|
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:hint="@string/profile_source">
|
||||||
|
|
||||||
|
<AutoCompleteTextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="none"
|
||||||
|
android:text="@string/profile_source_create_new"
|
||||||
|
app:simpleItems="@array/profile_source" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/importFileButton"
|
||||||
|
style="@style/Widget.Material3.Button.ElevatedButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="@string/profile_import_file"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/sourceURL"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:hint="@string/profile_url"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/remoteFields"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/remoteURL"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:hint="@string/profile_url">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/autoUpdate"
|
||||||
|
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:hint="@string/profile_auto_update">
|
||||||
|
|
||||||
|
<AutoCompleteTextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="none"
|
||||||
|
android:text="@string/enabled"
|
||||||
|
app:simpleItems="@array/enabled" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/autoUpdateInterval"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:hint="@string/profile_auto_update_interval">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="number" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/importFileButton"
|
android:id="@+id/createProfile"
|
||||||
style="@style/Widget.Material3.Button.ElevatedButton"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
tools:visibility="visible"
|
android:text="@string/profile_create">
|
||||||
android:text="@string/profile_import_file"
|
|
||||||
android:visibility="gone">
|
|
||||||
|
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
|
||||||
android:id="@+id/sourceURL"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:hint="@string/profile_url"
|
|
||||||
android:visibility="gone">
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/remoteFields"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:visibility="gone">
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
|
||||||
android:id="@+id/remoteURL"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:hint="@string/profile_url">
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
|
||||||
android:id="@+id/autoUpdate"
|
|
||||||
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:hint="@string/profile_auto_update">
|
|
||||||
|
|
||||||
<AutoCompleteTextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:inputType="none"
|
|
||||||
android:text="@string/enabled"
|
|
||||||
app:simpleItems="@array/enabled" />
|
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
|
||||||
android:id="@+id/autoUpdateInterval"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:hint="@string/profile_auto_update_interval">
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:inputType="number" />
|
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/createProfile"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text="@string/profile_create">
|
|
||||||
|
|
||||||
</Button>
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
</LinearLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|
||||||
</ScrollView>
|
|
||||||
@@ -1,89 +1,98 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<LinearLayout
|
<include layout="@layout/view_appbar" />
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:clipChildren="false"
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
android:clipToPadding="false"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<LinearLayout
|
||||||
android:id="@+id/perAppProxyCard"
|
|
||||||
style="?attr/materialCardViewElevatedStyle"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
android:clipChildren="false"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
<LinearLayout
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/perAppProxyCard"
|
||||||
|
style="?attr/materialCardViewElevatedStyle"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content">
|
||||||
android:orientation="vertical"
|
|
||||||
android:paddingStart="16dp"
|
|
||||||
android:paddingTop="16dp"
|
|
||||||
android:paddingEnd="16dp"
|
|
||||||
android:paddingBottom="8dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<com.google.android.material.materialswitch.MaterialSwitch
|
|
||||||
android:id="@+id/switchPerAppProxy"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/title_per_app_proxy"
|
|
||||||
android:textAppearance="?attr/textAppearanceTitleLarge" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text="@string/per_app_proxy_description" />
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
|
||||||
android:id="@+id/perAppProxyUpdateOnChange"
|
|
||||||
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:hint="@string/per_app_proxy_update_on_change">
|
|
||||||
|
|
||||||
<AutoCompleteTextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:inputType="none"
|
|
||||||
android:text="@string/disabled"
|
|
||||||
app:simpleItems="@array/per_app_proxy_update_on_change_value" />
|
|
||||||
|
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
android:orientation="vertical"
|
||||||
android:gravity="center_vertical|end"
|
android:paddingStart="16dp"
|
||||||
android:orientation="horizontal">
|
android:paddingTop="16dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:paddingBottom="8dp">
|
||||||
|
|
||||||
<Button
|
<LinearLayout
|
||||||
android:id="@+id/configureAppListButton"
|
android:layout_width="match_parent"
|
||||||
style="@style/Widget.Material3.Button.TextButton"
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<com.google.android.material.materialswitch.MaterialSwitch
|
||||||
|
android:id="@+id/switchPerAppProxy"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/title_per_app_proxy"
|
||||||
|
android:textAppearance="?attr/textAppearanceTitleLarge" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/profile_override_configure" />
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="@string/per_app_proxy_description" />
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/perAppProxyUpdateOnChange"
|
||||||
|
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:hint="@string/per_app_proxy_update_on_change">
|
||||||
|
|
||||||
|
<AutoCompleteTextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="none"
|
||||||
|
android:text="@string/disabled"
|
||||||
|
app:simpleItems="@array/per_app_proxy_update_on_change_value" />
|
||||||
|
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:gravity="center_vertical|end"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/configureAppListButton"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/profile_override_configure" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</ScrollView>
|
||||||
|
|
||||||
</ScrollView>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
@@ -1,8 +1,15 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout 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_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:orientation="vertical">
|
|
||||||
|
<include layout="@layout/view_appbar" />
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -63,4 +70,6 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
@@ -1,82 +1,44 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<LinearLayout
|
<include layout="@layout/view_appbar" />
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical">
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
|
|
||||||
<com.google.android.material.progressindicator.LinearProgressIndicator
|
|
||||||
android:id="@+id/progressView"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:indeterminate="true" />
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/profileLayout"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:clipToPadding="false"
|
android:orientation="vertical">
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="16dp"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:visibility="visible">
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||||
android:id="@+id/name"
|
android:id="@+id/progressView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:hint="@string/profile_name">
|
android:indeterminate="true" />
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
|
||||||
android:id="@+id/type"
|
|
||||||
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:enabled="false"
|
|
||||||
android:hint="@string/profile_type">
|
|
||||||
|
|
||||||
<AutoCompleteTextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:inputType="none"
|
|
||||||
android:text="@string/profile_type_local"
|
|
||||||
app:simpleItems="@array/profile_type" />
|
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/editButton"
|
|
||||||
style="@style/Widget.Material3.Button.ElevatedButton"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text="@string/profile_edit_content" />
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/remoteFields"
|
android:id="@+id/profileLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
|
android:clipToPadding="false"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:paddingTop="8dp">
|
android:padding="16dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/remoteURL"
|
android:id="@+id/name"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
android:hint="@string/profile_name">
|
||||||
android:hint="@string/profile_url">
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -84,66 +46,112 @@
|
|||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/autoUpdate"
|
android:id="@+id/type"
|
||||||
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
|
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:hint="@string/profile_auto_update">
|
android:enabled="false"
|
||||||
|
android:hint="@string/profile_type">
|
||||||
|
|
||||||
<AutoCompleteTextView
|
<AutoCompleteTextView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:inputType="none"
|
android:inputType="none"
|
||||||
android:text="@string/disabled"
|
android:text="@string/profile_type_local"
|
||||||
app:simpleItems="@array/enabled" />
|
app:simpleItems="@array/profile_type" />
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
|
||||||
android:id="@+id/autoUpdateInterval"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:hint="@string/profile_auto_update_interval">
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:inputType="number" />
|
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
|
||||||
android:id="@+id/lastUpdated"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:enabled="false"
|
|
||||||
android:hint="@string/profile_last_updated">
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/updateButton"
|
android:id="@+id/editButton"
|
||||||
style="@style/Widget.Material3.Button.ElevatedButton"
|
style="@style/Widget.Material3.Button.ElevatedButton"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:text="@string/profile_update" />
|
android:text="@string/profile_edit_content" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/remoteFields"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingTop="8dp">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/remoteURL"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:hint="@string/profile_url">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/autoUpdate"
|
||||||
|
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:hint="@string/profile_auto_update">
|
||||||
|
|
||||||
|
<AutoCompleteTextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="none"
|
||||||
|
android:text="@string/disabled"
|
||||||
|
app:simpleItems="@array/enabled" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/autoUpdateInterval"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:hint="@string/profile_auto_update_interval">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="number" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/lastUpdated"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:enabled="false"
|
||||||
|
android:hint="@string/profile_last_updated">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/updateButton"
|
||||||
|
style="@style/Widget.Material3.Button.ElevatedButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="@string/profile_update" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
</LinearLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|
||||||
</ScrollView>
|
|
||||||
@@ -1,14 +1,29 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout 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_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<com.google.android.material.progressindicator.LinearProgressIndicator
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/progressView"
|
android:id="@+id/appbar_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:indeterminate="true" />
|
android:background="?colorSurfaceContainer"
|
||||||
|
android:fitsSystemWindows="true">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
style="?attr/toolbarStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?actionBarSize" />
|
||||||
|
|
||||||
|
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||||
|
android:id="@+id/progressView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:indeterminate="true" />
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<com.blacksquircle.ui.editorkit.widget.TextProcessor
|
<com.blacksquircle.ui.editorkit.widget.TextProcessor
|
||||||
android:id="@+id/editor"
|
android:id="@+id/editor"
|
||||||
@@ -18,6 +33,8 @@
|
|||||||
android:gravity="top|start"
|
android:gravity="top|start"
|
||||||
android:padding="8dp"
|
android:padding="8dp"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
android:typeface="monospace" />
|
android:typeface="monospace"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
@@ -1,54 +1,66 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<RadioGroup
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/radio_group_per_app_mode"
|
android:id="@+id/appbar_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingHorizontal="16dp">
|
android:background="?colorSurfaceContainer"
|
||||||
|
android:fitsSystemWindows="true">
|
||||||
|
|
||||||
<RadioButton
|
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||||
android:id="@+id/radio_per_app_exclude"
|
style="?attr/collapsingToolbarLayoutLargeStyle"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="?attr/collapsingToolbarLayoutLargeSize"
|
||||||
android:text="@string/per_app_proxy_mode_exclude" />
|
app:layout_scrollFlags="scroll|enterAlways|snap">
|
||||||
|
|
||||||
<RadioButton
|
<LinearLayout
|
||||||
android:id="@+id/radio_per_app_include"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:orientation="vertical">
|
||||||
android:text="@string/per_app_proxy_mode_include" />
|
|
||||||
|
|
||||||
</RadioGroup>
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?actionBarSize" />
|
||||||
|
|
||||||
<com.google.android.material.divider.MaterialDivider
|
|
||||||
android:layout_width="match_parent"
|
<RadioGroup
|
||||||
android:layout_height="1dp" />
|
android:id="@+id/radio_group_per_app_mode"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingHorizontal="16dp">
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/radio_per_app_exclude"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/per_app_proxy_mode_exclude" />
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/radio_per_app_include"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/per_app_proxy_mode_include" />
|
||||||
|
|
||||||
|
</RadioGroup>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/recycler_view_app_list"
|
android:id="@+id/recycler_view_app_list"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="match_parent"
|
||||||
android:layout_weight="1"
|
|
||||||
android:scrollbars="vertical"
|
android:scrollbars="vertical"
|
||||||
android:visibility="gone"
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:id="@+id/layout_progress"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_weight="1">
|
|
||||||
|
|
||||||
<ProgressBar
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_centerInParent="true" />
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
55
app/src/main/res/layout/activity_per_app_proxy0.xml
Normal file
55
app/src/main/res/layout/activity_per_app_proxy0.xml
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout 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">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:id="@+id/appbar_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?colorSurfaceContainer"
|
||||||
|
android:fitsSystemWindows="true">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
style="?attr/toolbarStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?actionBarSize" />
|
||||||
|
|
||||||
|
|
||||||
|
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||||
|
android:id="@+id/progress"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:indeterminate="true" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:background="?colorSurfaceContainerLow"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingLeft="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/per_app_proxy_mode"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/per_app_proxy_mode_include" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/app_list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||||
|
tools:listitem="@layout/view_app_list_item" />
|
||||||
|
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
@@ -1,14 +1,31 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout 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_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<com.google.android.material.progressindicator.LinearProgressIndicator
|
|
||||||
android:id="@+id/scanVPNProgress"
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:id="@+id/appbar_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:indeterminate="true" />
|
android:background="?colorSurfaceContainer"
|
||||||
|
android:fitsSystemWindows="true">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
style="?attr/toolbarStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?actionBarSize" />
|
||||||
|
|
||||||
|
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||||
|
android:id="@+id/scanVPNProgress"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:indeterminate="true" />
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/scanVPNResult"
|
android:id="@+id/scanVPNResult"
|
||||||
@@ -17,6 +34,7 @@
|
|||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
android:paddingStart="16dp"
|
android:paddingStart="16dp"
|
||||||
android:paddingEnd="16dp"
|
android:paddingEnd="16dp"
|
||||||
android:paddingBottom="16dp" />
|
android:paddingBottom="16dp"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
|
|
||||||
</LinearLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
18
app/src/main/res/layout/dialog_progressbar.xml
Normal file
18
app/src/main/res/layout/dialog_progressbar.xml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?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="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||||
|
android:id="@+id/progress"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/message"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="16dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -36,8 +36,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="bottom|end"
|
android:layout_gravity="bottom|end"
|
||||||
android:layout_margin="16dp"
|
android:layout_margin="16dp"
|
||||||
app:icon="@drawable/ic_note_add_24"
|
app:icon="@drawable/ic_note_add_24" />
|
||||||
app:iconTint="@android:color/white" />
|
|
||||||
|
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?selectableItemBackground"
|
android:background="?selectableItemBackground"
|
||||||
@@ -7,40 +8,59 @@
|
|||||||
android:paddingHorizontal="16dp"
|
android:paddingHorizontal="16dp"
|
||||||
android:paddingVertical="8dp">
|
android:paddingVertical="8dp">
|
||||||
|
|
||||||
<ImageView
|
<LinearLayout
|
||||||
android:id="@+id/image_app_icon"
|
android:layout_width="wrap_content"
|
||||||
android:layout_width="48dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="48dp"
|
android:gravity="top">
|
||||||
android:contentDescription="@string/content_description_app_icon" />
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/app_icon"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:contentDescription="@string/content_description_app_icon"
|
||||||
|
tools:src="@drawable/ic_launcher_foreground" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/text_app_name"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:textColor="?android:textColorPrimary"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/text_app_package_name"
|
android:id="@+id/application_label"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="?colorOnSurface"
|
||||||
|
android:textSize="16sp"
|
||||||
|
tools:text="sing-box" />
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/package_name"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
tools:text="io.nekohasekai.sfa" />
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<CheckBox
|
<LinearLayout
|
||||||
android:id="@+id/checkbox_app_selected"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:clickable="false"
|
android:gravity="top">
|
||||||
android:focusable="false" />
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/selected"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clickable="false"
|
||||||
|
android:focusable="false" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
64
app/src/main/res/layout/view_app_list_item0.xml
Normal file
64
app/src/main/res/layout/view_app_list_item0.xml
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?selectableItemBackground"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingHorizontal="16dp"
|
||||||
|
android:paddingVertical="8dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="top">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/app_icon"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:contentDescription="@string/content_description_app_icon"
|
||||||
|
tools:src="@drawable/ic_launcher_foreground" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/application_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="?colorOnSurface"
|
||||||
|
android:textSize="16sp"
|
||||||
|
tools:text="sing-box" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/package_name"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
tools:text="io.nekohasekai.sfa" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="top">
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/selected"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clickable="false"
|
||||||
|
android:focusable="false" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
15
app/src/main/res/layout/view_appbar.xml
Normal file
15
app/src/main/res/layout/view_appbar.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.appbar.AppBarLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/appbar_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?colorSurfaceContainer"
|
||||||
|
android:fitsSystemWindows="true">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
style="?attr/toolbarStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?actionBarSize" />
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
25
app/src/main/res/menu/app_menu.xml
Normal file
25
app/src/main/res/menu/app_menu.xml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_copy"
|
||||||
|
android:icon="@drawable/ic_insert_drive_file_24"
|
||||||
|
android:title="@string/per_app_proxy_action_copy"
|
||||||
|
app:iconTint="?colorPrimary"
|
||||||
|
app:iconTintMode="src_in">
|
||||||
|
<menu>
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_copy_application_label"
|
||||||
|
android:title="@string/per_app_proxy_action_copy_application_label" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_copy_package_name"
|
||||||
|
android:title="@string/per_app_proxy_action_copy_package_name" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_copy_uid"
|
||||||
|
android:title="@string/per_app_proxy_action_copy_uid" />
|
||||||
|
|
||||||
|
</menu>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
</menu>
|
||||||
@@ -20,10 +20,10 @@
|
|||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_import"
|
android:id="@+id/action_import"
|
||||||
android:title="@string/menu_import_from_clipboard" />
|
android:title="@string/per_app_proxy_import" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_export"
|
android:id="@+id/action_export"
|
||||||
android:title="@string/menu_export_to_clipboard" />
|
android:title="@string/per_app_proxy_export" />
|
||||||
|
|
||||||
</menu>
|
</menu>
|
||||||
99
app/src/main/res/menu/per_app_menu0.xml
Normal file
99
app/src/main/res/menu/per_app_menu0.xml
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_search"
|
||||||
|
android:icon="@drawable/ic_find_in_page_24"
|
||||||
|
android:title="@string/search"
|
||||||
|
app:actionViewClass="androidx.appcompat.widget.SearchView"
|
||||||
|
app:iconTint="?colorControlNormal"
|
||||||
|
app:showAsAction="collapseActionView|always" />
|
||||||
|
|
||||||
|
<item android:title="@string/per_app_proxy_mode">
|
||||||
|
<menu>
|
||||||
|
<group android:checkableBehavior="single">
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_mode_include"
|
||||||
|
android:title="@string/per_app_proxy_mode_include" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_mode_exclude"
|
||||||
|
android:title="@string/per_app_proxy_mode_exclude" />
|
||||||
|
</group>
|
||||||
|
</menu>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item android:title="@string/per_app_proxy_sort_mode">
|
||||||
|
<menu>
|
||||||
|
<group android:checkableBehavior="single">
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_sort_by_name"
|
||||||
|
android:title="@string/per_app_proxy_sort_mode_name" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_sort_by_package_name"
|
||||||
|
android:title="@string/per_app_proxy_sort_mode_package_name" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_sort_by_uid"
|
||||||
|
android:title="@string/per_app_proxy_sort_mode_uid" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_sort_by_install_time"
|
||||||
|
android:title="@string/per_app_proxy_sort_mode_install_time" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_sort_by_update_time"
|
||||||
|
android:title="@string/per_app_proxy_sort_mode_update_time" />
|
||||||
|
</group>
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_sort_reverse"
|
||||||
|
android:checkable="true"
|
||||||
|
android:title="@string/per_app_proxy_sort_mode_reverse" />
|
||||||
|
</menu>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item android:title="@string/per_app_proxy_filter">
|
||||||
|
<menu>
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_hide_system_apps"
|
||||||
|
android:checkable="true"
|
||||||
|
android:title="@string/per_app_proxy_hide_system_apps" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_hide_offline_apps"
|
||||||
|
android:checkable="true"
|
||||||
|
android:title="@string/per_app_proxy_hide_offline_apps" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_hide_disabled_apps"
|
||||||
|
android:checkable="true"
|
||||||
|
android:title="@string/per_app_proxy_hide_disabled_apps" />
|
||||||
|
</menu>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item android:title="@string/action_select">
|
||||||
|
<menu>
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_select_all"
|
||||||
|
android:title="@string/per_app_proxy_select_all" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_deselect_all"
|
||||||
|
android:title="@string/per_app_proxy_select_none" />
|
||||||
|
</menu>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item android:title="@string/per_app_proxy_backup">
|
||||||
|
<menu>
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_import"
|
||||||
|
android:title="@string/per_app_proxy_import" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_export"
|
||||||
|
android:title="@string/per_app_proxy_export" />
|
||||||
|
</menu>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item android:title="@string/per_app_proxy_scan">
|
||||||
|
<menu>
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_scan_china_apps"
|
||||||
|
android:title="@string/per_app_proxy_scan_china_apps" />
|
||||||
|
</menu>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
</menu>
|
||||||
@@ -1,11 +1,8 @@
|
|||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<style name="AppTheme" parent="Theme.Material3.DayNight">
|
<style name="AppTheme" parent="Theme.Material3.DayNight.NoActionBar">
|
||||||
<item name="android:windowLightStatusBar" tools:targetApi="m">false</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="AppTheme.NoActionBar" parent="Theme.Material3.DayNight.NoActionBar">
|
|
||||||
<item name="android:windowLightStatusBar" tools:targetApi="m">false</item>
|
<item name="android:windowLightStatusBar" tools:targetApi="m">false</item>
|
||||||
|
<item name="android:windowLightNavigationBar" tools:targetApi="m">false</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -99,22 +99,53 @@
|
|||||||
<string name="title_profile_override">Profile Override</string>
|
<string name="title_profile_override">Profile Override</string>
|
||||||
<string name="profile_override_description">Overrides profile configuration items with platform-specific values.</string>
|
<string name="profile_override_description">Overrides profile configuration items with platform-specific values.</string>
|
||||||
<string name="profile_override_configure">Configure</string>
|
<string name="profile_override_configure">Configure</string>
|
||||||
|
|
||||||
<string name="title_per_app_proxy">Per-app Proxy</string>
|
<string name="title_per_app_proxy">Per-app Proxy</string>
|
||||||
<string name="per_app_proxy_description">Override include_package and exclude_package in the configuration.</string>
|
<string name="per_app_proxy_description">Override include_package and exclude_package in the configuration.</string>
|
||||||
<string name="per_app_proxy_mode_exclude">Do not proxy selected apps</string>
|
<string name="per_app_proxy_mode">Proxy Mode</string>
|
||||||
<string name="per_app_proxy_mode_include">Proxy only selected apps</string>
|
<string name="per_app_proxy_mode_include">Include</string>
|
||||||
|
<string name="per_app_proxy_mode_include_description">Only selected apps are allowed through the VPN</string>
|
||||||
|
<string name="per_app_proxy_mode_exclude">Exclude</string>
|
||||||
|
<string name="per_app_proxy_mode_exclude_description">Selected apps will be excluded from VPN</string>
|
||||||
|
<string name="per_app_proxy_action_copy">Copy</string>
|
||||||
|
<string name="per_app_proxy_action_copy_application_label">Name</string>
|
||||||
|
<string name="per_app_proxy_action_copy_package_name">Package Name</string>
|
||||||
|
<string name="per_app_proxy_action_copy_uid">UID</string>
|
||||||
|
|
||||||
|
<string name="per_app_proxy_sort_mode">Sort Mode</string>
|
||||||
|
<string name="per_app_proxy_sort_mode_name">By name</string>
|
||||||
|
<string name="per_app_proxy_sort_mode_package_name">By package name</string>
|
||||||
|
<string name="per_app_proxy_sort_mode_uid">By UID</string>
|
||||||
|
<string name="per_app_proxy_sort_mode_install_time">By install time</string>
|
||||||
|
<string name="per_app_proxy_sort_mode_update_time">By update time</string>
|
||||||
|
<string name="per_app_proxy_sort_mode_reverse">Reverse</string>
|
||||||
|
|
||||||
|
<string name="per_app_proxy_filter">Filter</string>
|
||||||
|
<string name="per_app_proxy_hide_system_apps">Hide system apps</string>
|
||||||
|
<string name="per_app_proxy_hide_offline_apps">Hide offline apps</string>
|
||||||
|
<string name="per_app_proxy_hide_disabled_apps">Hide disabled apps</string>
|
||||||
|
|
||||||
|
<string name="per_app_proxy_select">Select</string>
|
||||||
|
<string name="per_app_proxy_select_all">Select all</string>
|
||||||
|
<string name="per_app_proxy_select_none">Deselect all</string>
|
||||||
|
|
||||||
|
<string name="per_app_proxy_backup">Backup</string>
|
||||||
|
<string name="per_app_proxy_import">Import from clipboard</string>
|
||||||
|
<string name="per_app_proxy_export">Export to clipboard</string>
|
||||||
|
|
||||||
|
<string name="per_app_proxy_scan">Scan</string>
|
||||||
|
<string name="per_app_proxy_scan_china_apps">China apps</string>
|
||||||
|
|
||||||
<string name="content_description_app_icon">App icon</string>
|
<string name="content_description_app_icon">App icon</string>
|
||||||
<string name="menu_hide_system">Hide system apps</string>
|
<string name="menu_hide_system">Hide system apps</string>
|
||||||
<string name="menu_show_system">Show system apps</string>
|
<string name="menu_show_system">Show system apps</string>
|
||||||
<string name="menu_scan_china_apps">Scan China apps</string>
|
<string name="menu_scan_china_apps">Scan China apps</string>
|
||||||
<string name="menu_import_from_clipboard">Import from clipboard</string>
|
|
||||||
<string name="menu_export_to_clipboard">Export to clipboard</string>
|
|
||||||
<string name="toast_clipboard_empty">Clipboard is empty</string>
|
<string name="toast_clipboard_empty">Clipboard is empty</string>
|
||||||
<string name="toast_app_list_empty">App list is empty</string>
|
<string name="toast_app_list_empty">App list is empty</string>
|
||||||
<string name="toast_copied_to_clipboard">Copied to clipboard</string>
|
<string name="toast_copied_to_clipboard">Exported to clipboard</string>
|
||||||
<string name="toast_imported_from_clipboard">Imported from clipboard</string>
|
<string name="toast_imported_from_clipboard">Imported from clipboard</string>
|
||||||
<string name="message_import_from_clipboard">Importing app list from clipboard will overwrite your current list. Are you sure to continue?</string>
|
<string name="message_import_from_clipboard">Importing app list from clipboard will overwrite your current list. Are you sure to continue?</string>
|
||||||
<string name="message_scanning">Scanning… Please wait</string>
|
<string name="message_scanning">Scanning…</string>
|
||||||
<string name="message_scan_app_error">Error scanning apps</string>
|
<string name="message_scan_app_error">Error scanning apps</string>
|
||||||
<string name="message_scan_app_no_apps_found">No matching apps found</string>
|
<string name="message_scan_app_no_apps_found">No matching apps found</string>
|
||||||
<string name="message_scan_app_found">Found the following apps, please choose the action you want.</string>
|
<string name="message_scan_app_found">Found the following apps, please choose the action you want.</string>
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<style name="AppTheme" parent="Theme.Material3.DayNight">
|
<style name="AppTheme" parent="Theme.Material3.DayNight.NoActionBar">
|
||||||
<item name="android:windowLightStatusBar" tools:targetApi="m">true</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="AppTheme.NoActionBar" parent="Theme.Material3.DayNight.NoActionBar">
|
|
||||||
<item name="android:windowLightStatusBar" tools:targetApi="m">true</item>
|
<item name="android:windowLightStatusBar" tools:targetApi="m">true</item>
|
||||||
|
<item name="android:windowLightNavigationBar" tools:targetApi="m">true</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="AppTheme.Translucent" parent="Theme.Material3.DayNight.Dialog.Alert">
|
<style name="AppTheme.Translucent" parent="Theme.Material3.DayNight.Dialog.Alert">
|
||||||
|
|||||||
Reference in New Issue
Block a user