diff --git a/app/src/main/aidl/net/typeblog/shelter/services/IShelterService.aidl b/app/src/main/aidl/net/typeblog/shelter/services/IShelterService.aidl index 62e49c0..f5251f5 100644 --- a/app/src/main/aidl/net/typeblog/shelter/services/IShelterService.aidl +++ b/app/src/main/aidl/net/typeblog/shelter/services/IShelterService.aidl @@ -12,7 +12,7 @@ import net.typeblog.shelter.util.UriForwardProxy; interface IShelterService { void ping(); void stopShelterService(boolean kill); - void getApps(IGetAppsCallback callback); + void getApps(IGetAppsCallback callback, boolean showAll); void loadIcon(in ApplicationInfoWrapper info, ILoadIconCallback callback); void installApp(in ApplicationInfoWrapper app, IAppInstallCallback callback); void installApk(in UriForwardProxy uri, IAppInstallCallback callback); diff --git a/app/src/main/java/net/typeblog/shelter/services/ShelterService.java b/app/src/main/java/net/typeblog/shelter/services/ShelterService.java index a5d0014..2b3034d 100644 --- a/app/src/main/java/net/typeblog/shelter/services/ShelterService.java +++ b/app/src/main/java/net/typeblog/shelter/services/ShelterService.java @@ -61,7 +61,7 @@ public class ShelterService extends Service { } @Override - public void getApps(IGetAppsCallback callback) { + public void getApps(IGetAppsCallback callback, boolean showAll) { new Thread(() -> { int pmFlags = PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.MATCH_UNINSTALLED_PACKAGES; List list = mPackageManager.getInstalledApplications(pmFlags) @@ -73,7 +73,7 @@ public class ShelterService extends Service { boolean isInstalled = (it.flags & ApplicationInfo.FLAG_INSTALLED) != 0; boolean canLaunch = mPackageManager.getLaunchIntentForPackage(it.packageName) != null; - return (!isSystem && isInstalled) || isHidden || canLaunch; + return showAll || (!isSystem && isInstalled) || isHidden || canLaunch; }) .map(ApplicationInfoWrapper::new) .map((it) -> it.loadLabel(mPackageManager) diff --git a/app/src/main/java/net/typeblog/shelter/ui/AppListFragment.java b/app/src/main/java/net/typeblog/shelter/ui/AppListFragment.java index 3dab44e..89cdc77 100644 --- a/app/src/main/java/net/typeblog/shelter/ui/AppListFragment.java +++ b/app/src/main/java/net/typeblog/shelter/ui/AppListFragment.java @@ -46,7 +46,7 @@ import java.util.Set; import java.util.stream.Collectors; public class AppListFragment extends BaseFragment { - private static final String BROADCAST_REFRESH = "net.typeblog.shelter.broadcast.REFRESH"; + static final String BROADCAST_REFRESH = "net.typeblog.shelter.broadcast.REFRESH"; // Menu Items private static final int MENU_ITEM_CLONE = 10001; @@ -202,7 +202,7 @@ public class AppListFragment extends BaseFragment { mRefreshing = false; }); } - }); + }, ((MainActivity) getActivity()).mShowAll); } catch (RemoteException e) { // Just... do nothing for now } @@ -260,15 +260,24 @@ public class AppListFragment extends BaseFragment { if (mSelectedApp == null) return; if (mIsRemote) { + // Determine if the app is able to launch + // Un-launchable apps are normally hidden, but can be shown with the "show all" option + // All launching-related menu options should be hidden for apps that cannot be launched + boolean canLaunch = mSelectedApp.canLaunch(getContext().getPackageManager()); + if (!mSelectedApp.isSystem()) menu.add(Menu.NONE, MENU_ITEM_CLONE, Menu.NONE, R.string.clone_to_main_profile); // Freezing / Unfreezing is only available in profiles that we can control if (mSelectedApp.isHidden()) { menu.add(Menu.NONE, MENU_ITEM_UNFREEZE, Menu.NONE, R.string.unfreeze_app); - menu.add(Menu.NONE, MENU_ITEM_LAUNCH, Menu.NONE, R.string.unfreeze_and_launch); + + if (canLaunch) + menu.add(Menu.NONE, MENU_ITEM_LAUNCH, Menu.NONE, R.string.unfreeze_and_launch); } else { menu.add(Menu.NONE, MENU_ITEM_FREEZE, Menu.NONE, R.string.freeze_app); - menu.add(Menu.NONE, MENU_ITEM_LAUNCH, Menu.NONE, R.string.launch); + + if (canLaunch) + menu.add(Menu.NONE, MENU_ITEM_LAUNCH, Menu.NONE, R.string.launch); } // Cross-profile widget settings is also limited to work profile MenuItem crossProfileWdiegt = @@ -277,15 +286,19 @@ public class AppListFragment extends BaseFragment { crossProfileWdiegt.setCheckable(true); crossProfileWdiegt.setChecked( mCrossProfileWidgetProviders.contains(mSelectedApp.getPackageName())); - // TODO: If we implement God Mode (i.e. Shelter as device owner), we should - // TODO: use two different lists to store auto freeze apps because we'll be - // TODO: able to freeze apps in main profile. - MenuItem autoFreeze = menu.add(Menu.NONE, MENU_ITEM_AUTO_FREEZE, Menu.NONE, R.string.auto_freeze); - autoFreeze.setCheckable(true); - autoFreeze.setChecked( - LocalStorageManager.getInstance().stringListContains( - LocalStorageManager.PREF_AUTO_FREEZE_LIST_WORK_PROFILE, mSelectedApp.getPackageName())); - menu.add(Menu.NONE, MENU_ITEM_CREATE_UNFREEZE_SHORTCUT, Menu.NONE, R.string.create_unfreeze_shortcut); + + // Auto-freeze only works with launchable apps + if (canLaunch) { + // TODO: If we implement God Mode (i.e. Shelter as device owner), we should + // TODO: use two different lists to store auto freeze apps because we'll be + // TODO: able to freeze apps in main profile. + MenuItem autoFreeze = menu.add(Menu.NONE, MENU_ITEM_AUTO_FREEZE, Menu.NONE, R.string.auto_freeze); + autoFreeze.setCheckable(true); + autoFreeze.setChecked( + LocalStorageManager.getInstance().stringListContains( + LocalStorageManager.PREF_AUTO_FREEZE_LIST_WORK_PROFILE, mSelectedApp.getPackageName())); + menu.add(Menu.NONE, MENU_ITEM_CREATE_UNFREEZE_SHORTCUT, Menu.NONE, R.string.create_unfreeze_shortcut); + } } else { menu.add(Menu.NONE, MENU_ITEM_CLONE, Menu.NONE, R.string.clone_to_work_profile); } diff --git a/app/src/main/java/net/typeblog/shelter/ui/MainActivity.java b/app/src/main/java/net/typeblog/shelter/ui/MainActivity.java index 33e3f9d..d501d0a 100644 --- a/app/src/main/java/net/typeblog/shelter/ui/MainActivity.java +++ b/app/src/main/java/net/typeblog/shelter/ui/MainActivity.java @@ -64,6 +64,10 @@ public class MainActivity extends AppCompatActivity { private ViewPager mPager = null; private TabLayout mTabs = null; + // Show all applications or not + // default to false + boolean mShowAll = false; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -367,6 +371,25 @@ public class MainActivity extends AppCompatActivity { openApkIntent.setType("application/vnd.android.package-archive"); startActivityForResult(openApkIntent, REQUEST_DOCUMENTS_CHOOSE_APK); return true; + case R.id.main_menu_show_all: + Runnable update = () -> { + mShowAll = !item.isChecked(); + item.setChecked(mShowAll); + LocalBroadcastManager.getInstance(this) + .sendBroadcast(new Intent(AppListFragment.BROADCAST_REFRESH)); + }; + + if (!item.isChecked()) { + new AlertDialog.Builder(this) + .setMessage(R.string.show_all_warning) + .setPositiveButton(R.string.first_run_alert_continue, + (dialog, which) -> update.run()) + .setNegativeButton(R.string.first_run_alert_cancel, null) + .show(); + } else { + update.run(); + } + return true; } return super.onOptionsItemSelected(item); } diff --git a/app/src/main/java/net/typeblog/shelter/util/ApplicationInfoWrapper.java b/app/src/main/java/net/typeblog/shelter/util/ApplicationInfoWrapper.java index 37b5308..6448afb 100644 --- a/app/src/main/java/net/typeblog/shelter/util/ApplicationInfoWrapper.java +++ b/app/src/main/java/net/typeblog/shelter/util/ApplicationInfoWrapper.java @@ -37,6 +37,10 @@ public class ApplicationInfoWrapper implements Parcelable { return this; } + public boolean canLaunch(PackageManager pm) { + return pm.getLaunchIntentForPackage(mInfo.packageName) != null; + } + // Only used from ShelterService public ApplicationInfoWrapper setHidden(boolean hidden) { mIsHidden = hidden; diff --git a/app/src/main/res/menu/main_activity_menu.xml b/app/src/main/res/menu/main_activity_menu.xml index e4abe1c..3c3ec15 100644 --- a/app/src/main/res/menu/main_activity_menu.xml +++ b/app/src/main/res/menu/main_activity_menu.xml @@ -20,4 +20,10 @@ + + \ No newline at end of file diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 5b1427c..08805cb 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -36,6 +36,8 @@ 冻结 安装 APK 到 Shelter 已成功在工作用户内安装 APK + 显示全部应用 + 对列表中默认隐藏的应用执行操作可能导致崩溃以及其他各种无法预料的行为。但是,当您的手机厂商没有正确在工作用户中开启所有必要的系统组件应用的时候,这个功能可以帮助您解决问题。如果您选择继续,您确保您了解您在做什么,Shelter 无法提供任何保证。 设置 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1576b9c..9125a62 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -38,6 +38,8 @@ Freeze Install APK into Shelter Application installation finished in work profile. + Show All Apps + Manipulating apps that are hidden from the list could cause crashes and all sorts of unexpected behavior. However, this feature can be useful when faulty vendor-customized ROMs does not enable all necessary system apps in work profile by default. If you continue, you are on your own. Settings