From ab03e551da4e36f5af71759fbdbfce48bc7fe66f Mon Sep 17 00:00:00 2001 From: Kr328 <39107975+Kr328@users.noreply.github.com> Date: Wed, 19 Feb 2020 15:28:32 +0800 Subject: [PATCH] add package list --- app/build.gradle | 2 +- app/src/main/AndroidManifest.xml | 5 + .../clash/AccessControlPackagesActivity.kt | 68 +++++++++++ .../kr328/clash/adapter/PackagesAdapter.kt | 107 ++++++++++++++++++ .../kr328/clash/settings/BehaviorFragment.kt | 9 ++ .../kr328/clash/settings/NetworkFragment.kt | 12 +- .../activity_access_control_packages.xml | 34 ++++++ app/src/main/res/layout/adapter_package.xml | 51 +++++++++ app/src/main/res/xml/settings_network.xml | 1 + .../kr328/clash/service/ClashNotification.kt | 15 ++- service/src/main/res/values-zh/strings.xml | 2 +- 11 files changed, 297 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/com/github/kr328/clash/AccessControlPackagesActivity.kt create mode 100644 app/src/main/java/com/github/kr328/clash/adapter/PackagesAdapter.kt create mode 100644 app/src/main/res/layout/activity_access_control_packages.xml create mode 100644 app/src/main/res/layout/adapter_package.xml diff --git a/app/build.gradle b/app/build.gradle index fe2fa11..fa9b2c1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,7 +11,7 @@ android { applicationId "com.github.kr328.clash" minSdkVersion 24 targetSdkVersion 29 - versionCode 10035 + versionCode 10100 versionName "1.1.0" } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 56ea3bd..efefaa2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -83,6 +83,11 @@ android:label="@string/interface_" android:exported="false" android:configChanges="uiMode" /> + (Channel.CONFLATED) + + private var keyword: String = "" + private var sort: PackagesAdapter.Sort = PackagesAdapter.Sort.NAME + private var decrease: Boolean = false + private var systemApp: Boolean = true + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContentView(R.layout.activity_access_control_packages) + setSupportActionBar(toolbar) + + launch { + val apps = withContext(Dispatchers.IO) { + val pm = packageManager + val packages = pm.getInstalledPackages(0) + + packages.parallelStream() + .map { + PackagesAdapter.AppInfo( + it.packageName, + it.applicationInfo.loadLabel(pm).toString(), + it.applicationInfo.loadIcon(pm), + it.firstInstallTime, it.lastUpdateTime, + it.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM != 0) + } + .sorted(compareBy(PackagesAdapter.AppInfo::label)) + .toList() + } + + val adapter = PackagesAdapter(activity, apps) + + mainList.adapter = adapter + mainList.layoutManager = LinearLayoutManager(activity) + + progress.visibility = View.GONE + + refreshChannel.send(Unit) + + while ( isActive ) { + refreshChannel.receive() + + adapter.applyFilter(keyword, sort, decrease, systemApp) + + delay(200) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/kr328/clash/adapter/PackagesAdapter.kt b/app/src/main/java/com/github/kr328/clash/adapter/PackagesAdapter.kt new file mode 100644 index 0000000..7776fd0 --- /dev/null +++ b/app/src/main/java/com/github/kr328/clash/adapter/PackagesAdapter.kt @@ -0,0 +1,107 @@ +package com.github.kr328.clash.adapter + +import android.content.Context +import android.content.pm.ApplicationInfo +import android.content.pm.PackageInfo +import android.graphics.drawable.Drawable +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.RecyclerView +import com.github.kr328.clash.R +import com.google.android.material.checkbox.MaterialCheckBox +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import kotlin.streams.toList + +class PackagesAdapter(private val context: Context, + private val apps: List) : + RecyclerView.Adapter() { + data class AppInfo(val packageName: String, val label: String, val icon: Drawable, + val installTime: Long, val updateTime: Long, val isSystem: Boolean) + + enum class Sort { + NAME, PACKAGE, INSTALL_TIME, UPDATE_TIME + } + + private var filteredCache: List = apps + + inner class Holder(view: View) : RecyclerView.ViewHolder(view) { + val root: View = view.findViewById(R.id.root) + val icon: ImageView = view.findViewById(R.id.icon) + val label: TextView = view.findViewById(R.id.label) + val packageName: TextView = view.findViewById(R.id.packageName) + val checkbox: MaterialCheckBox = view.findViewById(R.id.checkbox) + } + + suspend fun applyFilter(keyword: String, sort: Sort, decrease: Boolean, systemApp: Boolean) { + withContext(Dispatchers.Default) { + val newList = apps.parallelStream() + .filter { + it.label.contains(keyword, true) + || it.packageName.contains(keyword, true) + || systemApp + || it.isSystem + } + .sorted { a, b -> + val result = when (sort) { + Sort.NAME -> a.label.compareTo(b.label, true) + Sort.PACKAGE -> a.packageName.compareTo(b.packageName) + Sort.INSTALL_TIME -> (a.installTime - b.installTime).toInt() + Sort.UPDATE_TIME -> (a.updateTime - b.updateTime).toInt() + } + + if (decrease) -result else result + } + .toList() + val oldList = filteredCache + + val result = DiffUtil.calculateDiff(object : DiffUtil.Callback() { + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + return oldList[oldItemPosition].packageName == newList[newItemPosition].packageName + } + + override fun getOldListSize(): Int { + return oldList.size + } + + override fun getNewListSize(): Int { + return newList.size + } + + override fun areContentsTheSame( + oldItemPosition: Int, + newItemPosition: Int + ): Boolean { + return areItemsTheSame(oldItemPosition, newItemPosition) + } + + }) + + withContext(Dispatchers.Main) { + filteredCache = newList + result.dispatchUpdatesTo(this@PackagesAdapter) + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder { + return Holder(LayoutInflater.from(context).inflate(R.layout.adapter_package, parent, false)) + } + + override fun getItemCount(): Int { + return filteredCache.size + } + + override fun onBindViewHolder(holder: Holder, position: Int) { + val current = filteredCache[position] + + holder.icon.setImageDrawable(current.icon) + holder.label.text = current.label + holder.packageName.text = current.packageName + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/kr328/clash/settings/BehaviorFragment.kt b/app/src/main/java/com/github/kr328/clash/settings/BehaviorFragment.kt index 00465bc..1a54c2e 100644 --- a/app/src/main/java/com/github/kr328/clash/settings/BehaviorFragment.kt +++ b/app/src/main/java/com/github/kr328/clash/settings/BehaviorFragment.kt @@ -1,8 +1,11 @@ package com.github.kr328.clash.settings import android.content.pm.PackageManager +import android.os.Bundle +import com.github.kr328.clash.BaseActivity import com.github.kr328.clash.OnBootReceiver import com.github.kr328.clash.R +import com.github.kr328.clash.remote.Broadcasts import com.github.kr328.clash.service.settings.ServiceSettings import com.github.kr328.clash.service.util.componentName @@ -19,6 +22,12 @@ class BehaviorFragment: BaseSettingFragment() { } } + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + findPreference(KEY_SHOW_TRAFFIC).isEnabled = !Broadcasts.clashRunning + } + override val xmlResourceId: Int get() = R.xml.settings_behavior diff --git a/app/src/main/java/com/github/kr328/clash/settings/NetworkFragment.kt b/app/src/main/java/com/github/kr328/clash/settings/NetworkFragment.kt index 6c84bec..1396c0e 100644 --- a/app/src/main/java/com/github/kr328/clash/settings/NetworkFragment.kt +++ b/app/src/main/java/com/github/kr328/clash/settings/NetworkFragment.kt @@ -1,10 +1,13 @@ package com.github.kr328.clash.settings import android.os.Bundle +import com.github.kr328.clash.AccessControlPackagesActivity import com.github.kr328.clash.BaseActivity import com.github.kr328.clash.R import com.github.kr328.clash.preference.UiSettings +import com.github.kr328.clash.remote.Broadcasts import com.github.kr328.clash.service.settings.ServiceSettings +import com.github.kr328.clash.service.util.intent class NetworkFragment: BaseSettingFragment() { companion object { @@ -15,13 +18,18 @@ class NetworkFragment: BaseSettingFragment() { private const val KEY_DNS_OVERRIDE = "dns_override" private const val KEY_APPEND_SYS_DNS = "append_system_dns" private const val KEY_ACCESS_CONTROL_MODE = "access_control_mode" + private const val KEY_ACCESS_CONTROL_PACKAGES = "access_control_packages" } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - if ((requireActivity() as BaseActivity).clashRunning) - preferenceScreen.isEnabled = false + preferenceScreen.isEnabled = !Broadcasts.clashRunning + + findPreference(KEY_ACCESS_CONTROL_PACKAGES).setOnPreferenceClickListener { + startActivity(AccessControlPackagesActivity::class.intent) + true + } } override fun onCreateDataStore(): SettingsDataStore { diff --git a/app/src/main/res/layout/activity_access_control_packages.xml b/app/src/main/res/layout/activity_access_control_packages.xml new file mode 100644 index 0000000..63bc4ce --- /dev/null +++ b/app/src/main/res/layout/activity_access_control_packages.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/adapter_package.xml b/app/src/main/res/layout/adapter_package.xml new file mode 100644 index 0000000..aa546c5 --- /dev/null +++ b/app/src/main/res/layout/adapter_package.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/settings_network.xml b/app/src/main/res/xml/settings_network.xml index 489c9fd..c2a0335 100644 --- a/app/src/main/res/xml/settings_network.xml +++ b/app/src/main/res/xml/settings_network.xml @@ -40,6 +40,7 @@ android:entryValues="@array/access_control_mode_value" android:defaultValue="access_control_mode_all" /> diff --git a/service/src/main/java/com/github/kr328/clash/service/ClashNotification.kt b/service/src/main/java/com/github/kr328/clash/service/ClashNotification.kt index aab95b2..afb182a 100644 --- a/service/src/main/java/com/github/kr328/clash/service/ClashNotification.kt +++ b/service/src/main/java/com/github/kr328/clash/service/ClashNotification.kt @@ -19,7 +19,7 @@ import com.github.kr328.clash.core.utils.asSpeedString import kotlinx.coroutines.* import kotlinx.coroutines.channels.Channel -class ClashNotification(private val context: ClashService, enableRefresh: Boolean) : +class ClashNotification(private val context: ClashService, private val enableRefresh: Boolean) : CoroutineScope by context { companion object { private const val CLASH_STATUS_NOTIFICATION_CHANNEL = "clash_status_channel" @@ -63,7 +63,7 @@ class ClashNotification(private val context: ClashService, enableRefresh: Boolea init { createNotificationChannel() - notifyBaseNotification() + updateBase() if (enableRefresh) { launch { @@ -119,7 +119,9 @@ class ClashNotification(private val context: ClashService, enableRefresh: Boolea } fun destroy() { - context.unregisterReceiver(observer) + if ( enableRefresh ) + context.unregisterReceiver(observer) + context.stopForeground(true) } @@ -127,11 +129,14 @@ class ClashNotification(private val context: ClashService, enableRefresh: Boolea launch { this@ClashNotification.profile = profile - update() + if ( enableRefresh ) + update() + else + updateBase() } } - private fun notifyBaseNotification() { + private fun updateBase() { val notification = baseBuilder .setContentTitle(profile) .setContentText(context.getText(R.string.running)) diff --git a/service/src/main/res/values-zh/strings.xml b/service/src/main/res/values-zh/strings.xml index 83e2ced..ffaf39a 100644 --- a/service/src/main/res/values-zh/strings.xml +++ b/service/src/main/res/values-zh/strings.xml @@ -12,5 +12,5 @@ 更新 %s 失败 正在更新 %s 正在运行 - Tun 服务 + Tun 设备服务 \ No newline at end of file