Intentando DESINSTALAR_SHORTCUT pero el acceso directo no desaparecerá

Creé una actividad de prueba que instala un atajo de sí mismo en la pantalla de inicio de Android. Cuando haces clic en un botón, se supone que la Actividad elimina el mismo atajo que acaba de crear. Sin embargo, nada de lo que hago parece eliminar el atajo.

Aquí está el código de Java (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); } } 

Aquí está el Manifiesto:

             <!--  -->   

Estoy casi seguro de que hay algún tipo de problema de permisos, aunque he visto otras publicaciones en Internet que indican que esto debería ser posible.

Cualquier consejo es muy apreciado.

Gracias.

OBSOLETO; MANTENER SOLAMENTE PARA PROPÓSITOS HISTÓRICOS

Esta respuesta se publicó en 2014, cuando el método descrito se basó en la funcionalidad que existía en la mayoría de los dispositivos Android. Sin embargo, como mencionó Adrian-Costin Ţundrea , esta funcionalidad fue eliminada hace un par de años de Launcher3, que es el iniciador de AOSP en el que se basa Google Now Launcher 1 . El mensaje de compromiso dijo:

Extracción de soporte debido a su diseño flácido. La eliminación de un atajo causa una recarga completa. Además, no tenemos ningún concepto de propietario, por lo que cualquier aplicación puede eliminar cualquier atajo.

A partir de marzo de 2017, este lanzador también se eliminará a favor de “Google Search Launcher Services”, lo que significa que los fabricantes podrían integrar cierta biblioteca de Google en sus propios lanzadores personalizados, en lugar de confiar en un lanzador estandarizado proporcionado por Google. .

Teniendo en cuenta que cada fabricante es libre de implementar su lanzador de la forma que quiera, y suponiendo que algunos de ellos se basen en Launcher3, es difícil decir qué dispositivos funcionará en el siguiente método, ya que Launcher3 se ejecutó incluso en algunos dispositivos Android 4.1 , que se encuentran entre los dispositivos más antiguos todavía en uso .


¡Saludos!

Acabo de tratar con el mismo problema exacto, y me gustaría compartir mi experiencia después de resolverla con éxito. tl; dr – saltar a “En Conclusión” a continuación.

Algunos antecedentes:

Mientras trabajaba en la “próxima versión” de una aplicación, surgió la necesidad de cambiar el punto de entrada predeterminado (es decir, cambiar el nombre de la “Actividad principal”). Esto es desaconsejable porque los usuarios que estarían actualizando desde una versión anterior seguirán teniendo el acceso directo anterior, apuntando al lugar equivocado. Para evitar problemas tanto como fuera posible, en el primer lanzamiento, sin su conocimiento, el antiguo atajo iba a ser reemplazado por uno nuevo.

Paso 1: configurar un nuevo punto de entrada

Esta es la parte más fácil. Para declarar un punto de entrada, lo único esencial es colocar la siguiente etiqueta en la statement de actividad apropiada dentro de su Manifiesto:

      

Lo que hace que un punto de entrada sea predeterminado en algún sentido, es que el atajo del iniciador apunta a él. Esta es la razón por la que los desarrolladores generalmente también incluyen esto en el :

  

Se debe tener en cuenta que cada actividad que tenga esto en su creará un elemento en el cajón de su aplicación; esta es la razón por la que para la mayoría de los casos, 1 instancia es todo lo que necesita.

Paso 2: averiguar cómo funciona el antiguo atajo

