Détecter la pression du bouton d'accueil dans Android

94

Cela me rend dingue depuis un moment maintenant.

Existe-t-il un moyen de détecter de manière fiable si le bouton d'accueil a été enfoncé dans une application Android?

À défaut, y a-t-il un moyen fiable de dire ce qui a provoqué la mise en pause d'une activité? ie Peut-on détecter si elle a été causée par le lancement d'une nouvelle activité ou en appuyant sur back / home.

Une suggestion que j'ai vue est de remplacer onPause () et d'appeler isFinishing (), mais cela retournera false lorsque vous appuyez sur le bouton d'accueil, tout comme si une nouvelle activité démarre, cela ne permet pas de faire la distinction entre les deux.

Toute aide très appréciée.

** Mise à jour **: Merci à @ android-hungry pour ce lien: https://nishandroid.blogspot.com/

Remplacer la méthode suivante:

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

Ensuite, l'événement suivant sera déclenché pour les pressions sur le bouton d'accueil:

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

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

Je ne sais pas s'il y a des effets secondaires avec cette ligne:

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

Il semblerait donc que contrairement à la croyance populaire, vous puissiez en fait écouter la clé de la maison. Fait inquiétant, vous pouvez renvoyer false et laisser la clé d'accueil ne rien faire.

Mise à jour : Comme prévu, cela a des effets secondaires - il semble que les vidéos intégrées et les cartes Google ne soient pas visibles avec ce mode activé.

Mise à jour : supposément, ce hack ne fonctionne plus à partir d'Android 4.0

Dean sauvage
la source
Mon problème n'était pas de me déguiser entre le dos et la touche d'accueil mais je voulais terminer l'application sur les deux cas. Ce que j'ai fait en utilisant Activity.onUserLeaveHint().
harism
Le seul problème est que onUserLeaveHint () se déclenchera également lorsque je démarre une activité à partir de ladite activité, je veux seulement savoir si back ou home a été pressé. Merci pour la suggestion
Dean Wild
C'est vrai, mais malheureusement, pour autant que je sache, c'est le seul endroit pour recevoir des informations sur l'utilisation de la touche Home. Rendre plus difficile la récolte de faux-dépôts, parmi tant d'autres, peut être facilement reconnu, mais cela rend la tâche de sondage facile plutôt compliquée.
harism
2
@DeanWild: avez-vous lu ceci: nisha113a5.blogspot.com
Pratik Bhat
2
La constante TYPE_KEYGUARD a été supprimée de WindowManager.LayoutParams dans Android 5.0
theb1uro

Réponses:

136

Le code suivant fonctionne pour moi :)

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();
}
Jack
la source
3
Votre onHomeLongPressedsemble en fait correspondre à l'ouverture de l'activité système "Récents". Sur mon téléphone, cela se déclenche en appuyant sur le bouton Récents à côté du bouton d'accueil, donc l'hypothèse de votre code selon laquelle il s'agit d'un appui long à domicile n'est pas toujours correcte.
Sam
pourquoi cela ne fonctionne pas pour moi, j'ai fait exactement la même chose sauf enregistré la diffusion via le manifeste.
Farhan le
Enregistré dans la classe d'application, fonctionnant jusqu'à présent .. +1, je me demande quel est le piège? Je veux dire, quel cas original nous manquerait-il ..: ^)
Farhan
1
parfois intent.getStringExtra (SYSTEM_DIALOG_REASON_KEY); retourne null. J'aimerais savoir ce qu'il se passe ??
Fakher
1
La raison de la presse longue est maintenant appeléefinal String SYSTEM_DIALOG_REASON_LONG_PRESS = "assist"
JWqvist
49

C'est une vieille question mais cela pourrait aider quelqu'un.

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

Selon la documentation, la méthode onUserLeaveHint () est appelée lorsque l'utilisateur clique sur le bouton d'accueil OU lorsque quelque chose interrompt votre application (comme un appel téléphonique entrant).

Cela fonctionne pour moi .. :)

AL̲̳I
la source
26
Pas correcte !!! Cela fonctionne lorsque vous appuyez sur le bouton d'accueil, mais cela fonctionne également lorsque vous permutez l'activité avec intention !!
Nikunj Paradva
Cela s'exécutera toujours avant onStop (), même si une autre activité arrive en haut ou que l'utilisateur quitte l'activité de force ou clique sur le bouton d'accueil ...
Navas pk
7

Il est impossible de détecter et / ou d'intercepter le bouton HOME à partir d'une application Android. Ceci est intégré au système pour empêcher les applications malveillantes qui ne peuvent pas être quittées.

