Wykryj naciśnięcie przycisku Home w Androidzie

94

To już od jakiegoś czasu doprowadza mnie do szału.

Czy istnieje sposób na niezawodne wykrycie, czy przycisk Home został naciśnięty w aplikacji na Androida?

Jeśli to się nie powiedzie, czy istnieje skuteczny sposób na stwierdzenie, co spowodowało rozpoczęcie działania onPause? tj. czy możemy wykryć, czy było to spowodowane uruchomieniem nowej aktywności lub naciśnięciem przycisku wstecz / home.

Jedną z sugestii, które widziałem, jest zastąpienie onPause () i wywołanie isFinishing (), ale po naciśnięciu przycisku home zwróci to fałsz, tak jak w przypadku rozpoczęcia nowej czynności, więc nie rozróżnia tych dwóch.

Każda pomoc bardzo doceniona.

** Aktualizacja **: dzięki @ android-hungry za ten link: https://nishandroid.blogspot.com/

Pomijając następującą metodę:

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

Następnie następujące zdarzenie zostanie zwolnione z powodu naciśnięcia przycisku głównego:

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

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

Nie jestem pewien, czy są jakieś skutki uboczne z tą linią:

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

Wydawałoby się więc, że wbrew powszechnemu przekonaniu można faktycznie posłuchać klucza domowego. Niepokojące jest to, że możesz zwrócić fałsz i pozwolić, aby klucz domowy nic nie robił.

Aktualizacja : zgodnie z oczekiwaniami są pewne skutki uboczne - wygląda na to, że osadzone filmy i mapy Google nie są widoczne przy włączonym tym trybie.

Aktualizacja : podobno ten hack nie działa już od Androida 4.0

Dean Wild
źródło
Moim problemem nie było zamaskowanie klucza wstecz i do domu, ale chciałem dokończyć aplikację w obu przypadkach. Którego użyłem Activity.onUserLeaveHint().
harizm
Jedynym problemem jest to, że onUserLeaveHint () również zostanie uruchomiony, gdy zacznę działanie z wymienionej aktywności, chcę tylko wiedzieć, czy naciśnięto przycisk powrotu lub domu. Jednak dzięki za sugestię
Dean Wild
To prawda, ale niestety, o ile wiem, jest to jedyne miejsce, w którym można uzyskać jakiekolwiek informacje na temat użycia klucza Home. Co sprawia, że ​​wyłapywanie fałszywie pozytywnych wyników jest większym problemem, z wielu można je łatwo rozpoznać, ale nadal sprawia, że ​​łatwo brzmiące zadanie jest raczej skomplikowane.
harizm
2
@DeanWild: czy czytałeś to: nisha113a5.blogspot.com
Pratik Bhat
2
Stała TYPE_KEYGUARD została usunięta z WindowManager.LayoutParams w systemie Android 5.0
theb1uro

Odpowiedzi:

136

Poniższy kod działa u mnie :)

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();
}
Jacek
źródło
3
onHomeLongPressedWygląda na to, że Twój faktycznie odpowiada otwarciu aktywności systemu „Ostatnie”. Na moim telefonie jest to wyzwalane przez naciśnięcie przycisku Ostatnie obok przycisku strony głównej, więc założenie w kodzie, że jest to długie naciśnięcie w domu, nie zawsze jest poprawne.
Sam
dlaczego to nie działa dla mnie, zrobiłem dokładnie to samo, z wyjątkiem zarejestrowania transmisji przez manifest.
Farhan
Zarejestrowany w klasie aplikacji, działający do tej pory .. +1, ciekawe co to za haczyk? To znaczy, jakiego oryginalnego przypadku by nam brakowało ..: ^)
Farhan
1
czasami intent.getStringExtra (SYSTEM_DIALOG_REASON_KEY); return null. Chciałbym wiedzieć, co się dzieje?
Fakher
1
Powód długiej prasy jest teraz nazywanyfinal String SYSTEM_DIALOG_REASON_LONG_PRESS = "assist"
JWqvist
49

To stare pytanie, ale może komuś pomóc.

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

Zgodnie z dokumentacją metoda onUserLeaveHint () jest wywoływana, gdy użytkownik kliknie przycisk strony głównej LUB coś zakłóci działanie aplikacji (np. Przychodzące połączenie telefoniczne).

To działa dla mnie .. :)

AL̲̳I
źródło
26
Niepoprawne !!! Działa to po naciśnięciu przycisku głównego, ale działa również, gdy działanie przełączania z zamiarem !!
Nikunj Paradva,
Wykona się to zawsze przed onStop (), nawet jeśli na górze pojawi się inna aktywność lub użytkownik siłą opuści aktywność lub kliknie przycisk strony głównej ...
Navas pk
7

Niemożliwe jest wykrycie i / lub przechwycenie przycisku HOME z poziomu aplikacji na Androida. Jest to wbudowane w system, aby zapobiec złośliwym aplikacjom, których nie można zamknąć.

Jave
źródło
sprawdź zaakceptowaną odpowiedź, jest to możliwe. Nie testowano jeszcze na wielu urządzeniach.
Dean Wild
Tak ... zawiesiła się aplikacja na mojej.
Jeremy Logan
a co z wyrzutniami / aplikacjami do wymiany domu?
Buduję
W przypadku Launcherów użyj tego: @Override protected void onNewIntent (zamiar zamiaru) {super.onNewIntent (zamiar); / * Rób, co chcesz * /}
Ton
Program uruchamiający @lisovaccaro i / lub zamienniki domu nadal nie są obsługiwane przez Google (src diane hackborn), dlatego nie można uniemożliwić użytkownikowi kliknięcia przycisku home. Nadal możesz dodać swój widok jako okno dialogowe z ostrzeżeniem systemowym, które pokryje wszystko. Ale kliknięcia przycisku głównego będą przez to przechodzić.
JacksOnF1re
7

Musiałem uruchomić / zatrzymać muzykę w tle w mojej aplikacji, gdy pierwsze działanie jest otwierane i zamykane lub gdy jakakolwiek czynność jest wstrzymywana przyciskiem Home, a następnie wznawiana z poziomu menedżera zadań. Czyste odtwarzanie zatrzymało się / wznowiło Activity.onPause()i Activity.onResume()przerwało na chwilę muzykę, więc musiałem napisać następujący kod:

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

która przerywa odtwarzanie tylko wtedy, gdy aplikacja (wszystkie jej działania) jest zamknięta lub gdy wciśnięty jest przycisk home. Niestety nie udało mi się zmienić kolejności wywołań onPause()metody czynności startowej i czynności onResume()rozpoczętej, gdy Activity.startActivity()jest wywoływana (lub wykryć w onPause()tej czynności inną aktywność), więc ten przypadek wymaga specjalnej obsługi:

private boolean mIsStarting;

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

Inną wadą jest to, że wymaga to GET_TASKSuprawnień dodanych do AndroidManifest.xml:

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

Modyfikacja tego kodu, tak aby reagował tylko na naciśnięcie przycisku głównego, jest prosta.

Blackhex
źródło
4

Zastąp onUserLeaveHint()w działaniu. Nigdy nie będzie żadnego oddzwaniania do działania, gdy nadejdzie nowe działanie lub użytkownik naciśnie przycisk wstecz.

Napolean
źródło
4
jest również wywoływany podczas przechodzenia od jednej czynności do drugiej w aplikacji
Amir Uval
3

onUserLeaveHint ();

zastępuje tę metodę klasy aktywności, co spowoduje wykrycie kliknięcia klawisza głównego. Ta metoda jest wywoływana tuż przed wywołaniem zwrotnym onPause () działania, ale nie będzie wywoływana, gdy działanie zostanie przerwane, tak jak działanie w wywołaniu, poza tym, że będzie wywoływane, gdy użytkownik kliknie klawisz home.

@Override
protected void onUserLeaveHint() {
    super.onUserLeaveHint();
    Log.d(TAG, "home key clicked");
}
Basheer Kohli
źródło
2

Spróbuj utworzyć licznik dla każdego ekranu. Jeśli użytkownik dotknie HOME, licznik będzie wynosił zero.

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

public void onStop() {
  super.onStop();
  counter--;    
  if (counter == 0) {
      // Do..
  }
}
lalosoft
źródło
3
Jeśli masz na myśli globalny licznik aplikacji, będzie to zero w momencie, gdy jedna czynność jest przenoszona na tylny stos, a inna jest przenoszona na górę lub gdy kończy się najwyższa aktywność i aktywność tylnego stosu jest przenoszona na szczyt, co jest zwykle miejscem, w którym chcesz reagować na naciśnięcie przycisku Home. Jeśli masz na myśli licznik obejmujący całą aktywność, będzie wynosić zero za każdym razem, gdy aktywność nie będzie widoczna (niekoniecznie spowodowana naciśnięciem przycisku strony głównej). Jedynym rozwiązaniem byłoby odłożenie reakcji za pomocą timera, aby pominąć to przejście, ale niezbędne opóźnienie może nie być przewidywalne lub pożądane.
Blackhex
2

Możesz rozważyć rozwiązanie Andreasa Shrade'a w jego poście na temat tworzenia działającego trybu kiosku w systemie Android . To trochę zepsute, ale biorąc pod uwagę powody, dla których zapobiega się przechwyceniu przycisku Home, musi tak być;)

Vito
źródło
1

Miałem ten problem, a ponieważ nadpisanie metody onKeyDown () nic nie dało, ponieważ podstawowy system Android nie wywołał tej metody, rozwiązałem to przez nadpisanie onBackPressed () i miałem ustawioną tam wartość logiczną na false , ponieważ nacisnąłem, pokażę Ci, co mam na myśli w kodzie:

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

Powodem, dla którego to zadziałało, jest to, że jedynym sposobem na poruszanie się poza tą czynnością jest naciśnięcie przycisku wstecz lub do domu, więc jeśli naciśnięto przycisk wstecz, wiem, że przyczyną nie było w domu, ale poza tym przyczyną był dom, dlatego ustawiam domyślną wartość logiczną wartość dla domuPressed, aby było prawdziwe. Jednak zadziała to tylko z pojedynczą instancją działania w aplikacji, ponieważ w przeciwnym razie masz więcej możliwości spowodowania wywołania metody onPause ().

Moshe Rabaev
źródło
w rzeczywistości, kiedy przechodzisz do innego działania, metoda onpause nazywa się
Fakher
Dlatego wyraźnie powiedziałem, że zadziała to tylko wtedy, gdy aplikacja zawiera tylko jedną czynność!
Moshe Rabaev,
A co, jeśli w tym samym czasie działa wiele niezwiązanych ze sobą działań, a użytkownik po prostu przełącza się między nimi? Nowoczesne urządzenia z Androidem obsługują wielozadaniowość. W przypadku tego kodu wygląda na to, że przełączenie z powrotem do aplikacji byłoby ustawione homePressedna wartość true, a następnie przełączenie do innej aplikacji mogłoby pomyśleć, że przycisk Home został naciśnięty, gdy tak naprawdę nie był.
Remy Lebeau
1

Od API 14 możesz używać funkcji onTrimMemory()i sprawdzać flagę TRIM_MEMORY_UI_HIDDEN. Dzięki temu dowiesz się, że Twoja aplikacja działa w tle.

Więc w swojej niestandardowej klasie Application możesz napisać coś takiego:

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

Aby uzyskać dogłębną analizę tego zagadnienia, zapraszam do przeczytania tego artykułu: http://www.developerphil.com/no-you-can-not-override-the-home-button-but-you-dont-have -do/

