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" />