02_auto.js基础操作4/4
应用
打开应用
var appName = rawInput ( "请输入应用名称" ) ;
launchApp ( appName) ;
发送意图-文本消息分享
var content = rawInput ( '请输入要分享的文本' ) ;
app. startActivity ( {
action : "android.intent.action.SEND" ,
type : "text/*" ,
extras : {
"android.intent.extra.TEXT" : content
} ,
packageName : "com.tencent.mobileqq" ,
className : "com.tencent.mobileqq.activity.JumpActivity"
} ) ;
强制停止应用
"auto" ;
var appName = rawInput ( "请输入应用名称" ) ;
openAppSetting ( getPackageName ( appName) ) ;
while ( ! click ( "强制停止" ) ) ;
卸载应用
var appName = rawInput ( '请输入要卸载的应用名称' ) ;
var packageName = getPackageName ( appName) ;
if ( ! packageName) {
toast ( "应用不存在!" ) ;
} else {
app. uninstall ( packageName) ;
}
应用工具
var i = dialogs. select ( "请选择工具" , "获取应用包名" , "打开应用详情页" , "卸载应用" ) ;
if ( i == - 1 ) {
alert ( "没有选择任何工具!" ) ;
}
switch ( i) {
case 0 :
appName = rawInput ( "请输入应用名称" , "QQ" ) ;
packageName = getPackageName ( appName) ;
toast ( packageName) ;
setClip ( packageName) ;
toast ( "已复制到剪贴板" ) ;
break ;
case 1 :
appName = rawInput ( "请输入应用名称" , "微信" ) ;
openAppSetting ( getPackageName ( appName) ) ;
break ;
case 2 :
appName = rawInput ( "请输入应用名称" ) ;
packageName = getPackageName ( appName) ;
if ( packageName == "" ) {
toast ( "应用不存在" ) ;
} else if ( confirm ( "确定卸载应用" + packageName + "吗?" ) ) {
app. uninstall ( packageName) ;
}
break ;
}
GoogleMLkit
OCR识别
let img = images. read ( "./1.png" )
let start = new Date ( )
let result = gmlkit. ocr ( img, "zh" )
log ( 'OCR识别耗时:' + ( new Date ( ) - start) + 'ms' )
result. sort ( )
log ( "识别信息: " + result)
log ( "-------------------------------------------" )
log ( "文本识别信息: " + result. text)
log ( "-------------------------------------------" )
toastLog ( "json识别信息: " + JSON . stringify ( result) )
img. recycle ( )
{
"abis" : [
"arm64-v8a" ,
"armeabi-v7a" ,
"x86" ,
"x86_64"
] ,
"assets" : [
{
"form" : "file:///android_asset/mlkit-google-ocr-models" ,
"to" : "/mlkit-google-ocr-models"
}
] ,
"buildDir" : "build" ,
"build" : {
"build_id" : null ,
"build_number" : 0 ,
"build_time" : 0
} ,
"useFeatures" : [ ] ,
"icon" : null ,
"ignoredDirs" : [ ] ,
"launchConfig" : {
"displaySplash" : false ,
"hideLauncher" : false ,
"hideLogs" : false ,
"stableMode" : false ,
"volumeUpcontrol" : false ,
"permissions" : [ ] ,
"serviceDesc" : "使脚本自动操作(点击、长按、滑动等)所需,若关闭则只能执行不涉及自动操作的脚本。" ,
"splashIcon" : null ,
"splashText" : "Powered by Google ML Kit ocr"
} ,
"libs" : [
"libmlkit_google_ocr_pipeline.so" ,
"libjackpal-androidterm5.so" ,
"libjackpal-termexec2.so"
] ,
"main" : "main.js" ,
"name" : "Google MlKitOCR" ,
"outputPath" : null ,
"packageName" : "com.script.gmlkitocr" ,
"scripts" : { } ,
"signingConfig" : {
"alias" : null ,
"keystore" : null
} ,
"sourcePath" : null ,
"versionCode" : 1 ,
"versionName" : "1.0.0"
}
OCR截图识别
let currentEngine = engines. myEngine ( )
let runningEngines = engines. all ( )
let currentSource = currentEngine. getSource ( ) + ''
if ( runningEngines. length > 1 ) {
runningEngines. forEach ( compareEngine => {
let compareSource = compareEngine. getSource ( ) + ''
if ( currentEngine. id !== compareEngine. id && compareSource === currentSource) {
compareEngine. forceStop ( )
}
} )
}
if ( ! requestScreenCapture ( ) ) {
toastLog ( '请求截图权限失败' )
exit ( )
}
sleep ( 1000 )
let result = [ ]
let img = null
let running = true
let capturing = true
function captureAndOcr ( ) {
capturing = true
img && img. recycle ( )
img = captureScreen ( )
if ( ! img) {
toastLog ( '截图失败' )
}
let start = new Date ( )
result = gmlkit. ocr ( img, "zh" ) . toArray ( 3 ) ;
log ( result) ;
toastLog ( '耗时' + ( new Date ( ) - start) + 'ms' )
capturing = false
}
captureAndOcr ( )
let offset = - getStatusBarHeightCompat ( )
let window = floaty. rawWindow (
< canvas id= "canvas" layout_weight= "1" / >
) ;
ui. post ( ( ) => {
window. setPosition ( 0 , offset)
window. setSize ( device. width, device. height)
window. setTouchable ( false )
} )
let clickButtonWindow = floaty. rawWindow (
< vertical>
< button id= "captureAndOcr" text= "截图识别" / >
< button id= "closeBtn" text= "退出" / >
< / vertical>
) ;
ui. run ( function ( ) {
clickButtonWindow. setPosition ( device. width / 2 - ~ ~ ( clickButtonWindow. getWidth ( ) / 2 ) , device. height * 0.65 )
} )
clickButtonWindow. captureAndOcr. click ( function ( ) {
result = [ ]
ui. run ( function ( ) {
clickButtonWindow. setPosition ( device. width, device. height)
} )
setTimeout ( ( ) => {
threads. start ( ( ) => {
captureAndOcr ( )
ui. run ( function ( ) {
clickButtonWindow. setPosition ( device. width / 2 - ~ ~ ( clickButtonWindow. getWidth ( ) / 2 ) , device. height * 0.65 )
} )
} )
} , 500 )
} )
clickButtonWindow. closeBtn. click ( function ( ) {
exit ( )
} )
let Typeface = android. graphics. Typeface
let paint = new Paint ( )
paint. setStrokeWidth ( 1 )
paint. setTypeface ( Typeface. DEFAULT_BOLD )
paint. setTextAlign ( Paint. Align. LEFT )
paint. setAntiAlias ( true )
paint. setStrokeJoin ( Paint. Join. ROUND )
paint. setDither ( true )
window. canvas. on ( 'draw' , function ( canvas ) {
if ( ! running || capturing) {
return
}
canvas. drawColor ( 0xFFFFFF , android. graphics. PorterDuff. Mode. CLEAR )
if ( result && result. length > 0 ) {
for ( let i = 0 ; i < result. length; i++ ) {
let ocrResult = result[ i]
drawRectAndText ( ocrResult. text + ' #信心:' + ocrResult. confidence. toFixed ( 2 ) , ocrResult. bounds, '#00ff00' , canvas, paint) ;
}
}
} )
setInterval ( ( ) => { } , 10000 )
events. on ( 'exit' , ( ) => {
running = false
img && img. recycle ( )
window. canvas. removeAllListeners ( )
} )
function drawRectAndText ( desc, rect, colorStr, canvas, paint ) {
let color = colors. parseColor ( colorStr)
paint. setStrokeWidth ( 1 )
paint. setStyle ( Paint. Style. STROKE )
paint. setARGB ( 255 , 255 - ( color >> 16 & 0xff ) , 255 - ( color >> 8 & 0xff ) , 255 - ( color & 0xff ) )
canvas. drawRect ( rect, paint)
paint. setARGB ( 255 , color >> 16 & 0xff , color >> 8 & 0xff , color & 0xff )
paint. setStrokeWidth ( 1 )
paint. setTextSize ( 20 )
paint. setStyle ( Paint. Style. FILL )
canvas. drawText ( desc, rect. left, rect. top, paint)
paint. setTextSize ( 10 )
paint. setStrokeWidth ( 1 )
paint. setARGB ( 255 , 0 , 0 , 0 )
}
function getStatusBarHeightCompat ( ) {
let result = 0
let resId = context. getResources ( ) . getIdentifier ( "status_bar_height" , "dimen" , "android" )
if ( resId > 0 ) {
result = context. getResources ( ) . getDimensionPixelOffset ( resId)
}
if ( result <= 0 ) {
result = context. getResources ( ) . getDimensionPixelOffset ( R . dimen. dimen_25dp)
}
return result
}
OCR识别点击
var mainActivity = "org.autojs.autojs.ui.main.MainActivity"
if ( currentActivity != mainActivity) {
app. startActivity ( {
packageName : "org.autojs.autoxjs.v6" ,
className : mainActivity,
} ) ;
waitForActivity ( mainActivity)
}
requestScreenCapture ( )
sleep ( 1000 )
let img = captureScreen ( )
let start = new Date ( )
let result = gmlkit. ocr ( img, "zh" )
toastLog ( 'OCR识别耗时:' + ( new Date ( ) - start) + 'ms' )
let managerBtn = result. find ( 3 , e => e. text == "管理" )
if ( managerBtn) click ( managerBtn. bounds)
sleep ( 500 )
let homeBtn = result. find ( 3 , e => e. text == "主页" )
if ( homeBtn) click ( homeBtn. bounds)
sleep ( 500 )
let docBtn = result. find ( 3 , e => e. text == "文档" )
if ( docBtn) press ( docBtn. bounds, 500 )
img. recycle ( )
HTTP网络请求
获取网页
var url = "www.baidu.com" ;
var res = http. get ( url) ;
if ( res. statusCode == 200 ) {
toast ( "请求成功" ) ;
console. show ( ) ;
log ( res. body. string ( ) ) ;
} else {
toast ( "请求失败:" + res. statusMessage) ;
}
文件上传
console. show ( ) ;
example1 ( ) ;
example2 ( ) ;
example3 ( ) ;
example4 ( ) ;
example5 ( ) ;
function example1 ( ) {
var res = http. postMultipart ( "http://posttestserver.com/post.php" , {
"file" : open ( "/sdcard/1.txt" )
} ) ;
log ( "例子1:" ) ;
log ( res. body. string ( ) ) ;
}
function example2 ( ) {
var res = http. postMultipart ( "http://posttestserver.com/post.php" , {
"file" : [ "1.txt" , "/sdcard/1.txt" ]
} ) ;
log ( "例子2:" ) ;
log ( res. body. string ( ) ) ;
}
function example3 ( ) {
var res = http. postMultipart ( "http://posttestserver.com/post.php" , {
"file" : [ "1.txt" , "text/plain" , "/sdcard/1.txt" ]
} ) ;
log ( "例子3:" ) ;
log ( res. body. string ( ) ) ;
}
function example4 ( ) {
var res = http. postMultipart ( "http://posttestserver.com/post.php" , {
"file" : open ( "/sdcard/1.txt" ) ,
"aKey" : "aValue"
} ) ;
log ( "例子4:" ) ;
log ( res. body. string ( ) ) ;
}
文件下载
var url = "http://www.autojs.org/assets/uploads/profile/3-profileavatar.png" ;
var res = http. get ( url) ;
if ( res. statusCode != 200 ) {
toast ( "请求失败" ) ;
}
files. writeBytes ( "/sdcard/1.png" , res. body. bytes ( ) ) ;
toast ( "下载成功" ) ;
app. viewFile ( "/sdcard/1.png" ) ;
javascript
数字
a = 5 ;
b = 6 ;
c = - 1 ;
x = 1.5 ;
y = a * x * x + b * x * c;
log ( "y = " + y) ;
openConsole ( ) ;
E4X
print ( "----------------------------------------" ) ;
var John = "<employee><name>John</name><age>25</age></employee>" ;
var Sue = "<employee><name>Sue</name><age>32</age></employee>" ;
var tagName = "employees" ;
var employees = new XML ( "<" + tagName + ">" + John + Sue + "</" + tagName + ">" ) ;
print ( "The employees XML object constructed from a string is:\n" + employees) ;
print ( "----------------------------------------" ) ;
var order = < order>
< customer>
< firstname> John< / firstname>
< lastname> Doe< / lastname>
< / customer>
< item>
< description> Big Screen Television< / description>
< price> 1299.99 < / price>
< quantity> 1 < / quantity>
< / item>
< / order>
var name = order. customer. firstname + " " + order. customer. lastname;
var total = order. item. price * order. item. quantity;
print ( "The order XML object constructed using a literal is:\n" + order) ;
print ( "The total price of " + name + "'s order is " + total) ;
print ( "----------------------------------------" ) ;
var order = < order/ > ;
order. customer. name = "Fred Jones" ;
order. customer. address. street = "123 Long Lang" ;
order. customer. address. city = "Underwood" ;
order. customer. address. state = "CA" ;
order. item[ 0 ] = "" ;
order. item[ 0 ] . description = "Small Rodents" ;
order. item[ 0 ] . quantity = 10 ;
order. item[ 0 ] . price = 6.95 ;
print ( "The order custructed using expandos and super-expandos is:\n" + order) ;
order. item += < item> < description> Catapult< / description> < price> 139.95 < / price> < / item> ;
print ( "----------------------------------------" ) ;
print ( "The order after appending a new item is:\n" + order) ;
print ( "----------------------------------------" ) ;
var tagname = "name" ;
var attributename = "id" ;
var attributevalue = 5 ;
var content = "Fred" ;
var x = < { tagname} { attributename} = { attributevalue} > { content} < / { tagname} > ;
print ( "The dynamically computed element value is:\n" + x. toXMLString ( ) ) ;
print ( "----------------------------------------" ) ;
var message = < soap: Envelope
xmlns : soap= "http://schemas.xmlsoap.org/soap/envelope/"
soap : encodingStyle= "http://schemas.xmlsoap.org/soap/encoding/" >
< soap: Body>
< m: GetLastTradePrice xmlns: m= "http://mycompany.com/stocks" >
< symbol> DIS < / symbol>
< / m: GetLastTradePrice>
< / soap: Body>
< / soap: Envelope>
var soap = new Namespace ( "http://schemas.xmlsoap.org/soap/envelope/" ) ;
var stock = new Namespace ( "http://mycompany.com/stocks" ) ;
var encodingStyle = message. @soap: : encodingStyle;
print ( "The encoding style of the soap message is specified by:\n" + encodingStyle) ;
message. soap: : Body. stock: : GetLastTradePrice. symbol = "MYCO" ;
var body = message. soap: : Body;
print ( "The body of the soap message is:\n" + body) ;
print ( "----------------------------------------" ) ;
default xml namespace = "http://default.namespace.com" ;
var x = < x/ > ;
x. a = "one" ;
x. b = "two" ;
x. c = < c xmlns= "http://some.other.namespace.com" > three< / c> ;
print ( "XML object constructed using the default xml namespace:\n" + x) ;
default xml namespace= "" ;
print ( "----------------------------------------" ) ;
var order = < order id = "123456" timestamp= "Mon Mar 10 2003 16:03:25 GMT-0800 (PST)" >
< customer>
< firstname> John< / firstname>
< lastname> Doe< / lastname>
< / customer>
< item id= "3456" >
< description> Big Screen Television< / description>
< price> 1299.99 < / price>
< quantity> 1 < / quantity>
< / item>
< item id = "56789" >
< description> DVD Player< / description>
< price> 399.99 < / price>
< quantity> 1 < / quantity>
< / item>
< / order> ;
print ( "The order id is:" + order. @id) ;
print ( "The children of the order are:\n" + order. * ) ;
print ( "The order descriptions are:\n" + order. item. description) ;
print ( "The second item is:\n" + order. item[ 1 ] ) ;
print ( "The children of the items are:\n" + order. item. * ) ;
print ( "The second child of the order is:\n" + order. * [ 1 ] ) ;
var totalprice = 0 ;
for each ( i in order. item ) {
totalprice += i. price * i. quantity;
}
print ( "The total price of the order is: " + totalprice) ;
print ( "----------------------------------------" ) ;
var e = < employees>
< employee id= "1" > < name> Joe< / name> < age> 20 < / age> < / employee>
< employee id= "2" > < name> Sue< / name> < age> 30 < / age> < / employee>
< / employees> ;
print ( "All the employee names are:\n" + e. . name) ;
print ( "The employee named Joe is:\n" + e. employee. ( name == "Joe" ) ) ;
print ( "Employees with ids 1 & 2:\n" + e. employee. ( @id == 1 || @id == 2 ) ) ;
print ( "Name of the the employee with ID=1: " + e. employee. ( @id == 1 ) . name) ;
print ( "----------------------------------------" ) ;
openConsole ( ) ;
HelloWorld
log ( "Hello world!!!" ) ;
toast ( "Hello, AutoJs!" ) ;
console. show ( ) ;
PaddleOCR
PaddleOCR文本识别
let img = images. read ( "./0.jpg" )
let useSlim = true
let start = new Date ( )
let result = paddle. ocr ( img, useSlim)
log ( 'OCR识别耗时:' + ( new Date ( ) - start) + 'ms' )
toastLog ( "完整识别信息: " + JSON . stringify ( result) )
start = new Date ( )
const stringList = paddle. ocrText ( img, useSlim)
log ( 'OCR纯文本识别耗时:' + ( new Date ( ) - start) + 'ms' )
toastLog ( "文本识别信息: " + JSON . stringify ( stringList) )
img. recycle ( )
{
"abis" : [
"arm64-v8a" ,
"armeabi-v7a" ,
"x86" ,
"x86_64"
] ,
"assets" : [
{
"form" : "file:///android_asset/models" ,
"to" : "/models"
}
] ,
"buildDir" : "build" ,
"build" : {
"build_id" : null ,
"build_number" : 0 ,
"build_time" : 0
} ,
"useFeatures" : [ ] ,
"icon" : null ,
"ignoredDirs" : [ ] ,
"launchConfig" : {
"displaySplash" : false ,
"hideLauncher" : false ,
"hideLogs" : false ,
"stableMode" : false ,
"volumeUpcontrol" : false ,
"permissions" : [ ] ,
"serviceDesc" : "使脚本自动操作(点击、长按、滑动等)所需,若关闭则只能执行不涉及自动操作的脚本。" ,
"splashIcon" : null ,
"splashText" : "Powered by paddle ocr"
} ,
"libs" : [
"libc++_shared.so" ,
"libpaddle_light_api_shared.so" ,
"libhiai.so" ,
"libhiai_ir.so" ,
"libhiai_ir_build.so" ,
"libNative.so" ,
"libjackpal-androidterm5.so" ,
"libjackpal-termexec2.so"
] ,
"main" : "PaddleOCR.js" ,
"name" : "PaddleOCR" ,
"outputPath" : null ,
"packageName" : "com.script.paddleocr" ,
"scripts" : { } ,
"signingConfig" : {
"alias" : null ,
"keystore" : null
} ,
"sourcePath" : null ,
"versionCode" : 1 ,
"versionName" : "1.0.0"
}
PaddleOCR文本识别-自定义模型路径
let img = images. read ( "./0.jpg" )
let myModelPath = files. path ( "./models" ) ;
let start = new Date ( )
let result = paddle. ocr ( img, myModelPath)
log ( 'OCR识别耗时:' + ( new Date ( ) - start) + 'ms' )
toastLog ( "完整识别信息: " + JSON . stringify ( result) )
start = new Date ( )
const stringList = paddle. ocrText ( img, myModelPath)
log ( 'OCR纯文本识别耗时:' + ( new Date ( ) - start) + 'ms' )
toastLog ( "文本识别信息: " + JSON . stringify ( stringList) )
img. recycle ( )
{
"abis" : [
"arm64-v8a" ,
"armeabi-v7a" ,
"x86" ,
"x86_64"
] ,
"assets" : [
{
"form" : "models" ,
"to" : "/models"
}
] ,
"buildDir" : "build" ,
"build" : {
"build_id" : null ,
"build_number" : 0 ,
"build_time" : 0
} ,
"useFeatures" : [ ] ,
"icon" : null ,
"ignoredDirs" : [ ] ,
"launchConfig" : {
"displaySplash" : false ,
"hideLauncher" : false ,
"hideLogs" : false ,
"stableMode" : false ,
"volumeUpcontrol" : false ,
"permissions" : [ ] ,
"serviceDesc" : "使脚本自动操作(点击、长按、滑动等)所需,若关闭则只能执行不涉及自动操作的脚本。" ,
"splashIcon" : null ,
"splashText" : "Powered by paddle ocr"
} ,
"libs" : [
"libc++_shared.so" ,
"libpaddle_light_api_shared.so" ,
"libhiai.so" ,
"libhiai_ir.so" ,
"libhiai_ir_build.so" ,
"libNative.so" ,
"libjackpal-androidterm5.so" ,
"libjackpal-termexec2.so"
] ,
"main" : "PaddleOCR(自定义模型路径).js" ,
"name" : "PaddleOCR(自定义模型路径)" ,
"outputPath" : null ,
"packageName" : "com.script.paddleocr.custommodel" ,
"scripts" : { } ,
"signingConfig" : {
"alias" : null ,
"keystore" : null
} ,
"sourcePath" : null ,
"versionCode" : 1 ,
"versionName" : "1.0.0"
}
PaddleOCR-截图识别
let currentEngine = engines. myEngine ( )
let runningEngines = engines. all ( )
let currentSource = currentEngine. getSource ( ) + ''
if ( runningEngines. length > 1 ) {
runningEngines. forEach ( compareEngine => {
let compareSource = compareEngine. getSource ( ) + ''
if ( currentEngine. id !== compareEngine. id && compareSource === currentSource) {
compareEngine. forceStop ( )
}
} )
}
if ( ! requestScreenCapture ( ) ) {
toastLog ( '请求截图权限失败' )
exit ( )
}
sleep ( 1000 )
let result = [ ]
let img = null
let running = true
let capturing = true
function captureAndOcr ( ) {
capturing = true
img && img. recycle ( )
img = captureScreen ( )
if ( ! img) {
toastLog ( '截图失败' )
}
let start = new Date ( )
result = paddle. ocr ( img) ;
log ( result) ;
toastLog ( '耗时' + ( new Date ( ) - start) + 'ms' )
capturing = false
}
captureAndOcr ( )
let offset = - getStatusBarHeightCompat ( )
let window = floaty. rawWindow (
< canvas id= "canvas" layout_weight= "1" / >
) ;
ui. post ( ( ) => {
window. setPosition ( 0 , offset)
window. setSize ( device. width, device. height)
window. setTouchable ( false )
} )
let clickButtonWindow = floaty. rawWindow (
< vertical>
< button id= "captureAndOcr" text= "截图识别" / >
< button id= "closeBtn" text= "退出" / >
< / vertical>
) ;
ui. run ( function ( ) {
clickButtonWindow. setPosition ( device. width / 2 - ~ ~ ( clickButtonWindow. getWidth ( ) / 2 ) , device. height * 0.65 )
} )
clickButtonWindow. captureAndOcr. click ( function ( ) {
result = [ ]
ui. run ( function ( ) {
clickButtonWindow. setPosition ( device. width, device. height)
} )
setTimeout ( ( ) => {
threads. start ( ( ) => {
captureAndOcr ( )
ui. run ( function ( ) {
clickButtonWindow. setPosition ( device. width / 2 - ~ ~ ( clickButtonWindow. getWidth ( ) / 2 ) , device. height * 0.65 )
} )
} )
} , 500 )
} )
clickButtonWindow. closeBtn. click ( function ( ) {
exit ( )
} )
let Typeface = android. graphics. Typeface
let paint = new Paint ( )
paint. setStrokeWidth ( 1 )
paint. setTypeface ( Typeface. DEFAULT_BOLD )
paint. setTextAlign ( Paint. Align. LEFT )
paint. setAntiAlias ( true )
paint. setStrokeJoin ( Paint. Join. ROUND )
paint. setDither ( true )
window. canvas. on ( 'draw' , function ( canvas ) {
if ( ! running || capturing) {
return
}
canvas. drawColor ( 0xFFFFFF , android. graphics. PorterDuff. Mode. CLEAR )
if ( result && result. length > 0 ) {
for ( let i = 0 ; i < result. length; i++ ) {
let ocrResult = result[ i]
drawRectAndText ( ocrResult. words + ' #信心:' + ocrResult. confidence. toFixed ( 2 ) , ocrResult. bounds, '#00ff00' , canvas, paint) ;
}
}
} )
setInterval ( ( ) => { } , 10000 )
events. on ( 'exit' , ( ) => {
running = false
img && img. recycle ( )
window. canvas. removeAllListeners ( )
} )
function drawRectAndText ( desc, rect, colorStr, canvas, paint ) {
let color = colors. parseColor ( colorStr)
paint. setStrokeWidth ( 1 )
paint. setStyle ( Paint. Style. STROKE )
paint. setARGB ( 255 , 255 - ( color >> 16 & 0xff ) , 255 - ( color >> 8 & 0xff ) , 255 - ( color & 0xff ) )
canvas. drawRect ( rect, paint)
paint. setARGB ( 255 , color >> 16 & 0xff , color >> 8 & 0xff , color & 0xff )
paint. setStrokeWidth ( 1 )
paint. setTextSize ( 20 )
paint. setStyle ( Paint. Style. FILL )
canvas. drawText ( desc, rect. left, rect. top, paint)
paint. setTextSize ( 10 )
paint. setStrokeWidth ( 1 )
paint. setARGB ( 255 , 0 , 0 , 0 )
}
function getStatusBarHeightCompat ( ) {
let result = 0
let resId = context. getResources ( ) . getIdentifier ( "status_bar_height" , "dimen" , "android" )
if ( resId > 0 ) {
result = context. getResources ( ) . getDimensionPixelOffset ( resId)
}
if ( result <= 0 ) {
result = context. getResources ( ) . getDimensionPixelOffset ( R . dimen. dimen_25dp)
}
return result
}
Shell命令
冻结网易云音乐
shell ( "pm disable com.netease.cloudmusic" , true ) ;
结束所有后台进程
shell ( "am kill-all" , true ) ;
解冻并打开网易云音乐
shell ( "pm enable com.netease.cloudmusic" , true ) ;
launchApp ( "网易云音乐" ) ;
锁屏
KeyCode ( "KEYCODE_POWER" ) ;
TessractOCR
importClass ( com. googlecode. tesseract. android. TessBaseAPI)
var tessocr = new TessBaseAPI ( )
requestScreenCapture ( false ) ;
toastLog ( "3秒后截图" )
sleep ( 3000 )
toastLog ( "开始截图" )
var img = captureScreen ( ) ;
var dataPath = files. path ( "./" )
var ok = tessocr. init ( dataPath, "eng+chi_sim" )
if ( ok) {
toastLog ( "初始化成功: " + tessocr. getInitLanguagesAsString ( ) )
} else {
toastLog ( "初始化失败" )
}
tessocr. setImage ( img. getBitmap ( ) )
toastLog ( tessocr. getUTF8Text ( ) )
{
"abis" : [
"arm64-v8a" ,
"armeabi-v7a" ,
"x86" ,
"x86_64"
] ,
"assets" : [
] ,
"buildDir" : "build" ,
"build" : {
"build_id" : null ,
"build_number" : 0 ,
"build_time" : 0
} ,
"useFeatures" : [ ] ,
"icon" : null ,
"ignoredDirs" : [
"build"
] ,
"launchConfig" : {
"displaySplash" : false ,
"hideLauncher" : false ,
"hideLogs" : false ,
"stableMode" : false ,
"volumeUpcontrol" : false ,
"permissions" : [ ] ,
"serviceDesc" : "使脚本自动操作(点击、长按、滑动等)所需,若关闭则只能执行不涉及自动操作的脚本。" ,
"splashIcon" : null ,
"splashText" : "Powered by TessOCR"
} ,
"libs" : [
"libtesseract.so" ,
"libpng.so" ,
"libleptonica.so" ,
"libjpeg.so" ,
"libjackpal-androidterm5.so" ,
"libjackpal-termexec2.so"
] ,
"main" : "main.js" ,
"name" : "TessOCR" ,
"outputPath" : null ,
"packageName" : "com.script.tessocr" ,
"projectDirectory" : null ,
"scripts" : { } ,
"signingConfig" : {
"alias" : null ,
"keystore" : null
} ,
"sourcePath" : null ,
"versionCode" : 1 ,
"versionName" : "1.0.0"
}
Web扩展与游戏编程
贪吃蛇
"ui" ;
ui. layout (
< vertical>
< canvas id= "board" layout_weight= "1" / >
< relative h= "120" >
< button id= "up" text= "↑" w= "60" h= "60" layout_alignParentTop= "true" layout_centerHorizontal= "true" / >
< button id= "left" text= "←" w= "60" h= "60" layout_alignParentBottom= "true" layout_toLeftOf= "@id/down" / >
< button id= "down" text= "↓" w= "60" h= "60" layout_alignParentBottom= "true" layout_centerHorizontal= "true" / >
< button id= "right" text= "→" w= "60" h= "60" layout_alignParentBottom= "true" layout_toRightOf= "@id/down" / >
< / relative>
< / vertical>
) ;
const SNAKE_COLOR = colors. parseColor ( "#4caf50" ) ;
const BG_COLOR = colors. parseColor ( "#ffffff" ) ;
const APPLE_COLOR = colors. parseColor ( "#f44336" ) ;
const WALL_COLOR = colors. parseColor ( "#607d8b" ) ;
const TEXT_COLOR = colors. parseColor ( "#03a9f4" ) ;
const MOVE_INTERVAL = 500 ;
const BLOCK_WIDTH = 40 ;
const GAME_BOARD_HEIGHT = 20 ;
const GAME_BOARD_WIDTH = 15 ;
const DIRECTION_LEFT = { x : - 1 , y : 0 } ;
const DIRECTION_RIGHT = { x : 1 , y : 0 } ;
const DIRECTION_UP = { x : 0 , y : - 1 } ;
const DIRECTION_DOWN = { x : 0 , y : 1 } ;
var snake = [ { x : 4 , y : 2 } , { x : 3 , y : 2 } , { x : 2 , y : 2 } ] ;
var apple = generateApple ( ) ;
var direction = DIRECTION_RIGHT ;
var isGameOver = false ;
var score = 0 ;
var paint = new Paint ( ) ;
ui. board. on ( "draw" , function ( canvas ) {
canvas. drawColor ( BG_COLOR ) ;
paint. setColor ( TEXT_COLOR ) ;
paint. setTextSize ( 50 ) ;
canvas. drawText ( "分数: " + score, 30 , 70 , paint) ;
if ( isGameOver) {
canvas. drawText ( "游戏结束!" , canvas. getWidth ( ) - 280 , 70 , paint) ;
}
var offset = {
x : ( canvas. getWidth ( ) - ( GAME_BOARD_WIDTH + 2 ) * BLOCK_WIDTH ) / 2 ,
y : 100
} ;
canvas. translate ( offset. x, offset. y) ;
paint. setColor ( WALL_COLOR ) ;
for ( var i = 0 ; i <= GAME_BOARD_WIDTH + 1 ; i++ ) {
drawBlock ( canvas, paint, i, 0 ) ;
drawBlock ( canvas, paint, i, GAME_BOARD_HEIGHT + 1 ) ;
}
for ( var i = 0 ; i <= GAME_BOARD_HEIGHT + 1 ; i++ ) {
drawBlock ( canvas, paint, 0 , i) ;
drawBlock ( canvas, paint, GAME_BOARD_WIDTH + 1 , i) ;
}
paint. setColor ( SNAKE_COLOR ) ;
for ( var i = 0 ; i < snake. length; i++ ) {
drawBlock ( canvas, paint, snake[ i] . x, snake[ i] . y) ;
}
paint. setColor ( APPLE_COLOR ) ;
drawBlock ( canvas, paint, apple. x, apple. y) ;
} ) ;
var gameThread = threads. start ( game) ;
ui. left. on ( "click" , ( ) => direction = DIRECTION_LEFT ) ;
ui. right. on ( "click" , ( ) => direction = DIRECTION_RIGHT ) ;
ui. up. on ( "click" , ( ) => direction = DIRECTION_UP ) ;
ui. down. on ( "click" , ( ) => direction = DIRECTION_DOWN ) ;
function game ( ) {
setInterval ( ( ) => {
move ( direction. x, direction. y) ;
} , MOVE_INTERVAL ) ;
}
function move ( dx, dy ) {
log ( "move: %d, %d" , dx, dy) ;
direction. x = dx;
direction. y = dy;
var head = snake[ 0 ] ;
snake. splice ( 0 , 0 , {
x : head. x + dx,
y : head. y + dy
} ) ;
if ( snakeEatsApple ( ) ) {
score += 5 ;
apple = generateApple ( ) ;
} else {
snake. pop ( ) ;
}
collisionTest ( ) ;
}
function snakeEatsApple ( ) {
return snake[ 0 ] . x == apple. x && snake[ 0 ] . y == apple. y;
}
function generateApple ( ) {
var x, y;
do {
x = random ( 1 , GAME_BOARD_WIDTH ) ;
y = random ( 1 , GAME_BOARD_HEIGHT ) ;
} while ( ! isAppleValid ( x, y) ) ;
return { x : x, y : y} ;
}
function isAppleValid ( x, y ) {
for ( var i = 0 ; i < snake. length; i++ ) {
if ( snake[ i] . x == x && snake[ i] . y == y) {
return false ;
}
}
return true ;
}
function collisionTest ( ) {
var head = snake[ 0 ] ;
if ( head. x < 1 || head. x > GAME_BOARD_WIDTH
|| head. y < 1 || head. y > GAME_BOARD_HEIGHT ) {
gameOver ( ) ;
return ;
}
for ( var i = 1 ; i < snake. length; i++ ) {
if ( snake[ i] . x == head && snake[ i] . y == head) {
gameOver ( ) ;
return ;
}
}
}
function gameOver ( ) {
gameThread. interrupt ( ) ;
isGameOver = true ;
}
function drawBlock ( canvas, paint, x, y ) {
x *= BLOCK_WIDTH ;
y *= BLOCK_WIDTH ;
canvas. drawRect ( x, y, x + BLOCK_WIDTH , y + BLOCK_WIDTH , paint) ;
}
贪吃蛇重力版
"ui" ;
ui. layout (
< vertical>
< canvas id= "board" layout_weight= "1" / >
< relative h= "120" >
< button id= "up" text= "↑" w= "60" h= "60" layout_alignParentTop= "true" layout_centerHorizontal= "true" / >
< button id= "left" text= "←" w= "60" h= "60" layout_alignParentBottom= "true" layout_toLeftOf= "@id/down" / >
< button id= "down" text= "↓" w= "60" h= "60" layout_alignParentBottom= "true" layout_centerHorizontal= "true" / >
< button id= "right" text= "→" w= "60" h= "60" layout_alignParentBottom= "true" layout_toRightOf= "@id/down" / >
< / relative>
< / vertical>
) ;
const SNAKE_COLOR = colors. parseColor ( "#4caf50" ) ;
const BG_COLOR = colors. parseColor ( "#ffffff" ) ;
const APPLE_COLOR = colors. parseColor ( "#f44336" ) ;
const WALL_COLOR = colors. parseColor ( "#607d8b" ) ;
const TEXT_COLOR = colors. parseColor ( "#03a9f4" ) ;
const MOVE_INTERVAL = 500 ;
const BLOCK_WIDTH = 40 ;
const GAME_BOARD_HEIGHT = 20 ;
const GAME_BOARD_WIDTH = 15 ;
const DIRECTION_LEFT = { x : - 1 , y : 0 } ;
const DIRECTION_RIGHT = { x : 1 , y : 0 } ;
const DIRECTION_UP = { x : 0 , y : - 1 } ;
const DIRECTION_DOWN = { x : 0 , y : 1 } ;
var snake = [ { x : 4 , y : 2 } , { x : 3 , y : 2 } , { x : 2 , y : 2 } ] ;
var apple = generateApple ( ) ;
var direction = DIRECTION_RIGHT ;
var isGameOver = false ;
var score = 0 ;
var paint = new Paint ( ) ;
ui. board. on ( "draw" , function ( canvas ) {
canvas. drawColor ( BG_COLOR ) ;
paint. setColor ( TEXT_COLOR ) ;
paint. setTextSize ( 50 ) ;
canvas. drawText ( "分数: " + score, 30 , 70 , paint) ;
if ( isGameOver) {
canvas. drawText ( "游戏结束!" , canvas. getWidth ( ) - 280 , 70 , paint) ;
}
var offset = {
x : ( canvas. getWidth ( ) - ( GAME_BOARD_WIDTH + 2 ) * BLOCK_WIDTH ) / 2 ,
y : 100
} ;
canvas. translate ( offset. x, offset. y) ;
paint. setColor ( WALL_COLOR ) ;
for ( var i = 0 ; i <= GAME_BOARD_WIDTH + 1 ; i++ ) {
drawBlock ( canvas, paint, i, 0 ) ;
drawBlock ( canvas, paint, i, GAME_BOARD_HEIGHT + 1 ) ;
}
for ( var i = 0 ; i <= GAME_BOARD_HEIGHT + 1 ; i++ ) {
drawBlock ( canvas, paint, 0 , i) ;
drawBlock ( canvas, paint, GAME_BOARD_WIDTH + 1 , i) ;
}
paint. setColor ( SNAKE_COLOR ) ;
for ( var i = 0 ; i < snake. length; i++ ) {
drawBlock ( canvas, paint, snake[ i] . x, snake[ i] . y) ;
}
paint. setColor ( APPLE_COLOR ) ;
drawBlock ( canvas, paint, apple. x, apple. y) ;
} ) ;
var gameThread = threads. start ( game) ;
ui. left. on ( "click" , ( ) => direction = DIRECTION_LEFT ) ;
ui. right. on ( "click" , ( ) => direction = DIRECTION_RIGHT ) ;
ui. up. on ( "click" , ( ) => direction = DIRECTION_UP ) ;
ui. down. on ( "click" , ( ) => direction = DIRECTION_DOWN ) ;
function game ( ) {
setInterval ( ( ) => {
move ( direction. x, direction. y) ;
} , MOVE_INTERVAL ) ;
}
function move ( dx, dy ) {
log ( "move: %d, %d" , dx, dy) ;
direction. x = dx;
direction. y = dy;
var head = snake[ 0 ] ;
snake. splice ( 0 , 0 , {
x : head. x + dx,
y : head. y + dy
} ) ;
if ( snakeEatsApple ( ) ) {
score += 5 ;
apple = generateApple ( ) ;
} else {
snake. pop ( ) ;
}
collisionTest ( ) ;
}
function snakeEatsApple ( ) {
return snake[ 0 ] . x == apple. x && snake[ 0 ] . y == apple. y;
}
function generateApple ( ) {
var x, y;
do {
x = random ( 1 , GAME_BOARD_WIDTH ) ;
y = random ( 1 , GAME_BOARD_HEIGHT ) ;
} while ( ! isAppleValid ( x, y) ) ;
return { x : x, y : y} ;
}
function isAppleValid ( x, y ) {
for ( var i = 0 ; i < snake. length; i++ ) {
if ( snake[ i] . x == x && snake[ i] . y == y) {
return false ;
}
}
return true ;
}
function collisionTest ( ) {
var head = snake[ 0 ] ;
if ( head. x < 1 || head. x > GAME_BOARD_WIDTH
|| head. y < 1 || head. y > GAME_BOARD_HEIGHT ) {
gameOver ( ) ;
return ;
}
for ( var i = 1 ; i < snake. length; i++ ) {
if ( snake[ i] . x == head && snake[ i] . y == head) {
gameOver ( ) ;
return ;
}
}
}
function gameOver ( ) {
gameThread. interrupt ( ) ;
isGameOver = true ;
}
function drawBlock ( canvas, paint, x, y ) {
x *= BLOCK_WIDTH ;
y *= BLOCK_WIDTH ;
canvas. drawRect ( x, y, x + BLOCK_WIDTH , y + BLOCK_WIDTH , paint) ;
}
AutoX注入webviwe
"ui" ;
ui. layout (
< vertical>
< horizontal bg= "#c7edcc" gravity= "center" h= "auto" >
< button text= "网络冲浪" id= "surfInternetBtn" style= "Widget.AppCompat.Button.Colored" w= "auto" / >
< button text= "记忆翻牌" id= "loadLocalHtmlBtn" style= "Widget.AppCompat.Button.Colored" w= "auto" / >
< button text= "控制台" id= "consoleBtn" style= "Widget.AppCompat.Button.Colored" w= "auto" / >
< / horizontal>
< vertical h= "*" w= "*" >
< webview id= "webView" layout_below= "title" w= "*" h= "*" / >
< / vertical>
< / vertical>
) ;
function calljavascript ( webViewWidget, script, callback ) {
try {
console. assert ( webViewWidget != null , "webView控件为空" ) ;
webViewWidget. evaluatejavascript ( "javascript:" + script, new JavaAdapter ( android. webkit. ValueCallback, {
onReceiveValue : ( val ) => {
if ( callback) {
callback ( val) ;
}
}
} ) ) ;
} catch ( e) {
console. error ( "执行javascript失败" ) ;
console. trace ( e) ;
}
}
function AutoX ( ) {
let getAutoXFrame = ( ) => {
let bridgeFrame = document. getElementById ( "AutoXFrame" ) ;
if ( ! bridgeFrame) {
bridgeFrame = document. createElement ( 'iframe' ) ;
bridgeFrame. id = "AutoXFrame" ;
bridgeFrame. style = "display: none" ;
document. body. append ( bridgeFrame) ;
}
return bridgeFrame;
} ;
const h5Callbackers = { } ;
let h5CallbackIndex = 1 ;
let setCallback = ( callback ) => {
let callId = h5CallbackIndex++ ;
h5Callbackers[ callId] = {
"callback" : callback
} ;
return callId;
} ;
let getCallback = ( callId ) => {
let callback = h5Callbackers[ callId] ;
if ( callback) {
delete h5Callbackers[ callId] ;
}
return callback;
} ;
function invoke ( cmd, params, callback ) {
let callId = null ;
try {
let paramsStr = JSON . stringify ( params) ;
let AutoXFrame = getAutoXFrame ( ) ;
callId = setCallback ( callback) ;
AutoXFrame. src = "jsbridge://" + cmd + "/" + callId + "/" + encodeURIComponent ( paramsStr) ;
} catch ( e) {
if ( callId) {
getCallback ( callId) ;
}
console. trace ( e) ;
}
} ;
let callback = ( data ) => {
let callId = data. callId;
let params = data. params;
let callbackFun = getCallback ( callId) ;
if ( callbackFun && callbackFun. callback) {
callbackFun. callback ( params) ;
}
} ;
return {
invoke : invoke,
callback : callback
} ;
} ;
function bridgeHandler_handle ( cmd, params ) {
console. log ( 'bridgeHandler处理 cmd=%s, params=%s' , cmd, JSON . stringify ( params) ) ;
let fun = this [ cmd] ;
if ( ! fun) {
throw new Error ( "cmd= " + cmd + " 没有定义实现" ) ;
}
let ret = fun ( params)
return ret;
}
function mFunction ( params ) {
toastLog ( params. toString ( ) ) ;
device. vibrate ( 120 ) ;
return files. isDir ( '/storage/emulated/0/Download' )
}
function webViewExpand_init ( webViewWidget ) {
webViewWidget. webViewClient = new JavaAdapter ( android. webkit. WebViewClient, {
onPageFinished : ( webView, curUrl ) => {
try {
calljavascript ( webView, AutoX. toString ( ) + ";var auto0 = AutoX();auto0.invoke('mFunction','This is AutoX!',(data) => {console.log('接收到callback1:' + JSON.stringify(data));});" , null ) ;
} catch ( e) {
console. trace ( e)
}
} ,
shouldOverrideUrlLoading : ( webView, request ) => {
let url = '' ;
try {
url = ( request. a && request. a. a) || ( request. url) ;
if ( url instanceof android. net. Uri ) {
url = url. toString ( ) ;
}
if ( url. indexOf ( "jsbridge://" ) == 0 ) {
let uris = url. split ( "/" ) ;
let cmd = uris[ 2 ] ;
let callId = uris[ 3 ] ;
let params = java. net. URLDecoder. decode ( uris[ 4 ] , "UTF-8" ) ;
console. log ( 'AutoX处理javascript调用请求: callId=%s, cmd=%s, params=%s' , callId, cmd, params) ;
let result = null ;
try {
result = bridgeHandler_handle ( cmd, JSON . parse ( params) ) ;
} catch ( e) {
console. trace ( e) ;
result = {
message : e. message
} ;
}
result = result || { } ;
webView. loadUrl ( "javascript:auto0.callback({'callId':" + callId + ", 'params': " + JSON . stringify ( result) + "});" ) ;
} else if ( url. startsWith ( "http://" ) || url. startsWith ( "https://" ) || url. startsWith ( "file://" ) || url. startsWith ( "ws://" ) || url. startsWith ( "wss://" ) ) {
webView. loadUrl ( url) ;
} else {
}
return true ;
} catch ( e) {
if ( e. javaException instanceof android. content. ActivityNotFoundException ) {
webView. loadUrl ( url) ;
} else {
toastLog ( '无法打开URL: ' + url) ;
}
console. trace ( e) ;
}
} ,
onReceivedError : ( webView, webResourceRequest, webResourceError ) => {
let url = webResourceRequest. getUrl ( ) ;
let errorCode = webResourceError. getErrorCode ( ) ;
let description = webResourceError. getDescription ( ) ;
console. trace ( errorCode + " " + description + " " + url) ;
}
} ) ;
webViewWidget. webChromeClient = new JavaAdapter ( android. webkit. WebChromeClient, {
onConsoleMessage : ( msg ) => {
console. log ( "[%s:%s]: %s" , msg. sourceId ( ) , msg. lineNumber ( ) , msg. message ( ) ) ;
}
} ) ;
}
webViewExpand_init ( ui. webView)
ui. webView. loadUrl ( "https://wht.im" ) ;
ui. surfInternetBtn. on ( "click" , ( ) => {
webViewExpand_init ( ui. webView) ;
ui. webView. loadUrl ( "https://wht.im" ) ;
} ) ;
ui. consoleBtn. on ( "click" , ( ) => {
app. startActivity ( "console" ) ;
} ) ;
ui. loadLocalHtmlBtn. on ( 'click' , ( ) => {
webViewExpand_init ( ui. webView) ;
let path = "file:" + files. path ( "game.html" ) ;
ui. webView. loadUrl ( path) ;
} ) ;
WebSocket
创建客户端
importPackage ( Packages[ "okhttp3" ] ) ;
var client = new OkHttpClient. Builder ( ) . retryOnConnectionFailure ( true ) . build ( ) ;
var request = new Request. Builder ( ) . url ( "ws://192.168.31.164:9317" ) . build ( ) ;
client. dispatcher ( ) . cancelAll ( ) ;
myListener = {
onOpen : function ( webSocket, response ) {
print ( "onOpen" ) ;
var json = { } ;
json. type= "hello" ;
json. data= { device_name : "模拟设备" , client_version : 123 , app_version : 123 , app_version_code : "233" } ;
var hello= JSON . stringify ( json) ;
webSocket. send ( hello) ;
} ,
onMessage : function ( webSocket, msg ) {
print ( "msg" ) ;
print ( msg) ;
} ,
onClosing : function ( webSocket, code, reason ) {
print ( "正在关闭" ) ;
} ,
onClosed : function ( webSocket, code, reason ) {
print ( "关闭" ) ;
} ,
onFailure : function ( webSocket, t, response ) {
print ( "错误" ) ;
print ( t) ;
}
}
var webSocket= client. newWebSocket ( request, new WebSocketListener ( myListener) ) ;
setInterval ( ( ) => {
} , 1000 ) ;
WebSocket示例
let ws = web. newWebSocket ( "wss://demo.piesocket.com/v3/channel_1?notify_self" , {
eventThread : 'this'
} ) ;
console. show ( ) ;
ws. on ( "open" , ( res, ws ) => {
log ( "WebSocket已连接" ) ;
} ) . on ( "failure" , ( err, res, ws ) => {
log ( "WebSocket连接失败" ) ;
console. error ( err) ;
} ) . on ( "closing" , ( code, reason, ws ) => {
log ( "WebSocket关闭中" ) ;
} ) . on ( "text" , ( text, ws ) => {
console. info ( "收到文本消息: " , text) ;
} ) . on ( "binary" , ( bytes, ws ) => {
console. info ( "收到二进制消息:" ) ;
console. info ( "hex: " , bytes. hex ( ) ) ;
console. info ( "base64: " , bytes. base64 ( ) ) ;
console. info ( "md5: " , bytes. md5 ( ) ) ;
console. info ( "size: " , bytes. size ( ) ) ;
console. info ( "bytes: " , bytes. toByteArray ( ) ) ;
} ) . on ( "closed" , ( code, reason, ws ) => {
log ( "WebSocket已关闭: code = %d, reason = %s" , code, reason) ;
} ) ;
log ( "发送消息: Hello, WebSocket!" ) ;
ws. send ( "h" ) ;
setTimeout ( ( ) => {
log ( "发送二进制消息: 5piO5aSp5L2g6IO96ICDMTAw5YiG44CC" ) ;
ws. send ( web. ByteString. decodeBase64 ( "5piO5aSp5L2g6IO96ICDMTAw5YiG44CC" ) ) ;
} , 2000 ) ;
setTimeout ( ( ) => {
log ( "断开WebSocket" ) ;
ws. close ( 1000 , null ) ;
} , 8000 ) ;
setTimeout ( ( ) => {
log ( "退出程序" ) ;
} , 12000 )