Jave
la source
vérifiez la réponse acceptée, c'est possible. Pas encore testé sur de nombreux appareils.
Dean Wild
Ouais ... l'application a planté sur la mienne.
Jeremy Logan
qu'en est-il des lanceurs / applications de remplacement à domicile? J'en construis un et je veux accéder au premier écran lorsque les utilisateurs
cliquent à la
Pour les lanceurs, utilisez ceci: @Override protected void onNewIntent (Intent intent) {super.onNewIntent (intent); / * Fais ce que tu veux * /}
Ton
Le lanceur @lisovaccaro et / ou les remplacements à domicile ne sont toujours pas pris en charge par Google (src diane hackborn) et vous ne pouvez donc pas empêcher l'utilisateur de cliquer sur le bouton d'accueil. Vous pouvez toujours ajouter votre vue en tant que boîte de dialogue d'alerte système, qui superposera tout. Mais les clics du bouton d'accueil passeront par là.
JacksOnF1re
7

J'avais besoin de démarrer / arrêter la musique de fond dans mon application lorsque la première activité s'ouvre et se ferme ou lorsqu'une activité est mise en pause par le bouton d'accueil, puis reprise à partir du gestionnaire de tâches. Lecture pure arrêt / reprise Activity.onPause()et Activity.onResume()interruption de la musique pendant un moment, j'ai donc dû écrire le code suivant:

@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
  }
}

qui interrompt la lecture uniquement lorsque l'application (toutes ses activités) est fermée ou lorsque le bouton d'accueil est enfoncé. Malheureusement je n'ai pas réussi à changer l'ordre des appels de la onPause()méthode de l'activité de départ et onResume()de l'activité commencée quand Activity.startActivity()est appelé (ou détecter dans onPause()cette activité le lancement d'une autre activité d'une autre manière) donc ce cas doit être traité spécialement:

private boolean mIsStarting;

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

Un autre inconvénient est que cela nécessite une GET_TASKSautorisation ajoutée à AndroidManifest.xml:

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

Modifier ce code pour qu'il ne réagisse qu'à la pression du bouton d'accueil est simple.

Blackhex
la source
4

Remplacer onUserLeaveHint()dans l'activité. Il n'y aura jamais de rappel de l'activité lorsqu'une nouvelle activité survient ou que l'utilisateur appuie sur.

Napoléon
la source
4
on l'appelle aussi lors du passage d'une activité à une autre dans l'application
Amir Uval
3

onUserLeaveHint ();

écraser cette méthode de classe d'activité. Cela détectera le clic sur la touche d'accueil. Cette méthode est appelée juste avant le rappel onPause () de l'activité, mais elle ne sera pas appelée lorsqu'une activité est interrompue comme une activité en cours d'appel entre au premier plan, à part les interruptions qu'elle appellera lorsque l'utilisateur clique sur la touche d'accueil.

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

Essayez de créer un compteur pour chaque écran. Si l'utilisateur touche HOME, le compteur sera zéro.

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

public void onStop() {
  super.onStop();
  counter--;    
  if (counter == 0) {
      // Do..
  }
}
lalosoft
la source
3
Si vous voulez dire le compteur d'application global, il sera égal à zéro à un moment où une activité est déplacée vers la pile arrière et une autre est déplacée vers le haut ou lorsque l'activité supérieure est terminée et l'activité de la pile arrière est déplacée vers le haut, ce qui est l'endroit habituel lorsque vous souhaitez réagir en appuyant sur le bouton d'accueil. Si vous voulez dire un compteur à l'échelle de l'activité, il sera nul chaque fois que l'activité n'est pas visible (pas nécessairement causée par la pression du bouton d'accueil). La seule solution serait de reporter votre réaction en utilisant le minuteur pour sauter cette transition, mais le délai nécessaire peut ne pas être prévisible ou souhaitable.
Blackhex
2

Vous pourriez envisager une solution d'Andreas Shrade dans son article sur Comment créer un mode kiosque fonctionnel sous Android . C'est un peu piraté, mais étant donné les raisons pour lesquelles l'interception du bouton d'accueil est empêchée, il doit l'être;)

Vito
la source
1

J'ai eu ce problème, et comme le remplacement de la méthode onKeyDown () n'a rien accompli car le système Android sous-jacent n'a pas appelé cette méthode, j'ai résolu le problème en remplaçant onBackPressed (), et j'avais une valeur booléenne définie sur false , parce que j'ai appuyé, laissez-moi vous montrer ce que je veux dire dans le code:

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

Donc, la raison pour laquelle cela a fonctionné est parce que le seul moyen de naviguer en dehors de cette activité est d'appuyer sur retour ou à la maison, donc si retour a été appuyé, je sais que la cause n'était pas à la maison, mais sinon la cause était à la maison, donc je définis le booléen par défaut value for home Pressed pour être vrai. Cependant, cela ne fonctionnera qu'avec une seule instance d'activité dans votre application, car sinon, vous avez plus de possibilités de provoquer l'appel de la méthode onPause ().

