20090826 Android上使用觸控面板(TouchScreen)模擬KeyEvent (一)

20090826 Android上使用觸控面板(TouchScreen)模擬KeyEvent

最近我們的機器上已經可以跑Android了,使用TI OMAP3530,這顆SOC不錯,網路上有許多人在幫忙porting Android到BeagleBoard上,所以使用人家的beagledroid git就可以了。但如果要使用expansion pin(ex: gpio、mmc、rs232 的bus)時,常常會發生電壓不夠,而需要另外接voltage/level translation,比較麻煩而已,因為TI不一定會想要賣給你幾顆Level Translation的IC,所以我們就必須用要Sample的方式來取得。我們的觸控面板使用dmc的晶片,分析Android Framework的Source Code之後,發現他是讀標準的input subsystem,所以driver要有註冊到input subsystem才行。我拿到兩種觸控板的Driver都沒有,一個是TSC的,一個就是DMC的。

要改Driver成Android可以使用也不是那麼麻煩,只要在probe裡面呼叫 input_register_device,然後再用set_bit、input_set_abs_params就好,這樣在/dev/input下面就會新增一個File Description了,但其實這個File Description是evdev.c弄出來的,evdev真是一個好物,關於Input Subsystem可以去google搜尋,有很多好的分析文章,而且看完之後大有收獲。當註冊完之後,接下來就是要回報了,什麼時候要回報,當然是我們從硬體拿到資料的時候,所以就是在irq函式裡面囉,於是把byte值轉為對應的座標,再使用input_report_XXX來回報至上層。

在修改這部份的時候,有想過校正的問題,這裡解釋一下校正的用意,一般來說觸控板原本的左上右下各會有一個座標,我拿到的dmc是(0,0)跟(2046,2046),但Android系統是(800,480),所以就需要加減乘除,當然如果觸控面板有瑕疵的話,左上也有可能不是(0,0),可能是負的,所以我們每次拿到一支新手機的時候,都要先做校正的動作。

第一版我是直接寫死在IRQ函式裡面,也就是固定左上右下座標,但這樣如果換了解析度就很麻煩,第二版使用module_param,想說可以在insmod的時候重新指定,結果發現如果編成module的話,再載入module之後,需要重新擦拔usb線,Driver才會probe到Device,而如果builtin到kernel裡面就不會有這個問題。我不知道這個問題怎麼發生的?

第三版就是要改為儲存校正的資料到檔案裡面去,然後driver載入時再去讀檔案裡面的座標。但我發現,在Driver裡面好像無法使用Open函式勒?!感覺open好像是給User Level的程式在用的,所以我又繞了一圈,先用一支User Program來讀取校正的檔案,然後再透過ioctl將值回傳給Driver。到這邊,觸控面板(TouchScreen)的使用應該是ok了。

只剩下在拖拉的時候,不知道為什麼座標好像會抖動,例如我在拉出Menu的選單,手在拖曳的時候,選單都會不定時向右邊跳一下,一開始以為是重複回報座標的問題,後來濾掉重複的座標還是會有,目前無解。

=====================================================================

接下來就是手機都需要的按鍵部份,應該沒有手機可以完全沒有實體按鍵的吧,這部份因為我們的ID(Industry Design)沒有留按鍵的地方,所以這個重責大任就要交給觸控面板(TouchScreen)了,為什麼是重責大任呢?不然你點進去某個程式之後,沒有Back和Home鍵,你就再也出不來了。剛好,我們的觸控面板比顯示面板大一點,所以下面還有一小塊區域是Android處理不到的。所以想法就是Android的解析度為800x480,可是我在Driver層把它轉為800x600,那如果使用者按到480以上的區域就代表是Back鍵。這裡我需要的是有一個Service可以攔到所有的Pointer座標,然後再向系統送出一個Back鍵的訊息,為什麼要用Service,是因為要能夠在背景執行,否則現在在使用Browser,就抓不到座標值了。很遺憾的,這兩個功能我都做不到,第一個是有看到某支程式可以讓使用者自定Gesture來執行程式,所以應該是有這個功能的,但我不會寫,第二個則是Android禁止的,某支應用程式不能對其他應用程式送出Key,這個動作的學名叫做InjectKey。

