分类 Android 下的文章

1. 背景

由于公司的办公电脑也用于深度学习的模型训练,该电脑就不能同时大型软件(连Chrome也不能多开标签),避免影响训练。于是就想把新手机“红米Note 12 Turbo”利用起来,处理一些“轻办公”的工作。

找到方案是:

  • 方案1:Android系统利用chroot安装完整Linux系统,运行基于X11的图形化软件(例如Chrome),PC端运行X server
  • 方案2:手机运行Android桌面模式,并投屏到PC。

由于方案1运行不流畅、不能方便利用GPU加速、软件兼容不佳等,只能放弃。方案2整理了一下,能达到比较高的实用性。

2. 原理

据说从Android 10开始,Android系统内置了桌面模式。该模式下,App可以自由拖动显示位置,并调整窗口大小(跟PC操作系统一样)。

有趣的是,“开发者选项”里开启了“模拟辅助显示设备”,就可以启用桌面模式,而且基本不影响手机正常显示模式。可以认为同一个Android手机上同时运行“手机模式”和“桌面模式”,App能在这两个模式之间显示。

最后,利用scrcpy把“模拟辅助显示设备”投屏到PC端,就可以使用大屏幕显示“桌面模式”,并且利用鼠标键盘进行输入。

据说手机直连显示器(需要手机直接输出HDMI功能),或者无线连接Miracast,都可以显示“桌面模式”。但是手上没有相关设备,不能验证。

3. 配置

3.1. PC端软件

3.2. Android必要配置

  • 启用“开发者模式”。
  • 进入“设置” -> “系统” -> “开发者选项”,勾选“启用可自由调整的窗口”、“强制使用桌面模式”。

    • 其中“强制使用桌面模式”,就是“模拟辅助显示设备”启用桌面模式。

3.3. 启动命令

在PC端的命令窗口执行以下命令即可启动。Linux Shell脚本参考如下:

#!/bin/bash

# Params
#  $1: SERIAL|HOST[:PORT]
P_SERIAL=$1

PHONE_NAME="My Phone"
CMD_ADB="/opt/android-sdk/platform-tools/adb"
ADB_SERIAL=
CMD_SCRCPY="/opt/scrcpy/scrcpy"
DISPLAY_ID=0
# Log level
declare -A LOG_LEVEL=([i]=INFO [w]=WARN [e]=ERR)

# Log message
# Params
#  $1: log level
#  $2: message
function func_log() {
    lv=${LOG_LEVEL[$1]}
    if [ -z "$lv" ]; then
        lv=NONE
    fi
    echo [$lv] $2 1>&2
    return
}

function func_init() {
    result=n
    
    # Do connect
    is_connected=$(func_connect)
    if [ "$is_connected" == "y" ]; then
        echo y
        return
    fi
    func_log w "Connect failed. SERIAL or HOST is invalid, or need pairing"
    
    # Do pair
    read -p "Need to pair device? Enter "y" for yes, otherwise exit:" need_pair
    if [ "$need_pair" != "y" ]; then
        echo Exit 1>&2
        echo $result
        return
    fi
    is_paired=$(func_pair)
    if [ "$is_paired" != "y" ]; then
        func_log e "Pair failed, exit"
        echo $result
        return
    else
        func_log i "Pair succeed"
    fi
    is_connected=$(func_connect)
    if [ "$is_connected" != "y" ]; then
        func_log e "Connect failed, exit"
        echo $result
        return
    else
        func_log i "Connect succeed"
    fi
    echo y
    return
}

function func_get_serialno() {
    dev_serialno=$($CMD_ADB get-serialno)
    if [ -z "$dev_serialno" ]; then
        return
    fi
    echo $dev_serialno
    return
}

function func_check_serialno() {
    result=n
    dev_serialno=$($CMD_ADB -s "$P_SERIAL" get-serialno)
    if [ "$P_SERIAL" != "$dev_serialno" ]; then
        echo $result
        return
    fi
    echo y
    return
}

function func_connect() {
    result=n
    
    # Get serialno of default device
    if [ -z $P_SERIAL ]; then
        dev_serialno=$(func_get_serialno)
        if [ -z "$dev_serialno" ]; then
            func_log e "No connected device"
            echo $result
        else
            echo y
            P_SERIAL=$dev_serialno
        fi
        return
    fi
    
    # Check if the device is connected
    is_connected=$(func_check_serialno)
    if [ "$is_connected" == "y" ]; then
        echo y
        return
    fi
    
    # Connect to the device
    dev_connect=$($CMD_ADB connect "$P_SERIAL")
    if [ "${dev_connect:0:9}" != "connected" ] && [ "${dev_connect:0:17}" != "already connected" ]; then
        echo $result
        return
    fi
    echo y
    return
}

function func_pair() {
    result=n
    echo Enter "HOST[:PORT]" of the paired device:; read pair_host
    echo Enter "PAIRING CODE":; read pair_code
    dev_pair=$($CMD_ADB pair "$pair_host" pair_code)
    if [ "${dev_pair:0:19}" != "Successfully paired" ]; then
        echo $result
        return
    fi
    echo y
    return
}

function func_tips() {
    echo + Tips ------------------
    echo   - Ctrl + Shift + O, turn ON screen of the connected device
    echo   - Ctrl + O, turn OFF screen of the connected device
    echo + -----------------------
}

