diff --git a/ActivityLauncherApp/src/main/AndroidManifest.xml b/ActivityLauncherApp/src/main/AndroidManifest.xml
index cd6f96a..7431245 100644
--- a/ActivityLauncherApp/src/main/AndroidManifest.xml
+++ b/ActivityLauncherApp/src/main/AndroidManifest.xml
@@ -43,10 +43,20 @@
+ android:exported="true"
+ android:theme="@style/AppTheme.NoDisplay">
+
+
+
+
+
+
diff --git a/ActivityLauncherApp/src/main/java/de/szalkowski/activitylauncher/AllTasksListFragment.java b/ActivityLauncherApp/src/main/java/de/szalkowski/activitylauncher/AllTasksListFragment.java
index d47bab8..dc3fe6a 100644
--- a/ActivityLauncherApp/src/main/java/de/szalkowski/activitylauncher/AllTasksListFragment.java
+++ b/ActivityLauncherApp/src/main/java/de/szalkowski/activitylauncher/AllTasksListFragment.java
@@ -21,7 +21,8 @@ import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
-import org.thirdparty.LauncherIconCreator;
+import org.thirdparty.IconCreator;
+import org.thirdparty.Launcher;
import de.szalkowski.activitylauncher.databinding.FragmentAllListBinding;
@@ -38,7 +39,7 @@ public class AllTasksListFragment extends Fragment implements AllTasksListAsyncP
ExpandableListAdapter adapter = parent.getExpandableListAdapter();
MyActivityInfo info = (MyActivityInfo) adapter.getChild(groupPosition, childPosition);
var rooted = isRootAllowed();
- LauncherIconCreator.launchActivity(getActivity(), info.component_name, rooted && info.is_private);
+ Launcher.launchActivity(getActivity(), info.component_name, rooted && info.is_private);
return false;
}
);
@@ -92,16 +93,16 @@ public class AllTasksListFragment extends Fragment implements AllTasksListAsyncP
MyActivityInfo activity = (MyActivityInfo) binding.expandableListView1.getExpandableListAdapter().getChild(ExpandableListView.getPackedPositionGroup(info.packedPosition), ExpandableListView.getPackedPositionChild(info.packedPosition));
switch (item.getItemId()) {
case 0:
- LauncherIconCreator.createLauncherIcon(getActivity(), activity);
+ IconCreator.createLauncherIcon(getActivity(), activity);
break;
case 1:
RootLauncherIconCreator.createLauncherIcon(getActivity(), activity);
break;
case 2:
- LauncherIconCreator.launchActivity(getActivity(), activity.component_name, false);
+ Launcher.launchActivity(getActivity(), activity.component_name, false);
break;
case 3:
- LauncherIconCreator.launchActivity(getActivity(), activity.component_name, true);
+ Launcher.launchActivity(getActivity(), activity.component_name, true);
break;
case 4:
DialogFragment dialog = new ShortcutEditDialogFragment();
@@ -118,7 +119,7 @@ public class AllTasksListFragment extends Fragment implements AllTasksListAsyncP
MyPackageInfo pack = (MyPackageInfo) binding.expandableListView1.getExpandableListAdapter().getGroup(ExpandableListView.getPackedPositionGroup(info.packedPosition));
switch (item.getItemId()) {
case 0:
- LauncherIconCreator.createLauncherIcon(requireActivity(), pack);
+ IconCreator.createLauncherIcon(requireActivity(), pack);
Toast.makeText(getActivity(), getString(R.string.error_no_default_activity), Toast.LENGTH_LONG).show();
break;
case 2:
diff --git a/ActivityLauncherApp/src/main/java/de/szalkowski/activitylauncher/RootLauncherActivity.java b/ActivityLauncherApp/src/main/java/de/szalkowski/activitylauncher/RootLauncherActivity.java
index d4a537d..2f69c14 100644
--- a/ActivityLauncherApp/src/main/java/de/szalkowski/activitylauncher/RootLauncherActivity.java
+++ b/ActivityLauncherApp/src/main/java/de/szalkowski/activitylauncher/RootLauncherActivity.java
@@ -4,11 +4,11 @@ import android.content.ComponentName;
import android.os.Bundle;
import android.widget.Toast;
-import androidx.fragment.app.FragmentActivity;
+import androidx.appcompat.app.AppCompatActivity;
-import org.thirdparty.LauncherIconCreator;
+import org.thirdparty.Launcher;
-public class RootLauncherActivity extends FragmentActivity {
+public class RootLauncherActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -28,7 +28,7 @@ public class RootLauncherActivity extends FragmentActivity {
var signer = new Signer(getApplicationContext());
if (signer.validateComponentNameSignature(componentName, signature)) {
- LauncherIconCreator.launchActivity(getApplicationContext(), componentName, true);
+ Launcher.launchActivity(getApplicationContext(), componentName, true);
}
} catch (Exception e) {
e.printStackTrace();
diff --git a/ActivityLauncherApp/src/main/java/de/szalkowski/activitylauncher/RootLauncherIconCreator.java b/ActivityLauncherApp/src/main/java/de/szalkowski/activitylauncher/RootLauncherIconCreator.java
index c130eb5..e71f57a 100644
--- a/ActivityLauncherApp/src/main/java/de/szalkowski/activitylauncher/RootLauncherIconCreator.java
+++ b/ActivityLauncherApp/src/main/java/de/szalkowski/activitylauncher/RootLauncherIconCreator.java
@@ -5,7 +5,7 @@ import android.content.Context;
import android.os.Bundle;
import android.widget.Toast;
-import org.thirdparty.LauncherIconCreator;
+import org.thirdparty.IconCreator;
public class RootLauncherIconCreator {
public static void createLauncherIcon(Context context, MyActivityInfo activity) {
@@ -29,6 +29,6 @@ public class RootLauncherIconCreator {
activity.is_private = true;
activity.component_name = new ComponentName("de.szalkowski.activitylauncher", "de.szalkowski.activitylauncher.RootLauncherActivity");
- LauncherIconCreator.createLauncherIcon(context, activity, extras);
+ IconCreator.createLauncherIcon(context, activity, extras);
}
}
diff --git a/ActivityLauncherApp/src/main/java/de/szalkowski/activitylauncher/ShortcutActivity.java b/ActivityLauncherApp/src/main/java/de/szalkowski/activitylauncher/ShortcutActivity.java
new file mode 100644
index 0000000..34df434
--- /dev/null
+++ b/ActivityLauncherApp/src/main/java/de/szalkowski/activitylauncher/ShortcutActivity.java
@@ -0,0 +1,27 @@
+package de.szalkowski.activitylauncher;
+
+import static org.thirdparty.Launcher.launchActivity;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import java.net.URISyntaxException;
+
+public class ShortcutActivity extends AppCompatActivity {
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ try {
+ Intent launchIntent = Intent.parseUri(getIntent().getStringExtra("extra_intent"), 0);
+ launchActivity(this, launchIntent.getComponent(), false);
+ } catch (URISyntaxException e) {
+ e.printStackTrace();
+ } finally {
+ finish();
+ }
+ }
+}
diff --git a/ActivityLauncherApp/src/main/java/de/szalkowski/activitylauncher/ShortcutEditDialogFragment.java b/ActivityLauncherApp/src/main/java/de/szalkowski/activitylauncher/ShortcutEditDialogFragment.java
index 77d10fa..7060014 100644
--- a/ActivityLauncherApp/src/main/java/de/szalkowski/activitylauncher/ShortcutEditDialogFragment.java
+++ b/ActivityLauncherApp/src/main/java/de/szalkowski/activitylauncher/ShortcutEditDialogFragment.java
@@ -12,7 +12,6 @@ import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
-import android.view.View;
import android.widget.Toast;
import androidx.annotation.NonNull;
@@ -21,7 +20,7 @@ import androidx.core.content.res.ResourcesCompat;
import androidx.fragment.app.DialogFragment;
import androidx.preference.PreferenceManager;
-import org.thirdparty.LauncherIconCreator;
+import org.thirdparty.IconCreator;
import java.util.Objects;
@@ -134,7 +133,7 @@ public class ShortcutEditDialogFragment extends DialogFragment {
if (as_root) {
RootLauncherIconCreator.createLauncherIcon(getActivity(), ShortcutEditDialogFragment.this.activity);
} else {
- LauncherIconCreator.createLauncherIcon(getActivity(), ShortcutEditDialogFragment.this.activity);
+ IconCreator.createLauncherIcon(getActivity(), ShortcutEditDialogFragment.this.activity);
}
})
.setNegativeButton(android.R.string.cancel, (dialog, which) -> Objects.requireNonNull(ShortcutEditDialogFragment.this.getDialog()).cancel());
diff --git a/ActivityLauncherApp/src/main/java/org/thirdparty/LauncherIconCreator.java b/ActivityLauncherApp/src/main/java/org/thirdparty/IconCreator.java
similarity index 68%
rename from ActivityLauncherApp/src/main/java/org/thirdparty/LauncherIconCreator.java
rename to ActivityLauncherApp/src/main/java/org/thirdparty/IconCreator.java
index 3bd4829..e30246b 100644
--- a/ActivityLauncherApp/src/main/java/org/thirdparty/LauncherIconCreator.java
+++ b/ActivityLauncherApp/src/main/java/org/thirdparty/IconCreator.java
@@ -28,24 +28,20 @@ import android.os.Build;
import android.os.Bundle;
import android.widget.Toast;
-import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.nio.charset.StandardCharsets;
import java.util.Objects;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+
import de.szalkowski.activitylauncher.MyActivityInfo;
import de.szalkowski.activitylauncher.MyPackageInfo;
import de.szalkowski.activitylauncher.R;
-public class LauncherIconCreator {
+public class IconCreator {
- private static Intent getActivityIntent(ComponentName activity, Bundle extras) {
+ private static String INTENT_LAUNCH_SHORTCUT = "activitylauncher.intent.action.LAUNCH_SHORTCUT";
+
+ public static Intent getActivityIntent(ComponentName activity, Bundle extras) {
Intent intent = new Intent();
intent.setComponent(activity);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -130,72 +126,6 @@ public class LauncherIconCreator {
return Icon.createWithBitmap(bmp);
}
- /**
- * Got reference from stackoverflow.com URL
- * https://stackoverflow.com/questions/9194725/run-android-program-as-root
- * https://stackoverflow.com/questions/12343227/escaping-bash-function-arguments-for-use-by-su-c
- */
- public static void launchActivity(Context context, ComponentName activity, boolean asRoot) {
- Intent intent = LauncherIconCreator.getActivityIntent(activity, null);
- Toast.makeText(context, String.format(context.getText(R.string.starting_activity).toString(), activity.flattenToShortString()),
- Toast.LENGTH_LONG).show();
-
- try {
- if (!asRoot) {
- context.startActivity(intent);
- } else {
- startRootActivity(context, activity);
- }
- } catch (Exception e) {
- e.printStackTrace();
- Toast.makeText(context, context.getText(R.string.error).toString() + ": " + e, Toast.LENGTH_LONG).show();
- }
- }
-
- private static void startRootActivity(Context context, ComponentName activity) throws IOException, InterruptedException, IllegalArgumentException {
- var component = activity.flattenToShortString();
- boolean isValid = validateComponentName(component);
- if (!isValid) {
- throw new IllegalArgumentException(String.format(context.getString(R.string.exception_invalid_component_name), component));
- }
- Process process = Runtime.getRuntime().exec(new String[]{"su", "-c", "am start -n " + component});
- String output = getProcessOutput(process);
-
- var exitValue = process.waitFor();
- if (exitValue > 0) {
- throw new RuntimeException(String.format(context.getString(R.string.exception_command_error), exitValue, output));
- }
- }
-
- /**
- * Got reference from stackoverflow.com URL:
- * https://stackoverflow.com/questions/309424/how-do-i-read-convert-an-inputstream-into-a-string-in-java
- */
- @NonNull
- private static String getProcessOutput(Process process) throws IOException {
- var stream = process.getErrorStream();
- int bufferSize = 1024;
- char[] buffer = new char[bufferSize];
- StringBuilder out = new StringBuilder();
- Reader in = new InputStreamReader(stream, StandardCharsets.UTF_8);
- for (int numRead; (numRead = in.read(buffer, 0, buffer.length)) > 0; ) {
- out.append(buffer, 0, numRead);
- }
- return out.toString();
- }
-
- /**
- * In order to be on the safe side, validate component name before merging it into a root shell command
- *
- * @param component component name
- * @return true, if valid
- */
- private static boolean validateComponentName(String component) {
- Pattern p = Pattern.compile("^[./a-zA-Z0-9]+$");
- Matcher m = p.matcher(component);
- return m.matches();
- }
-
private static void createShortcut(Context context, String appName, Drawable draw, Intent intent, String iconResourceName) {
Toast.makeText(context, String.format(context.getText(R.string.creating_application_shortcut).toString(), appName),
@@ -208,7 +138,6 @@ public class LauncherIconCreator {
}
}
- @TargetApi(19)
private static void doCreateShortcut(Context context, String appName, Intent intent, String iconResourceName) {
Intent shortcutIntent = new Intent();
shortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, intent);
@@ -228,12 +157,13 @@ public class LauncherIconCreator {
}
@TargetApi(26)
- private static void doCreateShortcut(Context context, String appName, Drawable draw, Intent intent) {
+ private static void doCreateShortcut(Context context, String appName, Drawable draw, Intent extraIntent) {
ShortcutManager shortcutManager = Objects.requireNonNull(context.getSystemService(ShortcutManager.class));
if (shortcutManager.isRequestPinShortcutSupported()) {
Icon icon = getIconFromDrawable(draw);
- intent.setAction(Intent.ACTION_CREATE_SHORTCUT);
+ Intent intent = new Intent(INTENT_LAUNCH_SHORTCUT);
+ intent.putExtra("extra_intent", extraIntent.toUri(0));
ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(context, appName)
.setShortLabel(appName)
@@ -241,8 +171,8 @@ public class LauncherIconCreator {
.setIcon(icon)
.setIntent(intent)
.build();
-
shortcutManager.requestPinShortcut(shortcutInfo, null);
+
} else {
new AlertDialog.Builder(context)
.setTitle(context.getText(R.string.error_creating_shortcut))
diff --git a/ActivityLauncherApp/src/main/java/org/thirdparty/Launcher.java b/ActivityLauncherApp/src/main/java/org/thirdparty/Launcher.java
new file mode 100644
index 0000000..8794d5c
--- /dev/null
+++ b/ActivityLauncherApp/src/main/java/org/thirdparty/Launcher.java
@@ -0,0 +1,83 @@
+package org.thirdparty;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.StandardCharsets;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import de.szalkowski.activitylauncher.R;
+
+public class Launcher {
+ /**
+ * Got reference from stackoverflow.com URL
+ * https://stackoverflow.com/questions/9194725/run-android-program-as-root
+ * https://stackoverflow.com/questions/12343227/escaping-bash-function-arguments-for-use-by-su-c
+ */
+ public static void launchActivity(Context context, ComponentName activity, boolean asRoot) {
+ Intent intent = IconCreator.getActivityIntent(activity, null);
+ Toast.makeText(context, String.format(context.getText(R.string.starting_activity).toString(), activity.flattenToShortString()),
+ Toast.LENGTH_LONG).show();
+ try {
+ if (!asRoot) {
+ context.startActivity(intent);
+ } else {
+ startRootActivity(context, activity);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ Toast.makeText(context, context.getText(R.string.error).toString() + ": " + e, Toast.LENGTH_LONG).show();
+ }
+ }
+
+ private static void startRootActivity(Context context, ComponentName activity) throws IOException, InterruptedException, IllegalArgumentException {
+ var component = activity.flattenToShortString();
+ boolean isValid = validateComponentName(component);
+ if (!isValid) {
+ throw new IllegalArgumentException(String.format(context.getString(R.string.exception_invalid_component_name), component));
+ }
+ Process process = Runtime.getRuntime().exec(new String[]{"su", "-c", "am start -n " + component});
+ String output = getProcessOutput(process);
+
+ var exitValue = process.waitFor();
+ if (exitValue > 0) {
+ throw new RuntimeException(String.format(context.getString(R.string.exception_command_error), exitValue, output));
+ }
+ }
+ /**
+ * Got reference from stackoverflow.com URL:
+ * https://stackoverflow.com/questions/309424/how-do-i-read-convert-an-inputstream-into-a-string-in-java
+ */
+ @NonNull
+ private static String getProcessOutput(Process process) throws IOException {
+ var stream = process.getErrorStream();
+ int bufferSize = 1024;
+ char[] buffer = new char[bufferSize];
+ StringBuilder out = new StringBuilder();
+ Reader in = new InputStreamReader(stream, StandardCharsets.UTF_8);
+ for (int numRead; (numRead = in.read(buffer, 0, buffer.length)) > 0; ) {
+ out.append(buffer, 0, numRead);
+ }
+ return out.toString();
+ }
+
+ /**
+ * In order to be on the safe side, validate component name before merging it into a root shell command
+ *
+ * @param component component name
+ * @return true, if valid
+ */
+ private static boolean validateComponentName(String component) {
+ Pattern p = Pattern.compile("^[./a-zA-Z0-9]+$");
+ Matcher m = p.matcher(component);
+ return m.matches();
+ }
+}
diff --git a/ActivityLauncherApp/src/main/res/values/themes.xml b/ActivityLauncherApp/src/main/res/values/themes.xml
index becde7d..7b5e095 100644
--- a/ActivityLauncherApp/src/main/res/values/themes.xml
+++ b/ActivityLauncherApp/src/main/res/values/themes.xml
@@ -1,6 +1,14 @@
-
-
-
-
+
+
+
+
+
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index d21b62e..7f7e1ce 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,7 +5,7 @@ buildscript {
google()
}
dependencies {
- classpath 'com.android.tools.build:gradle:7.2.2'
+ classpath 'com.android.tools.build:gradle:7.3.1'
}
}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index b916c04..ae04661 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionSha256Sum=f6b8596b10cce501591e92f229816aa4046424f3b24d771751b06779d58c8ec4
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists