How to detect whether Notifications Panel is expanded? - android

I found this way to expanded Notifications Panel
Object sbservice = getSystemService("statusbar");
Class<?> statusbarManager = Class.forName("android.app.StatusBarManager");
Method showsb;
if (Build.VERSION.SDK_INT >= 17) {
showsb = statusbarManager.getMethod("expandNotificationsPanel");
} else {
showsb = statusbarManager.getMethod("expand");
}
showsb.invoke(sbservice);
After looking into source, I also found collapsePanels method to collapse the notification
But I cannot find any way to detect the notification panel status
Because I want to check whether it is opened or closed, and then decide I should open it or close it
How can I know this status?

Though it's very late but it might help someone
Refer to this
You can use below method for detecting the notification panel pull,
In your manifest file
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR"/>
In your activity override the onWindowFocusChanged() and write the
below code.
This uses the permission
#Override public void onWindowFocusChanged(boolean hasFocus) {
try
{
if(!hasFocus)
{
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)
{
if(!hasFocus)
{
try {
Object service = getSystemService("statusbar");
Class<?> statusbarManager = Class.forName("android.app.StatusBarManager");
Method collapse = statusbarManager.getMethod("collapse");
collapse .setAccessible(true);
collapse .invoke(service);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ex.printStackTrace();
}
} }

Related

Disable status bar pull in Oreo

The method for kiosking an application by disabling pull and click of the status bar does not work on android 8. As anserwed on How to disable status bar click and pull down in Android?
You can lay a window over the status bar to disable any touch or pulling down.
As described by this answer, this method of doing it does works on android 7 and below however this method does not work on android 8(oreo).
I have tested it on android 7 and less and it works, but the status bar still pulls down when pulled on android 8.
If you have a solution on this please assist.
Thank you all.
For and 8 and above you cant realy fully overylay a view over other apps, so what you have to do is, when you pull the drawer down, return the drawer back up so fast that the user wont be able to click anything on it. This is the code that does that. Make sure you are doing this on an activity.
#Override
public void onWindowFocusChanged(boolean hasFocus) {
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (!hasFocus) {
Intent closeDialog = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
sendBroadcast(closeDialog);
// Method that handles loss of window focus
new BlockStatusBar(this,false).collapseNow();
}
}
}
Then the helper class that is doing the job of hiding the status bar is as indicated below.
public class BlockStatusBar {
Context context;
// To keep track of activity's window focus
boolean currentFocus;
// To keep track of activity's foreground/background status
boolean isPaused;
public static Handler collapseNotificationHandler;
Method collapseStatusBar = null;
public BlockStatusBar(Context context,boolean isPaused) {
this.context=context;
this.isPaused=isPaused;
collapseNow();
}
public void collapseNow() {
// Initialize 'collapseNotificationHandler'
if (collapseNotificationHandler == null) {
collapseNotificationHandler = new Handler();
}
// If window focus has been lost && activity is not in a paused state
// Its a valid check because showing of notification panel
// steals the focus from current activity's window, but does not
// 'pause' the activity
if (!currentFocus && !isPaused) {
// Post a Runnable with some delay - currently set to 300 ms
collapseNotificationHandler.postDelayed(new Runnable() {
#Override
public void run() {
// Use reflection to trigger a method from 'StatusBarManager'
Object statusBarService = context.getSystemService("statusbar");
Class<?> statusBarManager = null;
try {
statusBarManager = Class.forName("android.app.StatusBarManager");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
// Prior to API 17, the method to call is 'collapse()'
// API 17 onwards, the method to call is `collapsePanels()`
if (Build.VERSION.SDK_INT > 16) {
collapseStatusBar = statusBarManager .getMethod("collapsePanels");
} else {
collapseStatusBar = statusBarManager .getMethod("collapse");
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
collapseStatusBar.setAccessible(true);
try {
collapseStatusBar.invoke(statusBarService);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
// Check if the window focus has been returned
// If it hasn't been returned, post this Runnable again
// Currently, the delay is 100 ms. You can change this
// value to suit your needs.
if (!currentFocus && !isPaused) {
collapseNotificationHandler.postDelayed(this, 100L);
}
if (!currentFocus && isPaused) {
collapseNotificationHandler.removeCallbacksAndMessages(null);
}
}
}, 1L);
}
}
}

How to turn on/off wifi hotspot programmatically in Android 8.0 (Oreo)

I know how to turn on/off wifi hot spot using reflection in android using below method.
private static boolean changeWifiHotspotState(Context context,boolean enable) {
try {
WifiManager manager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
Method method = manager.getClass().getDeclaredMethod("setWifiApEnabled", WifiConfiguration.class,
Boolean.TYPE);
method.setAccessible(true);
WifiConfiguration configuration = enable ? getWifiApConfiguration(manager) : null;
boolean isSuccess = (Boolean) method.invoke(manager, configuration, enable);
return isSuccess;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
But the above method is not working Android 8.0(Oreo).
When I execute above method in Android 8.0, I am getting below statement in logcat.
com.gck.dummy W/WifiManager: com.gck.dummy attempted call to setWifiApEnabled: enabled = true
Is there any other way to on/off hotspot on android 8.0
I thought the LocalOnlyHotspot route was the way to, but as #edsappfactory.com said in the comments - it only gives closed network, no internet access.
In Oreo hot-spotting/tethering moved to ConnectionManager, and its annotated #SystemApi, so (nominally) inaccessible.
As part of something else I was doing, I made an app and put it on github here. It uses reflection to get at the function and DexMaker to generate a subclass of ConnectionManager.OnStartTetheringCallback (which is also inaccessible).
Think it all works okay - bit rough around the edges, so please feel free to make better!
Relevant bits of code are in:
MyOreoWifiManager and;
CallbackMaker
I lost patience trying to get my DexMaker-generated callback to fire the MyOnStartTetheringCallback so all that code is in disarray and commented out.
Finally I got the solution.
Android 8.0, they provided public api to turn on/off hotspot. WifiManager
Below is the code to turn on hotspot
private WifiManager.LocalOnlyHotspotReservation mReservation;
#RequiresApi(api = Build.VERSION_CODES.O)
private void turnOnHotspot() {
WifiManager manager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
manager.startLocalOnlyHotspot(new WifiManager.LocalOnlyHotspotCallback() {
#Override
public void onStarted(WifiManager.LocalOnlyHotspotReservation reservation) {
super.onStarted(reservation);
Log.d(TAG, "Wifi Hotspot is on now");
mReservation = reservation;
}
#Override
public void onStopped() {
super.onStopped();
Log.d(TAG, "onStopped: ");
}
#Override
public void onFailed(int reason) {
super.onFailed(reason);
Log.d(TAG, "onFailed: ");
}
}, new Handler());
}
private void turnOffHotspot() {
if (mReservation != null) {
mReservation.close();
}
}
onStarted(WifiManager.LocalOnlyHotspotReservation reservation) method will be called if hotspot is turned on.. Using WifiManager.LocalOnlyHotspotReservation reference you call close() method to turn off hotspot.
Note:
To turn on hotspot, the Location(GPS) should be enabled in the device. Otherwise, it will throw SecurityException
As per Jon suggestion, I got another way to enable WifiHotSpot in Android Oreo and above.
public boolean enableTetheringNew(MyTetheringCallback callback) {
File outputDir = mContext.getCodeCacheDir();
try {
proxy = ProxyBuilder.forClass(classOnStartTetheringCallback())
.dexCache(outputDir).handler(new InvocationHandler() {
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
switch (method.getName()) {
case "onTetheringStarted":
callback.onTetheringStarted();
break;
case "onTetheringFailed":
callback.onTetheringFailed();
break;
default:
ProxyBuilder.callSuper(proxy, method, args);
}
return null;
}
}).build();
} catch (IOException e) {
e.printStackTrace();
}
ConnectivityManager manager = (ConnectivityManager) mContext.getApplicationContext().getSystemService(ConnectivityManager.class);
Method method = null;
try {
method = manager.getClass().getDeclaredMethod("startTethering", int.class, boolean.class, classOnStartTetheringCallback(), Handler.class);
if (method == null) {
Log.e(TAG, "startTetheringMethod is null");
} else {
method.invoke(manager, TETHERING_WIFI, false, proxy, null);
}
return true;
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return false;
}
private Class classOnStartTetheringCallback() {
try {
return Class.forName("android.net.ConnectivityManager$OnStartTetheringCallback");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}

Android Hide & Disable Notification (Status) Bar

I've been able to hide the notification bar by going full screen, by using the code below
android:theme="#android:style/Theme.Holo.NoActionBar.Fullscreen"
or
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
But what I am trying to do is completely disable the status bar. I'm in what's known as a "kiosk mode" and I'd like to make sure that a user can not slide their finger down from the top bezel. Both of the solutions above work for hiding the notification bar, but it does not work for disabling it completely within the app.
Is this possible?
Instead of following links to other answers, here's what I did.
This solution does not disallow a user to "view" the status bar in it's 'preview' state if pulled down (even in a full screen app), but it DOES disallow a user from pulling the status bar down to it's full state to see settings, notifications, etc.
You must first add the permissions in your AndroidManifest.xml
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
Then add another class (Java file) called customViewGroup.java and place this code in it:
import android.content.Context;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewGroup;
public class customViewGroup extends ViewGroup {
public customViewGroup(Context context) {
super(context);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.v("customViewGroup", "**********Intercepted");
return true;
}
}
After you have both of those set up, you can then add this into your main onCreate()
WindowManager manager = ((WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE));
WindowManager.LayoutParams localLayoutParams = new WindowManager.LayoutParams();
localLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
localLayoutParams.gravity = Gravity.TOP;
localLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
// this is to enable the notification to recieve touch events
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
// Draws over status bar
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
localLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
localLayoutParams.height = (int) (50 * getResources().getDisplayMetrics().scaledDensity);
localLayoutParams.format = PixelFormat.TRANSPARENT;
customViewGroup view = new customViewGroup(this);
manager.addView(view, localLayoutParams);
This solution disables the ability to pull the status bar down always, until your app is closed. You'll have to remove this action on pause if you don't want to close your app every time.
Credit goes to #Abhimaan Madhav from This Answer
I think permanently disable the status bar is difficult. I am also working on the same concept and did lots of R&D and found that below code can be useful. if the user tries to expand the status bar then within a sec it will pull back it and it will work on oreo as well. I have tried on different OS.
public class BlockStatusBar {
Context context;
// To keep track of activity's window focus
boolean currentFocus;
// To keep track of activity's foreground/background status
boolean isPaused;
public Handler collapseNotificationHandler;
Method collapseStatusBar = null;
public BlockStatusBar(Context context, boolean isPaused) {
this.context = context;
this.isPaused = isPaused;
collapseNow();
}
public void collapseNow() {
// Initialize 'collapseNotificationHandler'
if (collapseNotificationHandler == null) {
collapseNotificationHandler = new Handler();
}
// If window focus has been lost && activity is not in a paused state
// Its a valid check because showing of notification panel
// steals the focus from current activity's window, but does not
// 'pause' the activity
if (!currentFocus && !isPaused) {
Runnable myRunnable = new Runnable() {
public void run() {
// do something
try {
// Use reflection to trigger a method from 'StatusBarManager'
Object statusBarService = context.getSystemService("statusbar");
Class<?> statusBarManager = null;
try {
statusBarManager = Class.forName("android.app.StatusBarManager");
} catch (ClassNotFoundException e) {
Log.e(LOG_TAG, "" + e.getMessage());
}
try {
// Prior to API 17, the method to call is 'collapse()'
// API 17 onwards, the method to call is `collapsePanels()`
if (Build.VERSION.SDK_INT > 16) {
collapseStatusBar = statusBarManager.getMethod("collapsePanels");
} else {
collapseStatusBar = statusBarManager.getMethod("collapse");
}
} catch (NoSuchMethodException e) {
Log.e(LOG_TAG, "" + e.getMessage());
}
collapseStatusBar.setAccessible(true);
try {
collapseStatusBar.invoke(statusBarService);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
// Check if the window focus has been returned
// If it hasn'kioskthread been returned, post this Runnable again
// Currently, the delay is 100 ms. You can change this
// value to suit your needs.
if (!currentFocus && !isPaused) {
collapseNotificationHandler.postDelayed(this, 100L);
}
if (!currentFocus && isPaused) {
collapseNotificationHandler.removeCallbacksAndMessages(null);
}
} catch (Exception e) {
Log.e("MSG", "" + e.getMessage());
}
}
};
// Post a Runnable with some delay - currently set to 300 ms
collapseNotificationHandler.postDelayed(myRunnable, 1L);
}
}
}

Toggle the notification bar in android

I know I can expand the notification bar by reflection
Class<?> statusbarManager = Class.forName( "android.app.StatusBarManager" );
Method showsb;
if (Build.VERSION.SDK_INT >= 17) {
showsb = statusbarManager.getMethod("expandNotificationsPanel");
}
else {
showsb = statusbarManager.getMethod("expand");
}
showsb.invoke( getSystemService( "statusbar" ) );
However, is there a way to expand it if it is collapsed, and collapse it if it is already expanded?
There is a toggle function for StatusBarManager in the android docs but it doesn't work for me.
EDITED
I am calling this function from inside a bound service.
I think that you may be missing the needed permissions. Make sure that you have the EXPAND_STATUS_BAR permission in your Manifest.xml:
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
Looking around, I found this code:
Object sbservice = getSystemService( "statusbar" );
Class<?> statusbarManager = Class.forName( "android.app.StatusBarManager" );
Method showsb = statusbarManager.getMethod( "expand" );
showsb.invoke( sbservice );
You may want to try it.
EDIT:
For actually detecting if it is down or not, see this answer. Here's what it looks like:
Override the onWindowFocusChanged() method in your activity with the code below:
In the permissions:
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
The overriding:
#Override
public void onWindowFocusChanged(boolean hasFocus)
{
try
{
if(!hasFocus)
{
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)
{
if(!hasFocus)
{
try {
Object service = getSystemService("statusbar");
Class<?> statusbarManager = Class.forName("android.app.StatusBarManager");
Method collapse = statusbarManager.getMethod("collapse");
collapse .setAccessible(true);
collapse .invoke(service);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ex.printStackTrace();
}
}
}
The below code will work for new and old Android OS.
You need to enable the EXPAND_STATUS_BAR permission in AndroidManifest.xml.
try {
Object sbservice = getSystemService("statusbar");
Class<?> statusbarManager = Class.forName("android.app.StatusBarManager");
Method expandMethod;
expandMethod = statusbarManager.getMethod(Build.VERSION.SDK_INT >= 17 ? "expandNotificationsPanel" : "expand");
expandMethod.invoke(sbservice);
} catch (Exception ex) {
ex.printStackTrace();
}

I can not expand statusbar no my nexus 4 with android 4.2

I can not expand statusbar no my nexus 4 with android 4.2,but other level is running OK ,My code is:
public void OpenNotify() {
// TODO Auto-generated method stub
int currentApiVersion = android.os.Build.VERSION.SDK_INT;
try {
Object service = getSystemService("statusbar");
Class<?> statusbarManager = Class.forName("android.app.StatusBarManager");
if (service != null) {
/*Method expand = service.getClass()statusbarManager.getMethod("expand");
expand.invoke(service);*/
if (currentApiVersion <= 16) {
Method collapse = statusbarManager.getMethod("collapse");
collapse.setAccessible(true);
collapse.invoke(service);
} else {
Method collapse2 = statusbarManager.getMethod("collapsePanels");
collapse2.setAccessible(true);
collapse2.invoke(service);
}
}
} catch (Exception e) {
}
}
And
How can I do ?
public void OpenNotify() {
// TODO Auto-generated method stub
int currentApiVersion = android.os.Build.VERSION.SDK_INT;
try {
Object service = getSystemService("statusbar");
Class<?> statusbarManager = Class
.forName("android.app.StatusBarManager");
Method expand = null;
if (service != null) {
if (currentApiVersion <= 16) {
expand = statusbarManager.getMethod("expand");
} else {
expand = statusbarManager
.getMethod("expandNotificationsPanel");
}
expand.setAccessible(true);
expand.invoke(service);
}
} catch (Exception e) {
}
}
In this way ,it runs OK !
Here's how I'm doing it and it actually works:
private static final String collapse_method = Build.VERSION.SDK_INT > 16
? "collapsePanels"
: "collapse";
try {
Object obj = context.getSystemService("statusbar");
Class.forName("android.app.StatusBarManager")
.getMethod(collapse_method, new Class[0])
.invoke(obj, (Object[]) null);
} catch (Exception e) {
Log.e("STATUSBAR", "Failed to collapse status panel: "+e);
// do nothing, it's OK
}
You should check which exactly exception was thrown (as it is possibly the case) and figure out the exact reason.

Categories

Resources