function func_start_apps() {
    # --windowingMode <WINDOWING_MODE>: The windowing mode to launch the activity into.
    # --activityType <ACTIVITY_TYPE>: The activity type to launch the activity as.
    $CMD_ADB $ADB_SERIAL shell am start-activity --display $DISPLAY_ID -a com.aistra.hail.action.LAUNCH -e package cu.axel.smartdock
    # $CMD_ADB $ADB_SERIAL shell am start-activity --display $DISPLAY_ID -a com.aistra.hail.action.UNFREEZE -e package cu.axel.smartdock
    # sleep 2
    # $CMD_ADB $ADB_SERIAL shell am start-activity --display $DISPLAY_ID -n cu.axel.smartdock/.activities.MainActivity
}

function func_stop_apps() {
    $CMD_ADB $ADB_SERIAL shell am start-activity --display $DISPLAY_ID -a com.aistra.hail.action.FREEZE -e package cu.axel.smartdock
}

# main function
function func_main() {
    is_init=$(func_init)
    if [ "$is_init" != "y" ]; then
        return
    fi

    # Turn on auxiliary display device
    $CMD_ADB $ADB_SERIAL shell settings put global overlay_display_devices 1920x1080/193
    #$CMD_ADB $ADB_SERIAL shell settings put global overlay_display_devices 1920x1008/180

    # Get display-id of Simulate secondary displays
    DISPLAY_ID=$($CMD_SCRCPY $ADB_SERIAL --list-displays | $CMD_ADB $ADB_SERIAL shell "grep -o 'display-id=[1-9][0-9]*' | sed 's/display-id=\([1-9][0-9]*\)/\1/'")
    func_log i "Display id: $DISPLAY_ID"

    # Show tips
    func_tips

    # Run apps
    func_start_apps

    # Run scrcpy
    # $CMD_SCRCPY $ADB_SERIAL --display-id=$DISPLAY_ID --keyboard=uhid --mouse=sdk --no-audio --power-off-on-close --shortcut-mod="lctrl,rctrl" --stay-awake --turn-screen-off --window-title="$PHONE_NAME - Android Desktop Mode" --window-x=0 --window-y=25
    $CMD_SCRCPY $ADB_SERIAL --display-id=$DISPLAY_ID --keyboard=sdk --mouse=sdk --no-audio --power-off-on-close --shortcut-mod="lctrl,rctrl" --stay-awake --turn-screen-off --window-title="$PHONE_NAME - Android Desktop Mode" --window-x=0 --window-y=25

    # Stop apps
    func_stop_apps

    # Turn off auxiliary display device
    $CMD_ADB $ADB_SERIAL shell settings put global overlay_display_devices null
}

func_main
exit

说明:

  • PHONE_NAME设置手机名称,作为投屏窗口的标题。
  • CMD_ADB设置adb命令所在位置。
  • ADB_SERIAL设置设备序列号,多设备时指定连接的设备。连接无线adb时,可设置IP:端口。可以作为脚本第一个参数传入。
  • CMD_SCRCPY设置scrcpy命令所在位置。
  • DISPLAY_ID是“模拟辅助显示设备”的“display-id”,用于scrcpy投屏。

    • 这里利用了Android自带的grepsed实现字符串匹配提取,避免操作系统缺乏相关命令(例如Windows的CMD)。
    • 如果PC端是Linux设备,可以改为“DISPLAY_ID=$(${CMD_SCRCPY} --list-displays | grep -oP '(?<=display-id=)1-9*')”,简化命令。
  • adb shell settings put global overlay_display_devices用于开启或关闭Android上“模拟辅助显示设备”。

    • 如果在“开发者选项”中开启“模拟辅助显示设备”,没有DPI选项,所以这里通过命令开启。
    • 值为1920x1080/180,设置“模拟辅助显示设备”的分辨率为1920x1080,DPI为180。此值适合针对20吋左右的屏幕。DPI的值,主要影响“模拟辅助显示设备”UI效果,包括字体大小,但不影响手机正常模式。
    • 值为"1920x1080/180\;1920x1080/180",显示两个“模拟辅助显示设备”。多个“模拟辅助显示设备”的配置,需要用双引号(")包裹,并以\;分隔。
    • 值为null,关闭“模拟辅助显示设备”。
    • 对于Windows 11系统,显示器分辨率为1080p时,“模拟辅助显示设备”的分辨率建议为1920x1008。
  • 启动scrcpy

    • 相关参数可以通过scrcpy --help查阅。
  • 关闭“模拟辅助显示设备”。

    • 由于scrcpy运行时,会占着终端,同时暂停了Shell脚本的运行。那么scrcpy停止后,就可以自动执行关闭“模拟辅助显示设备”。

Windows的CMD批处理脚本,参考如下:

@echo off
SETLOCAL ENABLEEXTENSIONS

rem Params
rem  %1: SERIAL|HOST[:PORT]
set P_SERIAL=%1

set PHONE_NAME=My Phone
set CMD_ADB="D:\tools\android-sdk\platform-tools\adb.exe"
set ADB_SERIAL=
set CMD_SCRCPY="D:\tools\scrcpy\scrcpy.exe"
set TEMP_FILE="C:\Users\%username%\AppData\Local\Temp\scrcpy_display_id_%RANDOM%.txt"
set DISPLAY_ID=0

call :func_main
goto end

rem Log message. If error, do exit.
rem Params
rem  %1: log level
rem  %2: message
:func_log
    set lv_index=%~1
    if "%lv_index%" == "i" (
        set lv=INFO
    ) else if "%lv_index%" == "w" (
        set lv=WARN
    ) else if "%lv_index%" == "e" (
        set lv=ERR
    ) else (
        set lv=NONE
    )
    echo [%lv%] %~2
goto :eof

:func_init
    set %~1=n
    
    rem Do connect
    call :func_connect is_connected
    if "%is_connected%" == "y" (
        set %~1=y
        set ADB_SERIAL=-s %P_SERIAL%
        goto :eof
    )
    call :func_log w,"Connect failed. SERIAL or HOST is invalid, or need pairing"
    
    rem Do pair
    set /p need_pair=Need to pair device? Enter "y" for yes, otherwise exit:
    if "%need_pair%" neq "y" (
        echo Exit
        goto :eof
    )
    call :func_pair is_paired
    if "%is_paired%" neq "y" (
        call :func_log e,"Pair failed, exit"
        goto :eof
    ) else (
        call :func_log i,"Pair succeed"
    )
    call :func_connect is_connected
    if "%is_connected%" neq "y" (
        call :func_log e,"Connect failed, exit"
        goto :eof
    ) else (
        call :func_log i,"Connect succeed"
    )
    set ADB_SERIAL=-s %P_SERIAL%
    set %~1=y
goto :eof

:func_get_serialno
    for /f "delims=" %%i in ('%CMD_ADB% get-serialno') do set dev_serialno=%%i
    if "%dev_serialno%" == "" (
        goto :eof
    )
    set %~1=%dev_serialno%
goto :eof

:func_check_serialno
    set %~1=n
    for /f "delims=" %%i in ('%CMD_ADB% -s %P_SERIAL% get-serialno') do set dev_serialno=%%i
    if "%P_SERIAL%" neq "%dev_serialno%" (
        goto :eof
    )
    set %~1=y
goto :eof

:func_connect
    set %~1=n
    
    rem Get serialno of default device
    if "%P_SERIAL%" == "" (
        call :func_get_serialno dev_serialno
    )
    if "%P_SERIAL%%dev_serialno%" == "" (
        call :func_log e,"No connected device"
        goto :eof
    ) else if "%P_SERIAL%%dev_serialno%" == "%dev_serialno%" (
        set %~1=y
        set P_SERIAL=%dev_serialno%
        goto :eof
    )
    
    rem Check if the device is connected
    call :func_check_serialno is_connected
    if "%is_connected%" == "y" (
        set %~1=y
        goto :eof
    )
    
    rem Connect to the device
    for /f "delims=" %%i in ('%CMD_ADB% connect %P_SERIAL%') do set dev_connect=%%i
    if "%dev_connect:~0,9%" == "connected" (
        rem Do nothing
    ) else if "%dev_connect:~0,17%" == "already connected" (
        rem Do nothing
    ) else (
        goto :eof
    )
    set %~1=y
goto :eof

:func_pair
    set %~1=n
    echo Enter "HOST[:PORT]" of the paired device: & set /p pair_host=
    echo Enter "PAIRING CODE": & set /p pair_code=
    for /f "delims=" %%i in ('%CMD_ADB% pair %pair_host% %pair_code%') do set dev_pair=%%i
    if "%dev_pair:~0,19%" neq "Successfully paired" (
        goto :eof
    )
    set %~1=y
goto :eof

:func_tips
    echo + Tips ------------------
    echo   - Ctrl + Shift + O, turn ON screen of the connected device
    echo   - Ctrl + O, turn OFF screen of the connected device
    echo + -----------------------
goto :eof

:func_start_apps
    rem --windowingMode <WINDOWING_MODE>: The windowing mode to launch the activity into.
    rem --activityType <ACTIVITY_TYPE>: The activity type to launch the activity as.
    %CMD_ADB% %ADB_SERIAL% shell am start-activity --display %DISPLAY_ID% -a com.aistra.hail.action.LAUNCH -e package cu.axel.smartdock
    rem %CMD_ADB% %ADB_SERIAL% shell am start-activity --display %DISPLAY_ID% -a com.aistra.hail.action.UNFREEZE -e package cu.axel.smartdock
    rem timeout /T 2
    rem %CMD_ADB% %ADB_SERIAL% shell am start-activity --display %DISPLAY_ID% -n cu.axel.smartdock/.activities.MainActivity
goto :eof

:func_stop_apps
    %CMD_ADB% %ADB_SERIAL% shell am start-activity --display %DISPLAY_ID% -a com.aistra.hail.action.FREEZE -e package cu.axel.smartdock
goto :eof

rem main function
:func_main
    call :func_init is_init
    if "%is_init%" neq "y" (
        goto :eof
    )
    call :func_log i,"Connected device: %P_SERIAL%"

    rem Turn on auxiliary display device
    rem %CMD_ADB% shell settings put global overlay_display_devices 1920x1080/180
    %CMD_ADB% %ADB_SERIAL% shell settings put global overlay_display_devices 1920x1008/180

    rem Get display id of auxiliary display device
    %CMD_SCRCPY% %ADB_SERIAL% --list-displays | %CMD_ADB% %ADB_SERIAL% shell "grep -o 'display-id=[1-9][0-9]*' | sed 's/display-id=\([1-9][0-9]*\)/\1/'" > %TEMP_FILE%
    set /p DISPLAY_ID=<%TEMP_FILE%
    del %TEMP_FILE%
    call :func_log i,"Display id: %DISPLAY_ID%"

    rem Show tips
    call :func_tips

    rem Run apps
    call :func_start_apps

    rem Run scrcpy
    rem %CMD_SCRCPY% --display-id=%DISPLAY_ID% --keyboard=sdk --mouse=sdk --no-audio --power-off-on-close --shortcut-mod="lctrl,rctrl" --stay-awake --turn-screen-off --window-title="%PHONE_NAME% - Android Desktop Mode" --window-x=0 --window-y=25
    %CMD_SCRCPY% %ADB_SERIAL% --display-id=%DISPLAY_ID% --keyboard=sdk --mouse=sdk --no-audio --power-off-on-close --shortcut-mod="lctrl,rctrl" --stay-awake --turn-screen-off --window-title="%PHONE_NAME% - Android Desktop Mode" --window-x=0 --window-y=25

    rem Stop apps
    call :func_stop_apps

    rem Turn off auxiliary display device
    %CMD_ADB% %ADB_SERIAL% shell settings put global overlay_display_devices null
goto :eof

:end

年初给老婆入手了“红米Note 12 Turbo”,整机较轻,运行速度快,最大惊喜是可以运行“yuzu”(Switch模拟器),可以玩“星之卡比:探索发现”。于是上个月也给自己入手一台(终于脱离“红米K30 5G”的苦海…)。

优点:

  • SoC采用高通的骁龙7+Gen2,高性能。
  • SoC能耗比高,OLED屏幕加持,续航很出色。
  • 塑料机身,取消屏幕支架,整机重量不到190g。
  • X轴线性马达,打字的震动反馈舒适。
  • 双扬声器。

缺点:

  • OLED屏幕,会有烧屏风险。
  • 即使国外有销售(国外名称为Poco F5),LineageOS官方没有支持。
  • Android 14的第三方ROM,自动亮度失效。(注:已修复)

1. 刷机

此机型有很多第三方ROM可选,推荐两个:

本人喜欢“Uvite 14”,下面介绍对应的刷机方法:

1.1. 下载文件

  • Recovery(可选)

  • ROM文件

    • Paranoid官方提供了针对Recovery和Fashboot的刷机包。
    • 个人建议下载Fashboot版,后面安装KernelSU会方便一点。

1.2. 刷ROM

注:

  • 刷机前,需要利用小米手机解锁工具进行解锁(此处略)。
  • 由于使用了A/B分区,Recovery集成在boot分区(跟系统Rom集成),所以刷入的第三方Recovery会被系统Rom所覆盖。
  • 由于Recovery集成在boot分区,所以就建议使用Fastboot模式刷系统Rom。
  • 使用Windows系统的,一定要先装好驱动。

方案1,使用Recovery

注:不推荐这个方案。

手机开机进入Fastboot模式(同时按音量减和开机键),刷入Recovery并进入,参考命令如下:

# 刷入Recovery
fastboot devices
fastboot flash recovery twrp-3.7.1_12-v8.6_A14-marble-skkk.img
fastboot reboot recovery

进入Revocery后,就是常规操作了:

  • 格式化data(首次刷这个系统,需要执行此步骤。记得先备份好文件。)
  • Recovery里刷入“Uvite 14”的Recovery版ROM
  • 重启后完成

方案2,使用Fastboot

注:如果使用Windows系统刷机,要先安装好驱动。具体驱动文件,在官方刷机工具里已提供。

Paranoid官方提供了Fastboot的刷机工具:https://github.com/ghostrider-reborn/aospa-flashing-kit/tree/marble

如果手工执行刷机,可参考以下步骤:

  • 手机开机进入Fastboot模式
  • 电脑端执行以下命令

    # Fastboot模式刷ROM
    fastboot update --skip-reboot aospa-uvite-beta-marble-20240617-image.zip
  • 期间手机会重启进入AOSPA Fastbootd,并执行余下处理,这里不要管
  • 直到电脑端提示Finished. Total time: 238.537s,表示完成刷机。此时,手机选Reboot system now,并按电源键,手机重启进入系统。

2. 获取Root权限

推荐使用KernelSU,天然自带隐藏功能,避免App检测Root。

参考资料:

参考步骤:

  • 在github下载最新的KernelSU apk,并在手机安装。
  • 从“Uvite 14”的Fastboot版的刷机包中,提取boot.img文件,并放到手机上。
  • 手机上使用KernelSU apk对boot.img打补丁。
  • 把打补丁后的boot.img刷入手机。

    • 这一步,我的做法是把打补丁后的boot.img,替换“Uvite 14”的Fastboot版的刷机包对应文件,并使用刷ROM的操作刷入。
  • 重启手机后,再打开KernelSU,就提示正常工作了。

3. 刷成砖

值得记录的是,试过使用Recovery刷Rom导致刷成砖。主要是刷机过程中,Recovery自动重启,导致刷机过程意外终止,手机被刷成砖,不能启动,也不能进入Fastboot模式。

由于采用高通的SoC,这个情况,可以进入9008模式,刷入Rom,实现拯救手机。而Android手机进入9008模式的方式,主要有两个,一个是使用刷机线,另一个是开机过程短接主板上的特定触点。查了下,这个手机只能通过“短接触点”的方式进入9008。由于我直接申请售后,就没有继续深入研究了。

关于9008刷机线(以下内容来源网络,没有实践过):

  • 使用Micro USB数据线,即USB 2.0的数据线。
  • 剥开“绿色”和“黑色”的线,露出金属,用于短接。
  • 使用此数据线连上电脑,另一头接上Type C转接头。
  • 手机关机状态下,短接“绿色”和“黑色”线(用手摁住即可),Type C转接头连上手机,几秒后松开两个金属线,在电脑设备管理器(我的电脑-右键-管理-设备管理器-端口COM和LPT)中可以看到9008端口。
  • 如果嫌麻烦,可以直接购买9008刷机线,有个按钮可以实现短接“绿色”和“黑色”的线。

使用Termux多年了。主要是可以安装N多Linux命令,一个应用即可实现N多功能。目前最新版是0.118.0,推荐从F-Droid安装。

这里记录一下相关的初始化动作。

1 相关资料

2 设置国内源

跟大多数Linux发行版类似,软件源设置为国内镜像,有效提高安装、更新软件的效率。

执行命令termux-change-repo即可设置。

参考:

手工修改的话,跟Debian类似,直接修改apt的source.list文件即可。即修改$PREFIX/etc/apt/sources.list文件的内容如下:

# The termux repository mirror from TUNA:
deb https://mirrors.tuna.tsinghua.edu.cn/termux/apt/termux-main stable main
deb https://mirrors.tuna.tsinghua.edu.cn/termux/apt/termux-root root stable
deb https://mirrors.tuna.tsinghua.edu.cn/termux/apt/termux-x11 x11 main

保存后,执行apt update,更新一下本地的缓存。

3 安装Termux工具

执行以下命令,安装Termux的相关工具。如果默认安装了,则不用再装。

pkg install termux-tools

4 开启储存访问

执行termux-setup-storage即可。对应Android的sdcard目录为~/storage/shared

5 关闭震动

本人不喜欢以震动方式提示错误,习惯关闭它。编辑文件~/.termux/termux.properties,末尾添加:

bell-character = ignore

6 设置辅助键盘(the extra keys)

官方详细说明: Termux Wiki - Extra Keys Row

值得一提,可以设置按键上滑,让辅助键盘拥有更多的功能。参考配置,修改文件~/.termux/termux.properties,末尾添加:

extra-keys = [ \
   [{key: ESC, popup: {macro: "CTRL d", display: "exit"}}, \
   {key: '/', popup: BACKSLASH}, \
   {key: '-', popup: '_'}, \
   {key: '(', popup: '{'}, \
   {key: UP, popup: PGUP}, \
   {key: ')', popup: '}'}, \
   {key: QUOTE, popup: '*'}], \
   [{key: TAB, popup: ':'}, \
   {key: CTRL}, \
   {key: ALT}, \
   {key: LEFT, popup: HOME}, \
   {key: DOWN, popup: PGDN}, \
   {key: RIGHT, popup: END}, \
   {key: APOSTROPHE, popup: '`'}] \
]

7 开启root

  • 如果系统已root,安装tsu,可以切换root用户,或者使用sudo以root用户执行命令。
pkg install tsu
sudo 命令
  • 如果Android系统没有root,安装proot,即可执行需要root权限的命令。
pkg install proot
proot 命令

8 安装vim及配置

本人习惯vim,其它编辑程序,可以选择nanoemacs等。安装命令如下:

pkg install vim

重点配置,编辑文件~/.vimrc,末尾添加:

" 解决中文显示
set fileencodings=utf-8,gb2312,gb18030,gbk,ucs-bom,cp936,latin1
set enc=utf8
set fencs=utf8,gbk,gb2312,gb18030

" 显示行号
set nu

" 颜色主题
colorscheme desert

" 语法高亮
syntax on

最后使配置生效:

source .vimrc

9 Termux相关应用

  • Termux:API

    从Termux访问Android功能。可以实现脚本控制Android。
  • Termux:Boot

    允许程序在启动时运行的Termux扩展应用。
  • Termux:Float

    在浮动终端窗口中使用Termux。
  • Termux:Styling

    自定义Termux终端的样式。
  • Termux:Tasker

    从Tasker运行Termux脚本的Tasker插件。需要配合Tasker使用。
  • Termux:Widget

    从主屏幕启动Termux命令。实现通过点击桌面图标执行相关的sh脚本。

10 其它常用命令

  • OpenSSH

    • 说明:完整的SSH客户端。
    • 安装命令:pkg install openssh
  • ADB

    • 说明:可以使用ADB连接本机,无需root权限就能执行input命令等。
    • 安装命令:pkg install android-tools

XDA论坛上看了很久,Android 11就只有crDroid有官方支持。加上Resurrection Remix OS用得真不爽,主要是自动杀进程,而且不分前后台。于是五一假期狠下心刷上crDroid,用了一段时间,很爽!流畅、不杀进程、支持国产支付app的指纹,续航还行。

1. 相关下载

1.1. crDroid ROM

当前版本是7.5。注:目前此ROM版本处于无人维护状态!
官网不仅可以下载ROM,还提供小米官方固件(欧版MIUI)、Recovery、OpenGapps等的链接:
https://crdroid.net/picasso/7

1.2. 欧版MIUI

目前最新版是12.1.8,据说今年6月会推出12.5适配。当然,这不是重点。下载地址:
xiaomi.eu_multi_HMK305G_V12.1.8.0.RGICNXM_v12-11.zip
https://sourceforge.net/projects/xiaomi-eu-multilang-miui-roms/files/xiaomi.eu/MIUI-STABLE-RELEASES/MIUIv12/

1.3. Recovery

crDroid推荐的Recovery是OrangeFox。界面跟Twrp非常不同,感觉更像是文件管理器,但功能还是一样。
OrangeFox-picasso-stable@R11.1_1.7.zip
https://orangefox.download/zh-CN/device/picasso

1.4. Margisk

目前几乎是获取root权限的唯一方案。下载地址:
https://github.com/topjohnwu/Magisk/releases

要注意,从Margisk 22开始,root安装包和root管理工具,都合并为一个apk文件。root系统时,只需用Recovery刷入该apk文件。

1.5. Gapps

由于OpenGapps的Android 11版还没正式发布(当前处于测试),所以我选用NikGApps。下载链接:
https://nikgapps.com/downloads.html#downloads

一般选在Core版就可以了。值得注意的是,如果要开启Google Assistant(Google助手),需要把Google Velvet(即Google搜索)安装为系统App。NikGApps提供了对应的Addons(文件名为:NikGapps-Addon-11-Velvet-signed.zip),可以通过Recovery直接刷入系统。

2. 刷机过程

2.1. 解锁及刷Recovery

都是常识性操作。刷Recovery后,最好先备份当前系统。

2.2. 备份数据

钛备份不再更新了,推荐使用OAndBackupX备份App及数据。可通过F-Droid安装:
https://f-droid.org/en/packages/com.machiav3lli.backup/

还有联系人和短信的备份,不再赘述。

2.3. 刷机步骤

按刷机顺序排列,crDroid的方案是:OrangeFox -> 欧版MIUI -> crDroid -> Margisk -> OpenGapps。我的方案只是用NikGApps替换OpenGapps。另外,最好先“双清”再开刷。

3. 初始设置

3.1. 去掉WiFi和信号出现“x”符号

需要使用adb执行以下命令:

adb shell settings put global captive_portal_mode 0
adb shell settings put global captive_portal_https_url https://www.google.cn/generate_204

命令执行成功后,手机开启飞行模式,然后关闭飞行模式,即可解决。
参考:https://www.uso.cn/post/view/47990

3.2. 开启Google Assistant(可选)

初次进入系统后,不插入SIM卡,语言选English(US)。安装Google Assistant并设置开启。成功后,再插入SIM卡,并设语言为中文。


更新 2023-06-22

1. 针对此机的crDroid 9(Android 13)终于有人维护。

详见:https://crdroid.net/picasso/9

刷机时注意:

  • 需要“双清”数据后全新刷入。
  • 需要更新对应的Firmware和Recovery。

2. GMS耗电的问题。

原因:可能是Android的安全补丁一直没更新,Google Play服务会不断尝试后台下载更新,但非Pixel手机会下载失败。

3. ROOT方案。

旧的ROOT方案是使用Magisk。隐藏ROOT是开启Zygisk,再安装Shamiko模块(Shamiko发布地址)。缺点是容易被应用检测到,导致不能适应,例如国内的各大银行app。

新的ROOT方案是使用KernelSU。内核集成ROOT功能,只需安装KernelSU管理应用即可使用(KernelSU发布地址)。缺点是需要内核支持,可玩模块也没那么多。优点是隐蔽性强,几乎不被检测到。

4. system_server高占用CPU。

在crdroid 9.10到9.12都出现,直接导致手机电量消耗过快,而且偶然出现。目前只能通过重启系统解决。

5. 卡屏。

一般是切换应用时“卡屏”,就是屏幕画面不变,且不接收任何输入。一般等几秒就恢复。“阿里系”的应用,例如“支付宝”、“淘宝”,切换时最容易出现。

作为MIUI恐惧者,无奈LineageOS官方没有支持这台红米K30 5G,只能诚惶诚恐地使用欧版MIUI。直到某天发现了Resurrection Remix OS官方支持这款手机,才终于脱离MIUI。

查了资料,才知道Resurrection Remix OS是基于LineageOS的一款开源ROM,基本体验与LineageOS一致,并增加了很多设置(基本上是界面的,个人感觉用途不大),适合喜欢原生Android的用户。

LineageOS的优点基本继承了,总结一下缺点吧:

  • 1)国内支付应用,基本不能使用指纹支付。历史原因,一直遗留下来的问题。跟LineageOS一样。
  • 2)关屏后不能双击屏幕打开锁屏界面
  • 3)不能双击桌面锁屏。可以设置双击任务栏或者三大金刚键进行锁屏。
  • 4)不能拍摄6400万像素的照片。ROM自带相机和Google Camera移植版,最高都只支持1610万像素。不过即使是MIUI的自带相机,拍出来的6400万像素照片基本直出(纯粹自我安慰)。
  • 5)刚开始使用,比较耗电。做了一些优化后,后面变得相对省电一点。不过,这手机本身也不怎么省电。
  • 6)长时间使用的应用,容易自动退出。比如Chrome开个视频后,很大几率会自动关掉并回到桌面。可能是电源管理自动优化吧。

