Detectar la pulsación del botón de inicio en Android


94

Esto me ha estado volviendo loco por un tiempo.

¿Hay alguna forma de detectar de manera confiable si se presionó el botón de inicio en una aplicación de Android?

De no ser así, ¿existe una forma sólida de saber qué provocó que una actividad entrara en pausa? es decir, ¿podemos detectar si fue causado por el inicio de una nueva actividad o presionando back / home?

Una sugerencia que he visto es anular onPause () y llamar a isFinishing (), pero esto devolverá falso al presionar el botón de inicio, tal como lo haría si se inicia una nueva actividad, por lo que esto no distingue entre los dos.

Cualquier ayuda muy apreciada.

** Actualización **: Gracias a @ android-hungry por este enlace: https://nishandroid.blogspot.com/

Anulando el siguiente método:

@Override
public void onAttachedToWindow() {
    super.onAttachedToWindow();
    this.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD);           
}

Luego, el siguiente evento se activará al presionar el botón de inicio:

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {     

    if(keyCode == KeyEvent.KEYCODE_HOME)
    {
       //The Code Want to Perform. 
    }
});

No estoy seguro de si hay efectos secundarios con esta línea:

this.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD);   

Por lo que parecería que, contrariamente a la creencia popular, de hecho puede escuchar la tecla de inicio. Es preocupante que pueda devolver false y que la tecla de inicio no haga nada.

Actualización : como se esperaba, hay algunos efectos secundarios con esto: parece que los videos incrustados y los mapas de Google no son visibles con este modo habilitado.

Actualización : supuestamente este truco ya no funciona a partir de Android 4.0 en adelante


Mi problema no era disfrazarme entre la tecla de retorno y la de inicio, pero quería terminar la aplicación en ambos casos. Que hice usando Activity.onUserLeaveHint().
harism

El único problema es que onUserLeaveHint () también se activará cuando inicie una actividad desde dicha actividad, solo quiero saber si se ha presionado Atrás o Inicio. Sin embargo, gracias por la sugerencia
Dean Wild

Eso es cierto, pero desafortunadamente, hasta donde yo sé, es el único lugar para recibir información sobre el uso de la tecla de inicio. Haciendo que sea más un problema cosechar falsos positivos, de muchos se pueden reconocer fácilmente, pero aún así hace que la tarea parezca fácil y bastante complicada.
harism

2
@DeanWild: ¿Leíste esto ?: nisha113a5.blogspot.com
Pratik Bhat

2
La constante TYPE_KEYGUARD se eliminó de WindowManager.LayoutParams en Android 5.0
theb1uro

Respuestas:


136

El siguiente código funciona para mí :)

HomeWatcher mHomeWatcher = new HomeWatcher(this);
mHomeWatcher.setOnHomePressedListener(new OnHomePressedListener() {
    @Override
    public void onHomePressed() {
        // do something here...
    }
    @Override
    public void onHomeLongPressed() {
    }
});
mHomeWatcher.startWatch();
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.util.Log;

public class HomeWatcher {

    static final String TAG = "hg";
    private Context mContext;
    private IntentFilter mFilter;
    private OnHomePressedListener mListener;
    private InnerReceiver mReceiver;

    public HomeWatcher(Context context) {
        mContext = context;
        mFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
    }

    public void setOnHomePressedListener(OnHomePressedListener listener) {
        mListener = listener;
        mReceiver = new InnerReceiver();
    }

    public void startWatch() {
        if (mReceiver != null) {
            mContext.registerReceiver(mReceiver, mFilter);
        }
    }

    public void stopWatch() {
        if (mReceiver != null) {
            mContext.unregisterReceiver(mReceiver);
        }
    }

    class InnerReceiver extends BroadcastReceiver {
        final String SYSTEM_DIALOG_REASON_KEY = "reason";
        final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
        final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
        final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (action.equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) {
                String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
                if (reason != null) {
                    Log.e(TAG, "action:" + action + ",reason:" + reason);
                    if (mListener != null) {
                        if (reason.equals(SYSTEM_DIALOG_REASON_HOME_KEY)) {
                            mListener.onHomePressed();
                        } else if (reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
                            mListener.onHomeLongPressed();
                        }
                    }
                }
            }
        }
    }
}
public interface OnHomePressedListener {
    void onHomePressed();
    void onHomeLongPressed();
}