Renaud C.
źródło
1
Dobry artykuł - użyteczna alternatywa, która prawdopodobnie spełnia to, czego większość ludzi potrzebuje
Dean Wild
Niezłe rozwiązanie. Czy wiesz, jak zresetować flagę, gdy aplikacja wraca z tła? Na przykład, jeśli utworzę wartość logiczną isInBackground, chciałbym ją zresetować, gdy wrócimy z tła.
MikeOscarEcho
0

Opcją dla twojej aplikacji byłoby napisanie zastępczego ekranu głównego przy użyciu intencji android.intent.category.HOME. Wierzę, że tego typu intencji można zobaczyć przycisk strony głównej.

Więcej szczegółów:

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

ademar111190
źródło
1
ciekawy pomysł, ale trochę rozwlekły i nie tak elegancki, jak bym się spodziewał
Dean Wild
0

Ponieważ chcesz, aby aktywność roota była ponownie wyświetlana po uruchomieniu aplikacji, może możesz uzyskać to zachowanie, zmieniając tryby uruchamiania itp. W manifeście?

Na przykład, czy próbowałeś zastosować atrybut android: clearTaskOnLaunch = "true" do swojej aktywności uruchamiania, być może w połączeniu z android: launchMode = "singleInstance" ?

Zadania i Back Stack to świetne źródło informacji o dostosowywaniu tego rodzaju zachowań.

Josh
źródło
Wydaje się, że jest to najbardziej eleganckie rozwiązanie, ale okazało się, że jest dość zawodne. Po kilku cyklach otwierania / zamykania / wstrzymywania aplikacja zacznie się po prostu wznowić, a nie całkowicie zrestartować
Dean Wild
0

Zmiana zachowania klucza domowego to zły pomysł. Dlatego Google nie zezwala na zastąpienie klucza głównego. Ogólnie rzecz biorąc, nie zadzierałbym z kluczem do domu. Musisz dać użytkownikowi możliwość wyjścia z aplikacji, jeśli z jakiegoś powodu znika ona w chwasty.

Wyobrażam sobie, że każde obejście będzie miało niepożądane skutki uboczne.

Andi Jay
źródło
1
Jesteś absolutnie na miejscu, ale niektórzy klienci nie przyjmą odpowiedzi „nie” i nie rozumieją, dlaczego nie powinni łamać wytycznych.
Dean Wild,
Problem polega na tym, że nawet jeśli nie chcesz zmieniać zachowania przycisku Home, czasami musisz inaczej zareagować na sytuację, w której aplikacja jest przenoszona do tyłu z powodu naciśnięcia przycisku Home, inaczej niż w sytuacji, gdy Twoja bieżąca aktywność jest wstrzymywana na czas. powód. Ten sam problem polega na tym, że Application.onDestroy () nie może być używany do kompilacji poduction. Takie przykłady to wstrzymywanie gry, gdy użytkownik ukrywa aplikację, zatrzymywanie muzyki w tle itp.
Blackhex
0

Ostatnio próbowałem wykryć przycisk home, ponieważ potrzebowałem go do zrobienia tego samego co metoda „ onBackPressed () ”. Aby to zrobić, musiałem zastąpić metodę „ onSupportNavigateUp () ” w następujący sposób:

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

Działało doskonale. =)

Alexander Cavalheiro Becker
źródło
0

Odpowiedź Jacka doskonale sprawdza się w clickprzypadku zdarzenia, podczas gdy longClickrozważa to menukliknięcie przycisku.

Swoją drogą, jeśli ktoś się zastanawia, jak to zrobić przez 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.
źródło
0

wprowadź opis obrazu tutaj Android Home Key obsługiwany przez warstwę frameworkową, nie można tego obsłużyć na poziomie warstwy aplikacji. Ponieważ akcja przycisku głównego jest już zdefiniowana na poniższym poziomie. Ale jeśli opracowujesz niestandardową pamięć ROM, może to być możliwe. Google ograniczyło funkcje nadpisywania PRZYCISKU HOME ze względów bezpieczeństwa.

Stephan J.
źródło