Виявити натиснення домашньої кнопки в android


94

Це вже деякий час зводить мене з розуму.

Чи є спосіб надійно визначити, чи натиснута кнопка додому в додатку для Android?

Якщо цього не вдасться, чи існує надійний спосіб сказати, що призвело до того, що діяльність перейшла до onPause? тобто чи можемо ми виявити, чи це було спричинено запуском нової діяльності або натисканням кнопки назад / додому.

Одне з пропозицій, яке я бачив, - переосмислити функцію onPause () і викликати isFinishing (), але це повернеться помилковим при натисканні кнопки додому так само, як це було б, якщо починається нова діяльність, і це не зможе розрізнити обидві.

Будь-яка допомога дуже цінується.

** Оновлення **: Дякую @ голодуючим на android за це посилання: https://nishandroid.blogspot.com/

Перекриваючи наступний метод:

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

Тоді наступна подія буде звільнена для натискання кнопки будинку:

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

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

Я не впевнений, чи є побічні ефекти з цієї лінії:

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

Отже, здається, всупереч поширеній думці, насправді ви можете вислухати ключ від дому. Турбує те, що ви можете повернути помилкове значення і змусити домашній ключ нічого не робити.

Оновлення . Як очікується, у цьому є деякі побічні наслідки - схоже, що вбудовані відео та карти Google не відображаються з увімкненим цим режимом.

Оновлення : нібито цей хак більше не працює з Android 4.0 і далі


Моя проблема полягала не в маскуванні між спиною і домом - ключем, але я хотів закінчити застосування в обох випадках. Що я зробив за допомогою Activity.onUserLeaveHint().
харизм

Єдина проблема - onUserLeaveHint () також запускатиметься, коли я починаю діяльність із зазначеної активності, я хочу лише знати, чи натиснуто назад або додому. Хоча дякую за пропозицію
Дін Уайлд

Це правда, але, на жаль, наскільки я знаю, це єдине місце, де можна отримати будь-яку інформацію про використання Home -key. Зробити більшою проблемою збір фальшивих позитивів з багатьох можна легко розпізнати, але все-таки зробити складне завдання досить складним.
харизм

2
@DeanWild: зробив і прочитати: nisha113a5.blogspot.com
Pratik Бхат

2
Константа TYPE_KEYGUARD була вилучена з WindowManager.LayoutParams в Android 5.0
theb1uro

Відповіді:


136

Наступний код працює для мене :)

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
Ви на onHomeLongPressedсамому справі , здається, відповідає відкриттю «Недавні» діяльності системи. На моєму телефоні це спрацьовує натисканням кнопки введення поруч із домашньою кнопкою, тому припущення вашого коду про те, що він є домашнім довгим натисканням, не завжди є правильним.
Сем

чому це не працює для мене, я зробив точно те саме, за винятком зареєстрованого мовлення через маніфест.
Фархан

Зареєстрований у класі додатків, працюючи поки що .. +1, мені цікаво, у чому улов? Я маю на увазі, який оригінальний випадок ми б пропали ..: ^)
Фархан

1
іноді intent.getStringExtra (SYSTEM_DIALOG_REASON_KEY); повернути null. Я хотів би знати, що це відбувається ??
Фахер

1
Причина тривалого натискання зараз називаєтьсяfinal String SYSTEM_DIALOG_REASON_LONG_PRESS = "assist"
JWqvist

49

Це давнє питання, але це може комусь допомогти.

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

Згідно з документацією, метод onUserLeaveHint () викликається, коли користувач натискає кнопку головного АБО, коли щось перериває вашу програму (наприклад, вхідний телефонний дзвінок).

Це працює для мене .. :)


26
Неправильно !!! Це працює вдома, натиснувши кнопку, але це також працює, коли Swiching Activity with Intent !!
Нікундж Парадва

Це виконуватиметься перед onStop () завжди, навіть якщо інша активність з’являється у верхній частині, або користувач насильно залишає активність або натискаючи кнопку додому ...
Navas pk

7

Неможливо виявити та / або перехопити кнопку HOME у додатку Android. Це вбудовано в систему, щоб запобігти шкідливим програмам, які неможливо вийти.


перевірити прийняту відповідь, можливо. Проте не перевірено на багатьох пристроях.
Дін Дикий

Так ... розбита програма на моїй.
Джеремі Логан

а як щодо пускових установок / програм для заміни будинку? Я
будую

Для Пускових установок використовуйте це: @Override protected void onNewIntent (Intent intent) {super.onNewIntent (intent); / * Роби, що хочеш * /}
Тон,

Запуск програми @lisovaccaro та / або домашні заміни все ще не підтримуються google (src diane hackborn), і тому ви не можете перешкодити користувачеві натиснути кнопку додому. Ви все ще можете додати свій вигляд як діалогове вікно системного сповіщення, яке перекриє все. Але натискання кнопки додому пройде через це.
JacksOnF1re

7

Мені потрібно було запускати / зупиняти фонову музику в моїй програмі, коли перша дія відкривається і закривається або коли будь-яка діяльність призупиняється кнопкою додому, а потім відновлюється з диспетчера завдань. Чиста зупинка відтворення / відновлення відтворення Activity.onPause()та Activity.onResume()перервало музику на деякий час, тому мені довелося написати наступний код:

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

яка перериває відтворення лише тоді, коли програма (усі її дії) закрита або при натисканні кнопки додому. На жаль, мені не вдалося змінити порядок викликів onPause()методу стартової активності та розпочатої активності, onResume()коли Activity.startActivity()викликається (або виявити, що в onPause()цій діяльності запускається інша діяльність іншим способом), тому цей випадок потрібно обробляти спеціально:

private boolean mIsStarting;

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

Ще одним недоліком є ​​те, що для цього потрібен GET_TASKSдозвіл, доданий до AndroidManifest.xml:

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

Змінити цей код так, щоб він реагував лише на натискання кнопки додому, просто.


4

Перевизначення onUserLeaveHint()в діяльності. Ніколи не буде жодного зворотного дзвінка до дії, коли над нею з’являється нова дія або користувач натискає зворотну пресу


4
його також називають також при переході від однієї діяльності до іншої в додатку
Amir Uval

3

onUserLeaveHint ();

замінити цей метод класу активності. Це виявить клацання клавіші додому. Цей метод викликається безпосередньо перед зворотним викликом активності onPause (). Але він не буде викликаний, коли діяльність переривається, коли активність під час виклику виходить на передній план, крім тих переривань, які він буде викликати, коли користувач натискає клавішу додому.

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

2

Спробуйте створити лічильник для кожного екрана. Якщо користувач торкнеться ДОМАШНО, то лічильник буде нульовим.

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

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

