apk及全部代码:https://gitee.com/tfnmdmx-gitee/autojs/tree/master
一、视频演示
手机本地密码本2.0【AutoJS】
二、笔记
1.加密解密
AES 对称加密
function AES_encrypt(message, key) {
//log("JSON对象: ", message);
message = JSON.stringify(message);
//log("JSON字符串: ", message);
let aes = $crypto.encrypt(message, key, "AES/ECB/PKCS5padding");
//log("AES加密后二进制数据: ", aes);
return aes;
}
function AES_decrypt(aes, key) {
res = $crypto.decrypt(aes, key, "AES/ECB/PKCS5padding", {
output: 'string' })
//log("AES解密字符串: ", res);
res = JSON.parse(res);
//log("对象: ", res);
return res;
}
2.缓存
function putput(keyName, value) {
var AES_value = AES_encrypt(value, key);
storage.put(keyName, AES_value);
//toastLog("缓存成功")
//log(AES_value);
}
function getget(keyName) {
var AES_value = storage.get(keyName, []); //Storage.get(key[, defaultValue])
//log(AES_value);
let res = []
if (AES_value.length != 0) {
res = AES_decrypt(AES_value, key);
}
//toastLog("获取缓存成功")
//log(res);
return res;
}
3.首页
function showIndexUI() {
curUI = 'indexUI'
ui.layout(
<drawer id="drawer">
<vertical w='*' h='*'>
<appbar>
<toolbar id="toolbar" title="密码本" bg="#FF4FB3FF">
<button id="add" text="+" layout_gravity="right" style="Widget.AppCompat.Button.Borderless"></button>
</toolbar>
</appbar>
<vertical w='*' h='*'>
<card w="*" h="auto" margin="10 50" cardCornerRadius="10dp" cardElevation="5dp" contentPadding="10 10 10 10">
<img id='background1' h='200' scaleType="fitXY" src="https://pic.3gbizhi.com/2020/0716/20200716112137267.jpg" />
</card>
<linear w='*' margin="10 50">
<input id="pass_word" layout_weight="1" password="true" hint='主密码'></input>
<button id="pass_btn" text="确定"></button>
</linear>
</vertical>
</vertical>
<vertical layout_gravity="left" bg="#ffffff" w="280">
<img id='background2' w="280" h="200" scaleType="fitXY" src="https://pic.3gbizhi.com/2020/0716/20200716112137267.jpg" />
<list id="menu">
<horizontal bg="?selectableItemBackground" w="*">
<img w="50" h="50" padding="16" src="{
{this.icon}}" />
<text textColor="black" textSize="15sp" text="{
{this.title}}" layout_gravity="center" />
</horizontal>
</list>
</vertical>
</drawer>
)
//设置图片
/*setTimeout(function () {
ui.background1.setSource(background_img);
ui.background2.setSource(background_img);
}, 1000);*/
ui.background1.on("click", () => {
ui.background1.setSource(background_img);
ui.background2.setSource(background_img);
});
ui.background2.on("click", () => {
ui.background1.setSource(background_img);
ui.background2.setSource(background_img);
});
//新增
ui.add.on("click", () => showAddUI());
//退出
ui.add.on("long_click", (event) => {
ui.finish();
event.consumed = true;
});
ui.pass_btn.on("click", () => {
pass_word = ui.pass_word.text();
if (pass_word == mainPassword) {
showSearchUI();
} else {
toast('密码错误')
pass_word == ''
ui.pass_word.setText('')
}
});
//让工具栏左上角可以打开侧拉菜单
ui.toolbar.setupWithDrawer(ui.drawer);
ui.menu.setDataSource([
{
title: "设置图片",
icon: "@drawable/ic_aspect_ratio_black_48dp"
},
{
title: "设置密码",
icon: "@drawable/ic_lock_black_48dp"
},
{
title: "导入数据",
icon: "@drawable/ic_settings_black_48dp"
},
{
title: "导出数据",
icon: "@drawable/ic_settings_black_48dp"
},
{
title: "查看日志",
icon: "@drawable/ic_assignment_black_48dp"
},
{
title: "清除缓存",
icon: "@drawable/ic_delete_black_48dp"
},
{
title: "退出",
icon: "@drawable/ic_exit_to_app_black_48dp"
}
]);
ui.menu.on("item_click", item => {
switch (item.title) {
case "设置图片":
setBackground();
//这里ui.background.setSource不起作用,猜测是ui不一样
break;
case "设置密码":
showSettingUI();
//if (mainPassword.length == 0) showSearchUI();//跳转页面
break;
case "导入数据":
//inFile();
var file = files.cwd() + "/DATA.txt"
rawInput("请输入txt文件路径", file).then(filePath => {
if (files.exists(filePath)) {
let temp_new = files.read(filePath);
dataList = getget('dataList');
if (temp_new != '') {
temp_new = JSON.parse(temp_new)//对象
dataList = dataList.concat(temp_new) //拼接
//2.修改id
var j=1
for(var i of dataList){
i.id = j.toString()
j++
}
putput('dataList', dataList);
toastLog('导入成功')
log(dataList)
} else {
toastLog("导入失败")
}
} else {
toast('文件不存在')
}
});
break;
case "导出数据":
outFile();
break;
case "查看日志":
app.startActivity("console");
break;
case "清除缓存":
dialogs.build({
title: "清除缓存",
content: "本程序所有数据均存储于缓存中,清除后将无法找回,确认清除吗?",
cancelable: true,
positive: "清除",
neutral: "取消",
}).on("positive", () => {
if (mainPassword.length == 0) {
storage.remove('dataList')
dataList = []
storage.remove('background_img')
background_img = "https://pic.3gbizhi.com/2020/0716/20200716112137267.jpg"
toastLog("清除缓存成功")
showSearchUI()
} else {
rawInput("请输入密码").then(pass => {
if (pass != mainPassword) {
toast('密码错误')
} else {
storage.remove('dataList')
dataList = []
storage.remove('mainPassword')
mainPassword = []
pass_word = ""
storage.remove('background_img')
background_img = "https://pic.3gbizhi.com/2020/0716/20200716112137267.jpg"
toastLog("清除缓存成功")
showSearchUI()
}
});
}
}).show();
break;
case "退出":
ui.finish();
break;
}
})
}
4.搜索页
function showSearchUI() {
curUI = 'searchUI'
ui.layout(
<drawer id='drawer'>
<vertical w='*' h='*'>
<appbar>
<toolbar id='toolbar' title="密码本" bg="#FF4FB3FF">
<button id="add" text="+" layout_gravity="right" style="Widget.AppCompat.Button.Borderless"></button>
</toolbar>
</appbar>
<linear w='*' margin="10 0 0 0">
<input id="search_word" singleLine="true" marginTop="5" layout_weight="1"></input>
<button id="search_btn" text="搜索"></button>
</linear>
<grid spanCount="1" id='dataList' h="*">
<card w="*" h="auto" margin="10 10 10 0" cardCornerRadius="6dp" cardElevation="2dp" gravity="center" foreground="?selectableItemBackground">
<vertical margin="10" layout_gravity="center_vertical" layout_weight="1">
<text size="18" color="#FF4FB3FF" text="{
{this.name}}" />
<horizontal>
<text size="14" color="#C0C0C0" text="{
{this.type}} " gravity="right" />
<text size="14" color="#C0C0C0" text="{
{this.key}}" gravity="right" />
</horizontal>
<text size="16" color="#444444" text="账号:{
{this.account}}" />
{
/* <text size="16" color="#444444" text="密码:{
{this.password}}" /> */}
<text size="16" color="#444444" text="备注:{
{this.desc}}" ellipsize="marquee" />
<text size="16" color="#C0C0C0" text="关联:{
{this.contact}}" />
</vertical>
</card>
</grid>
</vertical>
<vertical layout_gravity="left" bg="#ffffff" w="280">
<img id='background' w="280" h="200" scaleType="fitXY" src="https://pic.3gbizhi.com/2020/0716/20200716112137267.jpg" />
<list id="menu">
<horizontal bg="?selectableItemBackground" w="*">
<img w="50" h="50" padding="16" src="{
{this.icon}}" />
<text textColor="black" textSize="15sp" text="{
{this.title}}" layout_gravity="center" />
</horizontal>
</list>
</vertical>
</drawer>
);
/*setTimeout(function () {
ui.background.setSource(background_img);
}, 1000);*/
ui.background.on("click", () => {
ui.background.setSource(background_img);
});
if (search_word == '') {
if (dataList.length == 0) {
var tempList = [{
type: "类别和关键词用于搜索",
name: "暂无记录,快去添加吧",
key: " 名称和备注用于搜索",
account: " 点击卡片,复制密码",
password: " 长按卡片,编辑",
contact: " 长按搜索,刷新",
desc: " 点击账号、密码、备注,可以复制",
id: "id:时间戳",
}]
ui.dataList.setDataSource(tempList);
} else {
ui.dataList.setDataSource(dataList);
}
}
else {
ui.search_word.setText(search_word);
ui.dataList.setDataSource(search_res);
}
//点击复制
ui.dataList.on("item_click", function (item, i) {
log('click_elem:', item.name)
setClip(item.password)
toast(item.password)
});
//长按,修改
ui.dataList.on("item_long_click", function (event, item, i) {
//log(e)//com.stardust.autojs.core.ui.nativeview.NativeView$LongClickEvent@116bf5c
//log(item)//{,}
//log(i)//1
showEditUI(item)
event.consumed = true;
});
//新增
ui.add.on("click", () => showAddUI());
//退出
ui.add.on("long_click", (event) => {
ui.finish();
event.consumed = true;
});
//长按搜索,清空搜索框,刷新页面
ui.search_btn.on("long_click", (event) => {
search_word = ''
ui.search_word.setText(search_word);
dataList = getget('dataList');
dataList.reverse();
ui.dataList.setDataSource(dataList);
toastLog("**获取缓存成功**");
event.consumed = true;
});
//搜索
ui.search_btn.on("click", () => {
search_word = ui.search_word.text();
log("搜索关键词:" + search_word);
if (search_word == '') {
search_res = []
ui.dataList.setDataSource(dataList)
} else {
search_res = []
for (var i of dataList) {
if (i.name.indexOf(search_word) != -1 || i.type.indexOf(search_word) != -1 || i.key.indexOf(search_word) != -1 || i.desc.indexOf(search_word) != -1) {
search_res.push(i)
}
}
//log("搜索结果:" + search_res);
if (search_res.length == 0) toast('无记录')
ui.dataList.setDataSource(search_res)
}
});
//让工具栏左上角可以打开侧拉菜单
ui.toolbar.setupWithDrawer(ui.drawer);
ui.menu.setDataSource([
{
title: "设置图片",
icon: "@drawable/ic_aspect_ratio_black_48dp"
},
{
title: "设置密码",
icon: "@drawable/ic_lock_black_48dp"
},
{
title: "导入数据",
icon: "@drawable/ic_settings_black_48dp"
},
{
title: "导出数据",
icon: "@drawable/ic_settings_black_48dp"
},
{
title: "查看日志",
icon: "@drawable/ic_assignment_black_48dp"
},
{
title: "清除缓存",
icon: "@drawable/ic_delete_black_48dp"
},
{
title: "退出",
icon: "@drawable/ic_exit_to_app_black_48dp"
}
]);
ui.menu.on("item_click", item => {
switch (item.title) {
case "设置图片":
setBackground();
//这里ui.background.setSource不起作用,猜测是ui不一样
break;
case "设置密码":
showSettingUI();
//if (mainPassword.length == 0) showSearchUI();//跳转页面
break;
case "导入数据":
//inFile();//注意id重复的问题!1.覆盖重复2.修改id3.不导入重复
var file = files.cwd() + "/DATA.txt"
rawInput("请输入txt文件路径", file).then(filePath => {
if (files.exists(filePath)) {
let temp_new = files.read(filePath);
dataList = getget('dataList');
if (temp_new != '') {
temp_new = JSON.parse(temp_new)//对象
dataList = dataList.concat(temp_new) //拼接
//2.修改id
var j=1
for(var i of dataList){
i.id = j.toString()
j++
}
putput('dataList', dataList);
toastLog('导入成功')
log(dataList)
} else {
toastLog("导入失败")
}
} else {
toast('文件不存在')
}
});
break;
case "导出数据":
outFile();
break;
case "查看日志":
app.startActivity("console");
break;
case "清除缓存":
dialogs.build({
title: "清除缓存",
content: "本程序所有数据均存储于缓存中,清除后将无法找回,确认清除吗?",
cancelable: true,
positive: "清除",
neutral: "取消",
}).on("positive", () => {
if (mainPassword.length == 0) {
storage.remove('dataList')
dataList = []
storage.remove('background_img')
background_img = "https://pic.3gbizhi.com/2020/0716/20200716112137267.jpg"
toastLog("清除缓存成功")
showSearchUI()
} else {
rawInput("请输入密码").then(pass => {
if (pass != mainPassword) {
toast('密码错误')
} else {
storage.remove('dataList')
dataList = []
storage.remove('mainPassword')
mainPassword = []
pass_word = ""
storage.remove('background_img')
background_img = "https://pic.3gbizhi.com/2020/0716/20200716112137267.jpg"
toastLog("清除缓存成功")
showSearchUI()
}
});
}
}).show();
break;
case "退出":
ui.finish();
break;
}
})
}
5.新增页
function showAddUI() {
curUI = 'addUI'
ui.layout(
<vertical h="*">
<appbar>
<toolbar title="新增" bg="#FF4FB3FF">
</toolbar>
</appbar>
<card w="*" h="auto" margin="10 10 10 10" cardCornerRadius="6dp" cardElevation="2dp" gravity="center" contentPadding="0 10 10 10">
<vertical h="*">
<linear>
<text w="56" gravity="center" color="#111111" size="16">类别</text>
<input id="type" w="*" h="45" />
</linear>
<linear>
<text w="56" gravity="center" color="#111111" size="16">名称</text>
<input id="name" w="*" h="45" />
</linear>
<linear>
<text w="56" gravity="center" color="#111111" size="16">关键词</text>
<input id="key" w="*" h="45" />
</linear>
<linear>
<text w="56" gravity="center" color="#111111" size="16">账号</text>
<input id="account" w="*" h="45" />
</linear>
<linear>
<text w="56" gravity="center" color="#111111" size="16">密码</text>
<input id="password" w="*" h="45" />
</linear>
<linear>
<text w="56" gravity="center" color="#111111" size="16">关联</text>
<input id="contact" w="*" h="45" />
</linear>
<linear>
<text w="56" gravity="center" color="#111111" size="16">备注</text>
<input id="desc" w="*" h="45" />
</linear>
<linear gravity="center">
<button id="cancel">取消</button>
<button id="add">Add</button>
</linear>
</vertical>
</card>
</vertical>
);
ui.add.on("click", () => {
var timestamp = new Date().getTime(); //精确到毫秒
var temp_add = {
type: ui.type.text(),
name: ui.name.text(),
key: ui.key.text(),
account: ui.account.text(),
password: ui.password.text(),
contact: ui.contact.text(),
desc: ui.desc.text(),
id: timestamp.toString(),//时间戳
}
log('add_elem:', temp_add.id);
dataList.reverse(); //先将数组正序
dataList.push(temp_add); //尾部追加
putput('dataList', dataList) //更新缓存 //加密
toastLog("【更新缓存】--Add")
dataList.reverse(); //再逆序
if (search_word != '') {
search_res.reverse();
search_res.push(temp_add);
search_res.reverse();
}
ui.type.setText('')
ui.name.setText('')
ui.key.setText('')
ui.account.setText('')
ui.password.setText('')
ui.contact.setText('')
ui.desc.setText('')
});
ui.cancel.on("click", () => {
if (mainPassword.length == 0)//无密码
showSearchUI();
else if (mainPassword == pass_word)//密码正确
showSearchUI();
else
showIndexUI();
});
}
6.修改页
function showEditUI(item) {
curUI = 'editUI'
ui.layout(
<vertical h="*">
<appbar>
<toolbar title="修改" bg="#FF4FB3FF">
<button id="delete" text="-" layout_gravity="right" style="Widget.AppCompat.Button.Borderless"></button>
</toolbar>
</appbar>
<card w="*" h="auto" margin="10 10 10 10" cardCornerRadius="6dp" cardElevation="2dp" gravity="center" contentPadding="0 10 10 10">
<vertical h="*">
<linear gravity="center" >
<button id="id" style="Widget.AppCompat.Button.Borderless"></button>
</linear>
<linear>
<text w="56" gravity="center" color="#111111" size="16">类别</text>
<input id="type" w="*" h="45" />
</linear>
<linear>
<text w="56" gravity="center" color="#111111" size="16">名称</text>
<input id="name" w="*" h="45" />
</linear>
<linear>
<text w="56" gravity="center" color="#111111" size="16">关键词</text>
<input id="key" w="*" h="45" />
</linear>
<linear>
<text id="clip_account" w="56" gravity="center" color="#111111" size="16">账号</text>
<input id="account" w="*" h="45" />
</linear>
<linear>
<text id="clip_password" w="56" gravity="center" color="#111111" size="16">密码</text>
<input id="password" w="*" h="45" inputType='textPassword' />
</linear>
<linear>
<text w="56" gravity="center" color="#111111" size="16">关联</text>
<input id="contact" w="*" h="45" />
</linear>
<linear>
<text id="clip_desc" w="56" gravity="center" color="#111111" size="16">备注</text>
<input id="desc" w="*" />
</linear>
<linear gravity="center">
<button id="cancel">取消</button>
<button id="edit">Edit</button>
</linear>
</vertical>
</card>
</vertical>
);
ui.type.setText(item.type)
ui.name.setText(item.name)
ui.key.setText(item.key)
ui.account.setText(item.account)
ui.password.setText(item.password)
ui.contact.setText(item.contact)
ui.desc.setText(item.desc)
ui.id.setText(item.id)
ui.edit.on("click", () => {
log('edit_elem:', item.name)
var temp_edit = {
type: ui.type.text(),
name: ui.name.text(),
key: ui.key.text(),
account: ui.account.text(),
password: ui.password.text(),
contact: ui.contact.text(),
desc: ui.desc.text(),
id: item.id,
}
var temp_dataList = dataList.filter(function (temp_item) {
return temp_item.id != item.id; //先删
});
//log(temp_dataList)
temp_dataList.reverse(); //正序
temp_dataList.push(temp_edit); //后加
putput('dataList', temp_dataList) //更新缓存 //加密
toastLog("【更新缓存】--Edit")
temp_dataList.reverse(); //逆序
dataList = temp_dataList
if (search_word != '') {
var temp_dataList = search_res.filter(function (temp_item) {
return temp_item.id != item.id;//先删
});
temp_dataList.reverse();
temp_dataList.push(temp_edit);//后加
temp_dataList.reverse();
search_res = temp_dataList
}
showSearchUI();
});
ui.delete.on("click", () => {
confirm("确定要删除吗?")
.then((ok) => {
if (ok) {
log('del_elem:', item.name)
var temp_dataList = dataList.filter(function (temp_item) {
return temp_item.id != item.id; //删除
});
temp_dataList.reverse(); //正序
putput('dataList', temp_dataList) //更新缓存 //加密
toastLog("【更新缓存】--Delete");
temp_dataList.reverse(); //逆序
dataList = temp_dataList
if (search_word != '') {
var temp_dataList = search_res.filter(function (temp_item) {
return temp_item.id != item.id;//删除
});
search_res = temp_dataList
}
showSearchUI();
}
});
});
ui.clip_password.on("click", () => {
setClip(ui.password.text());
toast(ui.password.text());
});
ui.clip_account.on("click", () => {
setClip(ui.account.text());
toast(ui.account.text());
});
ui.clip_desc.on("click", () => {
setClip(ui.desc.text());
toast(ui.desc.text());
});
ui.cancel.on("click", () => {
showSearchUI();
});
}
7.设置密码页
function showSettingUI() {
curUI = 'settingUI'
ui.layout(
<vertical w="*" h="*" margin="30">
<Switch id="sw" text="启用主密码" marginBottom="30" />
<input id='oldPassword' password='true' hint="原密码"></input>
<input id='newPassword' password='true' hint="新密码"></input>
<button id='ok' marginTop="30">确认修改</button>
</vertical>
)
if (mainPassword.length == 0) {
//原来无密码
ui.sw.attr('checked', "false")
} else {
ui.sw.attr('checked', "true")
}
//操作
ui.sw.on("check", (checked) => {
if (checked) {
toastLog("请设置密码");
} else {
toast('请确认关闭')
}
});
ui.ok.on("click", () => {
if (!ui.sw.isChecked()) {
//未启用
if (mainPassword.length == 0) {
//原来未启用
toast('关闭中')
} else {
//原来启用
if (ui.oldPassword.text() != mainPassword) {
toast('原密码错误')
} else {
toast('已关闭')
storage.remove('mainPassword');
mainPassword = []
pass_word = ''
}
}
} else {
//当前启用
if (mainPassword.length == 0) {
//原来未启用
if (ui.oldPassword.text() != ui.newPassword.text()) {
toast("两次密码不一致")
} else if (ui.oldPassword.text() == '' && ui.newPassword.text() == '') {
toast("密码不能为空")
} else {
toast("设置成功")
putput('mainPassword', ui.newPassword.text());
mainPassword = ui.newPassword.text()
pass_word = ''
}
} else {
//原来启用,当前启用,修改密码
if (ui.newPassword.text() == '') {
toast("密码不能为空")
}
else if (ui.oldPassword.text() != mainPassword) {
toast('原密码错误')
}
else if (ui.oldPassword.text() == ui.newPassword.text()) {
toast('新旧密码一样')
} else {
toast('修改成功')
putput('mainPassword', ui.newPassword.text());
mainPassword = ui.newPassword.text()
pass_word = ''
}
}
}
});
}
8.设置图片
function setBackground() {
let imgRequestCode = 1;
let intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
activity.startActivityForResult(intent, imgRequestCode);
activity.getEventEmitter().on("activity_result", (requestCode, resultCode, data) => {
if (requestCode == imgRequestCode) {
if (resultCode == activity.RESULT_OK) {
var uri = data.getData();
let filepath = URIUtils_uriToFile(uri);
//log(filepath);// /storage/emulated/0/Pictures/QQ/找回密码图标.png
background_img = 'file://' + filepath;
putput('background_img', background_img);
toastLog('设置成功!')
}
}
});
}
9.导出文件
function outFile() {
//导出文件
var filePath = ''
if (mainPassword.length != 0) {
rawInput("请输入密码").then(pass => {
if (pass != mainPassword) {
toast('密码错误')
} else {
var text = getget('dataList');
text = JSON.stringify(text);
//log(text)
files.write("./DATA.txt", text);//写入文件
//app.viewFile("./DATA.txt");//用其他应用查看文件
toastLog("导出成功!");
filePath = files.cwd() + "/DATA.txt"
//log(filePath)
setClip(filePath)
alert("导出成功", "文件路径为" + filePath + ",已复制到剪切板,请妥善保存。");
}
});
} else {
files.write("./DATA.txt", JSON.stringify(getget('dataList')));
toastLog("导出成功!");
filePath = files.cwd() + "/DATA.txt"
//log(filePath)
setClip(filePath)
alert("导出成功", "文件路径为" + filePath + ",已复制到剪切板,请妥善保存。");
}
}