Moshe Rabaev
la source
en fait, lorsque vous allez à une autre activité, la méthode onpause est appelée
Fakher
C'est pourquoi j'ai explicitement déclaré que cela ne fonctionnera que si votre application n'intègre qu'une seule activité!
Moshe Rabaev
Et si plusieurs activités non liées s'exécutent en même temps et que l'utilisateur bascule simplement entre elles? Les appareils Android modernes prennent en charge le multitâche. Avec ce code, il semble que le retour à votre application serait défini homePressedsur true, puis le passage à une autre application penserait que Accueil a été enfoncé alors que ce n'était vraiment pas le cas.
Remy Lebeau
1

Depuis l'API 14, vous pouvez utiliser la fonction onTrimMemory()et vérifier l'indicateur TRIM_MEMORY_UI_HIDDEN. Cela vous indiquera que votre application passe en arrière-plan.

Ainsi, dans votre classe Application personnalisée, vous pouvez écrire quelque chose comme:

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

Pour une étude approfondie de cela, je vous invite à lire cet article: http://www.developerphil.com/no-you-can-not-override-the-home-button-but-you-dont-have -à/

Renaud C.
la source
1
Bon article - alternative utile qui fait probablement ce dont la plupart des gens ont besoin
Dean Wild
Belle solution. Savez-vous comment réinitialiser l'indicateur lorsque l'application revient de l'arrière-plan? Par exemple, si je crée un isInBackground booléen, je voudrais le réinitialiser une fois que nous revenons de l'arrière-plan.
MikeOscarEcho
0

Une option pour votre application serait d'écrire un écran d'accueil de remplacement à l'aide de l'intention android.intent.category.HOME. Je crois que ce type d'intention, vous pouvez voir le bouton d'accueil.

Plus de détails:

http://developer.android.com/guide/topics/intents/intents-filters.html#imatch

ademar111190
la source
1
idée intéressante mais un peu longue et pas aussi élégante que je l'aurais espéré
Dean Wild
0

Étant donné que vous souhaitez uniquement que l'activité racine soit à nouveau affichée lorsque l'application est lancée, vous pouvez peut-être obtenir ce comportement en modifiant les modes de lancement, etc. dans le manifeste?

Par exemple, avez-vous essayé d'appliquer l' attribut android: clearTaskOnLaunch = "true" à votre activité de lancement, peut-être en tandem avec android: launchMode = "singleInstance" ?

Tasks and Back Stack est une excellente ressource pour affiner ce type de comportement.

Josh
la source
Cela semble être la solution la plus élégante, mais je l'ai trouvée assez peu fiable. Après quelques cycles d'ouverture / fermeture / pause, l'application commencera à reprendre plutôt qu'à redémarrer complètement
Dean Wild
0

Changer le comportement de la clé d'accueil est une mauvaise idée. C'est pourquoi Google ne vous permet pas de remplacer la clé d'accueil. Je ne voudrais pas jouer avec la clé de la maison en général. Vous devez donner à l'utilisateur un moyen de sortir de votre application si elle s'enfonce dans les mauvaises herbes pour une raison quelconque.

J'imaginerais que tout travail aura des effets secondaires indésirables.

Andi Jay
la source
1
Vous êtes absolument parfait, mais certains clients n'accepteront pas de réponse et ne comprennent pas pourquoi ils ne devraient pas enfreindre les directives.
Dean Wild
Le problème est que même si vous ne voulez pas modifier le comportement du bouton d'accueil, vous devez parfois réagir différemment à la situation dans laquelle l'application est déplacée en arrière en raison de la pression sur le bouton d'accueil différemment de la situation dans laquelle votre activité actuelle est suspendue pendant toute la durée. raison. Le même problème vient du fait que Application.onDestroy () ne peut pas être utilisé pour les builds de poduction. De tels exemples mettent en pause un jeu lorsque l'utilisateur cache l'appication, arrête la musique de fond, etc.
Blackhex
0

Récemment, j'essayais de détecter le bouton home press, car j'avais besoin qu'il fasse la même chose que la méthode " onBackPressed () ". Pour ce faire, j'ai dû remplacer la méthode " onSupportNavigateUp () " comme ceci:

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

Cela a parfaitement fonctionné. =)

Alexander Cavalheiro Becker
la source
0

La réponse de Jack fonctionne parfaitement pour l' clickévénement tout en longClickenvisageant de menucliquer sur un bouton.

Au fait, si quelqu'un se demande comment faire via 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()
                }
            }
        }
    } 
}
Aditya S.
la source
0

entrez la description de l'image ici Android Home Key géré par la couche de structure, vous ne pouvez pas gérer cela au niveau de la couche d'application. Parce que l'action du bouton d'accueil est déjà définie dans le niveau ci-dessous. Mais si vous développez votre ROM personnalisée, cela pourrait être possible. Google a limité les fonctions de remplacement du BOUTON HOME pour des raisons de sécurité.

Stéphan J
la source