Назад нажатые события с окном системного предупреждения

Мне нужно закрыть окно системного оповещения при нажатии кнопки back и нажатии кнопки home. Я пробовал с onKeyEvent, но тщетно. Поскольку мы не можем зафиксировать событие back press в службе, как этого добиться?


person user3595323    schedule 16.06.2015    source источник
comment
вы можете сделать это, переопределив метод onBackButtonpressed   -  person SAM    schedule 16.06.2015
comment
Можете ли вы добавить фрагмент кода, который отображает диалоговое окно?   -  person Luca    schedule 16.06.2015
comment
Окно системного оповещения — это то, которое я использую для рисования поверх других приложений. Я хочу обрабатывать ключевые события, такие как нажатие кнопки «Назад», нажатие кнопки «Домой» из службы специальных возможностей.   -  person user3595323    schedule 16.06.2015


Ответы (10)


Поскольку это служба, в которой размещается оверлейное окно, это немного сложное решение, но оно возможно.

Вы должны обрабатывать эти 2 случая отдельно (переопределение нажатия кнопки «Домой» и нажатия кнопки «Назад»).


<сильный>1. Переопределение нажатия кнопки "Домой":

Создайте этот класс HomeWatcher, который содержит BroadcastReceiver, который будет уведомлять о нажатии кнопки «Домой». Зарегистрируйте этот приемник только тогда, когда появится ваше окно.

Android: привязать метод к домашней кнопке смартфона

Внутри вашего метода onCreate службы используйте это:

HomeWatcher mHomeWatcher = new HomeWatcher(this);
mHomeWatcher.setOnHomePressedListener(new OnHomePressedListener() {
    @Override
    public void onHomePressed() {
        yourWindow.hide()  //means: windowManager.removeView(view);
    }
    @Override
    public void onHomeLongPressed() {
    }
});
mHomeWatcher.startWatch();

<сильный>2. Отмена нажатия кнопки «Назад»:

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

Например, это будет ваш класс окна:

public class MyWindow
{
    private WindowManager windowManager;
    private WindowManager.LayoutParams params;
    private View view;

    // Add this empty layout:
    private MyLayout myLayout;

    public MyWindow()
    {
        windowManager = (WindowManager) context.getSystemService(context.WINDOW_SERVICE);
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = inflater.inflate(R.layout.your_original_window_layout, null);

        // Add your original view to the new empty layout:
        myLayout = new MyLayout(this);
        myLayout.addView(view, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
    }

    // And show this layout instead of your original view:
    public void show()
    {
        windowManager.addView(myLayout, params);
    }

    public void hide()
    {
        windowManager.removeView(myLayout);
    }
}

А теперь создайте класс MyLayout, чтобы переопределить нажатие кнопки «Назад»:

public class MyLayout extends LinearLayout
{
    private MyWindow myWindow;

    public MyLayout(MyWindow myWindow)
    {
        super(myWindow.context);

        this.myWindow = myWindow;
    }


    @Override public boolean dispatchKeyEvent(KeyEvent event)
    {
        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK)
        {
            if (event.getAction() == KeyEvent.ACTION_DOWN  &&  event.getRepeatCount() == 0)
            {
                getKeyDispatcherState().startTracking(event, this);
                return true;

            }

            else if (event.getAction() == KeyEvent.ACTION_UP)
            {
                getKeyDispatcherState().handleUpEvent(event);

                if (event.isTracking() && !event.isCanceled())
                {
                    // dismiss your window:
                    myWindow.hide();

                    return true;
                }
            }
        }

        return super.dispatchKeyEvent(event);
    }
}

Я знаю, что это немного сложно, как я уже сказал, поскольку это окно системного предупреждения, размещенное службой, НО оно работает. У меня такая же проблема, и она решена точно так же. Удачи.

person Eliran Kuta    schedule 07.11.2015
comment
Спасибо, я попробую это как можно скорее. - person Yaadm; 08.11.2015
comment
Вы можете загрузить мое приложение, чтобы получить впечатление о поведении системных предупреждений в нем, и задать мне дополнительные вопросы, если хотите, и я дам вам несколько советов и примеры кода. По сути, оверлейные окна системных предупреждений требуют особого ухода, управления и дополнительного обслуживания, поскольку они не являются деятельностью. play.google.com/store/apps/details?id= com.accio.app - person Eliran Kuta; 09.11.2015
comment
в классе MyWindow -> какой контекст в объявлении нет переменной - person Raegtime; 22.07.2016
comment
это не переопределение домашней кнопки... но это расширение... действие домашней кнопки тоже выполняется :-( - person Raegtime; 22.07.2016
comment
dispatchKeyEvent никогда не вызывался - person shantanu; 21.05.2017
comment
dispatchKeyEvent идеален, но почему onKeyUp и onKeyDown никогда не вызываются. - person DaSqy Stc; 11.09.2017
comment
В вашем классе MyWindow нет контекста имени поля, вы должны передать контекст представления? - person yarin; 27.09.2018

используйте приведенный ниже метод для обработки нажатия кнопки «Назад».

  public void onBackPressed()
  {
        super.onBackPressed();
  }
person santosh gore    schedule 16.06.2015

Вам нужно перезаписать метод onBackPressed.

@Override
public void onBackPressed() {

    super.onBackPressed(); // remove this if u want to handle this event
}
person Samira    schedule 09.11.2015

Используйте код ниже

public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK) {
        exitByBackKey();

        //moveTaskToBack(false);

        return true;
    }
    return super.onKeyDown(keyCode, event);
    }

    protected void exitByBackKey() {

    AlertDialog alertbox = new AlertDialog.Builder(this)
    .setMessage("Do you want to exit application?")
    .setPositiveButton("Yes", new DialogInterface.OnClickListener() {

        // do something when the button is clicked
        public void onClick(DialogInterface arg0, int arg1) {

            finish();
            //close();


        }
    })
    .setNegativeButton("No", new DialogInterface.OnClickListener() {

        // do something when the button is clicked
        public void onClick(DialogInterface arg0, int arg1) {
                       }
    })
      .show();

    }
