android intercept recent apps button - android

I have an application meant for children and I do not want them to be able to click the "Recent Apps" button (the one that looks like two rectangles on top of each other). I am taking care of capturing the back button and the home button and I have searched and read a lot about about trying to capture the Recent Apps button, but most say you cannot or the way they do it is very questionable.
The App "Kids Place" pops up a view that says "Action Not Allowed" and redirects you to its home screen if you press the Recent Apps button, this works even if you are in a different app, so how are they doing this?
Any suggestions, hints would be appreciated.
Thanks.

After lots of searching and coding the current best solution I found is the following:
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (!hasFocus) {
windowCloseHandler.postDelayed(windowCloserRunnable, 0);
}
}
private void toggleRecents() {
Intent closeRecents = new Intent("com.android.systemui.recent.action.TOGGLE_RECENTS");
closeRecents.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
ComponentName recents = new ComponentName("com.android.systemui", "com.android.systemui.recent.RecentsActivity");
closeRecents.setComponent(recents);
this.startActivity(closeRecents);
}
private Handler windowCloseHandler = new Handler();
private Runnable windowCloserRunnable = new Runnable() {
#Override
public void run() {
ActivityManager am = (ActivityManager)getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
if (cn != null && cn.getClassName().equals("com.android.systemui.recent.RecentsActivity")) {
toggleRecents();
}
}
}
This requires that you use <uses-permission android:name="android.permission.GET_TASKS" />
When using this approach when the user presses the recent apps button it will cause your activity will go through the activity lifecycle as follows: onPause -> onWindowFocusChanged -> onResume.
To the user the behavior appears that pressing the recent apps button has no response. NOTE: that I have found that if you press the recent apps button quickly it will display that view for brief time.
This is not the best solution, but it is a stab at it. If you have a better solution please share.

The best way I have found is to do this:
public class BaseActivity extends Activity {
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
Log.d("Focus debug", "Focus changed !");
if(!hasFocus) {
Log.d("Focus debug", "Lost focus !");
Intent closeDialog = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
sendBroadcast(closeDialog);
}
}
}// all credit goes here: http://www.juliencavandoli.com/how-to-disable-recent-apps-dialog-on-long-press-home-button/
This is not my own code, but this just hides the recent apps list from showing.

In the accepted answer you're using ClassName only for Android 4.2 - 4.4. It won't work on 5.0 and higher, or Android 4.1.
Here is the list of ClassNames for main Android versions:
Android 4.1: "com.android.internal.policy.impl.RecentApplicationsDialog"
Android 4.2 - 4.4: "com.android.systemui.recent.RecentsActivity"
Android 5.0 - 7.1: "com.android.systemui.recents.RecentsActivity" ("s" letter was added)
The best solution for you will be to utilize Accessibility Service.
Override onAccessibilityEvent() method, filter out ClassNames listed above and do something when you detect this event. For example simulate pressing the 'Home' button. You can do this by making a global action in Accessibility Service.

thanks esse for solution for higher SDK! I missed it.
But in my case I need to duplicate call (or effect is unstable)
if (SDK>15){
windowCloseHandler.postDelayed(windowCloserRunnable, 10);
windowCloseHandler.postDelayed(windowCloserRunnable, 300);
}

If you are interested in disabling all system buttons, may be the option would be to kill system bar completely, see Easy way to hide system bar on Android ICS

Related

Android dismiss a system dialog programmatically

I want to dismiss a system generated alert dialog programmatically. I have tried all solutions provided here(stackoverflow) but it does not seem to work.
This is the accepted answer mostly everywhere, but it only dismisses notification panel and recent tasks menu.
I have tested it on devices with os version 4.0.3, 4.2.2, 4.4.2 and 5.1.1, it has the same behavior on all of them. There are apps which can actually dismiss all system dialogs (Mubble). Can someone suggest how it is done?
Thanks
The usual answer to this is
sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
However, this does not work for everything. In particular, on some devices it does not work for the "recent apps" list.
The solution is to detect when your app loses focus and then move your app to the front. Note that your app will need the permission android.permission.REORDER_TASKS to do this.
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
this.keepFocus = true;
}
if (! hasFocus && this.keepFocus) {
ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
am.moveTaskToFront(getTaskId(), ActivityManager.MOVE_TASK_WITH_HOME );
}
}