总的来讲,曾经的LineageOS用户可以放心刷,也没有遇到影响日常使用的bug 。

刷机过程,跟LineageOS一样。重点还是那句:刷机前先备份好数据。Recovery备份分区(刷机失败时可以还原系统) + Ti Backup备份应用(用于迁移应用及数据)。

1. 下载相关数据

2. 刷机

  • 备份数据。最好连sdcard的数据也备份一下
  • wipe手机,即Recovery格式化data分区
  • 解锁、刷recovery
  • 刷ROM
  • Recovery里Root系统
  • 刷Opengapps
  • 清cache
  • 重启进入系统

要注意,进入系统后,需要连Google验证。

3. 耗电优化的处理。当前的节电设置如下:

  • a)屏幕刷新率设为 60Hz,在“设置”->“系统”->Device-specific settings->Minimum Refresh Rate。120Hz屏幕其实很丝滑,但60Hz确实够用。
  • b)关闭 5G 网络,只用 4G 。设置移动网络的首选网络类型为“LTE/WCDMA”。当前来说,5G除了耗电比较快,没感受到什么优势。
  • c)限制应用使用电量。在电池管理器设置受限应用。目前设了 Google Play 商店。
  • d)关闭 Google 账户的自动同步功能,只开启了 Chrome 和 Gmail 。
  • e)冻结了一些不常用但不可缺的应用。冰箱、Shelter 、island 等。其中 island 的名称太中二,个人接受不了而放弃。用 Shelter 的话,如果工作空间的应用没用到 GMS,最好冻结它,并尽量安装非无依赖 GMS 的应用。

