Skip to content

Commit b406700

Browse files
committed
fix: Uninstalled apps should not request package names in the scope.
1 parent 3e89f72 commit b406700

3 files changed

Lines changed: 167 additions & 21 deletions

File tree

app/src/main/java/com/sevtinge/hyperceiler/sub/ScopePickerActivity.java

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ public class ScopePickerActivity extends AppCompatActivity
6868
private final List<AppData> mCurrentAppDataList = new ArrayList<>();
6969
private final Set<String> mCurrentScopePackages = new LinkedHashSet<>();
7070
private final Set<String> mInitialSelectedPackages = new LinkedHashSet<>();
71+
private final Set<String> mAvailableScopePackages = new LinkedHashSet<>();
72+
private final Set<String> mSelectableScopePackages = new LinkedHashSet<>();
7173
private final Set<String> mExcludedPackages = new LinkedHashSet<>();
7274
private boolean mInitializationMode = false;
7375

@@ -162,14 +164,26 @@ private void initializeData() {
162164

163165
ThreadUtils.postOnBackgroundThread(() -> {
164166
try {
165-
mCurrentScopePackages.clear();
166167
Set<String> scopePackages = ScopeManager.peekNormalizedScopeSync();
167-
if (scopePackages != null) {
168-
mCurrentScopePackages.addAll(scopePackages);
169-
}
168+
Set<String> currentScopePackages = scopePackages != null
169+
? new LinkedHashSet<>(scopePackages)
170+
: new LinkedHashSet<>();
170171

171172
List<AppData> loadedData = mAppDataManager.getAppInfo(MODE_SCOPE);
172173
loadedData = prepareScopeModeData(new ArrayList<>(loadedData));
174+
Set<String> availableScopePackages = collectScopePackages(loadedData);
175+
Set<String> sanitizedScopePackages = ScopeManager.filterScopePackages(
176+
currentScopePackages,
177+
availableScopePackages
178+
);
179+
180+
mCurrentScopePackages.clear();
181+
mCurrentScopePackages.addAll(sanitizedScopePackages);
182+
mAvailableScopePackages.clear();
183+
mAvailableScopePackages.addAll(availableScopePackages);
184+
185+
cleanupUnavailableScopePackages(currentScopePackages, sanitizedScopePackages);
186+
173187
List<AppData> processedData = processAppData(new ArrayList<>(loadedData));
174188
mHandler.post(() -> displayAppData(processedData));
175189
} catch (Exception e) {
@@ -187,6 +201,9 @@ private void displayAppData(List<AppData> processedData) {
187201
mOriginalAppDataList.clear();
188202
mOriginalAppDataList.addAll(processedData);
189203

204+
mSelectableScopePackages.clear();
205+
mSelectableScopePackages.addAll(collectScopePackages(processedData));
206+
190207
mCurrentAppDataList.clear();
191208
mCurrentAppDataList.addAll(processedData);
192209

@@ -269,6 +286,35 @@ private List<AppData> prepareScopeModeData(List<AppData> data) {
269286
return data;
270287
}
271288

289+
private Set<String> collectScopePackages(List<AppData> data) {
290+
Set<String> packages = new LinkedHashSet<>();
291+
if (data == null || data.isEmpty()) {
292+
return packages;
293+
}
294+
295+
for (AppData appData : data) {
296+
String normalizedPackage = ScopeManager.normalizeScopePackageName(appData.packageName);
297+
if (normalizedPackage != null) {
298+
packages.add(normalizedPackage);
299+
}
300+
}
301+
return packages;
302+
}
303+
304+
private void cleanupUnavailableScopePackages(Set<String> currentScopePackages, Set<String> sanitizedScopePackages) {
305+
if (currentScopePackages == null || currentScopePackages.equals(sanitizedScopePackages)) {
306+
return;
307+
}
308+
309+
ScopeManager.applyScopeDiffAsync(this, currentScopePackages, sanitizedScopePackages, (success, message) -> {
310+
if (success) {
311+
AndroidLog.i(TAG, "cleanupUnavailableScopePackages: removed uninstalled scope entries");
312+
return;
313+
}
314+
AndroidLog.w(TAG, "cleanupUnavailableScopePackages: " + message);
315+
});
316+
}
317+
272318
private AppData createSystemFrameworkApp() {
273319
AppData appData = new AppData();
274320
appData.packageName = SYSTEM_SCOPE_PACKAGE;
@@ -291,9 +337,7 @@ private Drawable loadAndroidPackageIcon() {
291337

292338
private void syncSelectionFromScope(Set<String> scopePackages) {
293339
mCurrentScopePackages.clear();
294-
if (scopePackages != null) {
295-
mCurrentScopePackages.addAll(scopePackages);
296-
}
340+
mCurrentScopePackages.addAll(ScopeManager.filterScopePackages(scopePackages, mAvailableScopePackages));
297341

298342
mInitialSelectedPackages.clear();
299343
for (AppData appData : mOriginalAppDataList) {
@@ -317,8 +361,14 @@ private void applyScopeSelection() {
317361
return;
318362
}
319363

320-
Set<String> currentSelected = new LinkedHashSet<>(mInitialSelectedPackages);
321-
Set<String> targetSelected = mAppListAdapter.getSelectedPackages();
364+
Set<String> currentSelected = ScopeManager.peekNormalizedScopeSync();
365+
if (currentSelected == null) {
366+
currentSelected = new LinkedHashSet<>(mCurrentScopePackages);
367+
}
368+
369+
Set<String> targetSelected = ScopeManager.filterScopePackages(currentSelected, mAvailableScopePackages);
370+
targetSelected.removeAll(mSelectableScopePackages);
371+
targetSelected.addAll(ScopeManager.normalizeScopePackages(mAppListAdapter.getSelectedPackages()));
322372

323373
setScopeApplying(true);
324374
ScopeManager.applyScopeDiffAsync(this, currentSelected, targetSelected, (success, message) -> {

library/core/src/main/java/com/sevtinge/hyperceiler/sub/SubPickerActivity.java

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ public class SubPickerActivity extends AppCompatActivity
101101
private final AppDataManager mAppDataManager = new AppDataManager();
102102
private final List<AppData> mOriginalAppDataList = new ArrayList<>(); // 原始数据备份
103103
private final List<AppData> mCurrentAppDataList = new ArrayList<>(); // 当前显示数据
104+
private final Set<String> mAvailableImePackages = new LinkedHashSet<>();
104105
private boolean mImeSelectionApplying = false;
105106

106107
@Override
@@ -272,6 +273,21 @@ private void displayAppData(List<AppData> processedData) {
272273
mOriginalAppDataList.clear();
273274
mOriginalAppDataList.addAll(processedData);
274275

276+
if (supportsConfirmAction()) {
277+
mAvailableImePackages.clear();
278+
mAvailableImePackages.addAll(collectNormalizedPackages(processedData));
279+
280+
LinkedHashSet<String> persistedSelectedPackages = getPersistedSelectedPackages();
281+
LinkedHashSet<String> sanitizedSelectedPackages = ScopeManager.filterScopePackages(
282+
persistedSelectedPackages,
283+
mAvailableImePackages
284+
);
285+
if (!persistedSelectedPackages.equals(sanitizedSelectedPackages)) {
286+
saveImeSelection(sanitizedSelectedPackages);
287+
}
288+
mAppListAdapter.setSelectedPackages(sanitizedSelectedPackages);
289+
}
290+
275291
updateCurrentAppData(processedData);
276292
setLoadingState(false);
277293
mAppListRecyclerView.setVisibility(View.VISIBLE);
@@ -538,6 +554,21 @@ private LinkedHashSet<String> getPersistedSelectedPackages() {
538554
return new LinkedHashSet<>(PrefsBridge.getStringSet(mKey));
539555
}
540556

557+
private LinkedHashSet<String> collectNormalizedPackages(List<AppData> appDataList) {
558+
LinkedHashSet<String> packages = new LinkedHashSet<>();
559+
if (appDataList == null || appDataList.isEmpty()) {
560+
return packages;
561+
}
562+
563+
for (AppData appData : appDataList) {
564+
String normalizedPackage = ScopeManager.normalizeScopePackageName(appData.packageName);
565+
if (normalizedPackage != null) {
566+
packages.add(normalizedPackage);
567+
}
568+
}
569+
return packages;
570+
}
571+
541572
private void updateCurrentAppData(List<AppData> appDataList) {
542573
mCurrentAppDataList.clear();
543574
if (appDataList != null) {
@@ -584,29 +615,29 @@ private void confirmImeSelection() {
584615
return;
585616
}
586617

587-
LinkedHashSet<String> selectedPackages = new LinkedHashSet<>(mAppListAdapter.getSelectedPackages());
618+
LinkedHashSet<String> selectedPackages = ScopeManager.filterScopePackages(
619+
mAppListAdapter.getSelectedPackages(),
620+
mAvailableImePackages
621+
);
588622
LinkedHashSet<String> normalizedSelectedPackages = ScopeManager.normalizeScopePackages(selectedPackages);
589623
LinkedHashSet<String> currentScope = ScopeManager.peekNormalizedScopeSync();
590-
LinkedHashSet<String> missingScopePackages = new LinkedHashSet<>(normalizedSelectedPackages);
591-
if (currentScope != null) {
592-
missingScopePackages.removeAll(currentScope);
624+
if (!selectedPackages.equals(mAppListAdapter.getSelectedPackages())) {
625+
mAppListAdapter.setSelectedPackages(selectedPackages);
593626
}
594627

595-
if (missingScopePackages.isEmpty()) {
628+
LinkedHashSet<String> sourceScope = currentScope != null ? new LinkedHashSet<>(currentScope) : new LinkedHashSet<>();
629+
LinkedHashSet<String> targetScope = ScopeManager.filterInstalledScopePackages(this, sourceScope);
630+
targetScope.addAll(normalizedSelectedPackages);
631+
632+
if (targetScope.equals(sourceScope)) {
596633
saveImeSelection(selectedPackages);
597634
setResult(RESULT_OK);
598635
finish();
599636
return;
600637
}
601638

602-
LinkedHashSet<String> targetScope = new LinkedHashSet<>();
603-
if (currentScope != null) {
604-
targetScope.addAll(currentScope);
605-
}
606-
targetScope.addAll(normalizedSelectedPackages);
607-
608639
setImeSelectionApplying(true);
609-
ScopeManager.applyScopeDiffAsync(this, currentScope, targetScope,
640+
ScopeManager.applyScopeDiffAsync(this, sourceScope, targetScope,
610641
(success, message) -> {
611642
if (isFinishing() || isDestroyed()) {
612643
return;

library/core/src/main/java/com/sevtinge/hyperceiler/utils/ScopeManager.kt

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.sevtinge.hyperceiler.utils
22

33
import android.content.Context
4+
import android.content.pm.PackageManager
45
import android.os.Handler
56
import android.os.Looper
67
import com.sevtinge.hyperceiler.common.log.AndroidLog
@@ -104,12 +105,18 @@ object ScopeManager {
104105
getScopeSync()
105106
}
106107

108+
/**
109+
* 规范化单个作用域包名,统一去空白并转为小写。
110+
*/
107111
@JvmStatic
108112
fun normalizeScopePackageName(packageName: String?): String? {
109113
val normalized = packageName?.trim()?.lowercase()
110114
return if (normalized.isNullOrEmpty()) null else normalized
111115
}
112116

117+
/**
118+
* 规范化一组作用域包名,并自动去重、忽略空值。
119+
*/
113120
@JvmStatic
114121
fun normalizeScopePackages(packageNames: Collection<String>?): LinkedHashSet<String> {
115122
val normalized = LinkedHashSet<String>()
@@ -130,6 +137,59 @@ object ScopeManager {
130137
return normalizeScopePackages(scopePackages).contains(normalizedPackage)
131138
}
132139

140+
/**
141+
* 按给定可用包集合过滤作用域包,常用于保留当前页面仍可管理的项。
142+
*/
143+
@JvmStatic
144+
fun filterScopePackages(
145+
scopePackages: Collection<String>?,
146+
availablePackages: Collection<String>?
147+
): LinkedHashSet<String> {
148+
val filtered = normalizeScopePackages(scopePackages)
149+
filtered.retainAll(normalizeScopePackages(availablePackages))
150+
return filtered
151+
}
152+
153+
/**
154+
* 过滤出当前设备上仍已安装的作用域包。
155+
*/
156+
@JvmStatic
157+
fun filterInstalledScopePackages(
158+
context: Context?,
159+
scopePackages: Collection<String>?
160+
): LinkedHashSet<String> {
161+
val filtered = LinkedHashSet<String>()
162+
normalizeScopePackages(scopePackages).forEach { packageName ->
163+
if (isScopePackageInstalled(context, packageName)) {
164+
filtered.add(packageName)
165+
}
166+
}
167+
return filtered
168+
}
169+
170+
/**
171+
* 判断作用域包当前是否仍可用。
172+
*
173+
* 系统框架作用域始终视为可用;其余包通过 [PackageManager] 查询安装状态。
174+
*/
175+
@JvmStatic
176+
fun isScopePackageInstalled(context: Context?, packageName: String?): Boolean {
177+
val normalizedPackage = normalizeScopePackageName(packageName) ?: return false
178+
if (normalizedPackage == SYSTEM_SCOPE_PACKAGE) {
179+
return true
180+
}
181+
val safeContext = context ?: return false
182+
return try {
183+
safeContext.packageManager.getPackageInfo(normalizedPackage, PackageManager.MATCH_ALL)
184+
true
185+
} catch (_: Exception) {
186+
false
187+
}
188+
}
189+
190+
/**
191+
* 同步读取当前生效的作用域,并返回规范化后的包集合。
192+
*/
133193
@JvmStatic
134194
fun peekNormalizedScopeSync(): LinkedHashSet<String>? {
135195
val service = getService() ?: return null
@@ -141,6 +201,11 @@ object ScopeManager {
141201
}
142202
}
143203

204+
/**
205+
* 按当前集合与目标集合的差异批量更新作用域。
206+
*
207+
* 已存在但不在目标中的包会被移除;目标中新增的包会触发授权申请。
208+
*/
144209
@JvmStatic
145210
fun applyScopeDiffAsync(
146211
context: Context?,

0 commit comments

Comments
 (0)