28

In facebook chatheads, that are part of the facebook messenger app, I noticed the following behavior: As far as I can see, the chat head itself and the opened chat screen are all parts of a service. No activity is involved.

How can I be sure? After I press home on the opened chat screen, it gets minimized back to a chat head, and I can immediately reopen the chat screen. If the chat screen was an activity, then reopening the activity via startActivity(intent) after the home button was pressed, would delay the start of the activity, as specified here: Starting an activity from a service after HOME button pressed without the 5 seconds delay

and here: Reason for 5 sec delay to show an activity on pressing the home button?

in my service onCreate method, i use the following code to display a UI from service:

public class ServiceTest extends Service {
...
    @Override 
    public void onCreate() {
        super.onCreate();

        windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);

        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.TYPE_PHONE,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
            PixelFormat.TRANSLUCENT);

        windowManager.addView(someView, params);
    }
....
}

Does anyone have an idea how can I receive the 'home button pressed' event directly from a service displaying a UI? I would like to minimize my view (similar to facebook chat heads) when the user presses the home button.

Community
  • 1
  • 1
Michael Gregorov
  • 541
  • 1
  • 6
  • 14
  • I was supprised to see that you've right. I've did my own experiment, and indeed the activity that was in forgreound did not paused when the messages popup opened => what proves that no new activity involved. I'll do my research how it possible. that's indeed interesting – Tal Kanel Aug 13 '13 at 13:59
  • Thanks! hoping your research will fare better then mine... – Michael Gregorov Aug 13 '13 at 14:21
  • By the way, notice that they also catch the recent tasks button, if that is of any help. – Michael Gregorov Aug 13 '13 at 14:25
  • How do you start your service? – ozbek Aug 16 '13 at 11:41
  • By using `startService(new Intent(this, ServiceTest.class)` from an activity, but I can also start it by binding from an activity. How is that relevant? – Michael Gregorov Aug 16 '13 at 12:03
  • You could listen to user actions from that activity – ozbek Aug 16 '13 at 12:05
  • I use startService, and the calling activity isn't guaranteed to stay alive. And as mentioned in my post and @Tal Kanel comment, the facebook chathead feature doesn't use an activity to detect the home button pressed event. – Michael Gregorov Aug 16 '13 at 12:10

8 Answers8

12

well after doing my own research about that issue, I've came with the following conclusions:

Facebook "intercepting" the navigation buttons by providing the flag TYPE_SYSTEM_OVERLAY attribute. look on this post answer provided by @jawsware

doing what he advices not to do - will lead to the "affect" of controling the navigation buttons

using this flag provides focus on your overlay, and takes the focus from the activity behind.

with the onFocusChangedListener view callback or the OnKey listener they reacts to it with closing the full screen mode of the overlay.

that's also explains how they reacts to it from the 3 navigation buttons - home / back / recent tasks

Community
  • 1
  • 1
Tal Kanel
  • 10,475
  • 10
  • 60
  • 98
5

You can achieve it by overriding View.onCloseSystemDialogs(). You may need to check View.java because this method is not visible in API doc.

Obviously, we can not detect 'Home button pressed' in general. In most cases, the window is created by startActivity(). And CloseSystemDialogs routine, which is caused by 'Home button pressed', is handled by internal class of android framework, PhoneWindow.DecorView as a view ancestor of ViewRootImpl. But when we add views to window manager directly, view ancestor of ViewRootImpl is being our custom view but PhoneWindow.DecorView. Checking the source code of ViewRootImpl.java might be easier to understand.

KeyEvent.KEYCODE_APP_SWITCH causes closeSystemDialogs routine, too. This explains how facebook chatheads works.

Box Jeon
  • 91
  • 4
  • 1
    Not sure how long the onCloseSystemDialogs() method will be around, but overriding it worked like a charm for an overlay view. – Tom Bollwitt Apr 16 '14 at 19:12
2

ok complete edit of my answer,

Override below method in your activity.

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

After overriding above method, now you can easily listen HOME Key press in your activity using onKeyDown() method.

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

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

try this hope it helps