Teniendo un dispositivo rooteado, podría acceder a la tabla de la base de datos donde están almacenados los elementos de inicio / inicio / escritorio (vea la imagen de cómo se ven las entradas de SQLite) que se encuentra en:

 /data/data/com.android.launcher/databases/launcher.db -> SELECT * FROM favorites` 

Aquí hay una versión más legible de la entrada resaltada de la imagen:

 #Intent; action=android.intent.action.MAIN; category=android.intent.category.LAUNCHER; launchFlags=0x10200000; package=gidutz.soft.bluecard; component=gidutz.soft.bluecard/.LoadingScreen; end 

Tenga en cuenta el 0x10200000 – esto se explica en el Paso 4 – Intento 1 a continuación.

Paso 3: averiguar qué está esperando el desinstalador de acceso directo

Las líneas 38-42 en UninstallShortcutReceiver.java nos dicen que:

 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) { ... } 

Lo que significa que el “bash de desinstalación” debe tener Intent.EXTRA_SHORTCUT_INTENT e Intent.EXTRA_SHORTCUT_NAME o, de lo contrario, ni siquiera considerará la ejecución.

Paso 4: Encontrar la syntax correcta

Este es un caso de prueba un error con un final feliz.

Intento 1: Reconstruir la intención

 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); 

Resultado: icono no eliminado. El 0x10200000 es en realidad una sum de dos argumentos como se explica aquí .

Intento 2: utilizar el código tal como es de viralpatel

 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); 

Resultado: icono no eliminado.

Intento 3: “Fuerza bruta”

Intentando copiar y pegar el bash exactamente como aparece en el 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); 

Resultado: icono eliminado !!

En conclusión

  1. Asegúrese de que su bash de “Desinstalador de íconos” utiliza el mismo URI exacto utilizado para crear el ícono que intenta eliminar, ya sea almacenando el URI utilizado para crearlo, o obteniéndolo desde launcher.db .
  2. Espere unos 2-3 segundos para que aparezca la tostada “icono eliminado”.

Fuentes

1) Esta guía en viralpatel.net

2) Implementación de Google de UninstallShortcutReceiver.java

3) Este hilo en xdadevelopers

PD

Para simular y depurar una actualización de Google Play (que conserva el antiguo acceso directo), hice lo siguiente:

  1. Instalé la versión anterior de la aplicación en la tienda: un ícono con el “acceso directo antiguo” se colocó automáticamente en mi pantalla.
  2. Respaldado mi launcher.db usando Total Commander.
  3. Instalé la nueva versión a través de mi IDE (también puedes usar una .apk para eso) – el “viejo atajo” ahora se había ido.
  4. Abrió Total Commander y lo minimizó (para que haya un atajo disponible en el menú “ALT-TAB”).
  5. Fui a la configuración del dispositivo >> Aplicaciones >> ALL, encontré mi lanzador (para mí fue “Trebuchet” ya que estoy en CM11) y Force lo detuvo .
  6. ALT-TAB en Total Commander y restaurado el DB.
  7. Hizo clic en el botón “Inicio” del hardware para volver a iniciar el iniciador.
  8. ¡Viola! El antiguo atajo fue restaurado.

Nota 1: en retrospectiva, podría haber sido más fácil crear el acceso directo anterior de forma manual utilizando el URI obtenido de la base de datos en lugar de pasar por toda la prueba de respaldo y parada de fuerza.

Nota 2: No he intentado eliminar icons pertenecientes a otras aplicaciones con este método, pero podría ser lo suficientemente loco como para funcionar.

Si bien se recomienda que ambas soluciones de Dev-iL y Funt lo hagan hasta Marshmallow. Con Android 6.0 (que tiene Launcher v3) Google ha eliminado el UninstallShortcutReceiver debido a sus problemas de seguridad (probablemente porque se puso de manifiesto aquí ). Así que no esperes que funcione con Android 6.0. Esperemos que en una versión futura se vuelva a listar de una forma u otra.

PD: Normalmente esto debería ser un comentario, pero no puedo comentar debido a la reputación …

Debes establecerAction para shortcutIntent directoIntent like:

 shortcutIntent.setAction(Intent.ACTION_MAIN); 

Tratar de usar

 public void removeShortcut(Context context) { Intent intent = new Intent(); intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, "ShortcutTest"); try { Intent shortcutIntent = Intent.parseUri(shortcutUri, 0); intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); } catch (URISyntaxException e) { } intent.setAction("com.android.launcher.action.UNINSTALL_SHORTCUT"); context.sendBroadcast(intent); } 

Nota: No es necesario guardar el shortcutUri para eliminar el atajo. En cambio, puedes usar

 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(); try { intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, Intent.parseUri(shortcutIntent.toUri(0), 0)); } catch (URISyntaxException e) { e.printStackTrace(); } ... intent.setAction("com.android.launcher.action.UNINSTALL_SHORTCUT"); context.sendBroadcast(intent); 

Si desea usar intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); en lugar de

 intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, Intent.parseUri(shortcutIntent.toUri(0), 0)); 

luego, debe establecer la acción para el shortcutIntent cada vez, es decir, durante la instalación y durante la desinstalación, por ejemplo, Intent shortcutIntent = new Intent(Intent.ACTION_MAIN);

Me tomó alrededor de una hora de depuración y probando cada ejemplo en stackoverflow, pero la solución fue muy fácil. Hay un error tipográfico en tu código: necesitas usar com.android.launcher. acción .UNINSTALL_SHORTCUT (a diferencia del permiso, tal como está en el Manifiesto)

 intent.setAction("com.android.launcher.action.UNINSTALL_SHORTCUT"); 

Editar: respuesta modificada