由于安卓10以上应用不能在后台读取剪贴板,所以10以上系统需要刷面具装两个模块才能使用,
同步实现思路:
手机到手机:手机读取剪贴板内容,post发送到坚果云txt文本中,另一个手机读取坚果云txt文本内容写入剪贴板。
手机到电脑:手机读取剪贴板内容,post发送到quicker长链接。
电脑到手机:quicker动作读取剪贴板内容,post发送到坚果云txt文本中。
可以使用autox.js写一个悬浮窗,两个小按钮:“收”、“发”。点击收,读取坚果云txt内容写入剪贴板;点击发,读取剪贴板内容发送到坚果云txt文件。
也可以监听剪贴板内容,发生变化则自动发送到坚果云,省去手机点击发这一步,手机点击收这一步只能手动。
贴一下autoxjs源码,改一下前几行的数据就能运行了,建议打包成app安装使用,开启悬浮通知权限,点击收后会发送悬浮通知显示读取到的文字,一秒后自动消除通知。
坚果云账号="这段文字改成你的坚果云邮箱"
坚果云密码="这段文字改成你的坚果云第三方应用密码"
quicker账号="这段文字改成你的quicker邮箱"
quicker推送验证码="这段文字改成你的quicker推送验证码"
动作名或ID="这段文字改成你有的任意一个动作名或ID"
//修改以上内容,以下代码请勿修改
function JianGuoYunHelper() {
/**
* 坚果云SDK
*/
function JgySDK() { }
/**
* 初始化
* @param {*} folderName 操作的文件夹名称
*/
JgySDK.prototype.init = function (userName, password, folderName) {
this.folderName = folderName;
this.jgyHost = "http://dav.jianguoyun.com/dav/" + this.folderName;
this.key = this.getBase64(userName + ":" + password);
this.header = {
Authorization: "Basic " + this.key,
"Content-Type": "text/plain;charset=UTF-8",
Connection: "Keep-Alive",
"Accept-Encoding": "gzip",
"User-Agent": "okhttp/3.12.1",
};
this.fileName = "";
this.createFolder();
};
/**
* 设置 操作的fileName, 之后的方法不再需要传 fileName
* @param {string} fileName
* @param {string?} fileExtension 可空, 默认 "txt"
*/
JgySDK.prototype.setThisFileName = function (fileName, fileExtension) {
if (fileExtension == void 0) {
fileExtension = "txt";
}
this.fileName = fileName + "." + fileExtension;
};
/**
* 读取文件夹的目录
* @returns {string[]}
*/
JgySDK.prototype.getFolderCatalog = function () {
let httpRes = http.request(this.jgyHost, {
method: "PROPFIND",
headers: this.header,
});
let resArr = [];
let xmlData = httpRes.body.string();
if (xmlData) {
let dataArr = xmlData.match(/<d:displayname>(.*?)<\/d:displayname>/g);
for (let item of dataArr) {
item = item.replace("<d:displayname>", "").replace("</d:displayname>", "");
if (item != this.folderName) {
resArr.push(item);
}
}
}
return resArr;
};
/**
* 创建文件夹
*
* @private 私有方法
* @returns {boolean}
*/
JgySDK.prototype.createFolder = function () {
let httpRes = http.request(this.jgyHost, {
method: "MKCOL",
headers: this.header,
});
return httpRes.statusCode == 201;
};
/**
* 获取thisFileName
* @private 私有方法
* @param {string} fileName
* @returns {string}
*/
JgySDK.prototype.getThisFileName = function (fileName) {
if (fileName == void 0) {
if (this.fileName != "") {
fileName = this.fileName;
} else {
throw "当前必须传fileName, 调用setThisFileName后,才可以不传fileName";
}
}
return fileName;
};
/**
* 删除一个文件
* @param {string?} fileName 可空, 需要提前调用 setThisFileName
*/
JgySDK.prototype.delete = function (fileName) {
let res = { res: false, msg: "删除失败" };
try {
fileName = this.getThisFileName(fileName);
let fileArr = this.getFolderCatalog();
if (fileArr.indexOf(fileName) > -1) {
http.request(this.jgyHost + fileName, {
method: "DELETE",
headers: this.header,
});
fileArr = this.getFolderCatalog();
if (fileArr.indexOf(fileName) < 0) {
return true
} else {
log("删除失败,文件依然在目录中");
}
} else {
log("文件不存在,无需删除");
}
} catch (error) {
log("删除失败,其他错误")
}
return false
};
/**
* 获取一个文件内容
* @param {string?} fileName 可空, 需要提前调用 setThisFileName
*/
JgySDK.prototype.get = function (fileName) {
let res = "";
try {
fileName = this.getThisFileName(fileName);
let httpRes = http.get(this.jgyHost + fileName, {
headers: this.header,
});
//log(httpRes);
if (httpRes.statusCode == 404) {
let strRes = httpRes.body.string();
if (strRes.indexOf("doesn't exist") > -1) {
let errorMsg = "无备份文件,文件名:" + fileName;
toast(errorMsg);
console.error(errorMsg);
} else {
throw strRes;
}
} else if (httpRes.statusCode == 200) {
res = httpRes.body.string();
} else {
throw httpRes;
}
} catch (error) {
console.error("JgySDK get error: " + error);
}
return res;
};
/**
* 上传一个文件
* @param {string} path
* @param {string?} fileName 可空, 需要提前调用 setThisFileName
*/
JgySDK.prototype.put = function (data, fileName) {
let res = { res: false, msg: "上传失败" };
try {
fileName = this.getThisFileName(fileName);
let httpRes = http.request(this.jgyHost + fileName, {
method: "PUT",
headers: this.header,
body: data,
});
let newdata = this.get(fileName);
if (newdata == data) {
res.res = true;
res.msg = "本地数据 推送到 坚果云 成功";
} else {
res.msg = "数据推送失败,推送后网络数据与本次数据不同,result:" + httpRes.body.string();
}
} catch (error) {
res.msg = error;
}
return res;
};
/**
* 获取base64 结果
* @private 私有方法
* @param {string} str
* @returns {string}
*/
JgySDK.prototype.getBase64 = function (str) {
return java.lang.String(android.util.Base64.encode(java.lang.String(str).getBytes(), 2));
};
return new JgySDK();
}
/**
* 发送文本到坚果云文件
* @param {string} txt
* @param {string} 路径 文件夹路径
* @param {string} 文件名 含.拓展名
*/
function 坚果云发送1128(txt, 路径, 文件名) {
let Jgy = JianGuoYunHelper();
Jgy.init(坚果云账号, 坚果云密码, 路径);
Jgy.put(txt, 文件名);
}
/**
* 从坚果云文件获取内容
* @param {string} 路径 文件夹路径
* @param {string} 文件名 含.拓展名
* @returns {string}
*/
function 坚果云读取1128(路径, 文件名) {
let Jgy = JianGuoYunHelper();
Jgy.init(坚果云账号, 坚果云密码, 路径);
return Jgy.get(文件名)
}
/**
* 将文本发送到quicker动作,成功返回true,失败返回false
* @param {string} 文本
* @param {string} 动作名或ID
* @returns {boolean}
*/
function quicker发送(文本, 动作名或ID) {
var url = "https://push.getquicker.cn/to/quicker";
r = http.postJson(url, {
"toUser": quicker账号,
"code": quicker推送验证码,
"operation": "copy",
"data": 文本,
"action": 动作名或ID,
"wait": false,
"maxWaitMs": 2000,
"txt": false
});
if (r.statusCode == 200) {
return true
}
return false
}
/**
* 发送通知栏消息
* @param {string} 标题
* @param {string} 内容 可为空
*/
function 发送通知(标题, 内容) {
if (!内容) { 内容 = "" }
var manager = context.getSystemService(android.app.Service.NOTIFICATION_SERVICE);
var notification;
if (device.sdkInt >= 26) {
var channel = new android.app.NotificationChannel("channel_id", "channel_name", android.app.NotificationManager.IMPORTANCE_DEFAULT);
channel.enableLights(true);
channel.setLightColor(0xff0000);
channel.setShowBadge(false);
manager.createNotificationChannel(channel);
notification = new android.app.Notification.Builder(context, "channel_id")
.setContentTitle(标题)
.setContentText(内容)
.setWhen(new Date().getTime())
.setSmallIcon(android.R.drawable.ic_delete)
//.setTicker("这是状态栏显示的内容")
.build();
} else {
notification = new android.app.Notification.Builder(context)
.setContentTitle(标题)
.setContentText(内容)
.setWhen(new Date().getTime())
.setSmallIcon(org.autojs.autojs.R.drawable.autojs_material)
//.setTicker("这是状态栏显示的内容")
.build();
}
manager.notify(1, notification);
}
/**
* 消除通知
*/
function 消除通知() {
var manager = context.getSystemService(android.app.Service.NOTIFICATION_SERVICE);
manager.cancelAll();
// manager.cancel(1);
}
//新线程中创建悬浮窗=======================================================
threads.start(function () {
var ui = floaty.window(
<vertical gravity="center" bg="#009688">
<text id="id收" text="收" width="80px" height="80px" textColor="#ffffff" gravity="center" />
<vertical width="80px" height="1px" bg="#ffffff" />
<text id="id发" text="发" width="80px" height="80px" textColor="#ffffff" gravity="center" />
</vertical>
);
ui.setPosition(device.width - 80, device.height / 3)
ui.id收.click(() => {
收 = 1
});
ui.id发.click(() => {
发 = 1
});
});
select = confirm("是否同步到电脑?");
收 = 0
发 = 0
while (1) {
if (收 == 1) {
收 = 0
text = 坚果云读取1128("autoxjs/", "clip.txt")
//设置缩略文本
if (text.length > 10) {
txt = text.substring(0, 10) + "..."
} else if (0 < text.length <= 10) {
txt = text
}
//写入剪贴板,发送、消除通知
if (text.length != 0) {
setClip(text)
发送通知(txt)
threads.start(function () {
sleep(1000)
消除通知()
});
}
}
else if (发 == 1) {
发 = 0
text = getClip()
if (text && text.length > 1) {
坚果云发送1128(text, "autoxjs/", "clip.txt")
if (select) {
quicker发送(text, "clip提示")
}
toast("已发送")
}
}
sleep(200)
}