由于贪便宜,上一部手机红米Note4x(代号:mido)买了3G内存+32G存储的版本,导致用了两年半就内部空间不足。即使插上128GB的TF卡,并转为内部存储,也要面对偶尔出现的SD卡错误,导致装在卡上的应用不能运行。抓狂……然后CFO的批准下,一起换了红米K30 5G(代号:picasso)。

这个时间点,要换手机,就肯定选5G网络的了。然后,考虑高性价比,能解BL锁,能刷第三方ROM,就剩下红米了。最后在K30i 5G与K30 5G之间,选择了拥有主摄6400万像素的K30 5G。低配版6GB + 128GB,目测用3年应该没问题。

关于此手机的一切说明,可以参考这个文章:
老妈钦点,我买了一部Redmi K30 5G版
https://pockies.github.io/2020/03/27/redmi-k30-5g/

按照文章的刷机操作,就是手机绑定小账号、解BL锁、刷recovery、刷欧版MIUI、折腾流氓应用。但是解锁后直接刷最新版Recovery,进去时会黑屏。参考了网上的经验分享,需要先刷旧版Recovery,然后刷ROM,再刷新版Recovery。由于该Recovery自带Magisk,所以不用单独下载。相关软件如下:

1)小米官方解锁工具
https://www.miui.com/unlock/index.html

