【转】android实现扫码枪功能

转载自:android实现扫码枪功能


扫码枪扫码效果等同于键盘录入,会回调dispatchKeyEvent键盘按下事件。

开发环境:有线扫码枪,支持二维码

1. 接收数据

 /**
     * 扫码枪扫码处理
     */
    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        if (event.getAction() == KeyEvent.ACTION_DOWN) {
            int keyCode = event.getKeyCode();
            char aChar = (char) event.getUnicodeChar();
            if (aChar != 0) {
                mStringBufferResult.append(aChar);
            }
            mHandler.removeCallbacks(mScanningFishedRunnable);
            //若为回车键,直接返回
            if (keyCode == KeyEvent.KEYCODE_ENTER) {
                mHandler.post(mScanningFishedRunnable);
            } else {
                //延迟post,若500ms内,有其他事件
                mHandler.postDelayed(mScanningFishedRunnable, 500L);
            }
            return true;
        }
        return super.dispatchKeyEvent(event);
    }

2. 处理数据

 /**
 * 二维码信息对象
 */
   private QRCode qrCodeBean;
   /**
   * 二维码信息原始数据容器
   */
   private StringBuilder mStringBufferResult = new StringBuilder();
   private Handler mHandler = new Handler();
 
   private Runnable mScanningFishedRunnable = new Runnable() {
        @Override
        public void run() {
            scanOk = false;
            String qrcode = mStringBufferResult.toString();
            if (!TextUtils.isEmpty(qrcode)) {
                // 扫码确定参数
                Gson gson = new Gson();
                try {
                    qrCodeBean = gson.fromJson(qrcode, QRCode.class);
                    // 你的代码...
                    // 如果要支持中文,数据用可以URLEncoder/URLDecoder编解码
                } catch (JsonSyntaxException e) {
                    // 解析失败...
                } catch (UnsupportedEncodingException e) {
                    // 解码失败...
                } finally {
                    mStringBufferResult.setLength(0);
                }
            }
        }
    };

【转】Android按键事件KeyEvent的分发处理流程解析

节选自:Android按键事件KeyEvent的分发处理流程解析


1. 在 Activity 里重写 dispatchKeyEvent()—-最常用

举个栗子:

这在 Tv 开发中是很常见的,经常会在 Activity 里重写 dispatchKeyEvent(),然后要么去预先处理一些工作,要么就是对特定的按键进行拦截。

上面这段代码能看懂么?如果你已经清楚这代码是对左右方向按键的拦截,那么你清楚各种 return 的作用么,为什么又有 return true,又有 return false,还有 return super.dispatchKeyEvent() 的?

先说结论:这里的 return true 和 return false 都能起到按键拦截的作用,也就是子 View 不会接收到事件的分发或处理,Activity 的 onKeyDown/Up() 也不会收到任何消息。

要明白这点,先得搞清楚什么是 return, return 是返回的意思,什么情况下需要返回,不就是调用你的那个方法需要你给个反馈,所以 return 的消息是给上一级的调用者的,所以 return 只会对上一级的调用者的行为有影响。调用 Activity.dispatchKeyEvent() 的是 DecorView 的 dispatchKeyEvent() 里,如下图:

那么,既然 Activity 返回 true 或 false 都只对 DecorView 的行为有影响,那么为什么都能起到拦截事件分发的作用呢?

这是因为,事件的分发逻辑其实是在 Activity.java 的 dispatchKeyEvent() 里实现的,如果你重写了 Activity 的 dispatchKeyEvent() 方法,那么根据

Java 的特性程序就会执行你写的 dispatchKeyEvent(),而不会执行基类 Activity.java 的方法,因此你在重写的方法里没有自己实现事件的分发逻辑,事件当然就停止分发了啊。这也是为什么返回 super.dispatchKeyEvent() 时事件会继续分发,因为这最终会调用到基类 Activity.java 的 dispatchKeyEvent() 方法来执行事件分发的逻辑。

既然在 Activity 里返回 true 或 false 都表示拦截,那么有什么区别么?

