Android hardware按鍵觸感功能實現
筆者在最近一個項目中遇到一個問題就是客戶要求硬件的幾個key需要在觸感功能打開的情況下使用有觸感,android對於key是不做觸感的,就是沒有震動的。 辦法還是有的,感覺好彆扭,把key轉化成虛擬按鍵的座標報上去就可以了。
現在ctp上大多都有幾個觸摸鍵,可是客戶選擇的ctp爲了降低成本統一結構,沒有這幾個鍵,而是用另外一個小模塊來實現這幾個鍵,這個幾個鍵通過IIC讀出來就是實際的鍵值不是座標。下面就簡單介紹一下做法吧!
一、虛擬鍵佈局
虛擬鍵需要佈局在ctp有效範圍之外,比如320X480的屏,你的虛擬鍵要在這有效範圍之外。另外android默認從/sys/board_properties讀取配置,另外需要注意的地方就是虛擬鍵屬性裏的name也是有固定格式的,virtualkeys.DEVICENAME,這個DEVICENAME不是指你手機設備的名稱,而是指你input設備的名稱,你有多個input設備,這裏需要綁定清楚,這個很重要,一般ctp上有這幾個觸摸鍵的情況下都是綁定ctp input設備的名稱。
#ifdef CONFIG_MACH_YYY
static ssize_t virtual_keys_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return sprintf(buf,
/*leaguer old tp*/
__stringify(EV_KEY) ":" __stringify(KEY_MENU) ":100:519:20:20"
":" __stringify(EV_KEY) ":" __stringify(KEY_HOME) ":60:519:20:20"
":" __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":0:519:20:20"
/*leaguer new tp*/
":" __stringify(EV_KEY) ":" __stringify(KEY_MENU) ":159:513:6:6"
":" __stringify(EV_KEY) ":" __stringify(KEY_HOME) ":195:513:6:6"
":" __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":230:513:6:6"
/*lingju tp*/
":" __stringify(EV_KEY) ":" __stringify(KEY_MENU) ":208:533:6:6"
":" __stringify(EV_KEY) ":" __stringify(KEY_HOME) ":240:533:6:6"
":" __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":272:533:6:6"
"\n");
}
#elif defined(CONFIG_MACH_XXXX)
static ssize_t virtual_keys_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return sprintf(buf,
__stringify(EV_KEY) ":" __stringify(KEY_MENU) ":50:530:20:20"
":" __stringify(EV_KEY) ":" __stringify(KEY_HOME) ":100:530:20:20"
":" __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":150:530:20:20"
":" __stringify(EV_KEY) ":" __stringify(KEY_SEARCH) ":200:530:20:20"
"\n");
}
#else
static ssize_t virtual_keys_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return sprintf(buf,
__stringify(EV_KEY) ":" __stringify(KEY_MENU) ":50:532:70:35"
":" __stringify(EV_KEY) ":" __stringify(KEY_HOME) ":155:532:70:35"
":" __stringify(EV_KEY) ":" __stringify(KEY_BACK) ":255:532:70:35"
"\n");
}
#endif
static struct kobj_attribute virtual_keys_attr = {
.attr = {
#if defined(CONFIG_MACH_XXXX)
.name = "virtualkeys.gt106m_tp",
#else
.name = "virtualkeys.ft5x0x_ts",
#endif
.mode = S_IRUGO,
},
.show = &virtual_keys_show,
};
static struct attribute *virtual_keys_attrs[] = {
&virtual_keys_attr.attr,
NULL
};
static struct attribute_group virtual_keys_attr_group = {
.attrs = virtual_keys_attrs,
};
static void virtual_keys_init(void)
{
int ret;
struct kobject *kobj = kobject_create_and_add("board_properties", NULL);
if (kobj)
ret = sysfs_create_group(kobj, &virtual_keys_attr_group);
if (!kobj || ret)
atxxtp_err("failed to create board_properties\n");
}
#endif
二、硬件key驅動需要做什麼?
申請input設備,註冊設備類型,設置一些屬性,當然重點還是在報值上,把key值轉換爲point上報。
static int gt106m_tp_point[MAX_BUTTON_CNT][2] = { {200,530}, {150,530}, {100,530}, {50,530} };
gt106m_tp->input_dev = input_allocate_device();
if (gt106m_tp->input_dev == NULL) {
ret = -ENOMEM;
gt106m_err( "input_allocate_device failed to request irq%d,"" error %d\n", GPIO_TOUCHKEY_EINT, ret);
goto exit_input_dev_alloc_failed;
}
gt106m_tp->input_dev->name = GT106M_NAME;
s_input_dev = gt106m_tp->input_dev;
input_set_abs_params(s_input_dev, ABS_MT_POSITION_X, 0, 320, 0, 0);
input_set_abs_params(s_input_dev, ABS_MT_POSITION_Y, 0, 480, 0, 0);
input_set_abs_params(s_input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
set_bit(ABS_MT_TOUCH_MAJOR, s_input_dev->absbit);
set_bit(ABS_MT_POSITION_X, s_input_dev->absbit);
set_bit(ABS_MT_POSITION_Y, s_input_dev->absbit);
set_bit(EV_ABS, s_input_dev->evbit);
set_bit(EV_KEY, s_input_dev->evbit);
for(i = 0; i < MAX_BUTTON_CNT; i++)
set_bit(gt106m_keycode[i], s_input_dev->keybit);
ret = input_register_device(s_input_dev);
if (ret) {
gt106m_err( "input_register_device failed to request irq%d,"" error %d\n", GPIO_TOUCHKEY_EINT, ret);
goto exit_input_register_device_failed;
}
鍵值轉座標上報如下:
input_report_abs(s_input_dev, ABS_MT_TOUCH_MAJOR, 255);
input_report_abs(s_input_dev, ABS_MT_POSITION_X, gt106m_tp_point[i][0]);
input_report_abs(s_input_dev, ABS_MT_POSITION_Y, gt106m_tp_point[i][1]);
input_mt_sync(s_input_dev);
input_sync(s_input_dev);
input_report_abs(s_input_dev, ABS_MT_TOUCH_MAJOR, 0);
input_mt_sync(s_input_dev);
input_sync(s_input_dev);
三、android上層如何處理虛擬鍵?
Frameworks/base/services/java/com/android/server下InputManager.java有取virtualkey的定義,如下
public VirtualKeyDefinition[] getVirtualKeyDefinitions(String deviceName) {
ArrayList<VirtualKeyDefinition> keys = new ArrayList<VirtualKeyDefinition>();
try {
FileInputStream fis = new FileInputStream(
"/sys/board_properties/virtualkeys." + deviceName);
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(isr, 2048);
String str = br.readLine();
if (str != null) {
String[] it = str.split(":");
if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "***** VIRTUAL KEYS: " + it);
final int N = it.length-6;
for (int i=0; i<=N; i+=6) {
if (!"0x01".equals(it[i])) {
Slog.w(TAG, "Unknown virtual key type at elem #"
+ i + ": " + it[i] + " for device " + deviceName);
continue;
}
try {
VirtualKeyDefinition key = new VirtualKeyDefinition();
key.scanCode = Integer.parseInt(it[i+1]);
key.centerX = Integer.parseInt(it[i+2]);
key.centerY = Integer.parseInt(it[i+3]);
key.width = Integer.parseInt(it[i+4]);
key.height = Integer.parseInt(it[i+5]);
if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Virtual key "
+ key.scanCode + ": center=" + key.centerX + ","
+ key.centerY + " size=" + key.width + "x"
+ key.height);
keys.add(key);
} catch (NumberFormatException e) {
Slog.w(TAG, "Bad number in virtual key definition at region "
+ i + " in: " + str + " for device " + deviceName, e);
}
}
}
br.close();
} catch (FileNotFoundException e) {
Slog.i(TAG, "No virtual keys found for device " + deviceName + ".");
} catch (IOException e) {
Slog.w(TAG, "Error reading virtual keys for device " + deviceName + ".", e);
}
return keys.toArray(new VirtualKeyDefinition[keys.size()]);
}
Frameworks/base/libs/ui下InputReader.cpp下解析虛擬鍵,核心函數如下:
void TouchInputMapper::configureVirtualKeysLocked() {
assert(mRawAxes.x.valid && mRawAxes.y.valid);
// Note: getVirtualKeyDefinitions is non-reentrant so we can continue holding the lock.
Vector<VirtualKeyDefinition> virtualKeyDefinitions;
getPolicy()->getVirtualKeyDefinitions(getDeviceName(), virtualKeyDefinitions);
mLocked.virtualKeys.clear();
if (virtualKeyDefinitions.size() == 0) {
return;
}
mLocked.virtualKeys.setCapacity(virtualKeyDefinitions.size());
int32_t touchScreenLeft = mRawAxes.x.minValue;
int32_t touchScreenTop = mRawAxes.y.minValue;
int32_t touchScreenWidth = mRawAxes.x.getRange();
int32_t touchScreenHeight = mRawAxes.y.getRange();
for (size_t i = 0; i < virtualKeyDefinitions.size(); i++) {
const VirtualKeyDefinition& virtualKeyDefinition =
virtualKeyDefinitions[i];
mLocked.virtualKeys.add();
VirtualKey& virtualKey = mLocked.virtualKeys.editTop();
virtualKey.scanCode = virtualKeyDefinition.scanCode;
int32_t keyCode;
uint32_t flags;
if (getEventHub()->scancodeToKeycode(getDeviceId(), virtualKey.scanCode,
& keyCode, & flags)) {
LOGW(INDENT "VirtualKey %d: could not obtain key code, ignoring",
virtualKey.scanCode);
mLocked.virtualKeys.pop(); // drop the key
continue;
}
virtualKey.keyCode = keyCode;
virtualKey.flags = flags;
// convert the key definition's display coordinates into touch coordinates for a hit box
int32_t halfWidth = virtualKeyDefinition.width / 2;
int32_t halfHeight = virtualKeyDefinition.height / 2;
virtualKey.hitLeft = (virtualKeyDefinition.centerX - halfWidth)
* touchScreenWidth / mLocked.surfaceWidth + touchScreenLeft;
virtualKey.hitRight= (virtualKeyDefinition.centerX + halfWidth)
* touchScreenWidth / mLocked.surfaceWidth + touchScreenLeft;
virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight)
* touchScreenHeight / mLocked.surfaceHeight + touchScreenTop;
virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight)
* touchScreenHeight / mLocked.surfaceHeight + touchScreenTop;
}
}
結果以上幾步,就可以完成這個功能的開發。如果ctp上的觸摸鍵,也一樣的,有效ctp的觸摸鍵是報的座標,有效ctp直接也是報的key,如果要支持觸感的話,還是得轉換成座標報上去。