3
Якщо ви маєте на увазі лічильник глобальних додатків, він буде дорівнює нулю в момент, коли одна активність переміщується в задній стек, а інша переміщується вгору або коли закінчується активна активність, і активність зворотного стека переміщується вгору, що є звичайним місцем, коли Ви хочете реагувати на натискання кнопки додому. Якщо ви маєте на увазі лічильник для всієї активності, він буде нульовим, коли активність не буде видно (не обов'язково викликана натисканням кнопки будинку). Єдиним рішенням буде відкласти реакцію за допомогою таймера, щоб пропустити цей перехід, але необхідна затримка може бути не передбачуваною або бажаною.
Blackhex


1

У мене була ця проблема, і оскільки переосмислення методу onKeyDown () нічого не здійснило, оскільки основна система Android не викликала цей метод, я вирішив це за допомогою переопределення onBackPress (), і у мене було булеве значення, встановлене на false , тому що я натиснув назад, дозвольте мені показати вам, що я маю на увазі в коді:

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

Тож причина, чому це спрацювало, полягає в тому, що єдиний спосіб навігації поза цією діяльністю - натискання назад або додому, тож якщо натиснута спина, то я знаю, що причина не була вдома, але в іншому випадку причина була вдома, тому я встановив булевий код за замовчуванням значення для домуНадано, щоб було правдою. Однак це буде працювати лише з одним екземпляром активності у вашій програмі, оскільки в іншому випадку у вас є більше можливостей викликати метод onPause ().


насправді , коли ви йдете в інший діяльності, OnPause метод називається
Fakher

Ось чому я прямо заявив, що це буде працювати лише у тому випадку, якщо ваша програма включає лише одну діяльність!
Моше Рабаєв

А що робити, якщо одночасно запущено декілька непов'язаних активів, і користувач просто перемикається між ними? Сучасні пристрої Android підтримують багатозадачність. За допомогою цього коду схоже, що для повернення до додатка встановлено homePressedзначення "true", а тоді перехід на інший додаток міг би вважати, що додому натискали, коли цього насправді не було.
Ремі Лебо

1

З API 14 ви можете використовувати функцію onTrimMemory()та перевірити прапор TRIM_MEMORY_UI_HIDDEN. Це скаже вам, що ваша програма переходить на другий план.

Тож у вашому користувальницькому класі додатків ви можете написати щось на зразок:

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

Для поглибленого вивчення цього питання я запрошую вас прочитати цю статтю: http://www.developerphil.com/no-you-can-not-override-the-home-button-but-you-dont-have -до /


1
Хороша стаття - корисна альтернатива, яка, мабуть, робить те, що потрібно більшості людей
Дін Дикий

Гарне рішення. Чи знаєте ви, як скинути прапор, коли програма повернеться з фону? Наприклад, якщо я створив логічний isInBackground, я хотів би скинути його до, як тільки ми повернемося з фону.
MikeOscarEcho

0

Варіантом для вашої програми може бути написання замінного головного екрана за допомогою android.intent.category.HOME Intent. Я вважаю, що цей тип Намір ви можете побачити кнопку будинку.

Детальніше:

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


1
цікава ідея, але трохи затята і не така елегантна, як я сподівався б,
Дін Уайлд

0

Оскільки ви хочете, щоб коренева активність була відновлена ​​під час запуску програми, можливо, ви можете отримати таку поведінку, змінивши режими запуску тощо у маніфесті?

Наприклад, ви спробували застосувати атрибут android: clearTaskOnLaunch = "true" до вашої запускової діяльності, можливо, в тандемі з android: launchMode = "singleInstance" ?

Tasks and Back Stack - чудовий ресурс для точного налаштування такої поведінки.


Це здавалося б найелегантнішим рішенням, але я виявив його досить ненадійним. Після декількох циклів відкриття / закриття / паузи додаток почне просто відновитись, а не повністю перезапустити
Дін Дикий

0

Погана ідея змінити поведінку домашнього ключа. Ось чому Google не дозволяє вам замінити домашній ключ. Я б не возився з домашнім ключем взагалі. Вам потрібно надати користувачеві спосіб вийти з вашого додатка, якщо він з будь-якої причини потрапляє у бур’яни.

Я думаю, що будь-яка робота навколо буде мати небажані побічні ефекти.


1
Ви абсолютно не помічені, але деякі клієнти не приймуть відповіді і не розуміють, чому вони не повинні порушувати правила.
Дін Дикий

Проблема полягає в тому, що навіть якщо ви не хочете змінювати поведінку домашніх кнопок, вам періодично доводиться реагувати інакше на ситуацію, коли додаток переміщено назад, завдяки натисканню кнопки додому, інакше, ніж у випадку, коли ваша поточна діяльність призупинена для інший причина. Ця ж проблема полягає в тому, що Application.onDestroy () не може бути використаний для складання подукцій. Такі приклади призупиняють гру, коли користувач приховує додаток, зупиняє фонову музику тощо
Blackhex

0

Нещодавно я намагався виявити кнопку домашнього натискання, тому що мені знадобилося це зробити так само, як метод " onBackPress () ". Для цього мені довелося замінити метод " onSupportNavigateUp () " таким чином:

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

Це спрацювало чудово. =)


0

Відповідь Джека прекрасно працює на clickподію, а longClickрозглядає як menuнатискання кнопки.

До речі, якщо хтось цікавиться, як це зробити через 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

введіть тут опис зображення Ключ Android Home, який обробляється каркасним шаром, ви не можете впоратися з цим на рівні шару програми. Оскільки дію кнопки додому вже визначено на рівні нижче. Але якщо ви розробляєте власний ПЗУ, то це можливо. Google обмежив перевизначення функцій HOME BUTTON із міркувань безпеки.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.