当然有,因为会影响 DecorView 的行为,比如我们点击遥控器的方向键时界面上的焦点会跟随着移动,这部分逻辑其实是在 DecorView 的上一级调用者中实现的,Activity 返回 true 的话,会导致 DecorView 也返回 true,那么上一级将根据 DecorView 返回 true 的结果停止焦点的移动,这就是我们常见的在 Activity 里重写 dispatchKeyEvent() 返回 true 来实现停止焦点移动的原理。那么,如果 Activity 返回的是 false,DecorView 也跟随着返回 false,那么上一级会继续执行焦点移动的逻辑,表现出来的效果就是,界面上的焦点仍然会移动,但不会触发 Activity 和 View 的事件分发和处理方法,因为已经被 Activity 拦截掉了。

最后,还有一个问题,在 View 或 ViewGroup 里面重写 dispatchKeyEvent() 作用会跟 Activity 一样么?

return true 或 false 或 super 的含义还是一样的,但这里要明白一个层次结构。上层:Activity,中层:ViewGroup,下层:View。

不管在哪一层重写 dispatchKeyEvent(),如果返回 true 或 false,那么它下层包括它本层都不会接收到事件的分发处理,但是它的上层会接收。因为拦截的效果只作用于该层及下层,而上层只会根据你返回的值,行为受到影响。

比如在 ViewGroup 中返回 true,Activity 的 onKeyDown/Up() 就不会被触发,因为被消费了;如果返回 false,那么事件就交由 Activity 处理。但不管返回 true 或 false,子 View 的 dispatchKeyEvent()、各种 onClick() 等事件处理方法都不会被触发到了。

【转】国密算法SM2实现基于hutool工具类

转载自:国密算法SM2实现基于hutool工具类


首先引入maven

<dependency>
 <groupId>org.bouncycastle</groupId>
 <artifactId>bcprov-jdk15to18</artifactId>
 <version>1.69</version>
</dependency>

<dependency>
 <groupId>cn.hutool</groupId>
 <artifactId>hutool-all</artifactId>
 <version>5.4.1</version>
</dependency>

<dependency>
 <groupId>org.projectlombok</groupId>
 <artifactId>lombok</artifactId>
 <version>1.18.10</version>
</dependency>

直接上代码

import lombok.Data;

/**
 * 1. @description:
 * 2. @author: xh
 * 3. @time: 2022/7/14
 */
@Data
public class SMKeyPair {

    //私钥
    private String priKey;
    //公钥
    private String pubKey;

    public SMKeyPair(String priKey, String pubKey) {
        this.priKey = priKey;
        this.pubKey = pubKey;
    }
}

继续阅读【转】国密算法SM2实现基于hutool工具类

【转】解决IntelliJ IDEA控制台输出中文乱码问题

转载自:解决IntelliJ IDEA控制台输出中文乱码问题


1.打开IntelliJ IDEA本地安装目录中bin文件夹下的idea.exe.vmoptions和idea64.exe.vmoptions这两个文件。

2.分别在这两个文件内容的末尾添加-Dfile.encoding=UTF-8

3.打开IntelliJ IDEA>File>Setting>Editor>File Encodings,将Global Encoding、Project Encoding、Default encodeing for properties files这三项都设置成UTF-8,点击OK或者Apply。

【转】Android中关于Deviceid的那些事

转载自:Android中关于Deviceid的那些事


Android 中获取设备id一直是老生常谈的事情,先说下名词解释

    • Device ID:设备ID。
    • IMEI:International Mobile Equipment Identity,国际移动设备身份码的缩写。是由15位数字组成的“电子串号”,它与每台手机一一对应,每个IMEI在世界上都是唯一的。
    • IDFA:Identifier For Advertising,iOS独有的广告标识符。
    • UDID:Unique Device Identifier,唯一设备标识码。
    • UUID:Universally Unique Identifier,通用唯一识别码。目前最广泛应用的UUID,是微软公司的全局唯一标识符GUID。其目的是让分布式系统中的所有元素,都能有唯一的辨识信息,而不需要通过中央控制端来做辨识信息的指定。
    • ANDROID_ID:在 Android 8.0(API 级别 26)和更高版本的平台上,一个 64 位数字(表示为十六进制字符串),对于应用签名密钥、用户和设备的每个组合都是唯一的值。

今天我们先说一下获取deviceId的方法:

/**
 * 获取deviceId(手机唯一的标识)
 *
 * @param context
 * @return
 */