Recent apps button in android

I'm newbie in android development. I've created an android app which shows the current location, and I need to override the recent apps button in android and add some options, is that possible?
I faced similar problem, so, i needed to know when the user press the recent apps button (menu key button in old versions of android).
after a several hours of research i didn't found an event to catch when the home button or the menu button is pressed.
i found that it's take a longer time to call onStop() when the user press the home button, so i figure a trick to distinguish between these tow buttons relating on time of response and overriding two methods:
#Override
public void onUserLeaveHint() {
// do stuff
super.onUserLeaveHint();
userLeaveTime = System.currentTimeMillis() ;
isNotPower = true;
}
#Override
public void onStop() {
super.onStop();
if (isNotPower) {
defStop = System.currentTimeMillis() - userLeaveTime;
if (defStop > 200 ) {
// when the home button pressed
}
if (defStop < 200 ) {
// when the recent apps button pressed
}
}
isNotPower = false;
}
the isNotPower parameter that's to check that not the power button is pressed-
when the power button pressed the onStop() method is called but not th onUserLeaveHint().
Is that possible to override recent apps button in android?
Not from an ordinary SDK app.
You are welcome to build your own custom ROM that modifies the overview screen, then convince people to install your custom ROM on their devices.

WebView control with focus blocks media button events in Android 5

