From 0a05d768509fb2833e8d3be6f79fa71de1b99028 Mon Sep 17 00:00:00 2001 From: Kr328 <39107975+Kr328@users.noreply.github.com> Date: Mon, 2 Dec 2019 23:55:00 +0800 Subject: [PATCH] application settings & start on boot --- app/src/main/AndroidManifest.xml | 15 ++++ .../kr328/clash/BootCompleteReceiver.kt | 19 +++++ .../github/kr328/clash/ClashStartService.kt | 85 +++++++++++++++++++ .../com/github/kr328/clash/MainActivity.kt | 40 ++++++++- .../com/github/kr328/clash/ProxyActivity.kt | 13 +-- .../kr328/clash/SettingAccessActivity.kt | 53 ++++++------ .../kr328/clash/SettingApplicationActivity.kt | 82 ++++++++++++++++++ .../kr328/clash/SettingProxyActivity.kt | 14 +-- .../kr328/clash/adapter/ProxyAdapter.kt | 42 +++++---- .../com/github/kr328/clash/model/ListProxy.kt | 11 ++- app/src/main/res/drawable/ic_boot.xml | 9 ++ app/src/main/res/drawable/ic_cloud.xml | 10 +-- app/src/main/res/drawable/ic_expand_less.xml | 10 +-- app/src/main/res/drawable/ic_expand_more.xml | 10 +-- app/src/main/res/drawable/ic_settings.xml | 10 +-- .../main/res/drawable/ic_settings_color.xml | 10 +-- app/src/main/res/drawable/ic_url_test.xml | 10 +-- app/src/main/res/drawable/ic_vert.xml | 10 +-- app/src/main/res/layout/activity_proxies.xml | 1 + .../res/layout/activity_setting_access.xml | 14 +-- .../layout/activity_setting_application.xml | 23 +++++ .../main/res/layout/activity_setting_main.xml | 4 +- .../res/layout/activity_setting_proxy.xml | 9 +- .../main/res/layout/adapter_access_app.xml | 6 +- .../main/res/layout/adapter_proxy_item.xml | 5 +- .../main/res/layout/preference_proxy_item.xml | 4 +- app/src/main/res/layout/view_title.xml | 3 +- app/src/main/res/values/attrs.xml | 8 +- app/src/main/res/values/strings.xml | 7 ++ app/src/main/res/xml/setting_application.xml | 11 +++ app/src/main/res/xml/setting_main.xml | 11 ++- app/src/main/res/xml/setting_proxy.xml | 5 +- .../kr328/clash/service/ClashService.kt | 6 +- .../github/kr328/clash/service/TunService.kt | 4 + 34 files changed, 457 insertions(+), 117 deletions(-) create mode 100644 app/src/main/java/com/github/kr328/clash/BootCompleteReceiver.kt create mode 100644 app/src/main/java/com/github/kr328/clash/ClashStartService.kt create mode 100644 app/src/main/java/com/github/kr328/clash/SettingApplicationActivity.kt create mode 100644 app/src/main/res/drawable/ic_boot.xml create mode 100644 app/src/main/res/layout/activity_setting_application.xml create mode 100644 app/src/main/res/xml/setting_application.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4a0ebfa..31bfcad 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,6 +4,7 @@ package="com.github.kr328.clash"> + + + + + + + + diff --git a/app/src/main/java/com/github/kr328/clash/BootCompleteReceiver.kt b/app/src/main/java/com/github/kr328/clash/BootCompleteReceiver.kt new file mode 100644 index 0000000..bc334bc --- /dev/null +++ b/app/src/main/java/com/github/kr328/clash/BootCompleteReceiver.kt @@ -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)) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/kr328/clash/ClashStartService.kt b/app/src/main/java/com/github/kr328/clash/ClashStartService.kt new file mode 100644 index 0000000..f8412b6 --- /dev/null +++ b/app/src/main/java/com/github/kr328/clash/ClashStartService.kt @@ -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() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/kr328/clash/MainActivity.kt b/app/src/main/java/com/github/kr328/clash/MainActivity.kt index 5d55efc..11e9e9e 100644 --- a/app/src/main/java/com/github/kr328/clash/MainActivity.kt +++ b/app/src/main/java/com/github/kr328/clash/MainActivity.kt @@ -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 + ) + ) + } + } + } + } } } } diff --git a/app/src/main/java/com/github/kr328/clash/ProxyActivity.kt b/app/src/main/java/com/github/kr328/clash/ProxyActivity.kt index 4533a26..747d9f0 100644 --- a/app/src/main/java/com/github/kr328/clash/ProxyActivity.kt +++ b/app/src/main/java/com/github/kr328/clash/ProxyActivity.kt @@ -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() .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) diff --git a/app/src/main/java/com/github/kr328/clash/SettingAccessActivity.kt b/app/src/main/java/com/github/kr328/clash/SettingAccessActivity.kt index 59ab08c..dd8f0b5 100644 --- a/app/src/main/java/com/github/kr328/clash/SettingAccessActivity.kt +++ b/app/src/main/java/com/github/kr328/clash/SettingAccessActivity.kt @@ -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() { var applications: List = 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 diff --git a/app/src/main/java/com/github/kr328/clash/SettingApplicationActivity.kt b/app/src/main/java/com/github/kr328/clash/SettingApplicationActivity.kt new file mode 100644 index 0000000..af5b3b1 --- /dev/null +++ b/app/src/main/java/com/github/kr328/clash/SettingApplicationActivity.kt @@ -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(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) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/kr328/clash/SettingProxyActivity.kt b/app/src/main/java/com/github/kr328/clash/SettingProxyActivity.kt index 8bb3da8..f6da6ce 100644 --- a/app/src/main/java/com/github/kr328/clash/SettingProxyActivity.kt +++ b/app/src/main/java/com/github/kr328/clash/SettingProxyActivity.kt @@ -30,7 +30,8 @@ class SettingProxyActivity : BaseActivity() { requireActivity().runOnUiThread { findPreference(KEY_IPV6_SUPPORT)?.isChecked = ipv6 - findPreference(KEY_BYPASS_PRIVATE_NETWORK)?.isChecked = privateNetwork + findPreference(KEY_BYPASS_PRIVATE_NETWORK)?.isChecked = + privateNetwork } } } @@ -41,8 +42,11 @@ class SettingProxyActivity : BaseActivity() { (requireActivity() as SettingProxyActivity).runClash { val settings = it.settingService - settings.isIPv6Enabled = findPreference(KEY_IPV6_SUPPORT)?.isChecked ?: true - settings.isBypassPrivateNetwork = findPreference(KEY_BYPASS_PRIVATE_NETWORK)?.isChecked ?: true + settings.isIPv6Enabled = + findPreference(KEY_IPV6_SUPPORT)?.isChecked ?: true + settings.isBypassPrivateNetwork = + findPreference(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 -> diff --git a/app/src/main/java/com/github/kr328/clash/adapter/ProxyAdapter.kt b/app/src/main/java/com/github/kr328/clash/adapter/ProxyAdapter.kt index 268148b..3494ea6 100644 --- a/app/src/main/java/com/github/kr328/clash/adapter/ProxyAdapter.kt +++ b/app/src/main/java/com/github/kr328/clash/adapter/ProxyAdapter.kt @@ -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() { +class ProxyAdapter(private val context: Context, val listener: (String, String) -> Unit) : + RecyclerView.Adapter() { var elements: List = 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 } } \ No newline at end of file diff --git a/app/src/main/java/com/github/kr328/clash/model/ListProxy.kt b/app/src/main/java/com/github/kr328/clash/model/ListProxy.kt index 1c98f9e..b86a8f2 100644 --- a/app/src/main/java/com/github/kr328/clash/model/ListProxy.kt +++ b/app/src/main/java/com/github/kr328/clash/model/ListProxy.kt @@ -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 } \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_boot.xml b/app/src/main/res/drawable/ic_boot.xml new file mode 100644 index 0000000..b179b42 --- /dev/null +++ b/app/src/main/res/drawable/ic_boot.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_cloud.xml b/app/src/main/res/drawable/ic_cloud.xml index 0b9c887..ef9691d 100644 --- a/app/src/main/res/drawable/ic_cloud.xml +++ b/app/src/main/res/drawable/ic_cloud.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + 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" /> diff --git a/app/src/main/res/drawable/ic_expand_less.xml b/app/src/main/res/drawable/ic_expand_less.xml index 3afdf96..7b6a656 100644 --- a/app/src/main/res/drawable/ic_expand_less.xml +++ b/app/src/main/res/drawable/ic_expand_less.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M12,8l-6,6 1.41,1.41L12,10.83l4.59,4.58L18,14z" /> diff --git a/app/src/main/res/drawable/ic_expand_more.xml b/app/src/main/res/drawable/ic_expand_more.xml index 8d57dbc..c1f391b 100644 --- a/app/src/main/res/drawable/ic_expand_more.xml +++ b/app/src/main/res/drawable/ic_expand_more.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M16.59,8.59L12,13.17 7.41,8.59 6,10l6,6 6,-6z" /> diff --git a/app/src/main/res/drawable/ic_settings.xml b/app/src/main/res/drawable/ic_settings.xml index 24a5623..a671514 100644 --- a/app/src/main/res/drawable/ic_settings.xml +++ b/app/src/main/res/drawable/ic_settings.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + 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" /> diff --git a/app/src/main/res/drawable/ic_settings_color.xml b/app/src/main/res/drawable/ic_settings_color.xml index b67a13a..b11ad71 100644 --- a/app/src/main/res/drawable/ic_settings_color.xml +++ b/app/src/main/res/drawable/ic_settings_color.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + 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" /> diff --git a/app/src/main/res/drawable/ic_url_test.xml b/app/src/main/res/drawable/ic_url_test.xml index a3c81cc..4efdc7f 100644 --- a/app/src/main/res/drawable/ic_url_test.xml +++ b/app/src/main/res/drawable/ic_url_test.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M7,2v11h3v9l7,-12h-4l4,-8z" /> diff --git a/app/src/main/res/drawable/ic_vert.xml b/app/src/main/res/drawable/ic_vert.xml index 5176d8a..7b7f195 100644 --- a/app/src/main/res/drawable/ic_vert.xml +++ b/app/src/main/res/drawable/ic_vert.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + 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" /> diff --git a/app/src/main/res/layout/activity_proxies.xml b/app/src/main/res/layout/activity_proxies.xml index d1c73f1..396fe32 100644 --- a/app/src/main/res/layout/activity_proxies.xml +++ b/app/src/main/res/layout/activity_proxies.xml @@ -21,6 +21,7 @@ android:layout_marginEnd="15dp" android:layout_width="match_parent" android:layout_height="wrap_content"> + + - + android:layout_height="wrap_content" /> + android:layout_height="wrap_content" /> + android:layout_height="wrap_content" /> + android:background="@color/lightGray" /> + android:layout_height="wrap_content" /> + android:visibility="gone" /> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_setting_application.xml b/app/src/main/res/layout/activity_setting_application.xml new file mode 100644 index 0000000..fbaec5b --- /dev/null +++ b/app/src/main/res/layout/activity_setting_application.xml @@ -0,0 +1,23 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_setting_main.xml b/app/src/main/res/layout/activity_setting_main.xml index 912cb8b..8e13aee 100644 --- a/app/src/main/res/layout/activity_setting_main.xml +++ b/app/src/main/res/layout/activity_setting_main.xml @@ -7,6 +7,7 @@ + - + android:layout_height="match_parent" /> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_setting_proxy.xml b/app/src/main/res/layout/activity_setting_proxy.xml index 04aeb32..e813a30 100644 --- a/app/src/main/res/layout/activity_setting_proxy.xml +++ b/app/src/main/res/layout/activity_setting_proxy.xml @@ -9,6 +9,7 @@ + + app:summary="@string/proxy_setting_vpn_mode_summary" /> + app:summary="@string/proxy_setting_proxy_mode_summary" /> + android:background="@color/lightGray" /> + android:layout_height="match_parent" /> \ No newline at end of file diff --git a/app/src/main/res/layout/adapter_access_app.xml b/app/src/main/res/layout/adapter_access_app.xml index 35d15d6..468f61c 100644 --- a/app/src/main/res/layout/adapter_access_app.xml +++ b/app/src/main/res/layout/adapter_access_app.xml @@ -24,17 +24,19 @@ android:layout_toStartOf="@id/adapter_access_app_checkbox" android:layout_centerVertical="true" android:orientation="vertical"> + + + android:layout_height="wrap_content" /> + android:layout_height="70dp" /> \ No newline at end of file diff --git a/app/src/main/res/layout/adapter_proxy_item.xml b/app/src/main/res/layout/adapter_proxy_item.xml index 45aa63b..a6f5bcd 100644 --- a/app/src/main/res/layout/adapter_proxy_item.xml +++ b/app/src/main/res/layout/adapter_proxy_item.xml @@ -12,6 +12,7 @@ app:cardUseCompatPadding="true" app:cardElevation="2dp" app:cardCornerRadius="5dp"> + + android:layout_alignParentStart="true" /> + android:maxLines="1" /> + + + android:layout_height="wrap_content" /> \ No newline at end of file diff --git a/app/src/main/res/layout/view_title.xml b/app/src/main/res/layout/view_title.xml index 3509b84..d829e29 100644 --- a/app/src/main/res/layout/view_title.xml +++ b/app/src/main/res/layout/view_title.xml @@ -1,6 +1,7 @@ \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 8cba8da..456fff9 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -13,9 +13,9 @@ - - - - + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 46693ec..8fe895f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -72,4 +72,11 @@ Select All Select Invert Clear Selection + + Behavior + Start on Boot + Start clash on system boot + + Clash Start Service + Starting Clash diff --git a/app/src/main/res/xml/setting_application.xml b/app/src/main/res/xml/setting_application.xml new file mode 100644 index 0000000..a91f2c2 --- /dev/null +++ b/app/src/main/res/xml/setting_application.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/setting_main.xml b/app/src/main/res/xml/setting_main.xml index 04a0cd7..f8528ce 100644 --- a/app/src/main/res/xml/setting_main.xml +++ b/app/src/main/res/xml/setting_main.xml @@ -1,10 +1,19 @@ + + + - \ No newline at end of file diff --git a/app/src/main/res/xml/setting_proxy.xml b/app/src/main/res/xml/setting_proxy.xml index 50317d9..cef26c4 100644 --- a/app/src/main/res/xml/setting_proxy.xml +++ b/app/src/main/res/xml/setting_proxy.xml @@ -4,7 +4,7 @@ + android:summary="@string/proxy_setting_vpn_setting_bypass_private_network_summary" /> - diff --git a/service/src/main/java/com/github/kr328/clash/service/ClashService.kt b/service/src/main/java/com/github/kr328/clash/service/ClashService.kt index 37fd0bd..41f75d1 100644 --- a/service/src/main/java/com/github/kr328/clash/service/ClashService.kt +++ b/service/src/main/java/com/github/kr328/clash/service/ClashService.kt @@ -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() } } } diff --git a/service/src/main/java/com/github/kr328/clash/service/TunService.kt b/service/src/main/java/com/github/kr328/clash/service/TunService.kt index a1fc76a..1f7dc8c 100644 --- a/service/src/main/java/com/github/kr328/clash/service/TunService.kt +++ b/service/src/main/java/com/github/kr328/clash/service/TunService.kt @@ -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") } } }