如何为Android TvSettings应用菜单添加PIN码验证及设备校验功能
给LineageOS TvSettings添加PIN验证与设备校验功能
一、设备校验逻辑实现
通过读取build.prop中的ro.product.model字段,判断当前设备是否为授权设备,非授权设备强制触发PIN验证。
import android.os.SystemProperties; // 判断当前设备是否为授权设备 private boolean isAuthorizedDevice() { // 替换为你的目标设备型号 String TARGET_DEVICE_MODEL = "SampleTVModel"; String currentDeviceModel = SystemProperties.get("ro.product.model", ""); return TARGET_DEVICE_MODEL.equals(currentDeviceModel); }
二、数字键盘PIN验证弹窗实现
自定义适配TV端的数字键盘弹窗,实现PIN码输入、验证逻辑:
1. 弹窗布局(res/layout/dialog_pin_verify.xml)
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="24dp"> <TextView android:id="@+id/pin_input_tip" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="请输入PIN码" android:textSize="20sp" android:layout_gravity="center_horizontal"/> <TextView android:id="@+id/pin_input" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginVertical="16dp" android:textSize="24sp" android:inputType="numberPassword" android:layout_gravity="center_horizontal"/> <!-- 数字键盘区域 --> <GridLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:columnCount="3" android:rowCount="4"> <Button android:id="@+id/btn_num_1" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:text="1"/> <Button android:id="@+id/btn_num_2" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:text="2"/> <Button android:id="@+id/btn_num_3" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:text="3"/> <Button android:id="@+id/btn_num_4" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:text="4"/> <Button android:id="@+id/btn_num_5" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:text="5"/> <Button android:id="@+id/btn_num_6" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:text="6"/> <Button android:id="@+id/btn_num_7" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:text="7"/> <Button android:id="@+id/btn_num_8" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:text="8"/> <Button android:id="@+id/btn_num_9" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:text="9"/> <View android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1"/> <Button android:id="@+id/btn_num_0" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:text="0"/> <Button android:id="@+id/btn_delete" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:text="删除"/> </GridLayout> </LinearLayout>
2. 弹窗逻辑实现
import android.app.AlertDialog; import android.view.LayoutInflater; import android.view.View; import android.widget.TextView; import android.widget.Toast; // 显示PIN验证弹窗 private void showPinVerifyDialog(final OnPinVerifyCallback callback) { AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); View dialogView = LayoutInflater.from(getContext()).inflate(R.layout.dialog_pin_verify, null); TextView pinInputTv = dialogView.findViewById(R.id.pin_input); // 数字按钮点击事件 dialogView.findViewById(R.id.btn_num_1).setOnClickListener(v -> pinInputTv.append("1")); dialogView.findViewById(R.id.btn_num_2).setOnClickListener(v -> pinInputTv.append("2")); dialogView.findViewById(R.id.btn_num_3).setOnClickListener(v -> pinInputTv.append("3")); dialogView.findViewById(R.id.btn_num_4).setOnClickListener(v -> pinInputTv.append("4")); dialogView.findViewById(R.id.btn_num_5).setOnClickListener(v -> pinInputTv.append("5")); dialogView.findViewById(R.id.btn_num_6).setOnClickListener(v -> pinInputTv.append("6")); dialogView.findViewById(R.id.btn_num_7).setOnClickListener(v -> pinInputTv.append("7")); dialogView.findViewById(R.id.btn_num_8).setOnClickListener(v -> pinInputTv.append("8")); dialogView.findViewById(R.id.btn_num_9).setOnClickListener(v -> pinInputTv.append("9")); dialogView.findViewById(R.id.btn_num_0).setOnClickListener(v -> pinInputTv.append("0")); // 删除按钮逻辑 dialogView.findViewById(R.id.btn_delete).setOnClickListener(v -> { String currentText = pinInputTv.getText().toString(); if (!currentText.isEmpty()) { pinInputTv.setText(currentText.substring(0, currentText.length() - 1)); } }); builder.setView(dialogView) .setPositiveButton("确认", (dialog, which) -> { String inputPin = pinInputTv.getText().toString().trim(); // 替换为你的预设PIN码,建议放在strings.xml中 if ("1234".equals(inputPin)) { callback.onSuccess(); } else { Toast.makeText(getContext(), "PIN码错误", Toast.LENGTH_SHORT).show(); callback.onFailed(); } }) .setNegativeButton("取消", (dialog, which) -> callback.onCancel()) .create().show(); } // PIN验证回调接口 public interface OnPinVerifyCallback { void onSuccess(); void onFailed(); void onCancel(); }
三、菜单点击拦截与验证触发
在TvSettings对应的Fragment中,重写onPreferenceTreeClick方法,拦截目标菜单(显示、应用、开发者选项)的点击事件,触发验证逻辑:
import android.preference.Preference; import android.preference.PreferenceScreen; @Override public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { String preferenceKey = preference.getKey(); // 定义需要验证的菜单key,根据实际xml中的key调整 String[] protectedKeys = {"display_settings", "apps_settings", "developer_options"}; for (String key : protectedKeys) { if (key.equals(preferenceKey)) { // 判断是否需要触发验证:非授权设备强制验证,授权设备可按需调整 if (!isAuthorizedDevice()) { showPinVerifyDialog(new OnPinVerifyCallback() { @Override public void onSuccess() { // 验证通过,执行原跳转逻辑 TvSettingsFragment.super.onPreferenceTreeClick(preferenceScreen, preference); } @Override public void onFailed() { // 验证失败,不执行跳转 } @Override public void onCancel() { // 取消验证,不执行跳转 } }); return true; // 拦截原点击事件 } else { // 授权设备也需要验证的话,复制上面的showPinVerifyDialog逻辑即可 // 示例:授权设备跳过验证,直接跳转 return super.onPreferenceTreeClick(preferenceScreen, preference); } } } return super.onPreferenceTreeClick(preferenceScreen, preference); }
四、关键注意事项
- PIN码配置:不要硬编码PIN码,建议将预设PIN放在
res/values/strings.xml中,方便后续修改。 - TV端适配:确保数字键盘弹窗的按钮支持遥控器焦点导航,可通过
android:focusable="true"等属性优化。 - 开发者选项适配:开发者选项默认隐藏,需确保在用户开启开发者选项后,拦截逻辑依然生效。
- 权限说明:读取
ro.product.model无需额外权限,直接通过SystemProperties调用即可。
内容的提问来源于stack exchange,提问作者Cobra91
相关产品推荐
相关产品推荐

