Skip to content

Commit 8b87d43

Browse files
committed
opt(app): oobe 流程添加授权应用列表权限, 使用此权限的页面统一回调申请
1 parent fd8b1d9 commit 8b87d43

17 files changed

Lines changed: 309 additions & 33 deletions

File tree

app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
<uses-permission
4848
android:name="android.permission.QUERY_ALL_PACKAGES"
4949
tools:ignore="PackageVisibilityPolicy,QueryAllPackagesPermission" />
50+
<uses-permission android:name="com.android.permission.GET_INSTALLED_APPS" />
5051
<uses-permission
5152
android:name="android.permission.WRITE_SECURE_SETTINGS"
5253
tools:ignore="ProtectedPermissions" />

app/src/main/java/com/sevtinge/hyperceiler/settings/SettingsFragment.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import com.sevtinge.hyperceiler.common.base.BasePreferenceFragment;
4141
import com.sevtinge.hyperceiler.common.log.LogLevelManager;
4242
import com.sevtinge.hyperceiler.common.utils.AppSettingsStore;
43+
import com.sevtinge.hyperceiler.common.utils.PermissionUtils;
4344
import com.sevtinge.hyperceiler.common.utils.PrefsBridge;
4445
import com.sevtinge.hyperceiler.common.utils.api.ProjectApi;
4546
import com.sevtinge.hyperceiler.home.utils.HeaderManager;
@@ -77,6 +78,8 @@ public class SettingsFragment extends BasePreferenceFragment
7778
private int currentAction = -1;
7879
private static final int ACTION_BACKUP = 0;
7980
private static final int ACTION_RESTORE = 1;
81+
private static final int REQUEST_GET_INSTALLED_APPS = 1204;
82+
private Uri mPendingRestoreUri;
8083

8184
private final ActivityResultLauncher<Intent> mBackupLauncher = registerForActivityResult(
8285
new ActivityResultContracts.StartActivityForResult(),
@@ -366,6 +369,14 @@ private void processRestore(Uri uri) {
366369
);
367370
return;
368371
}
372+
if (!PermissionUtils.canReadInstalledApps(requireContext())) {
373+
mPendingRestoreUri = uri;
374+
requestPermissions(
375+
new String[]{PermissionUtils.PERMISSION_GET_INSTALLED_APPS},
376+
REQUEST_GET_INSTALLED_APPS
377+
);
378+
return;
379+
}
369380

370381
Set<String> currentSelected = ScopeManager.normalizeScopePackages(
371382
HeaderManager.getCurrentScopeManagedPackages(requireContext())
@@ -501,4 +512,29 @@ private void restartApp() {
501512
}
502513
android.os.Process.killProcess(android.os.Process.myPid());
503514
}
515+
516+
@Override
517+
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
518+
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
519+
if (requestCode != REQUEST_GET_INSTALLED_APPS) {
520+
return;
521+
}
522+
523+
Uri pendingRestoreUri = mPendingRestoreUri;
524+
mPendingRestoreUri = null;
525+
if (pendingRestoreUri == null) {
526+
return;
527+
}
528+
529+
if (PermissionUtils.canReadInstalledApps(requireContext())
530+
|| PermissionUtils.isInstalledAppsPermissionGranted(permissions, grantResults)) {
531+
processRestore(pendingRestoreUri);
532+
return;
533+
}
534+
535+
showDialog(
536+
getString(com.sevtinge.hyperceiler.core.R.string.rest_failed),
537+
getString(com.sevtinge.hyperceiler.core.R.string.rest_permission)
538+
);
539+
}
504540
}

app/src/main/java/com/sevtinge/hyperceiler/settings/development/DevelopmentKillFragment.java

Lines changed: 47 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import androidx.annotation.NonNull;
2727
import androidx.preference.Preference;
2828

29+
import com.sevtinge.hyperceiler.common.utils.PermissionUtils;
2930
import com.sevtinge.hyperceiler.common.utils.shell.ShellExec;
3031
import com.sevtinge.hyperceiler.common.utils.shell.ShellInit;
3132
import com.sevtinge.hyperceiler.core.R;
@@ -44,6 +45,8 @@
4445
import fan.appcompat.app.AlertDialog;
4546

