Skip to content

Commit

Permalink
merge master into release
Browse files Browse the repository at this point in the history
  • Loading branch information
TonyJiangWJ committed Aug 18, 2023
2 parents 2480156 + 3a6ee7a commit d9e638d
Show file tree
Hide file tree
Showing 6 changed files with 544 additions and 106 deletions.
267 changes: 267 additions & 0 deletions README.md

Large diffs are not rendered by default.

293 changes: 191 additions & 102 deletions lib/Unlock.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* @Author: TonyJiangWJ
* @Date: 2019-11-05 09:12:00
* @Last Modified by: TonyJiangWJ
* @Last Modified time: 2023-06-13 15:45:01
* @Last Modified time: 2023-08-18 01:03:47
* @Description:
*/
let { config: _config, storageName: _storageName } = require('../config.js')(runtime, global)
Expand All @@ -13,32 +13,47 @@ let FileUtils = singletonRequire('FileUtils')
let _commonFunctions = singletonRequire('CommonFunction')
let _widgetUtils = singletonRequire('WidgetUtils')
let _runningQueueDispatcher = singletonRequire('RunningQueueDispatcher')
let warningFloaty = singletonRequire('WarningFloaty')
let ExternalUnlockDevice = files.exists(FileUtils.getCurrentWorkPath() + '/extends/ExternalUnlockDevice.js') ? require('../extends/ExternalUnlockDevice.js') : null

if (ExternalUnlockDevice) {
logInfo('使用自定义解锁模块')
} else {
logInfo('使用内置解锁模块')
}
const DEVICE_TYPE = {
VIVO: 'vivo',
COLOROS: 'coloros',
MEIZU: 'meizu_flyme',
NORMAL: 'normal',
}

const DEVICES = {
'vivo': (mainUnlocker) => new VivoPinUnlocker(mainUnlocker),
'coloros': (mainUnlocker) => new ColorOsPinUnlocker(mainUnlocker),
'meizu_flyme': (mainUnlocker) => new MeizuPinUnlocker(mainUnlocker),
'normal': (mainUnlocker) => new PinUnlocker(mainUnlocker),
}

