最近做个ANDROID项目,需要屏蔽平板底层的导航按钮,难点是APP里不仅有自己写的activity,还有调用别的APP(GSF)里的activity里,不是很好实现,先将已有的不完善的解决方法写出来。
所有的实现都在NEXSU 7上测试的,别的机子没测过,不保证有用。
下面介绍的第五种方法是现在最好的解决方法。通过反射机制获取隐藏服务StatusBarService,调用它的disable()方法来屏蔽导航按钮。
一. 自己写的activity屏蔽导航按钮
1. 获取到界面上的一个view
2. 调用API
view.setSystemUiVisibility (View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
3. 不过Google认为导航按钮太重要了,当你点击屏幕的时候,导航按钮会再次显示出来。
我尝试的监听导航按钮显示事件,当收到显示事件后,再次调用上面的API来隐藏,不过这样显示有问题,屏幕有抖动。
view.setOnSystemUiVisibilityChangeListener(mSystemUiVisibilityChangeListener);
iew.OnSystemUiVisibilityChangeListener mSystemUiVisibilityChangeListener
= new View.OnSystemUiVisibilityChangeListener() {
@Override
public void onSystemUiVisibilityChange(int vis) {
if ((vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0) { //当前状态是显示,隐藏它
view.setSystemUiVisibility (View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
}
}
4. 由于目前还没有好的解决上面的问题,我就不去掉导航按钮整个一栏,而是仅仅去掉导航按钮
int INVISBLE = 0x00400000|0x00200000|0x01000000|0x00010000;//这几个整数参数API是不开放的
//INVISBLE = View.STATUS_BAR_DISABLE_EXPAND | View.STATUS_BAR_DISABLE_RECENT|View.STATUS_BAR_DISABLE_BACK|View.STATUS_BAR_DISABLE_HOME
view.setSystemUiVisibility(INVISBLE);
运行效果图如下,最下面的导航栏已经没有导航按钮了,只有一个圈圈,无法触控。
二. 调用第三方APK里的activity
由于需要GOOGLE的登录界面,我就调用了GSF提供的登录activity,方法如下:
Intent localIntent = new Intent(com.google.android.accounts.AccountIntro);
localIntent.putExtra("firstRun", true);
localIntent.putExtra("allowSkip", true);
startActivityForResult(localIntent, requestCode);
但是当登录界面弹出对话框时,导航按钮都显示出来了,这个是客户接受不了的,要我必须改掉,目前还没有解决掉(已经解决)。问题界面如下
目前想出来的方法是将下面导航栏都去掉,有三种方法,当必须都已经ROOT了。
两种方法屏蔽掉导航按钮,不需要ROOT。
第一种:
修改system/build.prop 添加系统属性 qemu.hw.mainkeys=1,重启。
缺点: 需要重启,所以就没有采用。
第二种:
删除system/app/SystemUI.apk ,这个就是用来显示系统导航栏的APK,但在需要的时候,将APK在放到app目录下,启动服务就行了。
在APK开始运行的时候,将SystemUI.apk移动另一个地方,当APK运行结束的时候,在将SystemUI.apk移回原位,启动SystemUIService服务。
源码如下:
protected static void moveSystemUIapkFile(boolean move) {
try {
File systemUIapkFile = new File("/system/app/SystemUI.apk");
Process p;
p = Runtime.getRuntime().exec("su");
// Attempt to write a file to a root-only
DataOutputStream os = new DataOutputStream(p.getOutputStream());
os.writeBytes("mount -o remount,rw /dev/block/stl6 /system\n");
if (move && systemUIapkFile.exists()) {
os.writeBytes("mv /system/app/SystemUI.apk /system/SystemUI.apk\n");
}
if (!move && !systemUIapkFile.exists()){
os.writeBytes("mv /system/SystemUI.apk /system/app/SystemUI.apk\n");
os.writeBytes("LD_LIBRARY_PATH=/vendor/lib:/system/lib am startservice -n com.android.systemui/.SystemUIService");
}
os.writeBytes("mount -o remount,ro /dev/block/stl6 /system\n");
// Close the terminal
os.writeBytes("exit\n");
os.flush();
p.waitFor();
} catch (Exception e) {
Log.e(TAG,"moveSystemUIapkFile",e);
}
}
先在开始的时候调用
moveSystemUIapkFile(true); 再在结束的时候调用
缺点: 当将SystemUI.apk移走时,平板会重启,重启后也就没有了导航栏。需要重启,也没有采用。moveSystemUIapkFile(false);
第三钟:
将系统进程com.android.systemui杀掉,不过系统会自动将它重启,那就循环的KILL。
实验下来循环时间在300MS左右是最好的,时间长了,屏幕就会由抖动现象。
首先将 killall 和 usleep 这两个命令程序拷贝到asserts目录下,源码和命令程序在这里下载。
一部分源码如下:
package com.dstvmobile.walkatouch.setupwizard; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Map; import android.content.Context; import android.content.Intent; import android.content.res.AssetManager; import android.util.Log; public enum Device { INSTANCE; private static String TAG = Device.class.getSimpleName(); private boolean mHasRootBeenChecked = false; private boolean mIsDeviceRooted = false; private boolean mHasBeenInitialized = false; private Context mAppContext = null; private boolean mSystembarVisible = true; static public void initialize(Context appContext) { if (INSTANCE.mHasBeenInitialized == true) { Log.e(TAG, "Initializing already initialized class " + TAG); } INSTANCE.mHasBeenInitialized = true; INSTANCE.mAppContext = appContext; AddKillAll(appContext, "killall"); AddKillAll(appContext, "usleep"); // moveSystemUIapkFile(true); } public static void AddKillAll(Context appContext, String commandFileName) { File killAllFile = new File("/system/xbin/"+commandFileName); if (!killAllFile.exists()) { AssetManager assetManager = appContext.getAssets(); InputStream inputStream = null; String commandFilePath = null; try { inputStream = assetManager.open(commandFileName); commandFilePath = appContext.getApplicationContext().getFilesDir() .getAbsolutePath() + File.separator + commandFileName; saveToFile(commandFilePath, inputStream); } catch (IOException e) { Log.e("tag", e.toString()); } try { Process p; p = Runtime.getRuntime().exec("su"); DataOutputStream os = new DataOutputStream(p.getOutputStream()); os.writeBytes("mount -o remount,rw /dev/block/stl6 /system\n"); os.writeBytes("cd system/xbin\n"); os.writeBytes("cat " + commandFilePath + " > " + commandFileName + "\n"); os.writeBytes("chmod 755 " + commandFileName + "\n"); os.writeBytes("mount -o remount,ro /dev/block/stl6 /system\n"); os.writeBytes("exit\n"); os.flush(); p.waitFor(); } catch (Exception e) { Log.e(TAG, e.toString()); } } } static public Device getInstance() { INSTANCE.checkInitialized(); return INSTANCE; } private void checkInitialized() { if (mHasBeenInitialized == false) throw new IllegalStateException("Singleton class " + TAG + " is not yet initialized"); } public boolean isRooted() { checkInitialized(); if (mHasRootBeenChecked) { return mIsDeviceRooted; } try { File file = new File("/system/app/Superuser.apk"); if (file.exists()) { mHasRootBeenChecked = true; mIsDeviceRooted = true; return true; } } catch (Exception e) { e.printStackTrace(); } try { ArrayList<String> envlist = new ArrayList<String>(); Map<String, String> env = System.getenv(); for (String envName : env.keySet()) { envlist.add(envName + "=" + env.get(envName)); } String[] envp = (String[]) envlist.toArray(new String[0]); Process proc = Runtime.getRuntime() .exec(new String[] { "which", "su" }, envp); BufferedReader in = new BufferedReader(new InputStreamReader( proc.getInputStream())); if (in.readLine() != null) { mHasRootBeenChecked = true; mIsDeviceRooted = true; return true; } } catch (Exception e) { e.printStackTrace(); } mHasRootBeenChecked = true; mIsDeviceRooted = false; return false; } public enum AndroidVersion { HC, ICS, JB, UNKNOWN }; public AndroidVersion getAndroidVersion() { checkInitialized(); int sdk = android.os.Build.VERSION.SDK_INT; if (11 <= sdk && sdk <= 13) { return AndroidVersion.HC; } else if (14 <= sdk && sdk <= 15) { return AndroidVersion.ICS; } else if (16 == sdk) { return AndroidVersion.JB; } else { return AndroidVersion.UNKNOWN; } } protected static void moveSystemUIapkFile(boolean move) { try { File systemUIapkFile = new File("/system/app/SystemUI.apk"); Process p; p = Runtime.getRuntime().exec("su"); // Attempt to write a file to a root-only DataOutputStream os = new DataOutputStream(p.getOutputStream()); os.writeBytes("mount -o remount,rw /dev/block/stl6 /system\n"); if (move && systemUIapkFile.exists()) { os.writeBytes("cp /system/app/SystemUI.apk /system/SystemUI.apk\n"); } if (!move && !systemUIapkFile.exists()){ os.writeBytes("cp /system/SystemUI.apk /system/app/SystemUI.apk\n"); } os.writeBytes("mount -o remount,ro /dev/block/stl6 /system\n"); // Close the terminal os.writeBytes("exit\n"); os.flush(); p.waitFor(); if(!move){ } } catch (Exception e) { Log.e(TAG,"moveSystemUIapkFile",e); } } public void showSystembar(boolean makeVisible) { checkInitialized(); try { ArrayList<String> envlist = new ArrayList<String>(); Map<String, String> env = System.getenv(); for (String envName : env.keySet()) { envlist.add(envName + "=" + env.get(envName)); } String[] envp = (String[]) envlist.toArray(new String[0]); if (makeVisible) { String command; Device dev = Device.getInstance(); if (dev.getAndroidVersion() == AndroidVersion.HC) { command = "LD_LIBRARY_PATH=/vendor/lib:/system/lib am startservice -n com.android.systemui/.SystemUIService"; } else { command = "" +"rm /sdcard/hidebar-lock\n" + "sleep 5\n" + "LD_LIBRARY_PATH=/vendor/lib:/system/lib am startservice -n com.android.systemui/.SystemUIService"; } Runtime.getRuntime().exec(new String[] { "su", "-c", command }, envp); mSystembarVisible = true; } else { String command; Device dev = Device.getInstance(); if (dev.getAndroidVersion() == AndroidVersion.HC) { command = "LD_LIBRARY_PATH=/vendor/lib:/system/lib service call activity 79 s16 com.android.systemui"; } else { command = "touch /sdcard/hidebar-lock\n" + "while [ -f /sdcard/hidebar-lock ]\n" + "do\n" + "killall com.android.systemui\n" + "usleep 300000\n" + "done\n" + "LD_LIBRARY_PATH=/vendor/lib:/system/lib am startservice -n com.android.systemui/.SystemUIService"; } Runtime.getRuntime().exec(new String[] { "su", "-c", command }, envp); mSystembarVisible = false; } } catch (Exception e) { e.printStackTrace(); } } public boolean isSystembarVisible() { checkInitialized(); return mSystembarVisible; } public void sendBackEvent() { try { ArrayList<String> envlist = new ArrayList<String>(); Map<String, String> env = System.getenv(); for (String envName : env.keySet()) { envlist.add(envName + "=" + env.get(envName)); } String[] envp = (String[]) envlist.toArray(new String[0]); Runtime.getRuntime().exec( new String[] { "su", "-c", "LD_LIBRARY_PATH=/vendor/lib:/system/lib input keyevent 4" }, envp); } catch (Exception e) { e.printStackTrace(); } } public static void saveToFile(String filePath, InputStream in){ FileOutputStream fos = null; BufferedInputStream bis = null; int BUFFER_SIZE = 1024; byte[] buf = new byte[BUFFER_SIZE]; int size = 0; bis = new BufferedInputStream(in); try { fos = new FileOutputStream(filePath); while ((size = bis.read(buf)) != -1) fos.write(buf, 0, size); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (fos != null) { fos.close(); } if (bis != null) { bis.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
在需要隐藏时候调度在需要去消隐藏的时候调用Device.initialize(this); Device.getInstance().showSystembar(false);
Device.getInstance().showSystembar(true);
效果界面:
目前在研究如何屏蔽第三方APK界面的三个导航按钮,而不是去掉整个导航栏。
刚发现了屏蔽三个导航按钮的方案。
第四种:
使用StatusBarManager类,不过这个类是隐藏的,必须在源码下编译,使用SDK编译的肯定是找不到类。
import android.app.StatusBarManager; mStatusBarManager = (StatusBarManager)this.getSystemService(Context.STATUS_BAR_SERVICE);
在需要隐藏的时候调用:
在需要显示的时候调度:mStatusBarManager.disable(StatusBarManager.DISABLE_HOME |StatusBarManager.DISABLE_BACK|StatusBarManager.DISABLE_RECENT);
mStatusBarManager.disable(StatusBarManager.DISABLE_NONE);
第五种:
对于第四种方法,SDK编译下也可以实现,使用JAVA的反射机制,获取隐藏接口。
这个方法在我的另一篇文章里已经介绍过了,来实现关机功能 http://blog.csdn.net/kobeyxyx/article/details/9112641。
private void statusBarDisable(boolean disable) { try { int DISABLE_NAVIGATION = 0x00200000 | 0x00400000 | 0x01000000; //int DISABLE_NAVIGATION = View.STATUS_BAR_DISABLE_HOME | View.STATUS_BAR_DISABLE_BACK | View.STATUS_BAR_DISABLE_RECENT; int DISABLE_NONE = 0x00000000; //获得ServiceManager类 Class<?> ServiceManager = Class .forName("android.os.ServiceManager"); //获得ServiceManager的getService方法 Method getService = ServiceManager.getMethod("getService", java.lang.String.class); //调用getService获取RemoteService Object oRemoteService = getService.invoke(null,"statusbar"); //获得IStatusBarService.Stub类 Class<?> cStub = Class .forName("com.android.internal.statusbar.IStatusBarService$Stub"); //获得asInterface方法 Method asInterface = cStub.getMethod("asInterface", android.os.IBinder.class); //调用asInterface方法获取IStatusBarService对象 Object oIStatusBarService = asInterface.invoke(null, oRemoteService); //获得disable()方法 Method disableMethod = oIStatusBarService.getClass().getMethod("disable",int.class,IBinder.class,String.class); //调用disable()方法 if(disable){ disableMethod.invoke(oIStatusBarService,DISABLE_NAVIGATION,new Binder(),this.getPackageName()); }else{ disableMethod.invoke(oIStatusBarService,DISABLE_NONE,new Binder(),this.getPackageName()); } }catch (Exception e) { Log.e(TAG, e.toString(), e); } }