4647
public class DevelopmentKillFragment extends SettingsPreferenceFragment implements Preference.OnPreferenceClickListener {
48+
private static final int REQUEST_GET_INSTALLED_APPS = 1203;
49+
4750
private List<AppData> appData = new ArrayList<>();
4851
private boolean init = false;
4952
Handler handler;
@@ -67,10 +70,12 @@ public void initPrefs() {
6770
mCheck = findPreference("prefs_key_development_kill_find_process");
6871
mKillPackage = findPreference("prefs_key_development_kill_package");
6972
mName = findPreference("prefs_key_development_kill_app_name");
70-
ToastHelper.makeText(ContextUtils.getContext(ContextUtils.FLAG_CURRENT_APP), "加载数据,请稍后");
7173
ExecutorService executorService = ThreadPoolManager.getInstance();
7274
handler = new Handler(requireContext().getMainLooper());
73-
initApp(executorService);
75+
if (ensureInstalledAppsPermission()) {
76+
ToastHelper.makeText(ContextUtils.getContext(ContextUtils.FLAG_CURRENT_APP), getString(R.string.development_kill_loading_data));
77+
initApp(executorService);
78+
}
7479
mCheck.setOnPreferenceClickListener(this);
7580
mName.setOnPreferenceClickListener(this);
7681
mKillPackage.setOnPreferenceClickListener(this);
@@ -80,7 +85,7 @@ public void initPrefs() {
8085
@Override
8186
public boolean onPreferenceClick(@NonNull Preference preference) {
8287
if (!init) {
83-
showOutDialog("资源尚未加载完毕,请稍后!");
88+
showOutDialog(getString(R.string.development_kill_resource_not_ready));
8489
return true;
8590
}
8691
switch (preference.getKey()) {
@@ -94,11 +99,11 @@ public void onInputReceived(String userInput) {
9499
}
95100
}
96101
if (!(pkg == null || pkg.isEmpty())) {
97-
showOutDialog(listToString("PID: Process:\n",
102+
showOutDialog(listToString(getString(R.string.development_kill_process_header),
98103
pidAndPkg(pkg)));
99104
return;
100105
}
101-
showOutDialog("包名错误或不存在,无法查找!\n" + "\"" + userInput + "\"");
106+
showOutDialog(getString(R.string.development_kill_package_invalid_for_find, userInput));
102107
}
103108
});
104109
case "prefs_key_development_kill_package" -> showInDialog(new EditDialogCallback() {
@@ -112,18 +117,18 @@ public void onInputReceived(String userInput) {
112117
}
113118
}
114119
if (pkg.isEmpty()) {
115-
showOutDialog("包名错误或不存在,请查证后输入!\n" + "\"" + userInput + "\"");
120+
showOutDialog(getString(R.string.development_kill_package_invalid, userInput));
116121
return;
117122
}
118123
if (!pidAndPkg(pkg).isEmpty()) {
119-
String result = listToString("成功 Kill:\n", pidAndPkg(pkg));
124+
String result = listToString(getString(R.string.development_kill_success_prefix), pidAndPkg(pkg));
120125
if (killPackage(pkg)) {
121126
showOutDialog(result);
122127
} else {
123-
showOutDialog("Kill: " + pkg + " 失败!");
128+
showOutDialog(getString(R.string.development_kill_failed_with_pkg, pkg));
124129
}
125130
} else {
126-
showOutDialog("未找到当前包名有任何正在运行的进程!\n" + "\"" + userInput + "\"");
131+
showOutDialog(getString(R.string.development_kill_process_not_found, userInput));
127132
}
128133
}
129134
}
@@ -140,17 +145,17 @@ public void onInputReceived(String userInput) {
140145
}
141146
if (!(pkg == null || pkg.isEmpty())) {
142147
if (!pidAndPkg(pkg).isEmpty()) {
143-
String result = listToString("成功 Kill:\n", pidAndPkg(pkg));
148+
String result = listToString(getString(R.string.development_kill_success_prefix), pidAndPkg(pkg));
144149
if (killPackage(pkg)) {
145150
showOutDialog(result);
146151
} else {
147-
showOutDialog("Kill: " + pkg + " 失败!");
152+
showOutDialog(getString(R.string.development_kill_failed_with_pkg, pkg));
148153
}
149154
} else {
150-
showOutDialog("未找到当前包名有任何正在运行的进程!\n" + "\"" + userInput + "\"");
155+
showOutDialog(getString(R.string.development_kill_process_not_found, userInput));
151156
}
152157
} else
153-
showOutDialog("包名错误或不存在,请查证后输入!\n" + "\"" + userInput + "\"");
158+
showOutDialog(getString(R.string.development_kill_package_invalid, userInput));
154159
}
155160
}
156161
});
@@ -196,26 +201,11 @@ public void run() {
196201
@Override
197202
public void run() {
198203
init = true;
199-
ToastHelper.makeText(ContextUtils.getContext(ContextUtils.FLAG_CURRENT_APP), "加载完毕");
204+
ToastHelper.makeText(ContextUtils.getContext(ContextUtils.FLAG_CURRENT_APP), getString(R.string.development_kill_loading_done));
200205
}
201206
});
202207
}
203208
});
204-
/*AsyncTask 已经弃用
205-
new AsyncTask<Void, Void, List<AppData>>() {
206-
@Override
207-
protected List<AppData> doInBackground(Void... voids) {
208-
// 在后台线程中执行耗时任务
209-
return PackageManagerUtils.getPackageByFlag(0);
210-
}
211-
212-
@Override
213-
protected void onPostExecute(List<AppData> result) {
214-
ToastHelper.makeText(ContextUtils.getContext(ContextUtils.FLAG_CURRENT_APP), "加载完毕");
215-
// 在UI线程更新UI
216-
appData = result;
217-
}
218-
}.execute();*/
219209
}
220210