2)非官方TWRP Recovery
https://mifirm.net/model/picasso.ttt#twrp
旧版:TWRP-3.4.0B-0209-REDMI_K30_5G-CN-wzsx150-fastboot
目前最新版:TWRP-3.4.2B-0623-REDMI_K30_5G-CN-wzsx150-fastboot

3)欧版MIUIv12
https://sourceforge.net/projects/xiaomi-eu-multilang-miui-roms/files/xiaomi.eu/MIUI-STABLE-RELEASES/MIUIv12/

简单的刷机流程是:刷旧版Recovery,格式化Data分区,刷入欧版MIUI,双清,在Recovery刷入最新版Recovery后,再进入Recovery进行root,重启进入系统。

刷完欧版ROM,我选择了冰箱(主要冻结无用的系统App,曾经买了付费版) + Island(主要是使用工作空间隔离流氓App,并且能进行冻结)结合使用。由于Shelter不能在MIUI上运行,只能暂时使用Island(缺点是没有自动冻结,用起来没有Shelter顺手)。

用了一段时间,一开始感觉是比较耗电,渐渐感觉跟红米Note4x差不多。已调低屏幕刷新率60mHz,没什么感觉。玩过Minecraft,手机发烫比较严重,耗电也比较快。

希望后面能刷上LineageOS。但是XDA-Developers上的网友说,小米官方一直没更新这手机的kernel源码,导致第三方系统不能完善。目前也就先这样了。


