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();
}
Related
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);
}
}
}
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();
}
} }
I am trying to make an application like "Bluetooth auto tethering" on play store. I read on the forum that Android is very security-aware and will not enable this setting without user interaction.
I need some explanations about how enable bluetooth tethering.
Thank you
I don't know if this is still an issue or not, but I found that using the connect method in the reflection call works. Working off of the code that pmont used from the link in Lorelorelore's answer:
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
Class<?> classBluetoothPan = null;
Constructor<?> BTPanCtor = null;
Object BTSrvInstance = null;
Method mBTPanConnect;
try {
classBluetoothPan = Class.forName("android.bluetooth.BluetoothPan");
mBTPanConnect = classBluetoothPan.getDeclaredMethod("connect", BluetoothDevice.class);
BTPanCtor = classBluetoothPan.getDeclaredConstructor(Context.class, BluetoothProfile.ServiceListener.class);
BTPanCtor.setAccessible(true);
BTSrvInstance = BTPanCtor.newInstance(myContext, new BTPanServiceListener(myContext));
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
// If there are paired devices
if (pairedDevices.size() > 0) {
// Loop through paired devices
for (BluetoothDevice device : pairedDevices) {
try{
mBTPanConnect.invoke(BTSrvInstance, device);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
Of course, this assumes that the bluetooth is enabled, and you only have one paired device. But enabling bluetooth is pretty straightforward using standard (not reflection) calls, and you can just check for the paired device that you want to connect to in the for loop. Also, don't forget the BTPanServiceListener class from the other answer as well.
Hope this helps.
The solution above required some modification in order to work for me. Specifically, the code to enable tethering needs to be in the OnServiceConnected() method. Also I have the following permissions set in the manifest:
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
Here is my solution:
public class BluetoothTethering extends ActionBarActivity {
Object instance = null;
Method setTetheringOn = null;
Method isTetheringOn = null;
Object mutex = new Object();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bluetooth_tethering);
String sClassName = "android.bluetooth.BluetoothPan";
try {
Class<?> classBluetoothPan = Class.forName(sClassName);
Constructor<?> ctor = classBluetoothPan.getDeclaredConstructor(Context.class, BluetoothProfile.ServiceListener.class);
ctor.setAccessible(true);
// Set Tethering ON
Class[] paramSet = new Class[1];
paramSet[0] = boolean.class;
synchronized (mutex) {
setTetheringOn = classBluetoothPan.getDeclaredMethod("setBluetoothTethering", paramSet);
isTetheringOn = classBluetoothPan.getDeclaredMethod("isTetheringOn", null);
instance = ctor.newInstance(getApplicationContext(), new BTPanServiceListener(getApplicationContext()));
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
public class BTPanServiceListener implements BluetoothProfile.ServiceListener {
private final Context context;
public BTPanServiceListener(final Context context) {
this.context = context;
}
#Override
public void onServiceConnected(final int profile,
final BluetoothProfile proxy) {
//Some code must be here or the compiler will optimize away this callback.
try {
synchronized (mutex) {
setTetheringOn.invoke(instance, true);
if ((Boolean)isTetheringOn.invoke(instance, null)) {
Toast.makeText(getApplicationContext(), "BT Tethering is on", Toast.LENGTH_LONG).show();
}
else {
Toast.makeText(getApplicationContext(), "BT Tethering is off", Toast.LENGTH_LONG).show();
}
}
}
catch (InvocationTargetException e) {
e.printStackTrace();
}
catch (IllegalAccessException e) {
e.printStackTrace();
}
}
#Override
public void onServiceDisconnected(final int profile) {
}
}
}
The below code works perfectly for me
String sClassName = "android.bluetooth.BluetoothPan";
try {
Class<?> classBluetoothPan = Class.forName(sClassName);
Constructor<?> ctor = classBluetoothPan.getDeclaredConstructor(Context.class, BluetoothProfile.ServiceListener.class);
ctor.setAccessible(true);
Object instance = ctor.newInstance(getApplicationContext(), new BTPanServiceListener(getApplicationContext()));
// Set Tethering ON
Class[] paramSet = new Class[1];
paramSet[0] = boolean.class;
Method setTetheringOn = classBluetoothPan.getDeclaredMethod("setBluetoothTethering", paramSet);
setTetheringOn.invoke(instance,true);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
public class BTPanServiceListener implements BluetoothProfile.ServiceListener {
private final Context context;
public BTPanServiceListener(final Context context) {
this.context = context;
}
#Override
public void onServiceConnected(final int profile,
final BluetoothProfile proxy) {
//Some code must be here or the compiler will optimize away this callback.
Log.e("MyApp", "BTPan proxy connected");
}
#Override
public void onServiceDisconnected(final int profile) {
}
}
Here you find a similar question: Bluetooth question
Just replace "isTetheringOn" with "setBluetoothTethering" in the reflection call and pass in a boolean parameter. It should work.
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.
I want to be able to turn off overscroll (the glowing effect when reaching the top or bottom of a page in 2.3+) however I also want my code to run in older versions of android that don't even have overscroll functionality. As per the documentation here: Android Backwards Compatibility I am using reflection in my custom webview class to call setOverScrollMode however everytime I call this on a device running 2.3.4, I get a NoSuchMethodException. Any idea why I can't retrieve this method?
Strangely, if I just call setOverScrollMode without any reflection, it works, so the method is definitely there.
public class MyWebView extends WebView{
public void compatibilitySetOverScroll(){
try {
Method mWebview_SetOverScroll = WebView.class.getMethod("setOverScrollMode", new Class[] { Integer.class } );
/* success, this is a 2.3+ */
if (mWebview_SetOverScroll != null) {
try {
mWebview_SetOverScroll.invoke(this, 2);
} catch (InvocationTargetException ite) {
throw new RuntimeException(ite.getCause());
} catch (IllegalAccessException ie) {
System.err.println("unexpected " + ie);
}
}
} catch (NoSuchMethodException nsme) {
/* failure, must be older device */
}
}
}
Try Integer.TYPE instead of Integer.class
More correct version:
public static void disableOverscroll(View view) {
Class<?> viewCls = view.getClass();
try {
Method m = viewCls.getMethod("setOverScrollMode",
new Class[] { int.class });
int OVER_SCROLL_NEVER = (Integer) viewCls.getField(
"OVER_SCROLL_NEVER").get(view);
m.invoke(view, OVER_SCROLL_NEVER);
} catch (Exception e) {
// swallow
}
}
another way :
try
{
Class<?> myTarget = Class.forName("android.widget.HorizontalScrollView");
Method myMethod = myTarget.getDeclaredMethod("setOverScrollMode", Integer.TYPE);
myMethod.invoke(scrollView, 2);
}
catch (Exception e)
{
e.printStackTrace();
}