I created a test Activity that installs a shortcut of itself on the Android Home screen. When you click a button, the Activity is supposed to remove the same shortcut it just created. However, nothing I do seems to delete the shortcut.
Here is the Java code (ShortcutTest.java):
import java.net.URISyntaxException;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class ShortcutTest extends Activity {
String shortcutUri;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
addShortcut(getBaseContext());
Button button = (Button)findViewById(R.id.Button01);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
removeShortcut(getBaseContext());
finish();
}
});
}
public void addShortcut(Context context) {
Intent shortcutIntent = new Intent();
shortcutIntent.setClassName("com.telespree.android.client", "com.telespree.android.client.ShortcutTest");
shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, "ShortcutTest");
intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(context, R.drawable.icon));
intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
shortcutUri = intent.toUri(MODE_WORLD_WRITEABLE);
context.sendBroadcast(intent);
}
public void removeShortcut(Context context) {
Intent intent = null;
try {
intent = Intent.parseUri(shortcutUri, 0);
} catch (URISyntaxException e) {
}
intent.setAction("com.android.launcher.permission.UNINSTALL_SHORTCUT");
context.sendBroadcast(intent);
}
}
Here is the Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.telespree.android.client"
android:versionCode="1"
android:versionName="1.0">
<permission
android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:protectionLevel="normal"
/>
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".ShortcutTest"
android:label="@string/app_name" android:theme="@android:style/Theme.Translucent">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
<!--
<uses-permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT"/>
-->
<uses-sdk android:minSdkVersion="7" />
</manifest>
I'm almost positive there is some kind of permissions problem, though I've seen other posts on the Internet that indicates this should be possible.
Any advice is greatly appreciated.
Thanks.
This answer was posted in 2014, when the described method relied on functionality that existed in most Android devices. However, as mentioned by Adrian-Costin Țundrea, this functionality was removed a couple of years ago from Launcher3, which is the AOSP launcher upon which the Google Now Launcher is based1. The commit message said:
Removing support due to its flacky design. Removing a shortcut causes a full reload. Also we do not have any concept of owner, so any app can remove any shortcut.
As of March 2017, this launcher, too, is being phased out in favor of "Google Search Launcher Services", which means that manufacturers could integrate a certain Google library into their own custom launchers, instead of relying on a standardized launcher provided by Google.
Considering that each manufacturer is free to implement their launcher whichever way they want, and assuming some of them are based off Launcher3, it's difficult to tell which devices the method below will work on, as Launcher3 was made to run even on some Android 4.1 devices, which are among the oldest devices still in use.
I have just dealt with the same exact problem, and would like to share my experience after successfully resolving it. tl;dr - skip to "In Conclusion" below.
While working on the "next version" of an app, a need arose to change the default entry point (i.e. to rename the "Main Activity"). This is frowned upon because users who would be upgrading from an old version will still have the old shortcut, pointing to the wrong place. In order to avoid problems as much as possible, on the first launch, unbeknownst to them, the old shortcut was to be replaced with a new one.
This is the easiest part. To declare an entry point the only essential thing to do is to put the following <action ...>
tag in the appropriate activity declaration inside your Manifest:
<activity
android:name="YOUR_PACKAGE_NAME.YOUR_ACTIVITY_NAME"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
What makes an entry point default in some sense, is that the launcher shortcut points to it. This is why developers usually also include this in the <intent-filter>
:
<category android:name="android.intent.category.LAUNCHER"/>
It should be noted that every activity that has this in its <intent-filter>
will create an item in your app drawer - this is why for most cases 1 instance is all you need.
Having a rooted device, I could access the database table where the launcher/homescreen/desktop items are stored (see image of what the SQLite entries looks like) that's located in:
/data/data/com.android.launcher/databases/launcher.db -> SELECT * FROM favorites`
Here's a more readable version of the highlighted entry from the image:
#Intent;
action=android.intent.action.MAIN;
category=android.intent.category.LAUNCHER;
launchFlags=0x10200000;
package=gidutz.soft.bluecard;
component=gidutz.soft.bluecard/.LoadingScreen;
end
Note the 0x10200000
- this is explained in Step 4 - Attempt 1 below.
Lines 38-42 in UninstallShortcutReceiver.java
tell us that:
Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
boolean duplicate = data.getBooleanExtra(Launcher.EXTRA_SHORTCUT_DUPLICATE, true);
if (intent != null && name != null) { ... }
Meaning that the "uninstallation intent" has to have both Intent.EXTRA_SHORTCUT_INTENT
and Intent.EXTRA_SHORTCUT_NAME
or else it will not even consider executing.
This is a case of trial an error with a happy ending.
Intent oldShortcutIntent = new Intent();
oldShortcutIntent.setAction(Intent.ACTION_MAIN);
oldShortcutIntent.addCategory(Intent.CATEGORY_LAUNCHER);
oldShortcutIntent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED +
Intent.FLAG_ACTIVITY_NEW_TASK);
oldShortcutIntent.setPackage("gidutz.soft.bluecard");
oldShortcutIntent.setComponent(new ComponentName("gidutz.soft.bluecard",
".LoadingScreen"));
// The above line is equivalent to:
Intent oldShortcutIntent = new Intent(getApplicationContext(),LoadingScreen.class);
Intent uninstaller = new Intent();
uninstaller.putExtra(Intent.EXTRA_SHORTCUT_INTENT, oldShortcutIntent);
uninstaller.putExtra(Intent.EXTRA_SHORTCUT_NAME, "Blue Card");
uninstaller.setAction("com.android.launcher.action.UNINSTALL_SHORTCUT");
getApplicationContext().sendBroadcast(uninstaller);
Result: Icon not removed.
The 0x10200000
is actually a sum of two arguments as explained here.
Intent shortcutIntent = new Intent(getApplicationContext(),LoadingScreen.class);
shortcutIntent.setAction(Intent.ACTION_MAIN);
Intent addIntent = new Intent();
addIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
addIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, "Blue Card");
addIntent.setAction("com.android.launcher.action.UNINSTALL_SHORTCUT");
getApplicationContext().sendBroadcast(addIntent);
Result: Icon not removed.
Trying to copy-paste the intent exactly as it appears in the launcher.db:
Intent intent = new Intent();
String oldShortcutUri = "#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;package=gidutz.soft.bluecard;component=gidutz.soft.bluecard/.LoadingScreen;end";
try {
Intent altShortcutIntent = Intent.parseUri(oldShortcutUri,0);
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, altShortcutIntent);
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, "Blue Card");
} catch (URISyntaxException e) {
}
intent.setAction("com.android.launcher.action.UNINSTALL_SHORTCUT");
getApplicationContext().sendBroadcast(intent);
Result: Icon removed!!
launcher.db
.1) This guide at viralpatel.net
2) Google's implementation of UninstallShortcutReceiver.java
3) This thread at xdadevelopers
In order to simulate and debug a Google Play update (which keeps the old shortcut) I did the following:
Note1: In retrospective, it might have been easier to create the old shortcut manually using the URI obtained from the database instead of going through all backing-up and force-stopping ordeal.
Note2: I haven't tried removing icons belonging to other apps using this method, but it might just be crazy enough to work.
While both solutions from Dev-iL and Funt work be advised they do so until Marshmallow. With Android 6.0 (which has Launcher v3) Google has removed the UninstallShortcutReceiver because of its security problems (probably because it became apparent here). So do not expect it to work with Android 6.0. Hopefully in some future release it will be readded in a form or another.
PS: Normally this should be a comment, but I am not allowed to comment because of the reputation...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With