Обнаружение нажатия кнопки Home в Android


94

Это уже какое-то время сводит меня с ума.

Есть ли способ надежно определить, была ли нажата кнопка домой в приложении для Android?

Если это не удается, есть ли надежный способ узнать, что заставило действие перейти в onPause? т.е. можем ли мы определить, было ли это вызвано запуском нового действия или нажатием кнопки «Назад / домой».

Одно из предложений, которое я видел, - переопределить onPause () и вызвать isFinishing (), но это вернет false при нажатии кнопки домой, как если бы начиналось новое действие, поэтому это не позволяет различить два.

Любая помощь очень ценится.

** Обновление **: спасибо @ android-hungry за эту ссылку: 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);   

Таким образом, может показаться, что вопреки распространенному мнению, на самом деле вы можете услышать домашний ключ. К сожалению, вы можете вернуть false, и домашний ключ ничего не сделает.

Обновление : как и ожидалось, здесь есть некоторые побочные эффекты - кажется, что встроенные видео и карты Google не отображаются в этом режиме.

Обновление : предположительно, этот хак больше не работает, начиная с Android 4.0.


Моя проблема заключалась не в том, чтобы замаскироваться между клавишами возврата и возврата, но я хотел завершить приложение в обоих случаях. Что я и сделал с помощью Activity.onUserLeaveHint().
harism

Единственная проблема заключается в том, что onUserLeaveHint () также срабатывает, когда я запускаю действие из указанного действия, я только хочу знать, нажата ли кнопка назад или домой. Тем не менее, спасибо за предложение
Дин Уайлд

Это правда, но, к сожалению, насколько мне известно, это единственное место, где можно получить любую информацию об использовании Home -key. Выявление ложноположительных результатов из многих становится еще более сложной задачей, но все же делает простую задачу довольно сложной.
harism

2
@DeanWild: сделал и прочитать: nisha113a5.blogspot.com
Pratik Бхат

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

Ответы:


137

Для меня работает следующий код :)

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); вернуть ноль. Хотелось бы знать, что происходит ??
Fakher

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
Не верно !!! Это работает при нажатой домашней кнопке, но также работает при переключении Activity with Intent !!
Никундж Парадва

Это всегда будет выполняться перед onStop (), даже если другое действие происходит сверху или пользователь принудительно покидает действие или нажимает кнопку домой ...
Navas pk

7

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


проверьте принятый ответ, возможно. Однако еще не тестировался на многих устройствах.
Dean Wild

Ага ... у меня разбилось приложение.
Джереми Логан

как насчет пусковых установок / приложений для замены дома? Я создаю один и хочу перейти к первому экрану, когда пользователь нажимает
кнопку «

Для пусковых установок используйте это: @Override protected void onNewIntent (намерение намерения) {super.onNewIntent (намерение); / * Делай, что хочешь * /}
Ton

Панель запуска @lisovaccaro и / или домашние замены все еще не поддерживаются Google (src diane hackborn), и поэтому вы не можете запретить пользователю нажимать кнопку Home. Вы все еще можете добавить свое представление в виде диалогового окна системных предупреждений, которое будет перекрывать все. Но нажатия кнопки домой будут проходить через это.
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
он также вызывается при переходе от одного занятия к другому в приложении
Амир Увал

3

onUserLeaveHint ();

переопределить этот метод класса активности. Это обнаружит нажатие клавиши Home. Этот метод вызывается непосредственно перед обратным вызовом onPause () активности, но он не будет вызываться, когда действие прерывается, например, действие вызова выходит на передний план, за исключением тех прерываний, которые он вызывает, когда пользователь нажимает кнопку Home.

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

2

Попробуйте создать счетчик для каждого экрана. Если пользователь коснется HOME, счетчик будет равен нулю.

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

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

3
Если вы имеете в виду глобальный счетчик приложения, он будет равен нулю в тот момент, когда одно действие перемещается в задний стек, а другое перемещается наверх, или когда верхнее действие завершено, а действие заднего стека перемещается наверх, что является обычным местом, когда вы хотите реагировать на нажатие кнопки «Домой». Если вы имеете в виду счетчик активности, он будет обнулен каждый раз, когда активность не отображается (не обязательно из-за нажатия кнопки «Домой»). Единственным решением было бы отложить вашу реакцию с помощью таймера, чтобы пропустить этот переход, но необходимая задержка может быть непредсказуемой или нежелательной.
Blackhex


