Erkennen Sie das Drücken der Home-Taste in Android

94

Das hat mich schon eine Weile verrückt gemacht.

Gibt es eine Möglichkeit, zuverlässig zu erkennen, ob die Home-Taste in einer Android-Anwendung gedrückt wurde?

Wenn dies nicht der Fall ist, gibt es eine zuverlässige Methode, um festzustellen, warum eine Aktivität in onPause gestartet wurde? dh Können wir feststellen, ob dies durch den Start einer neuen Aktivität oder durch Drücken von Zurück / Nach Hause verursacht wurde?

Ein Vorschlag, den ich gesehen habe, ist, onPause () zu überschreiben und isFinishing () aufzurufen. Dies gibt jedoch false zurück, wenn die Home-Taste gedrückt wird, genau wie beim Starten einer neuen Aktivität, sodass zwischen beiden nicht unterschieden werden kann.

Jede Hilfe sehr geschätzt.

** Update **: Danke an @ android-hungrig für diesen Link: https://nishandroid.blogspot.com/

Überschreiben der folgenden Methode:

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

Dann wird das folgende Ereignis für das Drücken der Home-Taste ausgelöst:

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

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

Ich bin nicht sicher, ob es irgendwelche Nebenwirkungen mit dieser Linie gibt:

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

Es scheint also, dass Sie entgegen der landläufigen Meinung tatsächlich auf den Home-Schlüssel achten können. Besorgniserregend ist, dass Sie false zurückgeben und den Home-Schlüssel nichts tun lassen können.

Update : Wie erwartet gibt es einige Nebenwirkungen - es scheint, dass eingebettete Videos und Google Maps bei aktiviertem Modus nicht sichtbar sind.

Update : Angeblich funktioniert dieser Hack ab Android 4.0 nicht mehr

Dean Wild
quelle
Mein Problem war nicht, mich zwischen Rücken- und Heimschlüssel zu verkleiden, aber ich wollte die Bewerbung in beiden Fällen beenden. Was ich benutzt habe Activity.onUserLeaveHint().
Harismus
Das einzige Problem ist, dass onUserLeaveHint () auch ausgelöst wird, wenn ich eine Aktivität von dieser Aktivität aus starte. Ich möchte nur wissen, ob Zurück oder Start gedrückt wurde. Vielen Dank für den Vorschlag
Dean Wild
Das stimmt, aber leider ist es meines Wissens der einzige Ort, an dem Sie Informationen zur Verwendung von Home-Schlüsseln erhalten. Es ist ein Problem, falsche Positive zu ernten, von vielen kann man leicht erkennen, aber es macht die einfach klingende Aufgabe immer noch ziemlich kompliziert.
Harismus
2
@ DeanWild: Hast du das gelesen: nisha113a5.blogspot.com
Pratik Bhat
2
TYPE_KEYGUARD Konstante wurde aus WindowManager.LayoutParams in Android 5.0 entfernt
theb1uro

Antworten:

136

Der folgende Code funktioniert für mich :)

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
quelle
3
Ihre onHomeLongPressedscheint tatsächlich der Eröffnung der Systemaktivität "Letzte" zu entsprechen. Auf meinem Telefon wird dies durch Drücken der letzten Taste neben der Home-Taste ausgelöst, sodass die Annahme Ihres Codes, dass es sich um eine lange Home-Taste handelt, nicht immer korrekt ist.
Sam
Warum es bei mir nicht funktioniert, habe ich genau das Gleiche getan, außer dass ich die Sendung über das Manifest registriert habe.
Farhan
Eingetragen in der Anwendungsklasse. Bisher gearbeitet. +1, ich frage mich, was der Haken ist. Ich meine, welchen Originalfall würden wir vermissen ..: ^)
Farhan
1
manchmal intent.getStringExtra (SYSTEM_DIALOG_REASON_KEY); return null. Ich würde gerne wissen, was passiert?
Fakher
1
Der lange Pressegrund heißt jetztfinal String SYSTEM_DIALOG_REASON_LONG_PRESS = "assist"
JWqvist
49

Dies ist eine alte Frage, aber sie könnte jemandem helfen.

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

Gemäß der Dokumentation wird die onUserLeaveHint () -Methode aufgerufen, wenn der Benutzer auf die Home-Schaltfläche klickt ODER wenn etwas Ihre Anwendung unterbricht (wie ein eingehender Anruf).

Das funktioniert bei mir .. :)

AL̲̳I
quelle
26
Nicht richtig !!! Das funktioniert zu Hause, wenn die Taste gedrückt wird, aber es funktioniert auch, wenn Aktivität mit Absicht gewechselt wird !!
Nikunj Paradva
Dies wird immer vor onStop () ausgeführt, selbst wenn eine andere Aktivität oben steht oder der Benutzer die Aktivität zwangsweise verlässt oder auf die Home-Schaltfläche klickt ...
Navas pk
7

Es ist unmöglich, die HOME-Schaltfläche in einer Android-App zu erkennen und / oder abzufangen. Dies ist in das System integriert, um schädliche Apps zu verhindern, die nicht beendet werden können.

Jave
quelle
Überprüfen Sie die akzeptierte Antwort, es ist möglich. Auf vielen Geräten noch nicht getestet.
Dean Wild
Ja ... meine App ist abgestürzt.
Jeremy Logan
Was ist mit Launchern / Home-Ersatz-Apps? Ich
baue
Verwenden Sie für Starter Folgendes: @Override protected void onNewIntent (Absichtserklärung) {super.onNewIntent (Absichtserklärung); / * Mach was du willst * /}
Ton
@ lisovaccaro Launcher und / oder Home-Ersatz werden von Google (src diane hackborn) immer noch nicht unterstützt. Daher können Sie den Benutzer nicht daran hindern, auf den Homebutton zu klicken. Sie können Ihre Ansicht dennoch als Systemwarnungsdialog hinzufügen, der alles überlagert. Aber die Home-Taste klickt durch.
JacksOnF1re
7

Ich musste Hintergrundmusik in meiner Anwendung starten / stoppen, wenn die erste Aktivität geöffnet und geschlossen wird oder wenn eine Aktivität über die Home-Schaltfläche angehalten und dann vom Task-Manager fortgesetzt wird. Die reine Wiedergabe wurde angehalten / fortgesetzt Activity.onPause()und Activity.onResume()die Musik für eine Weile unterbrochen, sodass ich den folgenden Code schreiben musste:

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

Dies unterbricht die Wiedergabe nur, wenn die Anwendung (alle ihre Aktivitäten) geschlossen ist oder wenn die Home-Taste gedrückt wird. Leider ist es mir nicht gelungen, die Reihenfolge der Aufrufe der onPause()Methode der onResume()Startaktivität und der gestarteten Aktivität zu ändern, wenn sie Activity.startActivity()aufgerufen wird (oder zu erkennen, onPause()dass in dieser Aktivität eine andere Aktivität auf andere Weise gestartet wird), sodass dieser Fall speziell behandelt werden muss:

private boolean mIsStarting;

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

Ein weiterer Nachteil ist, dass hierfür eine GET_TASKSGenehmigung erforderlich ist AndroidManifest.xml:

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

Das Ändern dieses Codes, der nur beim Drücken der Home-Taste reagiert, ist unkompliziert.

Blackhex
quelle
4

Überschreiben Sie onUserLeaveHint()die Aktivität. Es wird niemals einen Rückruf zu der Aktivität geben, wenn eine neue Aktivität darüber kommt oder der Benutzer auf Zurück drückt.

Napolean
quelle
4
Es wird auch aufgerufen, wenn Sie innerhalb der App von einer Aktivität zur nächsten
wechseln
3

onUserLeaveHint ();

Überschreiben Sie diese Aktivitätsklassenmethode. Dadurch wird das Klicken auf die Home-Taste erkannt. Diese Methode wird direkt vor dem Rückruf von onPause () der Aktivität aufgerufen. Sie wird jedoch nicht aufgerufen, wenn eine Aktivität unterbrochen wird, wie eine Aktivität während eines Anrufs in den Vordergrund tritt. Abgesehen von diesen Unterbrechungen wird sie aufgerufen, wenn der Benutzer auf die Home-Taste klickt.

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

Versuchen Sie, für jeden Bildschirm einen Zähler zu erstellen. Wenn der Benutzer HOME berührt, ist der Zähler Null.

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

public void onStop() {
  super.onStop();
  counter--;    
  if (counter == 0) {
      // Do..
  }
}
lalosoft
quelle
3
Wenn Sie den globalen Anwendungszähler meinen, ist er in einem Moment Null, in dem eine Aktivität in den Backstack und eine andere nach oben verschoben wird oder wenn die Topaktivität beendet ist und die Backstack-Aktivität nach oben verschoben wird. Dies ist der übliche Ort, an dem Sie möchten auf das Drücken der Home-Taste reagieren. Wenn Sie einen aktivitätsweiten Zähler meinen, ist er jedes Mal Null, wenn die Aktivität nicht sichtbar ist (nicht unbedingt durch Drücken der Home-Taste verursacht). Die einzige Lösung wäre, Ihre Reaktion mit einem Timer zu verschieben, um diesen Übergang zu überspringen, aber die erforderliche Verzögerung ist möglicherweise nicht vorhersehbar oder wünschenswert.
Blackhex
2

Sie könnten eine Lösung von Andreas Shrade in seinem Beitrag über das Erstellen eines funktionierenden Kiosk-Modus in Android in Betracht ziehen . Es ist ein bisschen hacky, aber angesichts der Gründe, warum das Abfangen der Home-Taste verhindert wird, muss es sein;)

Vito
quelle
1

Ich hatte dieses Problem und da das Überschreiben der onKeyDown () -Methode nichts bewirkt hat, weil das zugrunde liegende Android-System diese Methode nicht aufgerufen hat, habe ich dies mit dem Überschreiben von onBackPressed () gelöst und dort einen booleschen Wert auf false gesetzt , weil ich zurückgedrückt habe, möchte ich Ihnen zeigen, was ich im Code meine:

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

Der Grund, warum dies funktioniert hat, ist, dass die einzige Möglichkeit, außerhalb dieser Aktivität zu navigieren, darin besteht, zurück oder nach Hause zu drücken. Wenn also zurück gedrückt wurde, weiß ich, dass die Ursache nicht zu Hause war, aber ansonsten war die Ursache zu Hause, daher habe ich den Standard-Booleschen Wert festgelegt Wert für homePressed, um wahr zu sein. Dies funktioniert jedoch nur mit einer einzelnen Aktivitätsinstanz in Ihrer Anwendung, da Sie sonst mehr Möglichkeiten haben, den Aufruf der onPause () -Methode zu veranlassen.

Moshe Rabaev
quelle
Wenn Sie zu einer anderen Aktivität gehen, wird die Onpause-Methode aufgerufen
Fakher
Deshalb habe ich ausdrücklich erklärt, dass dies nur funktioniert, wenn Ihre Anwendung nur eine einzige Aktivität enthält!
Moshe Rabaev
Und was ist, wenn mehrere unabhängige Aktivitäten gleichzeitig ausgeführt werden und der Benutzer einfach zwischen ihnen wechselt? Moderne Android-Geräte unterstützen Multitasking. Mit diesem Code sieht es so aus, als würde das Zurückschalten zu Ihrer App homePressedauf "true" gesetzt, und beim Umschalten auf eine andere App würde "Home" gedrückt, wenn dies nicht der Fall war.
Remy Lebeau
1

Seit API 14 können Sie die Funktion verwenden onTrimMemory()und nach dem Flag suchenTRIM_MEMORY_UI_HIDDEN . Dadurch erfahren Sie, dass Ihre Anwendung in den Hintergrund tritt.

In Ihrer benutzerdefinierten Anwendungsklasse können Sie also Folgendes schreiben:

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

Für eine eingehende Untersuchung lade ich Sie ein, diesen Artikel zu lesen: http://www.developerphil.com/no-you-can-not-override-the-home-button-but-you-dont-have -zu/