Vardhan D G
  • 316
  • 1
  • 4
  • 13
  • 1
    This is a service, not an activity. therefore - no onStop(). – Michael Gregorov Aug 07 '13 at 12:28
  • Whenever I try to change the LayoutParams from `WindowManager.LayoutParams.TYPE_PHONE` to `WindowManager.LayoutParams.TYPE_KEYGUARD`, I get the following exception: Unable to add window android.view.ViewRootImpl$W@b3010920 -- permission denied for this window type – Michael Gregorov Aug 07 '13 at 14:01
  • And as I mentioned, this is a service, not an activity. – Michael Gregorov Aug 07 '13 at 14:16
  • 1
    Even from an activity this way of detecting HOME button won't work anymore. If you read its [documentation](http://developer.android.com/reference/android/view/KeyEvent.html#KEYCODE_HOME), it says :`This key is handled by the framework and is never delivered to applications.` – Shobhit Puri Aug 17 '13 at 03:35
2

You can monitor (e.g. check every 200 ms) the top running activity, and see if it is your activity or some other activity, and know when it changes. This will also let you handle cases like an incoming call.

ActivityManager am = (ActivityManager) context.getSystemService(Activity.ACTIVITY_SERVICE);
am.getRunningTasks(1).get(0).topActivity...
yoah
  • 7,180
  • 2
  • 30
  • 30
  • Already thought about it, but it doesn't seem like a very elegant way. Besides, in order to achieve that I need to use the permissions `android.permission.GET_TASKS`. If yo look at the facebook messenger app permissions, you can see they don't use it. So this is not the technique facebook chathead is using. – Michael Gregorov Aug 17 '13 at 09:37
  • Oh, and one other thing. The home button pressed event is caught even if the chat screen is open on top of the home screen. So in that case this technique wouldn't work. – Michael Gregorov Aug 17 '13 at 11:34
2

Here is working sample code.

mLinear =  new LinearLayout(getApplicationContext()) {

          //home or recent button
          public void onCloseSystemDialogs(String reason) {
              //The Code Want to Perform. 
          }

          @Override
          public boolean dispatchKeyEvent(KeyEvent event) {
              if (event.getKeyCode() == KeyEvent.KEYCODE_BACK
                || event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP
                || event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_DOWN
                || event.getKeyCode() == KeyEvent.KEYCODE_CAMERA ) {

              //The Code Want to Perform.
              }
          return super.dispatchKeyEvent(event);
          }
};

mLinear.setFocusable(true);

View mView = inflate.inflate(R.layout.floating_panel_layout, mLinear);
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);

//params
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
             width,
             height,
             WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
             WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                    | WindowManager.LayoutParams.FLAG_FULLSCREEN
                    | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                    | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
              PixelFormat.TRANSLUCENT);
params.gravity = Gravity.LEFT | Gravity.CENTER_VERTICAL;
wm.addView(mView, params);
2

This is my way. It work fine. Put it in onReceive() function.

if (intent.getAction().equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
    {
        String reason = intent.getStringExtra(SYSTEM_REASON);

        //Toast.makeText(context,"ACTION_CLOSE_SYSTEM_DIALOGS : Reason : " + reason ,Toast.LENGTH_LONG).show();

        // Detect home screen key press or "recent app" key pressed when screen is in unlocked state
        if (reason != null)
        {
            if (reason.equals(SYSTEM_HOME_KEY))
            {
            // For Home press
            }
            else if (reason.equals(SYSTEM_RECENT_APPS))
            {
            // For long press
            }
        }
    }
1

I'm fairly sure facebook doesn't listen for home button presses, because their logic requires the chat heads to show up regardless of what app is visible, as long as it isn't facebook.

A simple and crude way to do this would be to maintain a static flag visible in your Application singleton, and modify it from every Activity you have. In the onPause(), set it to false, and in onResume() and onCreate() set it to true.

Then simply check the state of this flag, and act accordingly. If its true, it means your app is visible.

You might want to add a small pause before you act on it to prevent your injected Views from flickering each time an Activity is changed.

Raghav Sood
  • 81,899
  • 22
  • 187
  • 195
  • 2
    It's not the chat heads that listens to the home button presses. The 'Home press' listener is in the chat screen that is being opened by pressing on the chat head. The chat screen UI also comes from a service. After pressing on the home button, the chat screen minimizes back to the chat head. In my case (and facebook case i guess), I don't have an activity that can listen to an `onPause()` and `onResume()`. – Michael Gregorov Aug 07 '13 at 15:24
0

You can add the category HOME to your manifest file.

user2606414
  • 718
  • 2
  • 7
  • 13