person sasikumar    schedule 16.06.2015

@Override
public void onBackPressed()
{
       super.onBackPressed();
}

Заявите об этом в своей деятельности. super.OnBackPressed автоматически вызывает метод обратного вызова в android. это обязательно отменит ваш диалог.

кроме того, ваш диалог должен выглядеть так.

AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder1.setMessage("TEST DIALOG.\n");
    builder1.setPositiveButton("Ok",
            new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int id) {
                Toast.makeText(MainActivity.this, "This Is test Dialog", Toast.LENGTH_SHORT).show();
        }
    });
    AlertDialog alert11 = builder1.create();
    alert11.show();

или вы можете установить кнопку «Отрицательный»..

Надеюсь это поможет!

person Lionell Libarios    schedule 16.06.2015

Определите пользовательский макет и переопределите dispatchKeyEvent, например:

public class CustomSystemAlertWindow extends FrameLayout {

    public static final String TAG = "CustomSystemAlertWindow";

    private WeakReference<Context> mContext;

    public CustomSystemAlertWindow(Context context) {
        super(context);

        mContext = new WeakReference<Context>(context);

        // Set a background color to identify the view on the screen
        this.setBackgroundColor(getResources().getColor(android.R.color.holo_red_light));
    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        if (event != null && event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
            Log.d(TAG, "back button pressed");

            if (mContext != null && mContext.get() != null) {
                WindowManager wm = (WindowManager) mContext.get().getSystemService(Context.WINDOW_SERVICE);
                wm.removeView(this);
            }

            return true;
        }

        return super.dispatchKeyEvent(event);
    }
}

Затем добавьте представление с помощью этого кода:

CustomSystemAlertWindow customSystemAlertWindow = new CustomSystemAlertWindow(context);

WindowManager.LayoutParams params = new WindowManager.LayoutParams(
        WindowManager.LayoutParams.MATCH_PARENT,
        WindowManager.LayoutParams.MATCH_PARENT,
        WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
        WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
        PixelFormat.TRANSLUCENT);

WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
wm.addView(customSystemAlertWindow, params);

Когда вы нажмете кнопку «Назад», вид исчезнет.

person Mattia Maestrini    schedule 04.11.2015
comment
Опубликуйте свой код, иначе вам действительно сложно помочь. Поскольку вопрос не ваш, вы можете опубликовать его на pastebin.com или аналогичном сервисе. - person Mattia Maestrini; 04.11.2015
comment
Подобное не то же самое. В чем разница между кодом ответа и вашим? - person Mattia Maestrini; 04.11.2015
comment
Мой макет создан из предопределенного XML, корнем которого является CustomsystemAlertWindow. - person Yaadm; 05.11.2015
comment
Я только что попробовал с XML, и это работает для меня. Вы уверены, что используете те же флаги ответа для создания WindowManager.LayoutParams? - person Mattia Maestrini; 05.11.2015

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

Реализуйте код для легкого обнаружения нажатия кнопки «Назад» или кнопки «Домой».

public class alertPopup extends Activity {

    Context context;
    final AlertDialog alertDialog;
    String TAG = "your Activity Name"
    boolean homePressed = false; // to detect the Homebutton pressed

    @Override
    public void onCreate(Bundle savedInstanceState) {
     AlertDialog.Builder builder = newAlertDialog.Builder(YourActivity.this, R.style.AppCompatAlertDialogStyle);
     builder.setTitle("AlertDialog Title");
            ..........
            ....... // Build ur AlertDialog

     alertDialog= builder.create();
     alertDialog.show();


         //to detect Alert Dialog cancel when user touches outside the Dialog prompt
     alertDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
            @Override
            public void onCancel(DialogInterface dialog) {
                Log.v(TAG,"Alert Dialog cancelled when user touches outside the Dialog prompt")
            }
        });

    }


    @Override
    public void onBackPressed()
    {
    Log.v(TAG,"Back Button Pressed");
     super.onBackPressed();

     alertDialog.dismiss();   //dismiss the alertDialog
     alertPopup.this.finish();  // Destroy the current activity

     homePressed = false;
    }

    @Override
    public void onResume() {
        super.onResume();
        homePressed = true; // default: other wise onBackPressed will set it to false
    }


    @Override
    public void onPause() {
        super.onPause();
        if(homePressed) { 

        alertDialog.dismiss();   //dismiss the alertDialog
        alertPopup.this.finish();  // Destroy the current activity

        Log.v(TAG, "Home Button Pressed"); }
    }


    public void onDestroy(){

        super.onDestroy();
    }


}

