add package list

This commit is contained in:
Kr328 committed 2020-02-19 15:28:32 +08:00
1 parent 168a097111
commit ab03e551da
11 files changed
+297 -9

No files matched your search

+1 -1
View File
@@ -11,7 +11,7 @@ android {
applicationId "com.github.kr328.clash"
minSdkVersion 24
targetSdkVersion 29
versionCode 10035
versionCode 10100
versionName "1.1.0"
}
buildTypes {
+5
View File
@@ -83,6 +83,11 @@
android:label="@string/interface_"
android:exported="false"
android:configChanges="uiMode" />
<activity
android:name=".AccessControlPackagesActivity"
android:label="@string/access_control_packages"
android:exported="false"
android:configChanges="uiMode" />
<service
android:name=".LogcatService"
android:exported="false"
@@ -0,0 +1,68 @@
package com.github.kr328.clash
import android.content.pm.ApplicationInfo
import android.os.Bundle
import android.view.Menu
import android.view.View
import androidx.recyclerview.widget.LinearLayoutManager
import com.github.kr328.clash.adapter.PackagesAdapter
import kotlinx.android.synthetic.main.activity_access_control_packages.*
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlin.streams.toList
class AccessControlPackagesActivity: BaseActivity() {
override val activityLabel: CharSequence?
get() = getText(R.string.access_control_packages)
private val activity: AccessControlPackagesActivity
get() = this
private val refreshChannel = Channel<Unit>(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)
}
}
}
}
@@ -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<AppInfo>) :
RecyclerView.Adapter<PackagesAdapter.Holder>() {
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<AppInfo> = 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
}
}
@@ -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
@@ -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 {
@@ -0,0 +1,34 @@
<?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/toolbar"
android:elevation="4dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/toolbarColor" />
</com.google.android.material.appbar.AppBarLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/mainList"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ProgressBar
android:id="@+id/progress"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center"
android:indeterminate="true" />
</FrameLayout>
</LinearLayout>
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root"
android:layout_marginTop="5dp"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="70dp"
android:focusable="true"
android:clickable="true"
android:background="?attr/selectableItemBackground">
<ImageView
android:id="@+id/icon"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:layout_marginTop="20dp"
android:layout_width="30dp"
android:layout_height="30dp"
android:contentDescription="@string/launch_name" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toEndOf="@+id/icon"
android:layout_toStartOf="@id/checkbox"
android:layout_centerVertical="true"
android:orientation="vertical">
<TextView
android:id="@+id/label"
android:textColor="?attr/colorOnSurface"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/packageName"
android:layout_marginTop="2dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/checkbox"
android:focusable="false"
android:clickable="false"
android:layout_alignParentEnd="true"
android:gravity="center"
android:layout_width="50dp"
android:layout_height="70dp" />
</RelativeLayout>
@@ -40,6 +40,7 @@
android:entryValues="@array/access_control_mode_value"
android:defaultValue="access_control_mode_all" />
<Preference
android:key="access_control_packages"
android:title="@string/access_control_packages"
android:summary="@string/access_control_packages_summary">
</Preference>
@@ -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))
+1 -1
View File
@@ -12,5 +12,5 @@
<string name="profile_status_update_failure">更新 %s 失败</string>
<string name="profile_status_updating">正在更新 %s</string>
<string name="running">正在运行</string>
<string name="tun_service_label">Tun 服务</string>
<string name="tun_service_label">Tun 设备服务</string>
</resources>