application settings & start on boot

This commit is contained in:
Kr328 committed 2019-12-02 23:55:00 +08:00
1 parent 996d82dfbf
commit 0a05d76850
34 files changed
+457 -117

No files matched your search

+15
View File
@@ -4,6 +4,7 @@
package="com.github.kr328.clash">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:name=".MainApplication"
@@ -59,5 +60,19 @@
android:label="@string/proxy_setting_vpn_setting_access_control"
android:exported="false"
android:screenOrientation="portrait"/>
<activity android:name=".SettingApplicationActivity"
android:label="@string/application_setting_application"
android:exported="false"
android:screenOrientation="portrait" />
<service android:name=".ClashStartService"
android:exported="false"/>
<receiver
android:name=".BootCompleteReceiver"
android:exported="true"
android:enabled="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>
</manifest>
@@ -0,0 +1,19 @@
package com.github.kr328.clash
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build
class BootCompleteReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (Intent.ACTION_BOOT_COMPLETED != intent?.action || context == null)
return
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(Intent(context, ClashStartService::class.java))
} else {
context.startService(Intent(context, ClashStartService::class.java))
}
}
}
@@ -0,0 +1,85 @@
package com.github.kr328.clash
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.net.VpnService
import android.os.Binder
import android.os.Build
import android.os.IBinder
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import com.github.kr328.clash.service.ClashService
import com.github.kr328.clash.service.TunService
import kotlin.concurrent.thread
class ClashStartService : Service() {
companion object {
private const val NOTIFICATION_CHANNEL_ID = "clash_start_service_notification_channel"
private const val NOTIFICATION_ID = 142
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
NotificationManagerCompat.from(this).apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel(NotificationChannel(NOTIFICATION_CHANNEL_ID,
getString(R.string.clash_start_service_notification_channel),
NotificationManager.IMPORTANCE_DEFAULT))
}
}
val notification = NotificationCompat
.Builder(this, NOTIFICATION_CHANNEL_ID)
.setContentTitle(getString(R.string.clash_start_service_notification))
.setSmallIcon(R.drawable.ic_notification_icon)
.build()
startForeground(NOTIFICATION_ID, notification)
thread {
getSharedPreferences("application", Context.MODE_PRIVATE).apply {
when (getString(
MainApplication.KEY_PROXY_MODE,
MainApplication.PROXY_MODE_VPN
)) {
MainApplication.PROXY_MODE_VPN -> {
if (VpnService.prepare(this@ClashStartService) == null)
startService(
Intent(
this@ClashStartService,
TunService::class.java
)
)
}
MainApplication.PROXY_MODE_PROXY_ONLY -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(
Intent(
this@ClashStartService,
ClashService::class.java
)
)
} else {
startService(
Intent(
this@ClashStartService,
ClashService::class.java
)
)
}
}
}
}
stopSelf()
}
return START_NOT_STICKY
}
override fun onBind(intent: Intent?): IBinder? {
return Binder()
}
}
@@ -1,8 +1,10 @@
package com.github.kr328.clash
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.VpnService
import android.os.Build
import android.os.Bundle
import android.view.View
import com.github.kr328.clash.core.event.BandwidthEvent
@@ -10,6 +12,7 @@ import com.github.kr328.clash.core.event.Event
import com.github.kr328.clash.core.event.ProcessEvent
import com.github.kr328.clash.core.event.ProfileChangedEvent
import com.github.kr328.clash.core.utils.ByteFormatter
import com.github.kr328.clash.service.ClashService
import com.github.kr328.clash.service.TunService
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.activity_main_clash_status.*
@@ -56,9 +59,40 @@ class MainActivity : BaseActivity() {
it.stop()
}
else -> runOnUiThread {
VpnService.prepare(this@MainActivity)?.apply {
startActivityForResult(this, VPN_REQUEST_CODE)
} ?: startService(Intent(this@MainActivity, TunService::class.java))
getSharedPreferences("application", Context.MODE_PRIVATE).apply {
when (getString(
MainApplication.KEY_PROXY_MODE,
MainApplication.PROXY_MODE_VPN
)) {
MainApplication.PROXY_MODE_VPN -> {
VpnService.prepare(this@MainActivity)?.apply {
startActivityForResult(this, VPN_REQUEST_CODE)
} ?: startService(
Intent(
this@MainActivity,
TunService::class.java
)
)
}
MainApplication.PROXY_MODE_PROXY_ONLY -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(
Intent(
this@MainActivity,
ClashService::class.java
)
)
} else {
startService(
Intent(
this@MainActivity,
ClashService::class.java
)
)
}
}
}
}
}
}
}
@@ -14,7 +14,7 @@ class ProxyActivity : BaseActivity() {
setSupportActionBar(activity_proxies_toolbar)
activity_proxies_list.also {
it.adapter = ProxyAdapter(this) {p, s ->
it.adapter = ProxyAdapter(this) { p, s ->
runClash { clash ->
clash.setSelectProxy(p, s)
}
@@ -63,7 +63,8 @@ class ProxyActivity : BaseActivity() {
})
)
.flatMap {
val header = ListProxy.ListProxyHeader(it.value.name, it.value.type, it.value.now)
val header =
ListProxy.ListProxyHeader(it.value.name, it.value.type, it.value.now)
sequenceOf(header) +
it.value.all
@@ -80,8 +81,8 @@ class ProxyActivity : BaseActivity() {
.asSequence()
}
.mapIndexed { index, listProxy ->
if ( listProxy is ListProxy.ListProxyItem ) {
if ( listProxy.name.hashCode() == listProxy.header.now )
if (listProxy is ListProxy.ListProxyItem) {
if (listProxy.name.hashCode() == listProxy.header.now)
listProxy.header.now = index
}
listProxy
@@ -97,11 +98,11 @@ class ProxyActivity : BaseActivity() {
.filterIsInstance<ListProxy.ListProxyHeader>()
.map { it.now }
val changed = if ( listDataOldChanged.size != listDataChanged.size )
val changed = if (listDataOldChanged.size != listDataChanged.size)
(0..listData.size).toList()
else {
listDataChanged.mapIndexed { index, i ->
if ( i == listDataOldChanged[index] )
if (i == listDataOldChanged[index])
emptyList()
else
listOf(listDataOldChanged[index], i)
@@ -16,7 +16,7 @@ import kotlin.concurrent.thread
class SettingAccessActivity : BaseActivity() {
private data class AppInfo(val packageName: String, val name: String, val icon: Drawable)
private class AppListAdapter(val context: Context):
private class AppListAdapter(val context: Context) :
RecyclerView.Adapter<AppListAdapter.Holder>() {
var applications: List<AppInfo> = emptyList()
@@ -30,8 +30,10 @@ class SettingAccessActivity : BaseActivity() {
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
return Holder(LayoutInflater.from(context)
.inflate(R.layout.adapter_access_app, parent, false))
return Holder(
LayoutInflater.from(context)
.inflate(R.layout.adapter_access_app, parent, false)
)
}
override fun getItemCount(): Int {
@@ -47,7 +49,7 @@ class SettingAccessActivity : BaseActivity() {
holder.checkbox.isChecked = current.packageName in selected
holder.view.setOnClickListener {
if ( holder.checkbox.isChecked )
if (holder.checkbox.isChecked)
selected.remove(current.packageName)
else
selected.add(current.packageName)
@@ -107,7 +109,7 @@ class SettingAccessActivity : BaseActivity() {
val settings = it.settingService
runOnUiThread {
when ( settings.accessControlMode ) {
when (settings.accessControlMode) {
ClashSettingService.ACCESS_CONTROL_MODE_ALLOW_ALL ->
activity_setting_access_allow_all.performClick()
ClashSettingService.ACCESS_CONTROL_MODE_ALLOW ->
@@ -124,9 +126,11 @@ class SettingAccessActivity : BaseActivity() {
exclude.contains(it.packageName)
}
.map { app ->
AppInfo(app.packageName,
AppInfo(
app.packageName,
app.loadLabel(packageManager).toString(),
app.loadIcon(packageManager))
app.loadIcon(packageManager)
)
}
.sortedBy { app ->
app.name
@@ -164,10 +168,12 @@ class SettingAccessActivity : BaseActivity() {
else -> return@runClash
}
if ( listLoaded )
it.settingService.setAccessControl(mode,
if (listLoaded)
it.settingService.setAccessControl(
mode,
(activity_setting_access_app_list.adapter as AppListAdapter)
.selected.toTypedArray())
.selected.toTypedArray()
)
}
}
@@ -177,9 +183,9 @@ class SettingAccessActivity : BaseActivity() {
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when ( item.itemId ) {
when (item.itemId) {
R.id.menu_setting_access_hide -> {
if ( hidden ) {
if (hidden) {
activity_setting_access_allow_all.visibility = View.VISIBLE
activity_setting_access_allow.visibility = View.VISIBLE
activity_setting_access_disallow.visibility = View.VISIBLE
@@ -187,13 +193,12 @@ class SettingAccessActivity : BaseActivity() {
item.title = getString(R.string.access_setting_hide_header)
hidden = false
}
else {
if ( !activity_setting_access_allow_all.isChecked )
} else {
if (!activity_setting_access_allow_all.isChecked)
activity_setting_access_allow_all.visibility = View.GONE
if ( !activity_setting_access_allow.isChecked )
if (!activity_setting_access_allow.isChecked)
activity_setting_access_allow.visibility = View.GONE
if ( !activity_setting_access_disallow.isChecked )
if (!activity_setting_access_disallow.isChecked)
activity_setting_access_disallow.visibility = View.GONE
item.title = getString(R.string.access_setting_show_header)
@@ -218,7 +223,8 @@ class SettingAccessActivity : BaseActivity() {
R.id.menu_setting_access_select_invert -> {
thread {
(activity_setting_access_app_list.adapter as AppListAdapter).apply {
selected = (applications.map { it.packageName }.toSet() - selected).toMutableSet()
selected =
(applications.map { it.packageName }.toSet() - selected).toMutableSet()
}
runOnUiThread {
@@ -242,23 +248,22 @@ class SettingAccessActivity : BaseActivity() {
}
private fun updateListStatus() {
if ( showList )
if (showList)
activity_setting_access_divider.visibility = View.VISIBLE
else
activity_setting_access_divider.visibility = View.GONE
if ( showList ) {
if ( listLoaded )
if (showList) {
if (listLoaded)
activity_setting_access_loading.visibility = View.GONE
else
activity_setting_access_loading.visibility = View.VISIBLE
}
else
} else
activity_setting_access_loading.visibility = View.GONE
if ( showList && listLoaded )
if (showList && listLoaded)
activity_setting_access_app_list.visibility = View.VISIBLE
else
activity_setting_access_app_list.visibility = View.GONE
@@ -0,0 +1,82 @@
package com.github.kr328.clash
import android.content.ComponentName
import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.os.Bundle
import androidx.preference.CheckBoxPreference
import androidx.preference.PreferenceFragmentCompat
import kotlinx.android.synthetic.main.activity_setting_application.*
import kotlin.concurrent.thread
class SettingApplicationActivity : BaseActivity() {
companion object {
const val KEY_START_ON_BOOT = "key_application_start_on_boot"
}
class Fragment : PreferenceFragmentCompat(),
SharedPreferences.OnSharedPreferenceChangeListener {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.setting_application, rootKey)
preferenceManager.sharedPreferences.registerOnSharedPreferenceChangeListener(this)
}
override fun onStart() {
super.onStart()
val status = requireContext().packageManager
.getComponentEnabledSetting(
ComponentName.createRelative(
requireContext(),
BootCompleteReceiver::class.java.name
)
)
findPreference<CheckBoxPreference>(KEY_START_ON_BOOT)?.isChecked =
status == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
}
override fun onSharedPreferenceChanged(
sharedPreferences: SharedPreferences?,
key: String?
) {
when (key) {
KEY_START_ON_BOOT -> {
val enabled = sharedPreferences!!.getBoolean(key, false)
thread {
if (enabled) {
requireContext().packageManager
.setComponentEnabledSetting(
ComponentName.createRelative(
requireContext(),
BootCompleteReceiver::class.java.name
),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP
)
} else {
requireContext().packageManager
.setComponentEnabledSetting(
ComponentName.createRelative(
requireContext(),
BootCompleteReceiver::class.java.name
),
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP
)
}
}
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_setting_application)
setSupportActionBar(activity_setting_application_toolbar)
}
}
@@ -30,7 +30,8 @@ class SettingProxyActivity : BaseActivity() {
requireActivity().runOnUiThread {
findPreference<CheckBoxPreference>(KEY_IPV6_SUPPORT)?.isChecked = ipv6
findPreference<CheckBoxPreference>(KEY_BYPASS_PRIVATE_NETWORK)?.isChecked = privateNetwork
findPreference<CheckBoxPreference>(KEY_BYPASS_PRIVATE_NETWORK)?.isChecked =
privateNetwork
}
}
}
@@ -41,8 +42,11 @@ class SettingProxyActivity : BaseActivity() {
(requireActivity() as SettingProxyActivity).runClash {
val settings = it.settingService
settings.isIPv6Enabled = findPreference<CheckBoxPreference>(KEY_IPV6_SUPPORT)?.isChecked ?: true
settings.isBypassPrivateNetwork = findPreference<CheckBoxPreference>(KEY_BYPASS_PRIVATE_NETWORK)?.isChecked ?: true
settings.isIPv6Enabled =
findPreference<CheckBoxPreference>(KEY_IPV6_SUPPORT)?.isChecked ?: true
settings.isBypassPrivateNetwork =
findPreference<CheckBoxPreference>(KEY_BYPASS_PRIVATE_NETWORK)?.isChecked
?: true
}
}
}
@@ -70,7 +74,7 @@ class SettingProxyActivity : BaseActivity() {
}
getSharedPreferences("application", Context.MODE_PRIVATE).apply {
when ( getString(MainApplication.KEY_PROXY_MODE, MainApplication.PROXY_MODE_VPN) ) {
when (getString(MainApplication.KEY_PROXY_MODE, MainApplication.PROXY_MODE_VPN)) {
MainApplication.PROXY_MODE_VPN ->
activity_setting_proxy_vpn_mode.performClick()
MainApplication.PROXY_MODE_PROXY_ONLY ->
@@ -81,7 +85,7 @@ class SettingProxyActivity : BaseActivity() {
override fun onStop() {
super.onStop()
getSharedPreferences("application", Context.MODE_PRIVATE).edit {
when {
activity_setting_proxy_vpn_mode.isChecked ->
@@ -13,11 +13,12 @@ import com.github.kr328.clash.core.model.ProxyPacket
import com.github.kr328.clash.model.ListProxy
import com.google.android.material.card.MaterialCardView
class ProxyAdapter(private val context: Context, val listener: (String, String) -> Unit) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
class ProxyAdapter(private val context: Context, val listener: (String, String) -> Unit) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
var elements: List<ListProxy> = emptyList()
var clickable: Boolean = false
class HeaderHolder(view: View): RecyclerView.ViewHolder(view) {
class HeaderHolder(view: View) : RecyclerView.ViewHolder(view) {
val name: TextView = view.findViewById(R.id.adapter_proxy_header_name)
val test: View = view.findViewById(R.id.adapter_proxy_header_url_test)
}
@@ -30,7 +31,7 @@ class ProxyAdapter(private val context: Context, val listener: (String, String)
}
override fun getItemViewType(position: Int): Int {
return when ( elements[position] ) {
return when (elements[position]) {
is ListProxy.ListProxyItem -> 1
is ListProxy.ListProxyHeader -> 2
else -> throw IllegalArgumentException("Invalid type")
@@ -39,8 +40,20 @@ class ProxyAdapter(private val context: Context, val listener: (String, String)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
1 -> ItemHolder(LayoutInflater.from(context).inflate(R.layout.adapter_proxy_item, parent, false))
2 -> HeaderHolder(LayoutInflater.from(context).inflate(R.layout.adapter_proxy_header, parent, false))
1 -> ItemHolder(
LayoutInflater.from(context).inflate(
R.layout.adapter_proxy_item,
parent,
false
)
)
2 -> HeaderHolder(
LayoutInflater.from(context).inflate(
R.layout.adapter_proxy_header,
parent,
false
)
)
else -> throw IllegalArgumentException("Invalid type")
}
}
@@ -58,9 +71,9 @@ class ProxyAdapter(private val context: Context, val listener: (String, String)
fun getLayoutManager(): GridLayoutManager {
return GridLayoutManager(context, 2).apply {
spanSizeLookup = object: GridLayoutManager.SpanSizeLookup() {
spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return when ( elements[position] ) {
return when (elements[position]) {
is ListProxy.ListProxyHeader -> 2
is ListProxy.ListProxyItem -> 1
else -> throw IllegalArgumentException("Invalid type")
@@ -71,20 +84,19 @@ class ProxyAdapter(private val context: Context, val listener: (String, String)
}
private fun bindItemView(holder: ItemHolder, current: ListProxy.ListProxyItem, position: Int) {
if ( position == current.header.now ) {
if (position == current.header.now) {
holder.card.setCardBackgroundColor(context.getColor(R.color.colorAccent))
holder.name.setTextColor(Color.WHITE)
holder.type.setTextColor(Color.WHITE - 0x22222222)
holder.delay.setTextColor(Color.WHITE - 0x11111111)
}
else {
} else {
holder.card.setCardBackgroundColor(Color.WHITE)
holder.name.setTextColor(Color.BLACK)
holder.type.setTextColor(Color.LTGRAY)
holder.delay.setTextColor(Color.DKGRAY)
}
if ( current.header.type == ProxyPacket.Type.SELECT ) {
if (current.header.type == ProxyPacket.Type.SELECT) {
holder.card.isFocusable = true
holder.card.isClickable = true
holder.card.setOnClickListener {
@@ -98,8 +110,7 @@ class ProxyAdapter(private val context: Context, val listener: (String, String)
listener(element.header.name, element.name)
}
}
else {
} else {
holder.card.setOnClickListener(null)
holder.card.isFocusable = false
holder.card.isClickable = false
@@ -111,7 +122,7 @@ class ProxyAdapter(private val context: Context, val listener: (String, String)
if (current.delay > 0) {
current.delay.toString()
} else {
if ( current.header.type != ProxyPacket.Type.SELECT )
if (current.header.type != ProxyPacket.Type.SELECT)
"N/A"
else
""
@@ -121,6 +132,7 @@ class ProxyAdapter(private val context: Context, val listener: (String, String)
private fun bindHeaderView(holder: HeaderHolder, current: ListProxy.ListProxyHeader) {
holder.name.text = current.name
holder.test.visibility = if ( current.type == ProxyPacket.Type.SELECT ) View.VISIBLE else View.GONE
holder.test.visibility =
if (current.type == ProxyPacket.Type.SELECT) View.VISIBLE else View.GONE
}
}
@@ -3,6 +3,13 @@ package com.github.kr328.clash.model
import com.github.kr328.clash.core.model.ProxyPacket
interface ListProxy {
data class ListProxyHeader(val name: String, val type: ProxyPacket.Type, var now: Int): ListProxy
data class ListProxyItem(val name: String, val type: String, val delay: Long, val header: ListProxyHeader): ListProxy
data class ListProxyHeader(val name: String, val type: ProxyPacket.Type, var now: Int) :
ListProxy
data class ListProxyItem(
val name: String,
val type: String,
val delay: Long,
val header: ListProxyHeader
) : ListProxy
}
+9
View File
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@color/colorAccent"
android:pathData="M5,16c0,3.87 3.13,7 7,7s7,-3.13 7,-7v-4L5,12v4zM16.12,4.37l2.1,-2.1 -0.82,-0.83 -2.3,2.31C14.16,3.28 13.12,3 12,3s-2.16,0.28 -3.09,0.75L6.6,1.44l-0.82,0.83 2.1,2.1C6.14,5.64 5,7.68 5,10v1h14v-1c0,-2.32 -1.14,-4.36 -2.88,-5.63zM9,9c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM15,9c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1z" />
</vector>
+5 -5
View File
@@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@color/colorAccent"
android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96z"/>
android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96z" />
</vector>
+5 -5
View File
@@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M12,8l-6,6 1.41,1.41L12,10.83l4.59,4.58L18,14z"/>
android:pathData="M12,8l-6,6 1.41,1.41L12,10.83l4.59,4.58L18,14z" />
</vector>
+5 -5
View File
@@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M16.59,8.59L12,13.17 7.41,8.59 6,10l6,6 6,-6z"/>
android:pathData="M16.59,8.59L12,13.17 7.41,8.59 6,10l6,6 6,-6z" />
</vector>
+5 -5
View File
@@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M19.1,12.9a2.8,2.8 0,0 0,0.1 -0.9,2.8 2.8,0 0,0 -0.1,-0.9l2.1,-1.6a0.7,0.7 0,0 0,0.1 -0.6L19.4,5.5a0.7,0.7 0,0 0,-0.6 -0.2l-2.4,1a6.5,6.5 0,0 0,-1.6 -0.9l-0.4,-2.6a0.5,0.5 0,0 0,-0.5 -0.4H10.1a0.5,0.5 0,0 0,-0.5 0.4L9.3,5.4a5.6,5.6 0,0 0,-1.7 0.9l-2.4,-1a0.4,0.4 0,0 0,-0.5 0.2l-2,3.4c-0.1,0.2 0,0.4 0.2,0.6l2,1.6a2.8,2.8 0,0 0,-0.1 0.9,2.8 2.8,0 0,0 0.1,0.9L2.8,14.5a0.7,0.7 0,0 0,-0.1 0.6l1.9,3.4a0.7,0.7 0,0 0,0.6 0.2l2.4,-1a6.5,6.5 0,0 0,1.6 0.9l0.4,2.6a0.5,0.5 0,0 0,0.5 0.4h3.8a0.5,0.5 0,0 0,0.5 -0.4l0.3,-2.6a5.6,5.6 0,0 0,1.7 -0.9l2.4,1a0.4,0.4 0,0 0,0.5 -0.2l2,-3.4c0.1,-0.2 0,-0.4 -0.2,-0.6ZM12,15.6A3.6,3.6 0,1 1,15.6 12,3.6 3.6,0 0,1 12,15.6Z"/>
android:pathData="M19.1,12.9a2.8,2.8 0,0 0,0.1 -0.9,2.8 2.8,0 0,0 -0.1,-0.9l2.1,-1.6a0.7,0.7 0,0 0,0.1 -0.6L19.4,5.5a0.7,0.7 0,0 0,-0.6 -0.2l-2.4,1a6.5,6.5 0,0 0,-1.6 -0.9l-0.4,-2.6a0.5,0.5 0,0 0,-0.5 -0.4H10.1a0.5,0.5 0,0 0,-0.5 0.4L9.3,5.4a5.6,5.6 0,0 0,-1.7 0.9l-2.4,-1a0.4,0.4 0,0 0,-0.5 0.2l-2,3.4c-0.1,0.2 0,0.4 0.2,0.6l2,1.6a2.8,2.8 0,0 0,-0.1 0.9,2.8 2.8,0 0,0 0.1,0.9L2.8,14.5a0.7,0.7 0,0 0,-0.1 0.6l1.9,3.4a0.7,0.7 0,0 0,0.6 0.2l2.4,-1a6.5,6.5 0,0 0,1.6 0.9l0.4,2.6a0.5,0.5 0,0 0,0.5 0.4h3.8a0.5,0.5 0,0 0,0.5 -0.4l0.3,-2.6a5.6,5.6 0,0 0,1.7 -0.9l2.4,1a0.4,0.4 0,0 0,0.5 -0.2l2,-3.4c0.1,-0.2 0,-0.4 -0.2,-0.6ZM12,15.6A3.6,3.6 0,1 1,15.6 12,3.6 3.6,0 0,1 12,15.6Z" />
</vector>
@@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@color/colorAccent"
android:pathData="M19.1,12.9a2.8,2.8 0,0 0,0.1 -0.9,2.8 2.8,0 0,0 -0.1,-0.9l2.1,-1.6a0.7,0.7 0,0 0,0.1 -0.6L19.4,5.5a0.7,0.7 0,0 0,-0.6 -0.2l-2.4,1a6.5,6.5 0,0 0,-1.6 -0.9l-0.4,-2.6a0.5,0.5 0,0 0,-0.5 -0.4H10.1a0.5,0.5 0,0 0,-0.5 0.4L9.3,5.4a5.6,5.6 0,0 0,-1.7 0.9l-2.4,-1a0.4,0.4 0,0 0,-0.5 0.2l-2,3.4c-0.1,0.2 0,0.4 0.2,0.6l2,1.6a2.8,2.8 0,0 0,-0.1 0.9,2.8 2.8,0 0,0 0.1,0.9L2.8,14.5a0.7,0.7 0,0 0,-0.1 0.6l1.9,3.4a0.7,0.7 0,0 0,0.6 0.2l2.4,-1a6.5,6.5 0,0 0,1.6 0.9l0.4,2.6a0.5,0.5 0,0 0,0.5 0.4h3.8a0.5,0.5 0,0 0,0.5 -0.4l0.3,-2.6a5.6,5.6 0,0 0,1.7 -0.9l2.4,1a0.4,0.4 0,0 0,0.5 -0.2l2,-3.4c0.1,-0.2 0,-0.4 -0.2,-0.6ZM12,15.6A3.6,3.6 0,1 1,15.6 12,3.6 3.6,0 0,1 12,15.6Z"/>
android:pathData="M19.1,12.9a2.8,2.8 0,0 0,0.1 -0.9,2.8 2.8,0 0,0 -0.1,-0.9l2.1,-1.6a0.7,0.7 0,0 0,0.1 -0.6L19.4,5.5a0.7,0.7 0,0 0,-0.6 -0.2l-2.4,1a6.5,6.5 0,0 0,-1.6 -0.9l-0.4,-2.6a0.5,0.5 0,0 0,-0.5 -0.4H10.1a0.5,0.5 0,0 0,-0.5 0.4L9.3,5.4a5.6,5.6 0,0 0,-1.7 0.9l-2.4,-1a0.4,0.4 0,0 0,-0.5 0.2l-2,3.4c-0.1,0.2 0,0.4 0.2,0.6l2,1.6a2.8,2.8 0,0 0,-0.1 0.9,2.8 2.8,0 0,0 0.1,0.9L2.8,14.5a0.7,0.7 0,0 0,-0.1 0.6l1.9,3.4a0.7,0.7 0,0 0,0.6 0.2l2.4,-1a6.5,6.5 0,0 0,1.6 0.9l0.4,2.6a0.5,0.5 0,0 0,0.5 0.4h3.8a0.5,0.5 0,0 0,0.5 -0.4l0.3,-2.6a5.6,5.6 0,0 0,1.7 -0.9l2.4,1a0.4,0.4 0,0 0,0.5 -0.2l2,-3.4c0.1,-0.2 0,-0.4 -0.2,-0.6ZM12,15.6A3.6,3.6 0,1 1,15.6 12,3.6 3.6,0 0,1 12,15.6Z" />
</vector>
+5 -5
View File
@@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M7,2v11h3v9l7,-12h-4l4,-8z"/>
android:pathData="M7,2v11h3v9l7,-12h-4l4,-8z" />
</vector>
+5 -5
View File
@@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z" />
</vector>
@@ -21,6 +21,7 @@
android:layout_marginEnd="15dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/activity_proxies_list"
android:layout_width="match_parent"
@@ -9,6 +9,7 @@
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/activity_setting_access_toolbar"
android:elevation="4dp"
@@ -17,27 +18,26 @@
</com.google.android.material.appbar.AppBarLayout>
<com.github.kr328.clash.view.RadioFatItem
app:title="@string/access_setting_allow_all"
app:summary="@string/access_setting_allow_all_summary"
android:id="@+id/activity_setting_access_allow_all"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
android:layout_height="wrap_content" />
<com.github.kr328.clash.view.RadioFatItem
app:title="@string/access_setting_allow"
app:summary="@string/access_setting_allow_summary"
android:id="@+id/activity_setting_access_allow"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
android:layout_height="wrap_content" />
<com.github.kr328.clash.view.RadioFatItem
app:title="@string/access_setting_disallow"
app:summary="@string/access_setting_disallow_summary"
android:id="@+id/activity_setting_access_disallow"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
android:layout_height="wrap_content" />
<View
android:id="@+id/activity_setting_access_divider"
@@ -45,12 +45,12 @@
android:layout_marginBottom="5dp"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/lightGray"/>
android:background="@color/lightGray" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/activity_setting_access_app_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
android:layout_height="wrap_content" />
<TextView
android:id="@+id/activity_setting_access_loading"
@@ -60,5 +60,5 @@
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginStart="70dp"
android:visibility="gone"/>
android:visibility="gone" />
</LinearLayout>
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/activity_setting_application_toolbar"
android:elevation="4dp"
android:layout_width="match_parent"
android:layout_height="60dp" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/activity_setting_application_content"
android:name="com.github.kr328.clash.SettingApplicationActivity$Fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
@@ -7,6 +7,7 @@
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/activity_setting_main_toolbar"
android:elevation="4dp"
@@ -18,6 +19,5 @@
android:id="@+id/activity_setting_main_content"
android:name="com.github.kr328.clash.SettingMainActivity$Fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
android:layout_height="match_parent" />
</LinearLayout>
@@ -9,6 +9,7 @@
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/activity_setting_proxy_toolbar"
android:elevation="4dp"
@@ -22,25 +23,25 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/proxy_setting_vpn_mode"
app:summary="@string/proxy_setting_vpn_mode_summary"/>
app:summary="@string/proxy_setting_vpn_mode_summary" />
<com.github.kr328.clash.view.RadioFatItem
android:id="@+id/activity_setting_proxy_proxy_only_mode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/proxy_setting_proxy_mode"
app:summary="@string/proxy_setting_proxy_mode_summary"/>
app:summary="@string/proxy_setting_proxy_mode_summary" />
<View
android:id="@+id/activity_setting_proxy_divider"
android:layout_marginTop="5dp"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/lightGray"/>
android:background="@color/lightGray" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/activity_setting_proxy_content"
android:name="com.github.kr328.clash.SettingProxyActivity$Fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
android:layout_height="match_parent" />
</LinearLayout>
@@ -24,17 +24,19 @@
android:layout_toStartOf="@id/adapter_access_app_checkbox"
android:layout_centerVertical="true"
android:orientation="vertical">
<TextView
android:id="@+id/adapter_access_app_name"
android:textColor="@android:color/black"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/adapter_access_app_package_name"
android:layout_marginTop="2dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
android:layout_height="wrap_content" />
</LinearLayout>
<com.google.android.material.checkbox.MaterialCheckBox
@@ -44,5 +46,5 @@
android:layout_alignParentEnd="true"
android:gravity="center"
android:layout_width="50dp"
android:layout_height="70dp"/>
android:layout_height="70dp" />
</RelativeLayout>
@@ -12,6 +12,7 @@
app:cardUseCompatPadding="true"
app:cardElevation="2dp"
app:cardCornerRadius="5dp">
<RelativeLayout
android:padding="15dp"
android:layout_width="wrap_content"
@@ -24,7 +25,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toStartOf="@id/adapter_proxy_item_delay"
android:layout_alignParentStart="true"/>
android:layout_alignParentStart="true" />
<TextView
android:id="@+id/adapter_proxy_item_type"
@@ -32,7 +33,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="12sp"
android:maxLines="1"/>
android:maxLines="1" />
<TextView
android:layout_alignParentEnd="true"
@@ -23,16 +23,18 @@
android:layout_toEndOf="@android:id/icon"
android:layout_centerVertical="true"
android:orientation="vertical">
<TextView
android:textColor="@android:color/black"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:layout_marginTop="2dp"
android:id="@android:id/summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
android:layout_height="wrap_content" />
</LinearLayout>
</RelativeLayout>
+2 -1
View File
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
</LinearLayout>
+4 -4
View File
@@ -13,9 +13,9 @@
</declare-styleable>
<declare-styleable name="RadioFatItem">
<attr name="icon"/>
<attr name="title"/>
<attr name="summary"/>
<attr name="operation"/>
<attr name="icon" />
<attr name="title" />
<attr name="summary" />
<attr name="operation" />
</declare-styleable>
</resources>
+7
View File
@@ -72,4 +72,11 @@
<string name="access_setting_select_all">Select All</string>
<string name="access_setting_select_invert">Select Invert</string>
<string name="access_setting_clean_selection">Clear Selection</string>
<string name="application_setting_behavior">Behavior</string>
<string name="application_setting_start_on_boot">Start on Boot</string>
<string name="application_setting_start_on_boot_summary">Start clash on system boot</string>
<string name="clash_start_service_notification_channel">Clash Start Service</string>
<string name="clash_start_service_notification">Starting Clash</string>
</resources>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="@string/application_setting_behavior">
<CheckBoxPreference
android:defaultValue="false"
android:icon="@drawable/ic_boot"
android:key="key_application_start_on_boot"
android:summary="@string/application_setting_start_on_boot_summary"
android:title="@string/application_setting_start_on_boot" />
</PreferenceCategory>
</PreferenceScreen>
+10 -1
View File
@@ -1,10 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<Preference
android:title="@string/application_setting_application"
android:icon="@drawable/ic_settings_color"
android:layout="@layout/preference_main_item">
<intent
android:targetClass="com.github.kr328.clash.SettingApplicationActivity"
android:targetPackage="com.github.kr328.clash" />
</Preference>
<Preference
android:title="@string/application_setting_proxy"
android:icon="@drawable/ic_cloud"
android:layout="@layout/preference_main_item">
<intent android:targetClass="com.github.kr328.clash.SettingProxyActivity"
<intent
android:targetClass="com.github.kr328.clash.SettingProxyActivity"
android:targetPackage="com.github.kr328.clash" />
</Preference>
</PreferenceScreen>
+3 -2
View File
@@ -4,7 +4,7 @@
<CheckBoxPreference
android:key="key_vpn_setting_bypass_private_network"
android:title="@string/proxy_setting_vpn_setting_bypass_private_network"
android:summary="@string/proxy_setting_vpn_setting_bypass_private_network_summary"/>
android:summary="@string/proxy_setting_vpn_setting_bypass_private_network_summary" />
<CheckBoxPreference
android:key="key_vpn_setting_ipv6_support"
android:title="@string/proxy_setting_vpn_setting_ipv6"
@@ -13,7 +13,8 @@
android:layout="@layout/preference_proxy_item"
android:title="@string/proxy_setting_vpn_setting_access_control"
android:summary="@string/proxy_setting_vpn_setting_access_control_summary">
<intent android:targetPackage="com.github.kr328.clash"
<intent
android:targetPackage="com.github.kr328.clash"
android:targetClass="com.github.kr328.clash.SettingAccessActivity" />
</Preference>
</PreferenceCategory>
@@ -85,7 +85,6 @@ class ClashService : Service(), IClashEventObserver, ClashEventService.Master,
notification.setVpn(true)
clash.startTunDevice(fd.fileDescriptor, mtu)
fd.close()
} catch (e: Exception) {
Log.e("Start tun failure", e)
@@ -93,6 +92,9 @@ class ClashService : Service(), IClashEventObserver, ClashEventService.Master,
ErrorEvent(ErrorEvent.Type.START_FAILURE, e.toString())
)
}
finally {
fd.close()
}
}
override fun getEventService(): IClashEventService {
@@ -226,6 +228,8 @@ class ClashService : Service(), IClashEventObserver, ClashEventService.Master,
eventService.preformBandwidthEvent(BandwidthEvent(0))
notification.cancel()
stopSelf()
}
}
}
@@ -107,10 +107,14 @@ class TunService : VpnService(), IClashEventObserver {
clash.start()
else
stopSelf()
Log.i("STOPPED")
}
ProcessEvent.STARTED -> {
start = false
clash.startTunDevice(fileDescriptor, VPN_MTU)
Log.i("STARTED")
}
}
}