Edit May 8, 2016
I found the reason for my troubles receiving media button events in my app. See the answer below. I editid the title of this question to make finding the issue easier. The original title was "What can possibly block media buttons on Android Lollipop".
Original question, April 2015:
Scratching my head and staring at all the code for 2 days to no avail... My Android app is supposed to react to media buttons (e.g. from a headset, test it with a Bluetooth headset), like play/pause, next, rewind. Works fine on KitKat and below. I swear it even worked on Lollipop as well up until a few days ago. Now nothing, no trace that it hears media button presses. Would anyone have a quick suggestion where to look for the trouble? Tested with two Lollipop phones, same Bluetooth headset, and the same headset works fine for lower versions of Android. Also the same headset works fine, media buttons presses heard in other apps. What possibly could I break???
I now tested both old and new ways of listening to media buttons. In AndroidManifest.xml:
<receiver android:name=".MediaButtonIntentReceiver" android:enabled="false">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>
The fact that it says enabled="false" is OK - I enable and disable the receiver as needed, and the MediaButtonIntentReceiver.java gets the events fine on KitKat and lower, complete silence on Lollipop.
I next switched to the latest appcompat (v22.1) and tried using MediaSessionCompat object and related code as follows. This worked great in a small test app, just one activity that I wrote - got my Logcat messages confirming that it hears media keys pressed on Lollipop. But when inserted into my app, again does not work on Lollipop. What the heck???
private MediaSessionCompat _mediaSession;
final String MEDIA_SESSION_TAG = "some_tag";
void initMediaSessions(Context context) {
// Start a new MediaSession
if (context == null)
return;
Lt.d("initMediaSession()...");
ComponentName eventReceiver = new ComponentName(context.getPackageName(), MediaButtonIntentReceiver.class.getName());
PendingIntent buttonReceiverIntent = PendingIntent.getBroadcast(
context,
0,
new Intent(Intent.ACTION_MEDIA_BUTTON),
PendingIntent.FLAG_UPDATE_CURRENT
);
// Parameters for new MediaSessionCompat():
// context The context.
// tag A short name for debugging purposes.
// mediaButtonEventReceiver The component name for your receiver. This must be non-null to support platform
// versions earlier than LOLLIPOP. May not be null below Lollipop.
// mbrIntent The PendingIntent for your receiver component that handles media button events. This is optional
// and will be used on JELLY_BEAN_MR2 and later instead of the component name.
_mediaSession = new MediaSessionCompat(context, MEDIA_SESSION_TAG, eventReceiver, buttonReceiverIntent);
_mediaSession.setCallback(new MediaSessionCallback());
_mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
_mediaSession.setActive(true);
PlaybackStateCompat state = new PlaybackStateCompat.Builder()
.setActions(
PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PLAY_PAUSE |
PlaybackStateCompat.ACTION_PAUSE |
PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS)
.setState(PlaybackStateCompat.STATE_STOPPED, 0, 1, SystemClock.elapsedRealtime())
.build();
_mediaSession.setPlaybackState(state);
}
final class MediaSessionCallback extends MediaSessionCompat.Callback {
#Override
public void onPlay() {
Lt.d("play");
}
#Override
public void onPause() {
Lt.d("pause");
}
#Override
public void onStop() {
Lt.d("stop.");
}
#Override
public void onSkipToNext() {
Lt.d("skipToNext");
}
#Override
public void onSkipToPrevious() {
Lt.d("skipToPrevious");
}
#Override
public boolean onMediaButtonEvent(final Intent mediaButtonIntent) {
Lt.d("GOT MediaButton EVENT");
KeyEvent keyEvent = (KeyEvent) mediaButtonIntent.getExtras().get(Intent.EXTRA_KEY_EVENT);
// ...do something with keyEvent, super... does nothing.
return super.onMediaButtonEvent(mediaButtonIntent);
}
}
May 8, 2016: Solved for sure
I finally found the reason why media buttons did not work with my app, when the main screen was on. I later noticed that they worked if any other app was on active, or screen turned off etc. - but not when my main activity was up. Turns out that in Android 5 at least, the WebView control I use on my main screen blocks the media buttons, if it has focus. Calling:
webView.setFocusable(false);
or
webView.setFocusableInTouchMode(false);
or setting the same attribute in the layout, solves the problem. With Android 6 the focusable setting of WebView did not matter for receiving media button events.
Apr. 24, 2015: Solved? - not quite...
From the comment to this post: Handling media buttons in Android 5.0 Lollipop by #mangini:
To receive media button events while your activity is in foreground,
call setMediaController() on your activity with a MediaController
instance connected to your active MediaSession. – mangini Jan 23 at
18:35
I was testing all the time with screen on and my activity opened on it, as it always worked that way under older versions of Android too... As soon as I pressed the home button, or turned off the screen, my old MediaButtonIntentReceiver started working normally.
Update Media button events come to my app only if my lock screen manager, using the depreciated RemoteControlClient is enabled, and of course my activity is not in foreground. If I disable the lock screen manager (which is an option in my app) and delete RemoteControlClient, again, I can't get any media events.
This issue under Lollipop is a complete mess. Or at least a complete mess about it exists in my mind right now. Wish someone posted a clear code sample for handling media buttons, that would work under older versions of Android, and Lollipop...
Greg
Pls override the function return false.
#Override
public boolean dispatchKeyEvent(KeyEvent event) {
return false;
}

Close status bar when opened: Android