所以要怎麼辦呢?只好透過jni,

http://davanum.wordpress.com/2007/12/09/android-invoke-jni-based-methods-bridging-cc-and-java/

為了避免文章消失,我還是做一下備份好了,重點是在第五步。

Step #1: Compile the project using ant. Some snippets are shown below.

NativeAdd.java class (with Native method)

   1: package org.apache;
   2:  
   3: import android.util.Log;
   4: public class NativeAdd {
   5:     static {
   6:         try {
   7:             Log.i("JNI", "Trying to load libNativeAdd.so");
   8:             System.loadLibrary("NativeAdd");
   9:         }
  10:         catch (UnsatisfiedLinkError ule) {
  11:             Log.e("JNI", "WARNING: Could not load libNativeAdd.so");
  12:         }
  13:     }
  14:  
  15:     public static native long add(long a, long b);
  16: }

Snippet that shows how this class/method is invoked from the main activity

   1: public void onClick(View view) {
   2:     Log.i(LOG_TAG, "onClick");
   3:  
   4:     EditText a = (EditText) findViewById(R.id.a);
   5:     EditText b = (EditText) findViewById(R.id.b);
   6:     EditText c = (EditText) findViewById(R.id.c);
   7:     Log.i(LOG_TAG, "calling native method");
   8:     long sum = NativeAdd.add(Long.parseLong(a.getText().toString()),
   9:             Long.parseLong(b.getText().toString()));
  10:     Log.i(LOG_TAG, "back from native method");
  11:     String text = Long.toString(sum);
  12:     c.setText("Native add returns = " + text.subSequence(0, text.length()));
  13: }

Step #2: Generate header files using java by running the following command.

javah -classpath ../../android.jar;../bin/classes; org.apache.NativeAdd

Here’s how the header file (org_apache_CallNative.h) looks like

/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class org_apache_NativeAdd */
 
#ifndef _Included_org_apache_NativeAdd
#define _Included_org_apache_NativeAdd
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     org_apache_NativeAdd
 * Method:    add
 * Signature: (JJ)J
 */
JNIEXPORT jlong JNICALL Java_org_apache_NativeAdd_add
  (JNIEnv *, jclass, jlong, jlong);
 
#ifdef __cplusplus
}
#endif
#endif

Step #3: Code a tiny C file (org_apache_NativeAdd.c) as shown below:

#include "org_apache_NativeAdd.h"
 
JNIEXPORT jlong JNICALL Java_org_apache_NativeAdd_add
  (JNIEnv *env, jclass c, jlong a, jlong b)
{
    return a + b;
}

Step #4: Please read the following article before you try the next step.
Shared library “Hello World!” for Android

Step #5: Compile and link the org_apache_NativeAdd.c/org_apache_NativeAdd.h into a shared library.

arm-none-linux-gnueabi-gcc  -I/usr/lib/jvm/java-1.5.0-sun/include -I/usr/lib/jvm/java-1.5.0-sun/include/linux  -fpic -c org_apache_NativeAdd.c
arm-none-linux-gnueabi-ld -T armelf_linux_eabi.xsc -shared -o libNativeAdd.so org_apache_NativeAdd.o
 
NOTE: details on armelf_linux_eabi.xsc are in the Step #4. Now, the default linker script need to be modified. The default linker script is available at $toolchains_home/arm-none-linux-gnueabi/lib/ldscripts/armelf_linux_eabi.xsc.
 
Step #6: copy the library to the emulator. Copy the Application as well.
adb push native/libNativeAdd.so /system/lib
adb install bin/CallNative.apk

留言

這個網誌中的熱門文章

好貴的東元冷氣維修--馬達啟動電容

台大醫院 婁培人 耳鼻喉科 就診

機車無法充電之整流器壞掉--$650