1

У меня была эта проблема, и поскольку переопределение метода onKeyDown () ничего не дало, потому что базовая система Android не вызвала этот метод, я решил это переопределением onBackPressed (), и у меня было логическое значение, установленное там на 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"); }
    }

Итак, причина, по которой это сработало, заключается в том, что единственный способ выйти за пределы этого действия - это нажать назад или домой, поэтому, если была нажата кнопка возврата, я знаю, что причина не в доме, но в противном случае причина была в доме, поэтому я установил логическое значение по умолчанию значение для homePressed, чтобы быть правдой. Однако это будет работать только с одним экземпляром действия в вашем приложении, потому что в противном случае у вас будет больше возможностей вызвать метод onPause ().


фактически, когда вы переходите к другому занятию, вызывается метод
onpause

Вот почему я прямо заявил, что это будет работать только в том случае, если ваше приложение включает только одно действие!
Моше Рабаев

А что, если одновременно запущено несколько несвязанных действий, и пользователь просто переключается между ними? Современные устройства Android поддерживают многозадачность. С этим кодом похоже, что для возврата к вашему приложению будет установлено homePressedзначение true, а затем переключение на другое приложение будет думать, что Home была нажата, хотя на самом деле это не так.
Реми Лебо

1

Начиная с API 14, вы можете использовать функцию onTrimMemory()и проверять наличие флага TRIM_MEMORY_UI_HIDDEN. Это сообщит вам, что ваше приложение переходит в фоновый режим.

Итак, в своем пользовательском классе Application вы можете написать что-то вроде:

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 -to /


1
Хорошая статья - полезная альтернатива, которая, вероятно, делает то, что нужно большинству людей
Дин Уайлд,

Хорошее решение. Вы знаете, как сбросить флаг, когда приложение возвращается из фона? Например, если я создаю логическое значение isInBackground, я бы хотел сбросить его до того, как мы вернемся из фона.
MikeOscarEcho

0

Вариант для вашего приложения - написать заменяющий домашний экран с использованием намерения android.intent.category.HOME. Я считаю, что при таком намерении вы можете увидеть кнопку «Домой».

Подробнее:

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


1
интересная идея, но немного затянувшаяся и не такая элегантная, как я бы надеялся
Дин Уайлд

0

Поскольку вы хотите, чтобы при запуске приложения отображалось только корневое действие, возможно, вы сможете добиться такого поведения, изменив режимы запуска и т. Д. В манифесте?

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

Задачи и Back Stack - отличный ресурс для тонкой настройки такого поведения.


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

0

Изменять поведение клавиши Home - плохая идея. Вот почему Google не позволяет вам переопределить домашний ключ. Вообще говоря, я бы не стал связываться с домашним ключом. Вы должны дать пользователю возможность выйти из вашего приложения, если оно по какой-то причине уйдет в тупик.

Я полагаю, что любая работа будет иметь нежелательные побочные эффекты.


1
Вы абсолютно правы, но некоторые клиенты не принимают отрицательный ответ и не понимают, почему они не должны нарушать правила.
Dean Wild

Проблема в том, что даже если вы не хотите менять поведение кнопки «Домой», вам иногда приходится по-разному реагировать на ситуацию, когда приложение перемещается назад из-за нажатия кнопки «Домой», иначе, чем в ситуации, когда текущая активность приостановлена ​​для любого причина. Та же проблема связана с тем, что Application.onDestroy () не может использоваться для сборки сборки. Такие примеры - приостановка игры, когда пользователь скрывает приложение, остановка фоновой музыки и т. Д.
Blackhex

0

Недавно я пытался обнаружить кнопку возврата домой, потому что мне нужно было, чтобы она делала то же, что и метод onBackPressed () . Для этого мне пришлось переопределить метод onSupportNavigateUp () следующим образом:

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

Это сработало отлично. знак равно


0

Ответ Джека отлично работает для clickсобытия, пока longClickрассматривается как menuнажатие кнопки.

Кстати, если кому интересно как через котлин сделать,

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 Key обрабатывается уровнем фреймворка, вы не можете справиться с этим на уровне уровня приложения. Потому что действие кнопки «Домой» уже определено на нижнем уровне. Но если вы разрабатываете свой собственный ПЗУ, это может быть возможно. Google ограничил функции переопределения HOME BUTTON из соображений безопасности.


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.