Примечание:

Добавьте это разрешение в манифест Android, чтобы показать окно предупреждения.

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

Удачного кодирования :)

person Venkatesh Selvam    schedule 04.11.2015
comment
Обратите внимание, что вопрос касается окна системного предупреждения, которое запускается из службы (без участия). - person Yaadm; 04.11.2015
comment
Да, я знаю. если вы хотите обнаружить нажатую кнопку «Назад» и кнопку «Домой», вы должны показать активность через службу. Есть ли другой способ? @Yaadm - person Venkatesh Selvam; 04.11.2015
comment
Я проверю это как можно скорее. Я не знаю, есть ли другой способ, поэтому я и спрашиваю ... Кроме того, я попытаюсь декомпилировать приложение FB messenger, чтобы увидеть, как они это сделали. - person Yaadm; 04.11.2015
comment
Ok. Я даю одно из решений, которое я знаю правильно. Не задавайте вопросы, если вы не знаете должным образом. - person Venkatesh Selvam; 04.11.2015

Насколько я понимаю, вы используете разрешение <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> для отображения плавающего представления.

Используя плавающий вид, вы можете перехватить нажатие кнопки back, но нажатие кнопки home не может быть перехвачено (Android не позволит вам в первую очередь из соображений безопасности).

Чтобы перехватить нажатие кнопки back, вам нужно добавить обертку, когда вы раздуваете свой плавающий вид. Ваша обертка должна выглядеть так:

    // Wrapper for intercepting System/Hardware key events
    ViewGroup wrapper = new FrameLayout(this) {
        @Override
        public boolean dispatchKeyEvent(KeyEvent event) {
            if (event.getKeyCode()==KeyEvent.KEYCODE_BACK) {
                hideAddNotesFloatingView();
                return true;
            }
            return super.dispatchKeyEvent(event);
        }
    };

Затем вы добавляете его в свой плавающий вид как корень:

        mAddNoteFloatingView = mInflater.inflate(R.layout.floating_add_note, wrapper);

Мой полный код выглядит так:

private void addFloatingView() {
    final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
            WindowManager.LayoutParams.MATCH_PARENT,
            WindowManager.LayoutParams.MATCH_PARENT,
            WindowManager.LayoutParams.TYPE_PHONE,
            0,
            PixelFormat.TRANSLUCENT);

    params.gravity = Gravity.CENTER | Gravity.LEFT;
    params.x = 0;
    params.y = 0;

    // Wrapper for intercepting System/Hardware key events
    FrameLayout wrapper = new FrameLayout(this) {
        @Override
        public boolean dispatchKeyEvent(KeyEvent event) {
            if (event.getKeyCode()==KeyEvent.KEYCODE_BACK) {
                // Add your code for handling the back button press
                return true; // Return true means that the event was handled
            }
            return super.dispatchKeyEvent(event);
        }
    };

    mAddNoteFloatingView = mInflater.inflate(R.layout.floating_view, wrapper);

    mWindowManager.addView(mAddNoteFloatingView, params);
}
person Vlad    schedule 06.04.2016
comment
вы добавляете mAddNoteFloatingView в windowManager вместо оболочки, почему? обертка - это самый верхний вид, его нужно добавить в WM, не так ли? - person shantanu; 21.05.2017
comment
@shantanu В данном случае mAddNoteFloatingView и wrapper относятся к одному и тому же представлению. LayoutInflater.inflate возвращает: корневое представление раздутой иерархии. Если был указан корень, это корневой вид; в противном случае это корень раздутого XML-файла. - person Vlad; 25.05.2017

Это просто. Следуй этим шагам:

  1. Создайте представление, такое как относительный макет, линейный макет или динамический макет кадра. 2. Переопределите dispatchKeyEvent при создании представления.
  2. Добавьте свое исходное представление в это динамически созданное представление с помощью метода addView().
  3. Добавьте динамически созданный вид в свой оконный менеджер или диалоговое окно предупреждений в зависимости от того, что вы хотите.
person Abhilash    schedule 22.09.2017

В дополнение к решению @Eliran Kuta, это более простой ответ для кнопки «Назад».

val view = getAlertView()
val windowParam = WindowManager.LayoutParams(
    WindowManager.LayoutParams.WRAP_CONTENT, // whatever
    WindowManager.LayoutParams.WRAP_CONTENT, // whatever
    WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, // use WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY before Oreo
    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, // whatever
    PixelFormat.TRANSLUCENT // whatever
)

view.isFocusableInTouchMode = true
view.setOnKeyListener { view, keyCode, event ->
    when (keyCode) {
        KeyEvent.KEYCODE_BACK -> {
            // do your work here
            true
        }
        else -> false
    }
}

val windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
windowManager.addView(view, windowParam)
person khcpietro    schedule 19.10.2018