Renaud C.
quelle
1
Guter Artikel - nützliche Alternative, die wahrscheinlich das tut, was die meisten Leute brauchen
Dean Wild
Schöne Lösung. Wissen Sie, wie Sie das Flag zurücksetzen, wenn die Anwendung aus dem Hintergrund zurückkehrt? Wenn ich zum Beispiel ein boolesches isInBackground erstelle, möchte ich es auf zurücksetzen, sobald wir vom Hintergrund zurückkehren.
MikeOscarEcho
0

Eine Option für Ihre Anwendung wäre das Schreiben eines Ersatz-Startbildschirms mit der Absicht android.intent.category.HOME. Ich glaube, diese Art von Absicht können Sie den Home-Button sehen.

Mehr Details:

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

ademar111190
quelle
1
interessante Idee, aber etwas langatmig und nicht so elegant, wie ich es mir erhofft hätte
Dean Wild
0

Da Sie nur möchten, dass die Stammaktivität beim Start der App erneut angezeigt wird, können Sie dieses Verhalten möglicherweise durch Ändern der Startmodi usw. im Manifest erreichen.

Haben Sie beispielsweise versucht, das Attribut android: clearTaskOnLaunch = "true" auf Ihre Startaktivität anzuwenden , möglicherweise zusammen mit android: launchMode = "singleInstance" ?

Aufgaben und Back Stack ist eine großartige Ressource für die Feinabstimmung dieser Art von Verhalten.

Josh
quelle
Dies scheint die eleganteste Lösung zu sein, aber ich habe festgestellt, dass sie ziemlich unzuverlässig ist. Nach einigen
Dean Wild
0

Es ist eine schlechte Idee, das Verhalten des Home-Schlüssels zu ändern. Aus diesem Grund können Sie bei Google den Home-Schlüssel nicht überschreiben. Ich würde mich im Allgemeinen nicht mit dem Home-Schlüssel anlegen. Sie müssen dem Benutzer eine Möglichkeit geben, aus Ihrer App herauszukommen, wenn sie aus irgendeinem Grund ins Unkraut gelangt.

Ich würde mir vorstellen, dass jede Umgehung unerwünschte Nebenwirkungen hat.

Andi Jay
quelle
1
Sie sind absolut genau richtig, aber einige Kunden nehmen kein Nein als Antwort und verstehen nicht, warum sie die Richtlinien nicht brechen sollten.
Dean Wild
Das Problem ist, dass Sie, selbst wenn Sie das Verhalten der Home-Taste nicht ändern möchten, gelegentlich anders auf die Situation reagieren müssen, in die die Anwendung aufgrund des Drücken der Home-Taste zurück verschoben wird, als auf die Situation, in der Ihre aktuelle Aktivität für was auch immer angehalten wird Grund. Das gleiche Problem besteht darin, dass Application.onDestroy () nicht für Poduction-Builds verwendet werden kann. Solche Beispiele pausieren ein Spiel, wenn der Benutzer die Anwendung versteckt, Hintergrundmusik stoppt usw.
Blackhex
0

Kürzlich habe ich versucht, die Home-Press-Taste zu erkennen, da ich sie genauso ausführen musste wie die Methode " onBackPressed () ". Dazu musste ich die Methode " onSupportNavigateUp () " wie folgt überschreiben :

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

Es hat perfekt funktioniert. =)

Alexander Cavalheiro Becker
quelle
0

Jacks Antwort funktioniert perfekt für ein clickEreignis, während er longClickals menuKnopfdruck betrachtet wird .

Übrigens, wenn sich jemand fragt, wie man es über Kotlin macht,

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.
quelle
0

Geben Sie hier die Bildbeschreibung ein Android Home Key, der von der Framework-Ebene verwaltet wird, kann auf der Ebene der Anwendungsebene nicht verarbeitet werden. Weil die Home-Button-Aktion bereits in der folgenden Ebene definiert ist. Wenn Sie jedoch Ihr benutzerdefiniertes ROM entwickeln, ist dies möglicherweise möglich. Google hat die Überschreibungsfunktionen der HOME-Taste aus Sicherheitsgründen eingeschränkt.

Stephan J.
quelle