diff --git a/mobile/src/main/java/com/github/shadowsocks/AppManager.kt b/mobile/src/main/java/com/github/shadowsocks/AppManager.kt index 0c5d7439..41e0995b 100644 --- a/mobile/src/main/java/com/github/shadowsocks/AppManager.kt +++ b/mobile/src/main/java/com/github/shadowsocks/AppManager.kt @@ -28,6 +28,8 @@ import android.content.BroadcastReceiver import android.content.ClipData import android.content.ClipboardManager import android.content.Context +import android.content.pm.ApplicationInfo +import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.graphics.drawable.Drawable import android.os.Bundle @@ -38,7 +40,6 @@ import android.support.v7.widget.DefaultItemAnimator import android.support.v7.widget.LinearLayoutManager import android.support.v7.widget.RecyclerView import android.support.v7.widget.Toolbar -import android.util.TypedValue import android.view.* import android.widget.ImageView import android.widget.Switch @@ -56,31 +57,33 @@ import java.util.concurrent.atomic.AtomicBoolean class AppManager : AppCompatActivity(), Toolbar.OnMenuItemClickListener { companion object { - data class ProxiedApp(val name: String, val packageName: String, val icon: Drawable) - @SuppressLint("StaticFieldLeak") private var instance: AppManager? = null private var receiver: BroadcastReceiver? = null - private var cachedApps: Array? = null - private fun getApps(pm: PackageManager): Array { + private var cachedApps: List? = null + private fun getApps(pm: PackageManager): List { if (receiver == null) receiver = app.listenForPackageChanges { synchronized(AppManager) { cachedApps = null } AppManager.instance?.reloadApps() } return synchronized(AppManager) { + // Labels and icons can change on configuration (locale, etc.) changes, therefore they are not cached. val cachedApps = cachedApps ?: pm.getInstalledPackages(PackageManager.GET_PERMISSIONS) .filter { it.packageName != app.packageName && it.requestedPermissions?.contains(Manifest.permission.INTERNET) ?: false } - .map { ProxiedApp(pm.getApplicationLabel(it.applicationInfo).toString(), it.packageName, - it.applicationInfo.loadIcon(pm)) } - .toTypedArray() this.cachedApps = cachedApps cachedApps - } + }.map { ProxiedApp(pm, it.applicationInfo, it.packageName) } } } + private class ProxiedApp(private val pm: PackageManager, private val appInfo: ApplicationInfo, + val packageName: String) { + val name: CharSequence = appInfo.loadLabel(pm) // cached for sorting + val icon: Drawable get() = appInfo.loadIcon(pm) + } + private inner class AppViewHolder(view: View) : RecyclerView.ViewHolder(view), View.OnClickListener { private val icon = view.findViewById(R.id.itemicon) private val check = view.findViewById(R.id.itemcheck) @@ -117,13 +120,8 @@ class AppManager : AppCompatActivity(), Toolbar.OnMenuItemClickListener { private var apps = listOf() fun reload() { - apps = getApps(packageManager).sortedWith(Comparator { a, b -> - when (Pair(proxiedApps.contains(a.packageName), proxiedApps.contains(b.packageName))) { - Pair(true, false) -> -1 - Pair(false, true) -> 1 - else -> a.name.compareTo(b.name, true) - } - }) + apps = getApps(packageManager) + .sortedWith(compareBy({ !proxiedApps.contains(it.packageName) }, { it.name.toString() })) } override fun onBindViewHolder(holder: AppViewHolder, position: Int) = holder.bind(apps[position])