3
En onHomeLongPressedrealidad, parece corresponder a la apertura de la actividad del sistema "Recientes". En mi teléfono, eso se activa al presionar el botón recientes al lado del botón de inicio, por lo que la suposición de su código de que es una pulsación larga en el hogar no siempre es correcta.
Sam

por qué no funciona para mí, hice exactamente lo mismo, excepto que registré la transmisión a través del manifiesto.
Farhan

Registrado en la clase de aplicación, Trabajando hasta ahora .. +1, me pregunto cuál es el truco? Quiero decir, ¿qué caso original nos faltaría? ...: ^)
Farhan

1
a veces intent.getStringExtra (SYSTEM_DIALOG_REASON_KEY); devuelve nulo. Me gustaria saber que esta pasando ??
Fakher

1
La razón de la prensa larga ahora se llamafinal String SYSTEM_DIALOG_REASON_LONG_PRESS = "assist"
JWqvist

49

Esta es una vieja pregunta, pero podría ayudar a alguien.

@Override
protected void onUserLeaveHint()
{
    Log.d("onUserLeaveHint","Home button pressed");
    super.onUserLeaveHint();
}

De acuerdo con la documentación, el método onUserLeaveHint () se llama cuando el usuario hace clic en el botón de inicio O cuando algo interrumpe su aplicación (como una llamada telefónica entrante).

Esto funciona para mí .. :)


26
Incorrecto !!! ¡Eso funciona en el botón de inicio presionado, pero también funciona cuando se cambia la actividad con intención!
Nikunj Paradva

Esto se ejecutará antes de onStop () siempre, incluso si otra actividad aparece en la parte superior o si el usuario abandona la actividad a la fuerza o hace clic en el botón de inicio ...
Navas pk

7

Es imposible detectar y / o interceptar el botón INICIO desde una aplicación de Android. Está integrado en el sistema para evitar aplicaciones maliciosas de las que no se puede salir.


compruebe la respuesta aceptada, es posible. Sin embargo, aún no se ha probado en muchos dispositivos.
Dean Wild

Sí ... la aplicación falló en la mía.
Jeremy Logan

¿Qué pasa con los lanzadores / aplicaciones de reemplazo del hogar? Estoy construyendo uno y quiero ir a la primera pantalla cuando los usuarios
hagan

Para los lanzadores, use esto: @Override protected void onNewIntent (Intent intent) {super.onNewIntent (intent); / * Haz lo que quieras * /}
Ton

El lanzador de @lisovaccaro y los reemplazos de inicio todavía no son compatibles con google (src diane hackborn) y, por lo tanto, no puede evitar que el usuario haga clic en el botón de inicio. Aún puede agregar su vista como un cuadro de diálogo de alerta del sistema, que se superpondrá a todo. Pero los clics del botón de inicio lo atravesarán.
JacksOnF1re

7

Necesitaba iniciar / detener la música de fondo en mi aplicación cuando la primera actividad se abre y se cierra o cuando cualquier actividad se detiene con el botón de inicio y luego se reanuda desde el administrador de tareas. La reproducción pura se detuvo / reanudó Activity.onPause()e Activity.onResume()interrumpió la música por un tiempo, así que tuve que escribir el siguiente código:

@Override
public void onResume() {
  super.onResume();

  // start playback here (if not playing already)
}

@Override
public void onPause() {
  super.onPause();

  ActivityManager manager = (ActivityManager) this.getSystemService(Activity.ACTIVITY_SERVICE);
  List<ActivityManager.RunningTaskInfo> tasks = manager.getRunningTasks(Integer.MAX_VALUE);
  boolean is_finishing = this.isFinishing();
  boolean is_last = false;
  boolean is_topmost = false;
  for (ActivityManager.RunningTaskInfo task : tasks) {
    if (task.topActivity.getPackageName().startsWith("cz.matelier.skolasmyku")) {
      is_last = task.numRunning == 1;
      is_topmost = task.topActivity.equals(this.getComponentName());
      break;
    }
  }

  if ((is_finishing && is_last) || (!is_finishing && is_topmost && !mIsStarting)) {
    mIsStarting = false;
    // stop playback here
  }
}

que interrumpe la reproducción solo cuando la aplicación (todas sus actividades) está cerrada o cuando se presiona el botón de inicio. Lamentablemente no logré cambiar el orden de las llamadas del onPause()método de la actividad de inicio y onResume()de la actividad iniciada cuando Activity.startActivity()se llama (o detectar en onPause()esa actividad está lanzando otra actividad de otra manera) por lo que este caso debe manejarse especialmente:

private boolean mIsStarting;

@Override
public void startActivity(Intent intent) {
  mIsStarting = true;
  super.startActivity(intent);
}

Otro inconveniente es que esto requiere GET_TASKSpermiso agregado a AndroidManifest.xml:

<uses-permission
  android:name="android.permission.GET_TASKS"/>

Modificar este código para que solo reaccione al presionar el botón de inicio es sencillo.


4

Anular onUserLeaveHint()en la actividad. Nunca habrá ninguna devolución de llamada a la actividad cuando se presente una nueva actividad o el usuario presione para retroceder.


4
también se llama también cuando se pasa de una actividad a otra dentro de la aplicación
Amir Uval

3

onUserLeaveHint ();

anular este método de clase de actividad. Esto detectará el clic de la tecla de inicio. Este método se llama justo antes de la devolución de llamada onPause () de la actividad, pero no se llamará cuando una actividad se interrumpa como una actividad en llamada entra en primer plano, aparte de las interrupciones, llamará cuando el usuario haga clic en la tecla de inicio.

@Override
protected void onUserLeaveHint() {
    super.onUserLeaveHint();
    Log.d(TAG, "home key clicked");
}

2

Intente crear un contador para cada pantalla. Si el usuario toca INICIO, el contador será cero.

public void onStart() {
  super.onStart();
  counter++;
}

public void onStop() {
  super.onStop();
  counter--;    
  if (counter == 0) {
      // Do..
  }
}

3
Si se refiere al contador de aplicaciones global, será cero en un momento en que una actividad se mueve a la pila de actividades y otra se mueve a la parte superior o cuando la actividad de la parte superior se termina y la actividad de la pila de actividades se mueve a la parte superior, que es el lugar habitual cuando desea reaccionar al presionar el botón de inicio. Si te refieres al contador de toda la actividad, será cero cada vez que la actividad no sea visible (no necesariamente debido a la presión del botón de inicio). La única solución sería posponer su reacción usando el temporizador para omitir esta transición, pero el retraso necesario puede no ser predecible o deseable.
Blackhex


1

Tuve este problema, y ​​dado que anular el método onKeyDown () no logró nada debido a que el sistema Android subyacente no llamó a este método, lo resolví anulando onBackPressed (), y tenía un valor booleano establecido en falso , porque presioné hacia atrás, déjame mostrarte lo que quiero decir en el código:

import android.util.Log;
public class HomeButtonActivity extends Activity {
    boolean homePressed = false;
    // override onCreate() here.

    @Override
    public void onBackPressed() {
        homePressed = false; // simply set homePressed to false
    }

    @Overide
    public void onResume() {
        super.onResume();
        homePressed = true; // default: other wise onBackPressed will set it to false
    }