221211
private void showInDialog(EditDialogCallback callback) {
@@ -247,4 +237,32 @@ private void showOutDialog(String show) {
247237
.setPositiveButton(android.R.string.ok, null)
248238
.show();
249239
}
240+
241+
private boolean ensureInstalledAppsPermission() {
242+
if (PermissionUtils.canReadInstalledApps(requireContext())) {
243+
return true;
244+
}
245+
requestPermissions(
246+
new String[]{PermissionUtils.PERMISSION_GET_INSTALLED_APPS},
247+
REQUEST_GET_INSTALLED_APPS
248+
);
249+
return false;
250+
}
251+
252+
@Override
253+
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
254+
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
255+
if (requestCode != REQUEST_GET_INSTALLED_APPS) {
256+
return;
257+
}
258+
259+
if (PermissionUtils.canReadInstalledApps(requireContext())
260+
|| PermissionUtils.isInstalledAppsPermissionGranted(permissions, grantResults)) {
261+
ToastHelper.makeText(ContextUtils.getContext(ContextUtils.FLAG_CURRENT_APP), getString(R.string.development_kill_loading_data));
262+
initApp(ThreadPoolManager.getInstance());
263+
return;
264+
}
265+
266+
showOutDialog(getString(R.string.development_kill_permission_not_granted));
267+
}
250268
}

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import com.sevtinge.hyperceiler.callback.SearchCallback;
2323
import com.sevtinge.hyperceiler.common.log.AndroidLog;
24+
import com.sevtinge.hyperceiler.common.utils.PermissionUtils;
2425
import com.sevtinge.hyperceiler.model.adapter.AppDataAdapter;
2526
import com.sevtinge.hyperceiler.model.data.AppData;
2627
import com.sevtinge.hyperceiler.model.data.AppDataManager;
@@ -48,6 +49,7 @@ public class ScopePickerActivity extends AppCompatActivity
4849
public static final String EXTRA_EXCLUDED_PACKAGES = "excluded_packages";
4950
public static final String EXTRA_INITIALIZATION_MODE = "initialization_mode";
5051
private static final int MODE_SCOPE = 6;
52+
private static final int REQUEST_GET_INSTALLED_APPS = 1201;
5153
private static final String SYSTEM_SCOPE_PACKAGE = "system";
5254