@SuppressLint({"HardwareIds"})
public static String getDeviceId(Context context) {
    String deviceId;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        deviceId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
    } else {
        if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
            return "";
        }
        TelephonyManager mTelephony = (TelephonyManager) context.getSystemService(TELEPHONY_SERVICE);
        if (mTelephony.getDeviceId() != null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                deviceId = mTelephony.getImei();
            } else {
                deviceId = mTelephony.getDeviceId();
            }
        } else {
            deviceId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
        }
    }
    return deviceId;
}

【转】下载apk并启动安装

转载自:下载apk并启动安装


本篇实现现在网络上的apk并启动安装程序。

权限

写入权限和网络访问权限

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.INTERNET" />

变量

    private DownloadManager downloadManager = null; //下载管理器
    private long mTaskId; // 任务id
    private String fileName; //下载下来文件保存时候的文件名称
    
    fileName = System.currentTimeMillis() + ".apk";
        downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
        String url = "http://111.202.99.13/imtt.dd.qq.com/16891/2A76B7A9A8E841F0D8C1E74AD65FCB3F.apk?mkey=57c3dd3fc5355f8e&f=6c25&c=0&fsname=com.tencent.mobileqq_6.5.3_398.apk&csr=4d5s&p=.apk";
        downloadAPK(url);

下载apk

上面代码调用了downloadAPK方法。
其中用到了文件保存位置,也可以使用缓存目录替换。

    // 使用系统下载器下载
    private void downloadAPK(String versionUrl) {
        // 创建下载任务
        DownloadManager.Request request = new DownloadManager.Request(
                Uri.parse(versionUrl));
        request.setAllowedOverRoaming(false);// 漫游网络是否可以下载

        // 设置文件类型,可以在下载结束后自动打开该文件
        MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
        String mimeString = mimeTypeMap.getMimeTypeFromExtension(MimeTypeMap
                .getFileExtensionFromUrl(versionUrl));
        request.setMimeType(mimeString);

        // 在通知栏中显示,默认就是显示的
        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE);
        request.setVisibleInDownloadsUi(true);

        // sdcard的目录下的download文件夹,必须设置
        request.setDestinationInExternalPublicDir("/download/", fileName);
        // request.setDestinationInExternalFilesDir(),也可以自己制定下载路径

        // 将下载请求加入下载队列
        downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
        // 加入下载队列后会给该任务返回一个long型的id,
        // 通过该id可以取消任务,重启任务等等
        mTaskId = downloadManager.enqueue(request);

        // 注册广播接收者,监听下载状态
        registerReceiver(receiver, new IntentFilter(
                DownloadManager.ACTION_DOWNLOAD_COMPLETE));
    }

广播接收

上面代码用到了receiver

    // 广播接受者,接收下载状态
    private BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            checkDownloadStatus();// 检查下载状态
        }
    };

    // 检查下载状态
    private void checkDownloadStatus() {
        DownloadManager.Query query = new DownloadManager.Query();
        query.setFilterById(mTaskId);// 筛选下载任务,传入任务ID,可变参数
        Cursor c = downloadManager.query(query);
        if (c.moveToFirst()) {
            int status = c.getInt(c
                    .getColumnIndex(DownloadManager.COLUMN_STATUS));
            switch (status) {
                case DownloadManager.STATUS_PAUSED:
                    Log.i("download", ">>>下载暂停");
                case DownloadManager.STATUS_PENDING:
                    Log.i("download", ">>>下载延迟");
                case DownloadManager.STATUS_RUNNING:
                    Log.i("download", ">>>正在下载");
                    break;
                case DownloadManager.STATUS_SUCCESSFUL:
                    Log.i("download", ">>>下载完成");
                    // 下载完成安装APK
                    String downloadPath = Environment
                            .getExternalStoragePublicDirectory(
                                    Environment.DIRECTORY_DOWNLOADS)
                            .getAbsolutePath()
                            + File.separator + fileName;
                    installAPK(new File(downloadPath));
                    break;
                case DownloadManager.STATUS_FAILED:
                    Log.e("download", ">>>下载失败");
                    break;
            }
        }
    }

下载完成

上面代码用到了installAPK,作用是下载完成后开始安装。

    // 下载到本地后执行安装
    protected void installAPK(File file) {
        if (!file.exists())
            return;
        Intent intent = new Intent(Intent.ACTION_VIEW);
        Uri uri = Uri.parse("file://" + file.toString());
        intent.setDataAndType(uri, "application/vnd.android.package-archive");
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
    }

如果要静默安装,上面的方法是不行的,设备必须root。可以参考这里