android 软按键 模拟按键

android 的概念对象,无穷无尽,被搞昏了头,在framework中添加一点自己的需求变得头大。为了少让别人走弯路,将软按键的实现代码分享下,不要让人再做无谓的牺牲。fuck android framework!

    在网络上有人把软案件做到StatusBar上面,目前我们有个需求就是按下案件弹出一个浮在最上面的窗口,窗口里面有各种案件的按钮,home,back,menu,search,...再按下,窗口消失。

   目前控制软按键窗口弹出隐藏的我们用KEYRECODE_STAR,你也可以根据你的实际情况自己定制。

    因为android APP的窗口不能满足需求,所以加在了framework上面,想让把软按键做成一个独立AP的同学可以不用往下看了。

  选了几种模型,如输入法,statusBar,能满足需求,那么还是决定仿造statusBar来增加软按键。(因为他足够简单,输入法框架没看懂)

看完了 /home/hd/codings/mipsandroid-2.1/frameworks/base/services/java/com/android/server/status 中的两个文件StatusBarService.java,StatusBarView.java两个文件,那么就是我们的实现思路。并结合SystemServer.java中statusBar的创建。

首先系统启动的时候由Zygote启动SystemServer里面的服务,初始化 StatusBarService,然后并调用它的systemReady函数,StatusBarService systemReady创建了status Bar的窗口:1:create View;2 add to winMan,那么StatusBarView.java就显示出来了。

(android View概念很麻烦,什么ViewGroup 主view,至今我没搞懂)

然后StatusBarView.java 中就是可以对你想要的时间进行响应和重载了。

大致了解了后我们就来仿StatusBar来实现我们的touchkey:

mkdir touchkey 和status在同一个路径下,然后创建两个java文件TouchkeyService.java,TouchkeyView.java,其中TouchkeyService继承了IStatusBar.Stub,因为我要在其他的进程来获取它的句柄来控制他,而我有没看懂IStatusBar.Stub这类东西怎么写,(概念多而杂fuck android framework)当你继承这个IStatusBar.Stub对象的时候一定要重载removeIcon updateIcon addIcon  disable toggle  deactivate activate 方法,这个过程编译起会辅导你完成,缺拿个就补哪个,函数体什么也不做。嘿嘿,看到deactivate,activate我们就可以通过重写这两个方法来控制我的软按键的显示和隐藏。

最后别完了systemReady 的方法把TouchkeyView.java中的TouchkeyView 加入win manager,在TouchkeyService的构造函数中创建

TouchkeyView,并配置其相应的xml文件来做TouchkeyView的布局,我们的具体按键都布局在这个touch_key.xml里面。

 

    public TouchkeyService(Context context) {
        mContext = context;
        mDisplay = ((WindowManager)context.getSystemService(
                Context.WINDOW_SERVICE)).getDefaultDisplay();

        TouchkeyView sb = (TouchkeyView)View.inflate(context,com.android.internal.R.layout.touch_key, null);
        sb.mService=this;
       
        mTouchkeyView=sb;
        mPixelFormat = PixelFormat.TRANSLUCENT;
        Drawable bg = sb.getBackground();
            if (bg != null) {
                    mPixelFormat = bg.getOpacity();
            }
    }
    public void systemReady() {
        int w,h;
        final TouchkeyView view = mTouchkeyView;
        final View viewBase=(View)mTouchkeyView;
       
        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
        WIDTH,
        HEIGHT,
                WindowManager.LayoutParams.TYPE_PRIORITY_PHONE,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
                WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING,mPixelFormat);
        // lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
        lp.setTitle("touchkey");
        w=mDisplay.getWidth();
        h=mDisplay.getHeight();
        lp.x=((w-WIDTH)/2);
        lp.y=0;
        lp.windowAnimations = R.style.Animation_Translucent;
        WindowManagerImpl.getDefault().addView(view, lp);
        mTouchkeyView.setVisibility(4);
    }

有了这两个函数和刚才的touch_key.xml文件,你就有了一个你特定大小的窗口浮动在你的右边。

在开始的时候这个窗口是隐藏的(mTouchkeyView.setVisibility(4);),那么我们去到 framework policy 下面的PhoneWindowManager.java,因为那里有按键的时间,我们在那边用类似于获取StatusBar句柄(暂前叫句柄android概念ZTMD麻烦,fuck)的方法来获取我们的TouchkeyService,然后调用 activate deactivate 方法,那么就可以控制到这个窗口的显示。

 

在 PhoneWindowManager.java代码中(如果你是MID,自己知道是拿个吧) 修改方法interceptKeyTi(别问我是怎么掉它的,反正我知道是WM掉它,然后再掉回去的),在这个函数任意个你看上去顺眼的地方加入

 if(code==KeyEvent.KEYCODE_STAR) {
        if(down) {
             IStatusBar touchkey = IStatusBar.Stub.asInterface(ServiceManager.getService("touchkey"));
            if(touchkey!=null) {
                if(mTouchKeyHiding) {
                    try {
                        touchkey.activate();
                    }
                    catch (RemoteException e) {
                                    // we're screwed anyway, since it's in this process
                                    throw new RuntimeException(e);
                                }
                }
                else {
                    try {
                        touchkey.deactivate();
                    }
                    catch(RemoteException e) {
                        throw new RuntimeException(e);
                    }
                }
                mTouchKeyHiding=!mTouchKeyHiding;
            }
        }
        return true;
    }

这个时候体会到为什么要继承IStatusBar.Stub它了吧,(搞懂android 的概念怎么就那么烦呢?fuck!!)

直到这里你就可以完成按下按钮发送了个STAR消息,显示和隐藏你的touchkey 窗口。

工作完成了一大半,剩下的只是在这个模型上去加功能了。

 

还有一个问题要主义,所有的控制界面显示的不能由调用者的线程去完成,而必须发送消息,给创建它的线程,我们这里借助Handle,所以还要Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {
            int  down;
            down=msg.arg1;
            switch(msg.what) {
                case TOUCHKEY_HIDE:
                    mTouchkeyView.setVisibility(4);
                break;
                case TOUCHKEY_SHOW:
                    mTouchkeyView.setVisibility(0);
                break;

                case TOUCHKEY_HOME:
                    sendKeyEventToAndroid(KeyEvent.KEYCODE_HOME,down);
                break;
                case TOUCHKEY_BACK:
                    sendKeyEventToAndroid(KeyEvent.KEYCODE_BACK,down);
                break;
                case TOUCHKEY_SEACH:
                    sendKeyEventToAndroid(KeyEvent.KEYCODE_SEARCH,down);
                break;
                case TOUCHKEY_MENU:
                    sendKeyEventToAndroid(KeyEvent.KEYCODE_MENU,down);
                break;
                case TOUCHKEY_VADD:
                    sendKeyEventToAndroid(KeyEvent.KEYCODE_VOLUME_UP,down);
                break;
                case TOUCHKEY_VSUB:
                    sendKeyEventToAndroid(KeyEvent.KEYCODE_VOLUME_DOWN,down);
                break;
                case TOUCHKEY_RCENT:
                    sendKeyEventToAndroid(KeyEvent.KEYCODE_RECENTAPP,down);
                break;
            }
        }
    };

这样,调用着发送消息给view所在的线程,然后由他来处理,并响应消息,别问我是为什么,我也不知道,也没看并白为什么一定要这样做。


你想要几个按钮那么清在view对应的xml文件里添加ImageButton,然后在TouchkeyView.java中来获取他们,并重载他们的onClick消息:

然后调用TouchkeyService sendKeyEvent来发送响应的消息:

TouchkeyView.java

 

    homeKey.setOnClickListener(new OnClickListener(){
        public void onClick(View arg0) {
            mService.sendKeyEvent(KeyEvent.KEYCODE_HOME,1);
        }
    });

 

TouchkeyService.java

 

  public void sendKeyEvent(int keyCode,int down) {
        Message m = new Message();
        m.arg1=down;
        switch(keyCode) {
            case KeyEvent.KEYCODE_HOME:
                m.what = TOUCHKEY_HOME;
                // mHandler.sendMessageDelayed(m,10);
                mHandler.sendMessage(m);
            break;
            case KeyEvent.KEYCODE_BACK:
                    m.what = TOUCHKEY_BACK;
                // mHandler.sendMessageDelayed(m,10);
                mHandler.sendMessage(m);
            break;
            case KeyEvent.KEYCODE_SEARCH:
                m.what = TOUCHKEY_SEACH;
                // mHandler.sendMessageDelayed(m,10);
                mHandler.sendMessage(m);
            break;
            case KeyEvent.KEYCODE_MENU:
                m.what = TOUCHKEY_MENU;
                // mHandler.sendMessageDelayed(m,10);
                mHandler.sendMessage(m);
            break;
            case KeyEvent.KEYCODE_VOLUME_DOWN:
                m.what = TOUCHKEY_VSUB;
                // mHandler.sendMessageDelayed(m,10);
                mHandler.sendMessage(m);
            break;
            case KeyEvent.KEYCODE_VOLUME_UP:
                m.what = TOUCHKEY_VADD;
                // mHandler.sendMessageDelayed(m,10);
                mHandler.sendMessage(m);
            break;
            case KeyEvent.KEYCODE_RECENTAPP:
                m.what = TOUCHKEY_RCENT;
                // mHandler.sendMessageDelayed(m,10);
                mHandler.sendMessage(m);
            break;
        }
    }

然后同样借助handle来更新UI,并发送消息

    private void sendKeyEventToAndroid(int eventCode,int downPress) {
            long now = SystemClock.uptimeMillis();
        Log.d("[hd debug]","sendKeyEventToAndroid begin/n");
            try {
            /*if(downPress==1) */{
                KeyEvent down = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, eventCode, 0);
                (IWindowManager.Stub
                        .asInterface(ServiceManager.getService("window")))
                        .injectKeyEvent(down, true);
            }
            /*else */{
                KeyEvent up = new KeyEvent(now, now, KeyEvent.ACTION_UP, eventCode, 0);
                (IWindowManager.Stub
                        .asInterface(ServiceManager.getService("window")))
                        .injectKeyEvent(up, true);
            }
                       
            } catch (RemoteException e) {
                    Log.i("Input", "DeadOjbectException");
            }
        Log.d("[hd debug]","sendKeyEventToAndroid end/n");
        }

代码有是抄写了input.java的,别问我是为什么。

 

这样就基本完成了你要的软按键的功能。

代码和xml除图片外我会以资源的形式给出来,如果你喜欢独立来做,那么你可以参考思路自己实现,不想的话就去下载来用吧,不过不好意思你的贡献2点资源分。

我建议你舍去2点资源分吧,因为android 的设计太过于复杂,概念太过于繁多,对于那些大吹android设计好的家伙,我不认同,违背了kiss原则,如果能在不造概念活沿用通俗概念的情况下完成设计,为什么不呢?

fuck android framework!!!

 

源码 http://download.csdn.net/source/2599645

你可能感兴趣的:(其他)