Android hardware按键触感功能实现

本文涉及的产品
文档翻译,文档翻译 1千页
文本翻译,文本翻译 100万字符
图片翻译,图片翻译 100张
简介: Android hardware按键触感功能实现 笔者在最近一个项目中遇到一个问题就是客户要求硬件的几个key需要在触感功能打开的情况下使用有触感,android对于key是不做触感的,就是没有震动的。

Android hardware按键触感功能实现


笔者在最近一个项目中遇到一个问题就是客户要求硬件的几个key需要在触感功能打开的情况下使用有触感,android对于key是不做触感的,就是没有震动的。 办法还是有的,感觉好别扭,把key转化成虚拟按键的坐标报上去就可以了。

   现在ctp上大多都有几个触摸键,可是客户选择的ctp为了降低成本统一结构,没有这几个键,而是用另外一个小模块来实现这几个键,这个几个键通过IIC读出来就是实际的键值不是坐标。下面就简单介绍一下做法吧!

 

一、虚拟键布局

虚拟键需要布局在ctp有效范围之外,比如320X480的屏,你的虚拟键要在这有效范围之外。另外android默认从/sys /board_properties读取配置,另外需要注意的地方就是虚拟键属性里的name也是有固定格式的,virtualkeys.DEVICENAME,这个DEVICENAME不是指你手机设备的名称,而是指你input设备的名称,你有多个 input设备,这里需要绑定清楚,这个很重要,一般ctp上有这几个触摸键的情况下都是绑定ctp input设备的名称。

定义按键区域的标准是:0x1:扫描码:X:Y:W:H,虚拟键之间用冒号隔开。注意X & Y是中心区域坐标。0x1表示是key。

#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上报。

把key转换成相应的坐标,转换如下:

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;

    }
}

android把超过有效范围的坐标跟virtualkey去对比,如果在定义里面就报上相应的key。

bool TouchInputMapper::configureSurfaceLocked() 函数中:
        // Configure X and Y factors.
        if (mRawAxes.x.valid && mRawAxes.y.valid) {
            mLocked.xOrigin = mRawAxes.x.minValue;
            mLocked.yOrigin = mRawAxes.y.minValue;
            mLocked.xScale = float(width) / mRawAxes.x.getRange();
            mLocked.yScale = float(height) / mRawAxes.y.getRange();
            mLocked.xPrecision = 1.0f / mLocked.xScale;
            mLocked.yPrecision = 1.0f / mLocked.yScale;

            configureVirtualKeysLocked();
        } else {
            LOGW(INDENT "Touch device did not report support for X or Y axis!");
            mLocked.xOrigin = 0;
            mLocked.yOrigin = 0;
            mLocked.xScale = 1.0f;
            mLocked.yScale = 1.0f;
            mLocked.xPrecision = 1.0f;
            mLocked.yPrecision = 1.0f;
        }


 


结果以上几步,就可以完成这个功能的开发。如果ctp上的触摸键,也一样的,有效ctp的触摸键是报的坐标,有效ctp直接也是报的key,如果要支持触感的话,还是得转换成坐标报上去。

罗罗嗦嗦说了这么多,从上到下,怎么转换在哪转换大概都分析了一下,希望对以后的项目有类似需求的工作有所帮助。





目录
相关文章
|
6月前
|
XML 缓存 Android开发
Android开发,使用kotlin学习多媒体功能(详细)
Android开发,使用kotlin学习多媒体功能(详细)
148 0
|
1月前
|
Android开发
Android开发表情emoji功能开发
本文介绍了一种在Android应用中实现emoji表情功能的方法,通过将图片与表情字符对应,实现在`TextView`中的正常显示。示例代码展示了如何使用自定义适配器加载emoji表情,并在编辑框中输入或删除表情。项目包含完整的源码结构,可作为开发参考。视频演示和源码详情见文章内链接。
64 4
Android开发表情emoji功能开发
|
16天前
|
安全 Android开发 iOS开发
Android vs iOS:探索移动操作系统的设计与功能差异###
【10月更文挑战第20天】 本文深入分析了Android和iOS两个主流移动操作系统在设计哲学、用户体验、技术架构等方面的显著差异。通过对比,揭示了这两种系统各自的独特优势与局限性,并探讨了它们如何塑造了我们的数字生活方式。无论你是开发者还是普通用户,理解这些差异都有助于更好地选择和使用你的移动设备。 ###
31 3
|
3月前
|
编解码 测试技术 Android开发
Android经典实战之用 CameraX 库实现高质量的照片和视频拍摄功能
本文详细介绍了如何利用CameraX库实现高质量的照片及视频拍摄功能,包括添加依赖、初始化、权限请求、配置预览与捕获等关键步骤。此外,还特别针对不同分辨率和帧率的视频拍摄提供了性能优化策略,确保应用既高效又稳定。
283 1
Android经典实战之用 CameraX 库实现高质量的照片和视频拍摄功能
|
2月前
|
Android开发 开发者
Android平台无纸化同屏如何实现实时录像功能
Android平台无纸化同屏,如果需要本地录像的话,实现难度不大,只要复用之前开发的录像模块的就可以,对我们来说,同屏采集这块,只是数据源不同而已,如果是自采集的其他数据,我们一样可以编码录像。
|
3月前
|
Java Android开发
全志 Android 11:实现响应全局按键
本文介绍了在全志平台Android 11上实现响应全局按键的方法,通过修改`TvWindowManager.java`来全局拦截特定的热键事件,并在`FocusActivity`中处理这些事件以启动调焦界面和控制步进电机调整焦距。
32 2
|
3月前
|
图形学 Android开发
小功能⭐️Unity调用Android常用事件
小功能⭐️Unity调用Android常用事件
|
5月前
|
数据库 Android开发 数据安全/隐私保护
在 Android Studio 中结合使用 SQLite 数据库实现简单的注册和登录功能
在 Android Studio 中结合使用 SQLite 数据库实现简单的注册和登录功能
214 2
|
5月前
|
Android开发
Android中如何快速的实现RecycleView的拖动重排序功能
使用`ItemTouchHelper`和自定义`Callback`,在`RecyclerView`中实现拖动排序功能。定义`ItemTouchHelperAdapter`接口,`Adapter`实现它以处理`onItemMove`方法。`SimpleItemTouchHelperCallback`设置拖动标志,如`LEFT`或`RIGHT`(水平拖动),并绑定到`RecyclerView`以启用拖动。完成这些步骤后,即可实现拖放排序。关注公众号“AntDream”获取更多内容。
109 3
|
6月前
|
移动开发 监控 Android开发
构建高效Android应用:从内存优化到电池寿命代码之美:从功能实现到艺术创作
【5月更文挑战第28天】 在移动开发领域,特别是针对Android系统,性能优化始终是关键议题之一。本文深入探讨了如何通过细致的内存管理和电池使用策略,提升Android应用的运行效率和用户体验。文章不仅涵盖了现代Android设备上常见的内存泄漏问题,还提出了有效的解决方案,包括代码级优化和使用工具进行诊断。同时,文中也详细阐述了如何通过减少不必要的后台服务、合理管理设备唤醒锁以及优化网络调用等手段延长应用的电池续航时间。这些方法和技术旨在帮助开发者构建更加健壮、高效的Android应用程序。
下一篇
无影云桌面