let NORMAL_DEVICE = function (obj) {
this.DEVICE_TYPE = {
VIVO: 'vivo',
COLOROS: 'coloros',
NORMAL: 'normal',
}
this.DEVICE_TYPE = DEVICE_TYPE
this.storage = storages.create(_storageName)
this.__proto__ = obj
this.ensurePinPassword = function () {
if (!/^\d+$/.test(this.password)) {
throw new Error('密码应当为纯数字')
}
}
// 图形密码解锁
this.unlock_pattern = function (password) {
this.unlock_pattern = function () {
debugInfo('使用图形密码解锁')
if (typeof password !== 'string') throw new Error('密码应为字符串!')
this.ensurePinPassword()
let lockBounds = id('com.android.systemui:id/lockPatternView')
.findOne(_config.timeout_findOne)
.bounds()
let boxWidth = (lockBounds.right - lockBounds.left) / 3
let boxHeight = (lockBounds.bottom - lockBounds.top) / 3
let positions = password.split('').map(p => {
let positions = this.password.split('').map(p => {
let checkVal = parseInt(p) - 1
return { r: parseInt(checkVal / 3), c: parseInt(checkVal % 3) }
}).map(p => {
Expand All @@ -49,126 +64,78 @@ let NORMAL_DEVICE = function (obj) {
}

// 密码解锁(仅ROOT可用)
this.unlock_password = function (password) {
this.unlock_password = function () {
debugInfo('使用字符串密码解锁 必须有root权限')
if (!automator.hasRootPermission()) {
if (!_config.hasRootPermission) {
errorInfo('无ROOT权限,无法使用字符串密码解锁')
}
if (typeof password !== 'string') throw new Error('密码应为字符串!')
if (typeof this.password !== 'string') throw new Error('密码应为字符串!')
// 直接在控件中输入密码
setText(0, password)
setText(0, this.password)
// 执行确认操作
KeyCode('KEYCODE_ENTER')
return this.check_unlock()
}

// PIN解锁
this.unlock_pin = function (password) {
debugInfo('使用PIN密码解锁')
if (typeof password !== 'string') throw new Error('密码应为字符串!')
// 模拟按键
let button = null
for (let i = 0; i < password.length; i++) {
let key_id = 'com.android.systemui:id/key' + password[i]
if ((button = id(key_id).findOne(_config.timeout_findOne)) !== null) {
button.click()
}
sleep(100)
}
return this.check_unlock()
}

/**
* coloros 解锁
* @param {string} password
* @returns
*/
this.unlock_coloros_sample = function (password) {
debugInfo('使用coloros的PIN密码解锁')
if (typeof password !== 'string') throw new Error('密码应为字符串!')
// 模拟按键
let button = null
let keyboardRoot = id('com.android.systemui:id/pinColorNumericKeyboard').findOne(_config.timeout_unlock)
if (!keyboardRoot) {
throw new Error('获取键盘控件失败')
}
for (let i = 0; i < password.length; i++) {
button = keyboardRoot.child((parseInt(password[i]) + 9) % 10)
if (button !== null) {
button.click()
} else {
errorInfo(['未找到数字按钮:{} 可能无法正常解锁', password[i]])
}
sleep(100)
}
_config.unlock_device_flag = this.DEVICE_TYPE.COLOROS
this.storage.put('unlock_device_flag', _config.unlock_device_flag)
return this.check_unlock()
}

this.unlock_vivo_pin = function (password) {
debugInfo('使用vivo的PIN密码解锁')
if (typeof password !== 'string') throw new Error('密码应为字符串!')
// 模拟按键
let button = null
for (let i = 0; i < password.length; i++) {
let key_id = 'com.android.systemui:id/VivoPinkey' + password[i]
if ((button = id(key_id).findOne(_config.timeout_findOne)) !== null) {
button.click()
}
sleep(100)
}
_config.unlock_device_flag = this.DEVICE_TYPE.VIVO
this.storage.put('unlock_device_flag', _config.unlock_device_flag)
return this.check_unlock()
}

// 判断解锁方式并解锁
this.unlock = function (password) {
if (typeof password === 'undefined' || password === null || password.length === 0) {
errorInfo('密码为空:' + JSON.stringify(password))
throw new Error('密码为空!')
}
this.password = password
let unlockSuccess = false
// 特殊设备 记住解锁方式
switch (_config.unlock_device_flag) {
case this.DEVICE_TYPE.VIVO:
unlockSuccess = this.unlock_vivo_pin(password)
break
case this.DEVICE_TYPE.COLOROS:
unlockSuccess = this.unlock_coloros_sample(password)
break
default:
// no operation
let storedUnlocker = DEVICES[_config.unlock_device_flag]
if (storedUnlocker != null && typeof storedUnlocker == 'function') {
debugInfo(['当前已存储使用特定设备解锁:{}', _config.unlock_device_flag])
unlockSuccess = storedUnlocker(this).unlock_pin()
}
// 如果是vivo或者coloros直接返回成功
// 如果已存储使用特定设备解锁方式成功则直接返回成功
if (unlockSuccess) {
return true
}
// 其他设备 依次判断 pin、手势、字符串、coloros、vivo
if (idMatches('com.android.systemui:id/(fixedP|p)inEntry').exists()) {
return this.unlock_pin(password)
} else if (id('com.android.systemui:id/lockPatternView').exists()) {
return this.unlock_pattern(password)
} else if (id('com.android.systemui:id/passwordEntry').exists()) {
return this.unlock_password(password)
} else if (id('com.android.systemui:id/pinColorNumericKeyboard').exists()) {
return this.unlock_coloros_sample(password)
} else if (id('com.android.systemui:id/vivo_pin_keyboard').exists()) {
return this.unlock_vivo_pin(password)
} else {
logInfo(
let _this = this
let unlockers = [
{ id: '(fixedP|p)inEntry', unlock: () => new PinUnlocker(_this).unlock_pin() },
{ id: 'lockPatternView', unlock: () => _this.unlock_pattern() },
{
id: 'passwordEntry', unlock: () => {
// 魅族手机特殊处理
if (id('com.android.systemui:id/lockPattern').exists()) {
return new MeizuPinUnlocker(_this).unlock_pin()
}
return _this.unlock_password()
}
},
{ id: 'pinColorNumericKeyboard', unlock: () => new ColorOsPinUnlocker(_this).unlock_pin() },
{ id: 'vivo_pin_keyboard', unlock: () => new VivoPinUnlocker(_this).unlock_pin() },
]
let patternMatched = false
unlockers.forEach(v => {
if (unlockSuccess) {
return
}
debugInfo(['准备查找id:[com.android.systemui:id/{}]', v.id])
if (idMatches('com.android.systemui:id/' + v.id).exists()) {
debugInfo(['找到了目标控件实际id:[{}]', idMatches('com.android.systemui:id/' + v.id).findOne().id()])
patternMatched = true
unlockSuccess = v.unlock()
} else {
debugInfo(['通过id[{}]查找控件不存在', v.id])
}
})
if (!patternMatched) {
errorInfo(
'识别锁定方式失败,型号:' + device.brand + ' ' + device.product + ' ' + device.release
)
logInfo('请运行unit/获取解锁界面控件信息.js 获取布局信息自行开发解锁代码 或者向开发者寻求帮助')
errorInfo('请运行unit/获取解锁界面控件信息.js 获取布局信息自行开发解锁代码 或者向开发者寻求帮助', true)
return this.check_unlock()
}
return unlockSuccess
}
}


const MyDevice = ExternalUnlockDevice || NORMAL_DEVICE

function Unlocker () {
const _km = context.getSystemService(context.KEYGUARD_SERVICE)

Expand Down Expand Up @@ -206,7 +173,7 @@ function Unlocker () {
// 检测是否解锁成功
this.check_unlock = function () {
sleep(_config.timeout_unlock)
if(!this.is_locked()) {
if (!this.is_locked()) {
return true
}
if (
Expand Down Expand Up @@ -355,6 +322,7 @@ function Unlocker () {
}
}

const MyDevice = ExternalUnlockDevice || NORMAL_DEVICE
const _unlocker = new MyDevice(new Unlocker())
module.exports = {
exec: function () {
Expand Down Expand Up @@ -389,3 +357,124 @@ module.exports = {
},
unlocker: _unlocker
}

// --- 内部类,扩展pin解锁

function PinUnlocker (mainUnlocker) {
this.password = mainUnlocker.password
mainUnlocker.ensurePinPassword()
// PIN解锁
this.unlock_pin = function () {
if (this.doPinUnlock()) {
return mainUnlocker.check_unlock()
}
return false
}
}
// 默认解锁
PinUnlocker.prototype.doPinUnlock = function () {
debugInfo('使用通用PIN密码解锁')
// 模拟按键
let button = null
for (let i = 0; i < this.password.length; i++) {
let key_id = 'com.android.systemui:id/key' + this.password[i]
if ((button = id(key_id).findOne(_config.timeout_findOne)) !== null) {
button.click()
}
sleep(100)
}
_config.overwrite('unlock_device_flag', DEVICE_TYPE.NORMAL)
return true
}
function extendPinUnlocker (child, doPinUnlock) {
child.prototype = Object.create(PinUnlocker.prototype)
child.prototype.doPinUnlock = doPinUnlock
}
function VivoPinUnlocker (mainUnlocker) {
PinUnlocker.call(this, mainUnlocker)
}
function ColorOsPinUnlocker (mainUnlocker) {
PinUnlocker.call(this, mainUnlocker)
}
function MeizuPinUnlocker (mainUnlocker) {
PinUnlocker.call(this, mainUnlocker)
}

extendPinUnlocker(VivoPinUnlocker, function () {
debugInfo('使用vivo的PIN密码解锁')
// 模拟按键
let button = null
for (let i = 0; i < this.password.length; i++) {
let key_id = 'com.android.systemui:id/VivoPinkey' + this.password[i]
if ((button = id(key_id).findOne(_config.timeout_findOne)) !== null) {
button.click()
}
sleep(100)
}
_config.overwrite('unlock_device_flag', DEVICE_TYPE.VIVO)
return true
})

extendPinUnlocker(ColorOsPinUnlocker, function () {
debugInfo('使用coloros的PIN密码解锁')
// 模拟按键
let button = null
let keyboardRoot = id('com.android.systemui:id/pinColorNumericKeyboard').findOne(_config.timeout_unlock)
if (!keyboardRoot) {
throw new Error('获取键盘控件失败')
}
for (let i = 0; i < this.password.length; i++) {
button = keyboardRoot.child((parseInt(this.password[i]) + 9) % 10)
if (button !== null) {
button.click()
} else {
errorInfo(['未找到数字按钮:{} 可能无法正常解锁', this.password[i]])
}
sleep(100)
}
_config.overwrite('unlock_device_flag', DEVICE_TYPE.COLOROS)
return true
})

extendPinUnlocker(MeizuPinUnlocker, function () {
debugInfo('使用魅族的PIN密码解锁')
let lockRegion = id('com.android.systemui:id/lockPattern').findOne(_config.timeout_unlock)
if (!lockRegion) {
errorInfo('未找到flyme专有的lockPattern无法执行解锁,请确认设置的是PIN密码')
return false
}
let region = lockRegion.bounds()
let height = region.height() / 4, width = region.width() / 3
if (_config.develop_mode) {
warningFloaty.addRectangle('pattern区域', [region.left, region.top, region.width(), region.height()], '#ff0000')
for (let i = 0; i <= 9; i++) {
let position = { x: 0, y: 0 }
if (i == 0) {
position = { x: region.left + width, y: region.top + 3 * height }
} else {
position = { x: region.left + ((i - 1) % 3) * width, y: region.top + (Math.ceil(i / 3) - 1) * height }
}
warningFloaty.addRectangle(i + '', [position.x, position.y, width, height])
}
}
// 模拟按键,区域
for (let i = 0; i < this.password.length; i++) {
let keyCenter = null
let c = parseInt(this.password[i])
if (c == 0) {
keyCenter = { x: 1.5 * width, y: 3.5 * height }
} else {
keyCenter = { x: ((c - 1) % 3 + 0.5) * width, y: (Math.ceil(c / 3) - 0.5) * height }
}
keyCenter.x += region.left
keyCenter.y += region.top
automator.click(keyCenter.x, keyCenter.y)
sleep(100)
}
_config.overwrite('unlock_device_flag', DEVICE_TYPE.MEIZU)
if (_config.develop_mode) {
sleep(5000)
warningFloaty.clearAll()
}
return true
})
4 changes: 2 additions & 2 deletions lib/prototype/FloatyUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* @Author: TonyJiangWJ
* @Date: 2019-12-02 19:05:01
* @Last Modified by: TonyJiangWJ
* @Last Modified time: 2023-07-11 22:22:43
* @Last Modified time: 2023-08-18 10:24:22
* @Description: 悬浮窗工具,单独提出来作为单例使用
*/
let { config: _config } = require('../../config.js')(runtime, global)
Expand Down Expand Up @@ -96,7 +96,7 @@ FloatyUtil.prototype.setFloatyInfo = function (position, text, option) {
if (position && isFinite(position.x) && isFinite(position.y)) {
_this.floatyWindow.setPosition(parseInt(position.x), parseInt(position.y) + _config.bang_offset)
}
if (text) {
if (text != null && typeof text != 'undefined') {
_this.floatyWindow.content.text(text)
_this.debugInfo(text)
}
Expand Down
Loading

0 comments on commit d9e638d

Please sign in to comment.