Skip to content

Commit ec76238

Browse files
committed
Add a hint for an empty app list
1 parent 963418f commit ec76238

File tree

7 files changed

+72
-12
lines changed

7 files changed

+72
-12
lines changed

app/src/main/java/io/nekohasekai/sagernet/ui/AppListActivity.kt

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package io.nekohasekai.sagernet.ui
22

33
import android.content.Intent
44
import android.content.pm.ApplicationInfo
5-
import android.content.pm.PackageInfo
65
import android.content.pm.PackageManager
76
import android.graphics.drawable.Drawable
87
import android.os.Bundle
@@ -45,6 +44,11 @@ import kotlin.coroutines.coroutineContext
4544
class AppListActivity : ThemedActivity() {
4645
companion object {
4746
private const val SWITCH = "switch"
47+
48+
private val cachedApps
49+
get() = PackageCache.installedPackages.toMutableMap().apply {
50+
remove(BuildConfig.APPLICATION_ID)
51+
}
4852
}
4953

5054
private class ProxiedApp(
@@ -96,7 +100,7 @@ class AppListActivity : ThemedActivity() {
96100
var filteredApps = apps
97101

98102
suspend fun reload() {
99-
apps = getCachedApps().mapNotNull { (packageName, packageInfo) ->
103+
apps = cachedApps.mapNotNull { (packageName, packageInfo) ->
100104
coroutineContext[Job]!!.ensureActive()
101105
packageInfo.applicationInfo?.let { ProxiedApp(packageManager, it, packageName) }
102106
}.sortedWith(compareBy({ !isProxiedApp(it) }, { it.name.toString() }))
@@ -156,7 +160,7 @@ class AppListActivity : ThemedActivity() {
156160

157161
private fun initProxiedUids(str: String = DataStore.routePackages) {
158162
proxiedUids.clear()
159-
val apps = getCachedApps()
163+
val apps = cachedApps
160164
for (line in str.lineSequence()) {
161165
val app = (apps[line] ?: continue)
162166
val uid = app.applicationInfo?.uid ?: continue
@@ -174,14 +178,12 @@ class AppListActivity : ThemedActivity() {
174178
val adapter = binding.list.adapter as AppsAdapter
175179
withContext(Dispatchers.IO) { adapter.reload() }
176180
adapter.filter.filter(binding.search.text?.toString() ?: "")
177-
binding.list.crossFadeFrom(loading)
178-
}
179-
}
180-
181-
fun getCachedApps(): MutableMap<String, PackageInfo> {
182-
val packages = PackageCache.installedPackages
183-
return packages.toMutableMap().apply {
184-
remove(BuildConfig.APPLICATION_ID)
181+
if (apps.isEmpty()) {
182+
binding.list.visibility = View.GONE
183+
binding.appPlaceholder.root.crossFadeFrom(loading)
184+
} else {
185+
binding.list.crossFadeFrom(loading)
186+
}
185187
}
186188
}
187189

@@ -191,6 +193,14 @@ class AppListActivity : ThemedActivity() {
191193
binding = LayoutAppListBinding.inflate(layoutInflater)
192194
setContentView(binding.root)
193195

196+
binding.appPlaceholder.openSettings.setOnClickListener {
197+
val intent =
198+
Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
199+
data = android.net.Uri.fromParts("package", packageName, null)
200+
}
201+
startActivity(intent)
202+
}
203+
194204
setSupportActionBar(binding.toolbar)
195205
supportActionBar?.apply {
196206
setTitle(R.string.select_apps)

app/src/main/java/io/nekohasekai/sagernet/ui/AppManagerActivity.kt

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,12 @@ class AppManagerActivity : ThemedActivity() {
184184
val adapter = binding.list.adapter as AppsAdapter
185185
withContext(Dispatchers.IO) { adapter.reload() }
186186
adapter.filter.filter(binding.search.text?.toString() ?: "")
187-
binding.list.crossFadeFrom(loading)
187+
if (apps.isEmpty()) {
188+
binding.list.visibility = View.GONE
189+
binding.appPlaceholder.root.crossFadeFrom(loading)
190+
} else {
191+
binding.list.crossFadeFrom(loading)
192+
}
188193
}
189194
}
190195

@@ -194,6 +199,14 @@ class AppManagerActivity : ThemedActivity() {
194199
binding = LayoutAppsBinding.inflate(layoutInflater)
195200
setContentView(binding.root)
196201

202+
binding.appPlaceholder.openSettings.setOnClickListener {
203+
val intent =
204+
Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
205+
data = android.net.Uri.fromParts("package", packageName, null)
206+
}
207+
startActivity(intent)
208+
}
209+
197210
setSupportActionBar(binding.toolbar)
198211
supportActionBar?.apply {
199212
setTitle(R.string.proxied_apps)

app/src/main/res/layout/layout_app_list.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,8 @@
116116
app:layout_behavior="@string/appbar_scrolling_view_behavior"
117117
tools:listitem="@layout/layout_apps_item" />
118118

119+
<include
120+
android:id="@+id/app_placeholder"
121+
layout="@layout/layout_app_placeholder" />
122+
119123
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3+
android:layout_width="match_parent"
4+
android:layout_height="match_parent"
5+
android:gravity="center"
6+
android:orientation="vertical"
7+
android:padding="32dp"
8+
android:visibility="gone">
9+
10+
<TextView
11+
android:id="@+id/empty_message"
12+
android:layout_width="wrap_content"
13+
android:layout_height="wrap_content"
14+
android:gravity="center"
15+
android:paddingBottom="16dp"
16+
android:text="@string/app_list_permission_denied"
17+
android:textColor="?attr/colorOnBackground"
18+
android:textSize="16sp" />
19+
20+
<com.google.android.material.button.MaterialButton
21+
android:id="@+id/open_settings"
22+
android:layout_width="wrap_content"
23+
android:layout_height="wrap_content"
24+
android:text="@string/open_app_settings" />
25+
</LinearLayout>

app/src/main/res/layout/layout_apps.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,4 +153,8 @@
153153
app:layout_behavior="@string/appbar_scrolling_view_behavior"
154154
tools:listitem="@layout/layout_apps_item" />
155155

156+
<include
157+
android:id="@+id/app_placeholder"
158+
layout="@layout/layout_app_placeholder" />
159+
156160
</androidx.coordinatorlayout.widget.CoordinatorLayout>

app/src/main/res/values-zh-rCN/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,4 +495,6 @@
495495
<string name="reset_settings">恢复默认设置</string>
496496
<string name="reset_settings_message">恢复默认设置,但节点、分组等数据将保留。如需完全清除数据,请在系统设置中直接清除应用数据。</string>
497497
<string name="minimize">最小化</string>
498+
<string name="app_list_permission_denied">无法读取已安装的应用。\n这通常是由于系统限制了应用的读取权限(例如某些中国厂商系统)。\n请在系统设置中授予权限。</string>
499+
<string name="open_app_settings">打开系统设置</string>
498500
</resources>

app/src/main/res/values/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,4 +575,6 @@
575575
<string name="reset_settings">Restore default settings</string>
576576
<string name="reset_settings_message">Restore default settings, but data such as nodes and groups will be retained. To completely clear data, clear application data directly in the system settings.</string>
577577
<string name="minimize">Minimize</string>
578+
<string name="app_list_permission_denied">Unable to read installed apps.\nThis is usually because the system has restricted app read permissions.\nPlease grant permissions in the system settings.</string>
579+
<string name="open_app_settings">Open System Settings</string>
578580
</resources>

0 commit comments

Comments
 (0)