Обнаружить нажатие кнопки домой в Android

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

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

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

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

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

** Обновление **: Спасибо @android-Hungry за эту ссылку: http://nisha113a5.blogspot.com/

Завершая следующий метод:

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

Тогда следующее событие БУДЕТ запущено для нажатий кнопки home:

@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 и выше

20 ответов

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

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

Это старый вопрос, но он может кому-то помочь.

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

Согласно документации, метод onUserLeaveHint() вызывается, когда пользователь нажимает кнопку "Домой" ИЛИ, когда что-то прерывает ваше приложение (например, входящий телефонный звонок).

Это работает для меня..:)

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

Мне нужно было запускать / останавливать фоновую музыку в моем приложении, когда первое действие открывается и закрывается, или когда какое-либо действие приостанавливается с помощью кнопки "Домой", а затем возобновляется из диспетчера задач. Чистое воспроизведение останавливается / возобновляется в 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"/>

Изменить этот код так, чтобы он реагировал только на нажатие кнопки "Домой", просто.

onUserLeaveHint();

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

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

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

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

У меня была эта проблема, и поскольку переопределение метода 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().

Вы можете рассмотреть решение Андреаса Шрэйда в его посте " Как создать режим рабочего киоска в Android". Это немного странно, но, учитывая причины, по которым перехват кнопки home предотвращается, это должно быть;)

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

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

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

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

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

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

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

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

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

Android Home Key обрабатывается уровнем фреймворка, вы не можете справиться с этим на уровне уровня приложения. Потому что действие кнопки "Домой" уже определено на нижнем уровне. Но если вы разрабатываете свой собственный ROM, это может быть возможно. Google ограничил функции переопределения HOME BUTTON из соображений безопасности.

РЕШЕНО: Это работает для НАЗАД, ДОМОЙ и ОБЗОР ЗАДАЧ

      @Override
public void onBackPressed() {
}

@Override
public void onPause() {
    if (isApplicationSentToBackground(MainActivity.this)){
        // Do what you want to do on detecting Home Key being Pressed
        Toast.makeText(this, "You pressed the task button!", Toast.LENGTH_LONG).show();
    }
    super.onPause();
    ActivityManager activityManager = (ActivityManager) getApplicationContext()
            .getSystemService(Context.ACTIVITY_SERVICE);

    activityManager.moveTaskToFront(getTaskId(), 0);
}

@SuppressWarnings("deprecation")
public boolean isApplicationSentToBackground(final Context context) {
    ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
    if (!tasks.isEmpty()) {
        ComponentName topActivity = tasks.get(0).topActivity;
        if (!topActivity.getPackageName().equals(context.getPackageName())) {
            return true;
        }
    }
    return false;
}

И добавьте в манифест: android.permission.REORDER_TASKS

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

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

Среднестатистическое приложение может прекрасно уживаться без необходимости знать разницу между нажатием кнопки "Домой" и событием паузы. Другими словами, onPause() обычно достаточно.

почему ты хочешь сделать это? Предоставление нам дополнительной информации о ваших намерениях может привести к улучшению общей стратегии для вас.

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

private boolean isUserClickToLeave = false;
private Handler mHandler = new Handler();

 Runnable resetUserClickFlagRunnable = new Runnable() {
    @Override
    public void run() {
        isUserClickToLeave = false;
    }
};

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_CANCEL || ev.getAction() == MotionEvent.ACTION_UP) {
        isUserClickToLeave = true;
        mHandler.removeCallbacks(resetUserClickFlagRunnable);
        mHandler.postDelayed(resetUserClickFlagRunnable, 1000);
    }
    return super.dispatchTouchEvent(ev);
}

@Override
protected void onUserLeaveHint() {
    super.onUserLeaveHint();
    if (!isUserClickToLeave ) {
        //user press home button case.
       onHomePressed();
    }
    isUserClickToLeave = false;
}

private onHomePressed() {
    //do something here.
}

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

Больше деталей:

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

Ответ Джека идеально подходит для 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()
                }
            }
        }
    } 
}

У меня это работает. Вы можете переопределить метод onUserLeaveHint https://www.tutorialspoint.com/detect-home-button-press-in-android

@Override
    protected void onUserLeaveHint() {
        //
        super.onUserLeaveHint();
    }
Другие вопросы по тегам