I am trying to come up with some ways of disabling the status bar without hiding it completely. This is an introlude attempt at disabling status bars in 3rd party apps. For now, I want to disable it in my own app, and then eventually create a background service to see if I can do so in other apps. The app I am creating is an operating system for children, and I am trying to develop a closed system.
Here is what I have tried. My initial idea was to monitor when status bar was accessed and then closing it.
#Override
public void onWindowFocusChanged(boolean hasFocus) {
Log.i(TAG, "onWindowFocusChanged()");
try {
if (!hasFocus) {
Log.d(TAG, "close status bar attempt");
Object service = getSystemService("statusbar");
Class<?> statusbarManager = Class
.forName("android.app.StatusBarManager");
Method collapse = statusbarManager.getMethod("collapse");
collapse.setAccessible(true);
collapse.invoke(service);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
This is the method I used. It works for detecting when status bar is being accessed, however, it does not close the status bar once it has focus. What am I missing? Thanks in advance.
I have found answer to my question. First, I was missing the following permission
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
With that permission, the following code now works really well.
#Override
public void onWindowFocusChanged(boolean hasFocus) {
Log.i(TAG, "onWindowFocusChanged()");
try {
if (!hasFocus) {
Log.d(TAG, "close status bar attempt");
//option 1
int currentApiVersion = android.os.Build.VERSION.SDK_INT;
Object service = getSystemService("statusbar");
Class<?> statusbarManager = Class
.forName("android.app.StatusBarManager");
if (currentApiVersion <= 16) {
Method collapse = statusbarManager.getMethod("collapse");
collapse.setAccessible(true);
collapse.invoke(service);
} else {
Method collapse = statusbarManager.getMethod("collapsePanels");
collapse.setAccessible(true);
collapse.invoke(service);
}
// option 2
Intent it = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
mContext.sendBroadcast(it);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
If you notice, there is a second option as well that I found to be working good. You can comment out option 1 if you want to use option 2, or vise versa. Both accomplish the same thing, although I believe option 2 is better.
Intent it = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
mContext.sendBroadcast(it);
The only downfall I found is that it is slow(er) when closing. However, both methods collapse quick enough to where no one can click on any notifications or options in the status bar. Hopefully this is helpful to someone else. Good luck, Cheers!
I am also working on the same thing. With Android 5.0 Lolipop they have released Screen Pinning mode (which is essentially Kiosk mode) which does a few things:
The status bar is blank, and user notifications and status information are hidden.
The Home and Recent Apps buttons are hidden.
Other apps cannot launch new activities.
The current app can start new activities, as long as doing so does not create new tasks.
When screen pinning is invoked by a device owner, the user remains locked to your app until the app calls stopLockTask().
If screen pinning is activity by another app that is not a device owner or by the user directly, the user can exit by holding both the Back and Recent buttons.
You can read about it further in the Android 5.0 Lolipop release documentation.
However, if you are looking for a more controlable solution, then you may want to create a custom ROM. Here is a great overview on making Kiosk applications (which also require disabling status bar).
Developing Kiosk Mode Applications in Android Tutorial
Here's an updated answer if anyone is still looking for a solution to something like this: https://developer.android.com/training/system-ui/immersive.html
For me, I added the following in my onResume() method:
View decorView = getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
decorView.setSystemUiVisibility(uiOptions);
This allows the status bar to stay hidden but the user can still access it by swiping the edge of the screen. Then after a few seconds of inactivity it will collapse and disappear again!

Pressing HOME button does not return widget ID from configuration activity

Error: appWidgetId (EXTRA_APPWIDGET_ID) was not returned from the \widget configuration activity.
public class WidgetConfigure extends Activity {
private int widgetID;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.v("Lifecycle", "WidgetConfigure.onCreate");
setContentView(R.layout.widget_configure);
widgetID = this.getIntent().getExtras().getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
setResult(RESULT_CANCELED, new Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetID));
}
public void onButtonClicked(View view) {
Log.v("Lifecycle", "WidgetConfigure.onButtonClicked");
if (((RadioButton) this.findViewById(RefreshStrategy.SYSTEM)).isChecked()) {
WidgetProvider.configure(widgetID, true);
} else if (((RadioButton) this.findViewById(RefreshStrategy.USER)).isChecked()) {
WidgetProvider.configure(widgetID, false);
}
setResult(RESULT_OK, new Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetID));
finish();
}
}
In theory RESULT_OK should be sent after pressing button in activity and RESULT_CANCELED in any other case, but it does not happen when physical button HOME on device is pressed.
Any ideas how to fix this issue?
This is not an issue in your code - this is normal, standard Android behaviour. (Maybe a bug?)
I cannot find it documented, but if you try to add Google's widgets that require configuration, and then press HOME, you see that the widgets do not get added. We can think of the HOME key as a CANCEL button in a way.
I spent most of today thinking my own code had a bug in it, and looking for a way to fix it.
If anything, I believe this could be a bug in the framework, because the result is not delivered at all. In addition, I get a similar failure if I launch a new Activity from the config activity and then exit from there.
FWIW I get that error message on my Galaxy Nexus 4.2.1; but on the Nexus 7 4.4.2 the error is not shown, but the behaviour is the same.
Update: According to Dianne Hackborn, this is expected behaviour, at least from the user's point of view:
The normal experience for an app widget that is going to provide a
configuration UI is that pressing back or home from that will cancel
the addition.
App Widget Configuration Activity does not behave like Activity
So as a developer this may seem strange, but as a user it all works out fine in the end.

Categories

Resources