    @Override
    public void onPause() {
        super.onPause();
        if(homePressed) { Log.i("homePressed", "yay"); }
    }

Entonces, la razón por la que esto funcionó es porque la única forma de navegar fuera de esta actividad es presionando Atrás o Inicio, por lo que si se presionó Atrás, sé que la causa no fue el hogar, pero de lo contrario, la causa fue el hogar, por lo tanto, establecí el booleano predeterminado. valor para homePressed para ser verdad. Sin embargo, esto solo funcionará con una única instancia de actividad en su aplicación porque, de lo contrario, tiene más posibilidades de hacer que se llame al método onPause ().


de hecho, cuando vas a otra actividad, se llama al método de pausa
Fakher

¡Es por eso que dije explícitamente que esto solo funcionará si su aplicación solo incorpora una sola actividad!
Moshe Rabaev

¿Y si se están ejecutando varias actividades no relacionadas al mismo tiempo y el usuario simplemente está cambiando entre ellas? Los dispositivos Android modernos admiten la multitarea. Con este código, parece que volver a su aplicación se establecería homePresseden verdadero, y luego cambiar a otra aplicación pensaría que se presionó Inicio cuando en realidad no lo estaba.
Remy Lebeau

1

Desde API 14, puede usar la función onTrimMemory()y verificar la bandera TRIM_MEMORY_UI_HIDDEN. Esto le dirá que su aplicación va a pasar a segundo plano.

Entonces, en su clase de aplicación personalizada, puede escribir algo como:

override fun onTrimMemory(level: Int) {
    if (level == TRIM_MEMORY_UI_HIDDEN) {
        // Application going to background, do something
    }
}

Para un estudio en profundidad de esto, lo invito a leer este artículo: http://www.developerphil.com/no-you-can-not-override-the-home-button-but-you-dont-have -a/


1
Buen artículo - alternativa útil que probablemente hace lo que la mayoría de la gente necesita
Dean Wild

Buena solucion. ¿Sabes cómo restablecer la bandera cuando la aplicación regresa del fondo? Por ejemplo, si creo un isInBackground booleano, me gustaría restablecerlo una vez que regresemos del fondo.
MikeOscarEcho


0

Dado que solo desea que se vuelva a mostrar la actividad raíz cuando se inicie la aplicación, ¿tal vez pueda obtener este comportamiento cambiando los modos de inicio, etc.en el manifiesto?

Por ejemplo, ¿ha intentado aplicar el atributo android: clearTaskOnLaunch = "true" a su actividad de lanzamiento, tal vez en conjunto con android: launchMode = "singleInstance" ?

Tasks and Back Stack es un gran recurso para ajustar este tipo de comportamiento.


Esta parecería la solución más elegante, pero la he encontrado bastante poco confiable. Después de algunos ciclos de apertura / cierre / pausa, la aplicación comenzará a reanudarse en lugar de reiniciarse por completo
Dean Wild

0

Es una mala idea cambiar el comportamiento de la tecla de inicio. Es por eso que Google no le permite anular la tecla de inicio. En general, no jugaría con la tecla de inicio. Debe brindarle al usuario una forma de salir de su aplicación si se pierde en la maleza por cualquier motivo.

Imagino que cualquier solución tendrá efectos secundarios no deseados.


1
Usted es absolutamente acertado, pero algunos clientes no aceptarán un no por respuesta y no entienden por qué no deben romper las pautas.
Dean Wild

El problema es que incluso si no desea cambiar el comportamiento del botón de inicio, ocasionalmente tiene que reaccionar de manera diferente en la situación en la que la aplicación se mueve hacia atrás debido a que se presiona el botón de inicio de manera diferente que en la situación en la que su actividad actual se detiene durante todo el tiempo. razón. El mismo problema es el hecho de que Application.onDestroy () no se puede usar para compilaciones de poduction. Tales ejemplos son pausar un juego cuando el usuario oculta la aplicación, detener la música de fondo, etc.
Blackhex

0

Recientemente estaba tratando de detectar el botón de inicio, porque necesitaba que hiciera lo mismo que el método " onBackPressed () ". Para hacer esto, tuve que anular el método " onSupportNavigateUp () " así:

override fun onSupportNavigateUp(): Boolean {
    onBackPressed()
    return true
}

Funcionó perfectamente. =)


0

La respuesta de Jack funciona perfectamente para el clickevento, mientras longClickque la considera como menuhacer clic en un botón.

Por cierto, si alguien se pregunta cómo hacerlo a través de kotlin,

class HomeButtonReceiver(private var context: Context,private var listener: OnHomeButtonClickListener) {
    private val mFilter: IntentFilter = IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
    private var mReceiver: InnerReceiver = InnerReceiver()

    fun startWatch() {
        context.registerReceiver(mReceiver, mFilter)
    }

    fun stopWatch() {
        context.unregisterReceiver(mReceiver)
    }

    inner class InnerReceiver: BroadcastReceiver() {
        private val systemDialogReasonKey = "reason"
        private val systemDialogReasonHomeKey = "homekey"
        override fun onReceive(context: Context?, intent: Intent?) {
            val action = intent?.action
            if (action == Intent.ACTION_CLOSE_SYSTEM_DIALOGS) {
                val reason = intent.getStringExtra(systemDialogReasonKey)
                if (reason != null && reason == systemDialogReasonHomeKey) {
                    listener.onHomeButtonClick()
                }
            }
        }
    } 
}

0

ingrese la descripción de la imagen aquí La clave de inicio de Android manejada por la capa de marco no puede manejar esto en el nivel de capa de aplicación. Porque la acción del botón de inicio ya está definida en el nivel inferior. Pero si está desarrollando su ROM personalizada, entonces podría ser posible. Google restringió las funciones de anulación del BOTÓN INICIO por razones de seguridad.


Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.