5355
private View mSearchBar;
@@ -146,6 +148,9 @@ private void startSearchActionMode() {
146148
}
147149

148150
private void initializeData() {
151+
if (!ensureInstalledAppsPermission()) {
152+
return;
153+
}
149154
mProgressBar.setVisibility(View.VISIBLE);
150155

151156
ThreadUtils.postOnBackgroundThread(() -> {
@@ -199,6 +204,14 @@ private void showLoadAppsError() {
199204
Toast.makeText(this, getString(com.sevtinge.hyperceiler.core.R.string.load_apps_failed), Toast.LENGTH_SHORT).show();
200205
}
201206

207+
private boolean ensureInstalledAppsPermission() {
208+
if (PermissionUtils.canReadInstalledApps(this)) {
209+
return true;
210+
}
211+
requestPermissions(new String[]{PermissionUtils.PERMISSION_GET_INSTALLED_APPS}, REQUEST_GET_INSTALLED_APPS);
212+
return false;
213+
}
214+
202215
private List<AppData> processAppData(List<AppData> data) {
203216
if (data == null || data.isEmpty()) {
204217
return new ArrayList<>();
@@ -402,6 +415,20 @@ public boolean onQueryTextSubmit(String query) {
402415
return true;
403416
}
404417

418+
@Override
419+
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
420+
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
421+
if (requestCode != REQUEST_GET_INSTALLED_APPS) {
422+
return;
423+
}
424+
if (PermissionUtils.canReadInstalledApps(this)
425+
|| PermissionUtils.isInstalledAppsPermissionGranted(permissions, grantResults)) {
426+
initializeData();
427+
return;
428+
}
429+
showLoadAppsError();
430+
}
431+
405432
@Override
406433
public void onCreateSearchMode(ActionMode actionMode, Menu menu) {
407434
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* This file is part of HyperCeiler.
3+
*
4+
* HyperCeiler is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU Affero General Public License as
6+
* published by the Free Software Foundation, either version 3 of the
7+
* License.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU Affero General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Affero General Public License
15+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
*
17+
* Copyright (C) 2023-2026 HyperCeiler Contributions
18+
*/
19+
package com.sevtinge.hyperceiler.common.utils;
20+
21+
import android.content.Context;
22+
import android.content.pm.PackageManager;
23+
24+
public final class PermissionUtils {
25+
26+
public static final String PERMISSION_GET_INSTALLED_APPS = "com.android.permission.GET_INSTALLED_APPS";
27+
public static final String PERMISSION_QUERY_ALL_PACKAGES = "android.permission.QUERY_ALL_PACKAGES";
28+
29+
private PermissionUtils() {}
30+
31+
public static boolean hasPermission(Context context, String permission) {
32+
if (context == null || permission == null || permission.isEmpty()) {
33+
return false;
34+
}
35+
try {
36+
return context.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED;
37+
} catch (Throwable ignored) {
38+
return false;
39+
}
40+
}
41+
42+
public static boolean hasInstalledAppsPermission(Context context) {
43+
return hasPermission(context, PERMISSION_GET_INSTALLED_APPS);
44+
}
45+
46+
public static boolean canReadInstalledApps(Context context) {
47+
return hasInstalledAppsPermission(context)
48+
|| hasPermission(context, PERMISSION_QUERY_ALL_PACKAGES);
49+
}
50+
51+
public static boolean isPermissionGranted(String permission, String[] permissions, int[] grantResults) {
52+
if (permission == null || permission.isEmpty()) {
53+
return false;
54+
}
55+
if (permissions == null || grantResults == null) {
56+
return false;
57+
}
58+
int size = Math.min(permissions.length, grantResults.length);
59+
for (int i = 0; i < size; i++) {
60+
if (permission.equals(permissions[i])) {
61+
return grantResults[i] == PackageManager.PERMISSION_GRANTED;
62+
}
63+
}
64+
return false;
65+
}
66+
67+
public static boolean isInstalledAppsPermissionGranted(String[] permissions, int[] grantResults) {
68+
return isPermissionGranted(PERMISSION_GET_INSTALLED_APPS, permissions, grantResults);
69+
}
70+
}

0 commit comments

Comments
 (0)