受这个动作启发,用autox.js写了个app实现手机——电脑,手机——手机剪贴板同步

经验创意 · 1926 次浏览
用户1278178615... 创建于 2023-08-24 15:33

由于安卓10以上应用不能在后台读取剪贴板,所以10以上系统需要刷面具装两个模块才能使用,

Riru  和  Riru - Clipboard Whitelist

 

同步实现思路:

手机到手机:手机读取剪贴板内容,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)
}

 

 


回复内容
暂无回复
回复主贴