后记 2023-06-22

不经不觉三年了。这手机的缺点总结如下:

  1. 重。贴上“钢化玻璃膜”和插上两个SIM卡,K30 5G整体重量超过220g。对比红米 Note4X是170g。
  2. 玻璃后盖容易碎。
  3. 5G网络“鸡肋”。开了5G网络,除了流量飞快,还容易发热。而且室内一般没信号,自动切换到4G。直接后果是耗电。
  4. 后置主摄6400万像素,可以说是欺骗。由于传感器是SONY IMX686,采用4合1像素,一般相机应用只能识别成1600万像素。只有MIUI自带的相机应用才能拍摄出6400万像素的照片。
  5. 可刷的开源ROM较少。没办法,毕竟不是销外国际的机型。

一个只有自己在用的新闻阅读器,终于改为使用OkHttp发起请求了。原来一直用Volley,但是改用OkHttp之后,做更多的请求定制,为后面添加更多功能做准备。记录一下一些知识点:

首先是Android官方教程,关于执行网络操作的部分。有教授使用HttpsURLConnection链接网络的方法。
Android官方教程 - 连接到网络
https://developer.android.com/training/basics/network-ops/connecting

然后是主线程不能发起网络请求的问题。主要是为了提高程序的性能,把不占用CPU的IO操作(例如网络请求)都移到子线程执行。网络请求执行结束后,调用runOnUiThread方法回到主线程更新UI。
Android 子线程更新UI了解吗?
https://juejin.im/post/5da14e8ae51d45782b0c1c20

最后是OkHttp的教程。其异步请求,用起来有点像jQuery的ajax方法。
1)okhttp代码
https://github.com/square/okhttp
2)OkHttp使用完全教程
https://www.jianshu.com/p/ca8a982a116b

已经不是第一次遇到Android Studio提示“gradle sync failed”的错误了,还是记录下来,免得又得烦。

原因一般是网络错误,导致Gradle不能下载项目相关的依赖包。于是配置网络代理,Android Studio的网络代理配置界面没有支持socks5。

解决方法,参考文章:
gradle代理 - 简书 - 蒸汽飞船
https://www.jianshu.com/p/7b3bc89d26e5

简单来说,就是不要用Android Studio设置网络代理。我选择修改$HOME/.gradle/gradle.properties文件,加上以下配置:

org.gradle.jvmargs=-DsocksProxyHost\=127.0.0.1 -DsocksProxyPort\=1080

本来一直在用Scrcpy,一个把手机屏幕显示在电脑屏幕的软件。作为一般的操作,可以接受。但是滑动太快,或者玩游戏,会出现马赛克。直到前几天发现了QtScrcpy,据称可以“吃鸡”!今晚终于在Lubuntu上编译出来,玩了下Minecraft,也看了下视频,非常不错~虽然已经在公司用了多天来划水。

详细的介绍,请查看项目的官方介绍。项目地址:https://github.com/barry-ran/QtScrcpy

应用场景:把手机屏幕的内容显示在电脑屏幕,支持 Windows、MAC、Linux 三大系统(其中 Linux 需要自己编译)。可以实现公司电脑上无痕划水(自备无限流量套餐、迷你蓝牙耳机,效果更佳),或者找个烂电脑作为手机伴侣(实现小屏转大屏)。

优点:
1)速度快! 720p可以玩游戏,1080p可以看电影。

2)有熄屏功能。手机画面投影在电脑上,手机屏幕同时可以关掉。

3)有功能完整的操作界面,基本的功能按钮都有,免得打命令。手机的输入可以直接用鼠标和键盘操作(跟 Scrcpy 一样)。

缺点:
1)输入法不能通过键盘选字、不能直接输入符号(这个缺点直接继承 Scrcpy )。直接用蓝牙键盘连手机的话,输入体验会好很多。

2)Linux上需要自己编译。装QtCreator、Android SDK、Android NDK……由于网络的问题,下载相关软件时折腾了一下。编译挺简单的,对于第一次接触QtCreator的我来说,一次编辑成功。对于没有编程经验的人来说,会有难度。但是这种人一般不会使用Linux。另外,QtCreator最好从 https://download.qt.io/archive/qt/ 下载安装包进行安装。

3)窗口模式不能随意调整大小(Scrcpy是可以的),只能固定大小,或者全屏。

4)手机声音不能通过电脑播放。不知道是不是我没找到设置的地方,所以需要配个蓝牙耳机。

在家使用还是有点浪费资源,毕竟要开两个机器来做一件事件。除了玩Minecraft PE,暂时想不到有什么应用场景。但是在公司划水的话,非常实用。

从G1时代开始,就了解到因为Android使用Linux内核,可以利用chroot运行大量Linux发行版。但是由于当时ARM CPU性能低下及内存不足,一般只能使用Terminal字符界面,或者ssh过去。然后升级过设备,并装上了“XServer XSDL”(Android上的Xserver)用来体验图形界面,但是手机屏幕太小,一弹出虚拟键盘就基本把桌面挡住了。后来想起X Window是基于客户端/服务器模式的,应该可以用PC电脑之类提供Xserver,显示手机上的Linux图形界面。最后终于弄明白了配置,出来的效果还是不错,至少可以用浏览器流畅播放视频了。

所需设备
S机,用于提供X Server的设备,最好是屏幕比较大的PC电脑(台式机或笔记本)。系统最好是Linux,装上X11。后来发现Windows也可以,因为有Xming。当然,Android也是可以,因为有XServer XSDL。这里只记录Linux的。

A机,Android设备(手机或平板),运行Linux发行版的X Client。Android上有很多装Linux的应用了,这里推荐Linux Deploy,因为这是github.com上的开源项目。装Linux的步骤不详述,这个应用已经做得很好了。

网络设置
只要两个设备在同一局域网内就可以了。下面列出几种方式:
1)使用有线/无线路由器组建局域网。路由器是性能瓶颈,特别是A机无线连接到路由。
2)A机分享无线网络,S机连过去。缺点是只能利用A机的移动网络上网,费钱……
3)S机分享无线网络,A机连过去。S机需要装个无线网卡,并成为性能瓶颈。
4)A机开启开发者模式,通过USB线连接S机。S机利用ADB命令的forward功能映射端口。ADB命令成为性能瓶颈。
5)A机分享有线网络,通过USB线连接S机。这个方案性能最佳,且使用设备最小,又不影响A机的网络。但A机可能会出现发热的情况。

X Server设置
这一步花了很多时间。理解后就是S机上的一个命令和一个配置文件,即如何启动X Server和设置验证。相关原理可查Google,这里设置X Server为:1.0(从0开始算,第2个服务),默认使用6001端口。
1)利用xhost设置可访问X Server的客户端IP。在S机上修改配置文件/etc/X1.hosts,若不存在则新建。把A机的IP地址填进去,并保存。
2)运行以下命令启动X Server:

sudo Xorg :1.0 -listen tcp

X Client设置
A机上需要装好桌面环境。推荐LXDE吧,轻量。启动前,连好网络,在Linux Deploy上设置:

图形界面 -> 勾选启用
图形子系统 -> 选X11
图形界面设置 -> 显示编号 -> 1.0
           -> X服务器地址 -> 填写S机的IP地址
           -> XServer XSDL -> 不要勾选
桌面环境 -> 选LXDE

然后启动Linux即可(实在太方便了)。对应的命令就是:

export DISPLAY=S机_IP:1.0
startlxde

总结
A机的性能越好,并且S机的性能越差,这个方案的实用性就越高。比起微软的“Continue on PC”,三星的DeX,Superbook等方案,便宜很多,并把旧电脑利用起来。使用过程中,不会影响A机的来电、通知等日常用途。但是目前来看,实用性确实不高。后面看看能不能用Raspberry Pi Zero + 显示屏 + 大容量移动电源来作为S机,以提高便携性。