diff --git a/app/src/main/java/org/lsposed/manager/ConfigManager.java b/app/src/main/java/org/lsposed/manager/ConfigManager.java index d0da0634..a25e43dc 100644 --- a/app/src/main/java/org/lsposed/manager/ConfigManager.java +++ b/app/src/main/java/org/lsposed/manager/ConfigManager.java @@ -134,18 +134,27 @@ public class ConfigManager { return list; } - public static boolean isAddShortcut() { + public static Intent getLaunchIntentForManager() { try { - return LSPManagerServiceHolder.getService().isAddShortcut(); + return LSPManagerServiceHolder.getService().getLaunchIntentForManager(); + } catch (RemoteException e) { + Log.e(App.TAG, Log.getStackTraceString(e)); + return null; + } + } + + public static boolean enableStatusNotification() { + try { + return LSPManagerServiceHolder.getService().enableStatusNotification(); } catch (RemoteException e) { Log.e(App.TAG, Log.getStackTraceString(e)); return false; } } - public static boolean setAddShortcut(boolean enabled) { + public static boolean setEnableStatusNotification(boolean enabled) { try { - LSPManagerServiceHolder.getService().setAddShortcut(enabled); + LSPManagerServiceHolder.getService().setEnableStatusNotification(enabled); return true; } catch (RemoteException e) { Log.e(App.TAG, Log.getStackTraceString(e)); diff --git a/app/src/main/java/org/lsposed/manager/ui/dialog/ShortcutDialog.java b/app/src/main/java/org/lsposed/manager/ui/dialog/ShortcutDialog.java deleted file mode 100644 index 9f3ef73d..00000000 --- a/app/src/main/java/org/lsposed/manager/ui/dialog/ShortcutDialog.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * This file is part of LSPosed. - * - * LSPosed is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * LSPosed is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with LSPosed. If not, see . - * - * Copyright (C) 2021 LSPosed Contributors - */ - -package org.lsposed.manager.ui.dialog; - -import android.app.Dialog; -import android.os.Bundle; -import android.os.RemoteException; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.DialogFragment; -import androidx.fragment.app.FragmentManager; - -import org.lsposed.manager.App; -import org.lsposed.manager.ConfigManager; -import org.lsposed.manager.R; -import org.lsposed.manager.receivers.LSPManagerServiceHolder; - -public class ShortcutDialog extends DialogFragment { - private static boolean shown = false; - - @NonNull - @Override - public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { - return new BlurBehindDialogBuilder(requireContext(), R.style.ThemeOverlay_MaterialAlertDialog_Centered_FullWidthButtons) - .setTitle(R.string.parasitic_recommend) - .setMessage(R.string.parasitic_recommend_summary) - .setNegativeButton(R.string.never_show, (dialog, which) -> - App.getPreferences().edit().putBoolean("never_show_shortcut", true).apply()) - .setNeutralButton(R.string.create_shortcut, (dialog, which) -> { - try { - LSPManagerServiceHolder.getService().createShortcut(); - } catch (RemoteException ignored) { - } - }) - .setPositiveButton(android.R.string.ok, null).create(); - } - - public static void showIfNeed(FragmentManager fm) { - if (App.isParasitic() || !ConfigManager.isBinderAlive()) return; - if (App.getPreferences().getBoolean("never_show_shortcut", false)) return; - if (shown) return; - shown = true; - new ShortcutDialog().show(fm, "shortcut"); - } -} diff --git a/app/src/main/java/org/lsposed/manager/ui/dialog/WelcomeDialog.java b/app/src/main/java/org/lsposed/manager/ui/dialog/WelcomeDialog.java new file mode 100644 index 00000000..a9fd6550 --- /dev/null +++ b/app/src/main/java/org/lsposed/manager/ui/dialog/WelcomeDialog.java @@ -0,0 +1,60 @@ +package org.lsposed.manager.ui.dialog; + +import android.app.Dialog; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.DialogFragment; +import androidx.fragment.app.FragmentManager; + +import org.lsposed.manager.App; +import org.lsposed.manager.ConfigManager; +import org.lsposed.manager.R; +import org.lsposed.manager.util.ShortcutUtil; + +public class WelcomeDialog extends DialogFragment { + private static boolean shown = false; + + private Dialog parasiticDialog(BlurBehindDialogBuilder builder) { + return builder + .setTitle(R.string.parasitic_welcome) + .setMessage(R.string.parasitic_welcome_summary) + .setNegativeButton(R.string.never_show, (dialog, which) -> + App.getPreferences().edit().putBoolean("never_show_welcome", true).apply()) + .setNeutralButton(R.string.create_shortcut, (dialog, which) -> + ShortcutUtil.requestPinLaunchShortcut()) + .setPositiveButton(android.R.string.ok, null) + .create(); + } + + private Dialog appDialog(BlurBehindDialogBuilder builder) { + return builder + .setTitle(R.string.app_welcome) + .setMessage(R.string.app_welcome_summary) + .setNegativeButton(R.string.never_show, (d, w) -> + App.getPreferences().edit().putBoolean("never_show_welcome", true).apply()) + .setPositiveButton(android.R.string.ok, null) + .create(); + } + + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + var builder = new BlurBehindDialogBuilder(requireContext(), + R.style.ThemeOverlay_MaterialAlertDialog_Centered_FullWidthButtons); + if (App.isParasitic()) { + return parasiticDialog(builder); + } else { + return appDialog(builder); + } + } + + public static void showIfNeed(FragmentManager fm) { + if (shown) return; + if (!ConfigManager.isBinderAlive()) return; + if (App.getPreferences().getBoolean("never_show_welcome", false)) return; + new WelcomeDialog().show(fm, "welcome"); + shown = true; + } +} diff --git a/app/src/main/java/org/lsposed/manager/ui/fragment/HomeFragment.java b/app/src/main/java/org/lsposed/manager/ui/fragment/HomeFragment.java index 03b39561..8eefa661 100644 --- a/app/src/main/java/org/lsposed/manager/ui/fragment/HomeFragment.java +++ b/app/src/main/java/org/lsposed/manager/ui/fragment/HomeFragment.java @@ -42,7 +42,7 @@ import org.lsposed.manager.databinding.DialogAboutBinding; import org.lsposed.manager.databinding.FragmentHomeBinding; import org.lsposed.manager.ui.dialog.BlurBehindDialogBuilder; import org.lsposed.manager.ui.dialog.FlashDialogBuilder; -import org.lsposed.manager.ui.dialog.ShortcutDialog; +import org.lsposed.manager.ui.dialog.WelcomeDialog; import org.lsposed.manager.util.NavUtil; import org.lsposed.manager.util.Telemetry; import org.lsposed.manager.util.UpdateUtil; @@ -61,7 +61,7 @@ public class HomeFragment extends BaseFragment { @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - ShortcutDialog.showIfNeed(getChildFragmentManager()); + WelcomeDialog.showIfNeed(getChildFragmentManager()); } @Override diff --git a/app/src/main/java/org/lsposed/manager/ui/fragment/SettingsFragment.java b/app/src/main/java/org/lsposed/manager/ui/fragment/SettingsFragment.java index 27ca432c..ce6ccd35 100644 --- a/app/src/main/java/org/lsposed/manager/ui/fragment/SettingsFragment.java +++ b/app/src/main/java/org/lsposed/manager/ui/fragment/SettingsFragment.java @@ -36,9 +36,6 @@ import androidx.annotation.Nullable; import androidx.core.text.HtmlCompat; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; - -import rikka.material.preference.MaterialSwitchPreference; - import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.color.DynamicColors; @@ -48,12 +45,12 @@ import org.lsposed.manager.BuildConfig; import org.lsposed.manager.ConfigManager; import org.lsposed.manager.R; import org.lsposed.manager.databinding.FragmentSettingsBinding; -import org.lsposed.manager.receivers.LSPManagerServiceHolder; import org.lsposed.manager.repo.RepoLoader; import org.lsposed.manager.ui.activity.MainActivity; import org.lsposed.manager.util.BackupUtils; import org.lsposed.manager.util.LangList; import org.lsposed.manager.util.NavUtil; +import org.lsposed.manager.util.ShortcutUtil; import org.lsposed.manager.util.ThemeUtil; import java.time.LocalDateTime; @@ -63,6 +60,7 @@ import java.util.Locale; import rikka.core.util.ResourceUtils; import rikka.material.app.DayNightDelegate; import rikka.material.app.LocaleDelegate; +import rikka.material.preference.MaterialSwitchPreference; import rikka.preference.SimpleMenuPreference; import rikka.recyclerview.RecyclerViewKt; import rikka.widget.borderview.BorderRecyclerView; @@ -159,26 +157,31 @@ public class SettingsFragment extends BaseFragment { }); } - MaterialSwitchPreference prefEnableShortcut = findPreference("enable_auto_add_shortcut"); - if (prefEnableShortcut != null) { - prefEnableShortcut.setEnabled(installed); - prefEnableShortcut.setVisible(!App.isParasitic()); - prefEnableShortcut.setChecked(installed && ConfigManager.isAddShortcut()); - prefEnableShortcut.setOnPreferenceChangeListener((preference, newValue) -> ConfigManager.setAddShortcut((boolean) newValue)); - } - Preference shortcut = findPreference("add_shortcut"); if (shortcut != null) { - shortcut.setEnabled(installed); + shortcut.setVisible(App.isParasitic()); + shortcut.setEnabled(!ShortcutUtil.isLaunchShortcutPinned()); shortcut.setOnPreferenceClickListener(preference -> { - try { - LSPManagerServiceHolder.getService().createShortcut(); - } catch (Throwable ignored) { - } + ShortcutUtil.requestPinLaunchShortcut(); return true; }); } + MaterialSwitchPreference notification = findPreference("enable_status_notification"); + if (notification != null) { + if (App.isParasitic() && !ShortcutUtil.isLaunchShortcutPinned()) { + var s = notification.getContext().getString(R.string.disable_status_notification_error); + notification.setSummaryOn(notification.getSummary() + "\n" + s); + notification.setEnabled(false); + } else { + notification.setEnabled(installed); + } + notification.setChecked(installed && ConfigManager.enableStatusNotification()); + notification.setOnPreferenceChangeListener((p, v) -> + ConfigManager.setEnableStatusNotification((boolean) v) + ); + } + Preference backup = findPreference("backup"); if (backup != null) { backup.setEnabled(installed); diff --git a/app/src/main/java/org/lsposed/manager/util/ShortcutUtil.java b/app/src/main/java/org/lsposed/manager/util/ShortcutUtil.java new file mode 100644 index 00000000..42a136fe --- /dev/null +++ b/app/src/main/java/org/lsposed/manager/util/ShortcutUtil.java @@ -0,0 +1,65 @@ +package org.lsposed.manager.util; + +import android.content.Context; +import android.content.pm.ShortcutInfo; +import android.content.pm.ShortcutManager; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.AdaptiveIconDrawable; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; +import android.graphics.drawable.LayerDrawable; + +import org.lsposed.manager.App; +import org.lsposed.manager.ConfigManager; +import org.lsposed.manager.R; + +public class ShortcutUtil { + private static final String SHORTCUT_ID = "org.lsposed.manager.shortcut"; + + private static Bitmap getBitmap(Context context, int id) { + var r = context.getResources(); + var res = r.getDrawable(id, r.newTheme()); + if (res instanceof BitmapDrawable) { + return ((BitmapDrawable) res).getBitmap(); + } else { + if (res instanceof AdaptiveIconDrawable) { + var layers = new Drawable[]{((AdaptiveIconDrawable) res).getBackground(), + ((AdaptiveIconDrawable) res).getForeground()}; + res = new LayerDrawable(layers); + } + var bitmap = Bitmap.createBitmap(res.getIntrinsicWidth(), + res.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); + var canvas = new Canvas(bitmap); + res.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + res.draw(canvas); + return bitmap; + } + } + + public static void requestPinLaunchShortcut() { + var context= App.getInstance(); + var intent = ConfigManager.getLaunchIntentForManager(); + var info = new ShortcutInfo.Builder(context, SHORTCUT_ID) + .setShortLabel(context.getString(R.string.app_name)) + .setIntent(intent) + .setIcon(Icon.createWithAdaptiveBitmap(getBitmap(context, R.drawable.ic_launcher))) + .build(); + var sm = context.getSystemService(ShortcutManager.class); + sm.requestPinShortcut(info, null); + } + + public static boolean isLaunchShortcutPinned() { + var context= App.getInstance(); + var sm = context.getSystemService(ShortcutManager.class); + boolean pinned = false; + for (var info : sm.getPinnedShortcuts()) { + if (SHORTCUT_ID.equals(info.getId())) { + pinned = true; + break; + } + } + return pinned; + } +} diff --git a/app/src/main/res/drawable/ic_outline_speaker_notes_24.xml b/app/src/main/res/drawable/ic_outline_speaker_notes_24.xml new file mode 100644 index 00000000..7b9b9df6 --- /dev/null +++ b/app/src/main/res/drawable/ic_outline_speaker_notes_24.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a7fa3922..94d0148f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -73,13 +73,17 @@ Mount failed SELinux is permissive SELinux policy is incorrect - Parasitic Manager Recommended - LSPosed now supports system parasitization to avoid detection. You can safely uninstall the manager after successfully creating a shortcut of the parasitic manager. In any case you can install the manager back from /data/adb/lspd/manager.apk. - Create shortcut - Never show Update LSPosed Confirm to update LSPosed? This device will reboot after update completion + + Welcome to LSPosed + You are using the parasitic manager, which can create shortcut or still open from notification. + Create shortcut + Never show + Parasitic Manager Recommended + LSPosed now supports system parasitization to avoid detection, you can open parasitic manager from notification. It is recommended to uninstall the current application. + Save Verbose Logs @@ -190,9 +194,11 @@ Translation contributors Participate in translation Help us translate %s into your language - Automatically add a shortcut Manually create parasitic manager shortcut - No new shortcuts will be added when disabled in the non-parasitic manager + Recommended use parasitic manager + Status Notification + Show a notification that can open parasitic manager + No shortcut, cannot disable notification Update channel Stable Beta diff --git a/app/src/main/res/xml/prefs.xml b/app/src/main/res/xml/prefs.xml index 0ed5c15b..1ded5368 100644 --- a/app/src/main/res/xml/prefs.xml +++ b/app/src/main/res/xml/prefs.xml @@ -106,11 +106,11 @@ + android:summary="@string/settings_enable_status_notification_summary" + android:title="@string/settings_enable_status_notification" />