跳转到内容

UI 示例项目模板

更新: 2025/7/20 字数: 0 字 时长: 0 分钟

组件和功能齐全 UI 界面模板(推荐)

在手机端 Bot.js 创建脚本项目时,你可以直接选择内置的 组件和功能齐全 UI 界面模板,快速搭建完整的 UI 页面。

✅ 快速上手 📦 组件丰富 🔧 可复用逻辑 🚀 高效开发

该模板包含:

  • 常用 UI 组件(按钮、输入框、开关等)
  • 各类弹窗(提示、确认)
  • 权限申请与检查
  • 悬浮窗菜单
  • UI 配置持久化(自动保存/读取)
  • 脚本运行日志输出

所有子模块都已封装完毕,可直接套用,无需重复造轮子。

项目创建时选择示意图

组件和功能齐全 UI 界面 模板选择

请确保你已在 Bot.js 中打开“新建项目”页面,并选择对应模板。


示例代码

项目根目录的脚本入口文件main.js 代码如下

js
"ui";
/* 导入 Java 类 */
importClass("android.content.pm.PackageManager");
importClass("android.provider.Settings");
importClass("android.widget.AdapterView");
/* 创建本地存储 用来保存数据 */
var 本地存储 = storages.create("UI模板");
/* 设置默认值 */
if (本地存储.get("floatyWindow") == undefined) {
  本地存储.put("floatyWindow", false);
}
if (本地存储.get("windowX") == undefined) {
  本地存储.put("windowX", -20);
}
if (本地存储.get("windowY") == undefined) {
  本地存储.put("windowY", device.height / 2);
}
if (本地存储.get("QQ") == undefined) {
  本地存储.put("QQ", "2874662633");
}
if (本地存储.get("单行文本输入框") == undefined) {
  本地存储.put("单行文本输入框", "我是单行文本输入框的默认文本");
}
if (本地存储.get("多行文本输入框") == undefined) {
  本地存储.put("多行文本输入框", "我是多行文本输入框的默认文本\n我是第二行文本");
}
if (本地存储.get("数字输入框") == undefined) {
  本地存储.put("数字输入框", 888);
}
if (本地存储.get("密码输入框") == undefined) {
  本地存储.put("密码输入框", "");
}
if (本地存储.get("下拉菜单") == undefined) {
  本地存储.put("下拉菜单", 0);
}
if (本地存储.get("单选框1") == undefined) {
  本地存储.put("单选框1", false);
}
if (本地存储.get("单选框2") == undefined) {
  本地存储.put("单选框2", true);
}
if (本地存储.get("单选框3") == undefined) {
  本地存储.put("单选框3", false);
}
if (本地存储.get("复选框1") == undefined) {
  本地存储.put("复选框1", true);
}
if (本地存储.get("复选框2") == undefined) {
  本地存储.put("复选框2", true);
}
if (本地存储.get("复选框3") == undefined) {
  本地存储.put("复选框3", false);
}
if (本地存储.get("进度条") == undefined) {
  本地存储.put("进度条", 100);
}
var window;
var isRun = null;
ui.layout(
  <vertical>
    <appbar bg="#000000">
      <toolbar id="toolbar" title="Bot.js系统-专业的自动化开发工具" />
    </appbar>
    <viewpager id="viewPager" layout_weight="1">
      <frame>
        <vertical>
          <ScrollView h="auto" layout_weight="25">
            <vertical h="auto" padding="10 5" layout_weight="25">
              <card margin="3 1" cardElevation="0" cardCornerRadius="10" cardBackgroundColor="#e9e9e9" alpha="1">
                <card margin="1 1" w="*" cardElevation="0" cardCornerRadius="10" cardBackgroundColor="#e9e9e9">
                  <vertical margin="5 10 5 10">
                    <text text="基本信息" padding="10 4" textSize="22" textColor="#222222" marginBottom="5px" />
                    <horizontal padding="10 4">
                      <text
                        text="屏幕宽度"
                        textColor="#222222"
                        textSize="14"
                        h="*"
                        w="400px"
                        gravity="left|center"
                        layout_weight="1"
                      />
                      <text
                        id="屏幕宽度"
                        text=""
                        textColor="#222222"
                        textSize="14"
                        h="*"
                        w="*"
                        gravity="left|center"
                        layout_weight="2"
                      />
                    </horizontal>
                    <horizontal padding="10 4">
                      <text
                        text="屏幕高度"
                        textColor="#222222"
                        textSize="14"
                        h="*"
                        w="400px"
                        gravity="left|center"
                        layout_weight="1"
                      />
                      <text
                        id="屏幕高度"
                        text=""
                        textColor="#222222"
                        textSize="14"
                        h="*"
                        w="*"
                        gravity="left|center"
                        layout_weight="2"
                      />
                    </horizontal>

                    <horizontal padding="10 4">
                      <text
                        text="当前版本号"
                        textColor="#222222"
                        textSize="14"
                        h="*"
                        w="400px"
                        gravity="left|center"
                        layout_weight="1"
                      />
                      <text
                        id="当前版本号"
                        text=""
                        textColor="#222222"
                        textSize="14"
                        h="*"
                        w="*"
                        gravity="left|center"
                        layout_weight="2"
                      />
                    </horizontal>
                  </vertical>
                </card>
              </card>
              <card margin="3 1" cardElevation="0" cardCornerRadius="10" cardBackgroundColor="#e9e9e9" alpha="1">
                <card margin="1 1" w="*" cardElevation="0" cardCornerRadius="10" cardBackgroundColor="#e9e9e9">
                  <vertical margin="5 10 5 10">
                    <text text="权限设置" padding="10 4" textSize="22" textColor="#222222" marginBottom="5px" />
                    <horizontal padding="10 4">
                      <vertical layout_weight="1">
                        <text text="无障碍" textColor="#222222" textStyle="bold" textSize="13" />
                        <text text="提供自动操作(点击,长按,滑动等)" textColor="#222222" textSize="10" marginTop="2" />
                      </vertical>
                      <Switch id="autoService" textSize="13" checked="true" marginRight="-2" marginTop="3" />
                    </horizontal>

                    <horizontal padding="10 4">
                      <vertical layout_weight="1">
                        <text text="悬浮窗权限" textColor="#222222" textSize="13" textStyle="bold" />
                        <text text="软件运行的必开权限" textColor="#222222" textSize="10" marginTop="2" />
                      </vertical>
                      <Switch id="floatyPermission" checked="true" marginRight="-2" marginTop="3" />
                    </horizontal>
                    <horizontal padding="10 4">
                      <vertical layout_weight="1">
                        <text text="忽略电池优化" textColor="#222222" textSize="13" textStyle="bold" />
                        <text text="软件运行的必开权限(不优化)" textColor="#222222" textSize="10" marginTop="2" />
                      </vertical>
                      <Switch id="battery" checked="true" marginRight="-2" marginTop="3" />
                    </horizontal>

                    <horizontal padding="10 4">
                      <vertical layout_weight="1">
                        <text text="稳定模式" textColor="#222222" textSize="13" textStyle="bold" />
                        <text text="切换后需要重写打开无障碍" textColor="#222222" textSize="10" marginTop="2" />
                      </vertical>
                      <Switch id="stableMode" textSize="13" checked="true" marginRight="-2" marginTop="3" />
                    </horizontal>

                    <horizontal padding="10 4">
                      <vertical layout_weight="1">
                        <text text="控制台" textColor="#222222" textSize="13" textStyle="bold" />
                        <text
                          text="调试输出,悬浮窗显示日志(推荐使用可以手势穿透的悬浮窗日志)"
                          textColor="#222222"
                          textSize="10"
                          marginTop="2"
                        />
                      </vertical>
                      <Switch id="screenCapturePermission" textSize="13" checked="false" marginRight="-2" marginTop="3" />
                    </horizontal>
                    <horizontal padding="10 4">
                      <vertical layout_weight="1">
                        <text text="悬浮菜单" textColor="#222222" textSize="13" textStyle="bold" />
                        <text text="控制脚本任务的运行和结束" textColor="#222222" textSize="10" marginTop="2" />
                      </vertical>
                      <Switch id="floatyWindow" textSize="13" checked="true" marginRight="-2" marginTop="3" />
                    </horizontal>
                    <horizontal padding="10 4">
                      <vertical layout_weight="1">
                        <text text="前台服务" textColor="#222222" textSize="13" textStyle="bold" />
                        <text text="保证脚本不被杀掉(前台服务)" textColor="#222222" textSize="10" marginTop="2" />
                      </vertical>
                      <Switch id="foregroundService" textSize="13" checked="true" marginRight="-2" marginTop="3" />
                    </horizontal>
                    <horizontal padding="10 4">
                      <vertical id="VideoTutorials" layout_weight="1">
                        <text text="使用说明" textColor="#222222" textSize="13" textStyle="bold" />
                        <text text="看第一代api的视频教程进行修改" textColor="#222222" textSize="10" marginTop="2" />
                      </vertical>
                      <img
                        w="23"
                        h="23"
                        layout_gravity="right|center"
                        src="@drawable/ic_keyboard_arrow_right_black_48dp"
                        tint="#222222"
                        marginLeft="-4"
                      />
                    </horizontal>
                  </vertical>
                </card>
              </card>
            </vertical>
          </ScrollView>
        </vertical>
      </frame>
      <frame>
        <vertical>
          <ScrollView h="auto" layout_weight="25">
            <vertical margin="5">
              <horizontal h="auto" w="*">
                <text text="单行文本输入框:" textColor="black" textSize="15sp" gravity="center" w="auto" />
                <input id="单行文本输入框" gravity="left" textSize="15sp" layout_weight="1" lines="1" hint="请输入单行文本……" />
              </horizontal>
              <vertical h="auto" w="*">
                <text text="多行文本输入框:" textColor="black" textSize="15sp" gravity="center" w="auto" />

                <input
                  id="多行文本输入框"
                  w="*"
                  h="80"
                  maxLines="5"
                  hint="请输入文本……"
                  background="#ffffff"
                  textColor="#1c1c1c"
                  textColorHint="#afb0ad"
                  padding="5 0 5 0"
                  gravity="left "
                  margin="2"
                  textSize="15sp"
                  layout_weight="1"
                />
              </vertical>
              <horizontal h="auto" w="*">
                <text text="数字输入框:" textColor="black" textSize="15sp" gravity="center" w="auto" />
                <input
                  id="数字输入框"
                  inputType="number"
                  text="600"
                  gravity="left"
                  textSize="15sp"
                  hint="请输入数字……"
                  layout_weight="1"
                />
              </horizontal>
              <horizontal h="auto" w="*">
                <text text="密码输入框:" textColor="black" textSize="15sp" gravity="center" w="auto" />
                <input id="密码输入框" gravity="left" textSize="15sp" layout_weight="1" hint="请输入密码……" password="true" />
              </horizontal>
              <horizontal>
                <text text="下拉菜单组件:" textColor="black" textSize="15sp" gravity="center" w="auto" />
                <spinner
                  id="下拉菜单"
                  entries="下拉菜单选项1|下拉菜单选项2|下拉菜单选项3"
                  layout_weight="1"
                  spinnerMode="dialog"
                />
              </horizontal>

              <radiogroup id="单选框组合">
                <radio id="单选框1" text="单选框1" />
                <radio id="单选框2" text="单选框2" />
                <radio id="单选框3" text="单选框3" />
              </radiogroup>
              <horizontal h="auto">
                <text text="复选框组件:" textColor="black" textSize="15sp" marginTop="2" w="auto" />
                <checkbox id="复选框1" text="复选框1" textSize="15sp" checked="true" />
                <checkbox id="复选框2" text="复选框2" textSize="15sp" checked="false" />
                <checkbox id="复选框3" text="复选框3" textSize="15sp" checked="true" />
              </horizontal>
              <horizontal h="*">
                <text text="进度条组件:" textColor="black" textSize="15sp" marginTop="2" w="auto" />
                <seekbar id="进度条" max="200" min="10" w="*" />
              </horizontal>

              <card
                w="*"
                h="auto"
                marginTop="10"
                padding="5"
                cardElevation="0"
                cardCornerRadius="10"
                cardBackgroundColor="#e9e9e9"
              >
                <vertical h="auto" gravity="center" marginTop="1" padding="10">
                  <text text="这只是一个常用UI组件模板" textColor="red" textSize="12sp" gravity="center" />
                  <text
                    text="包含组件的点击事件、初始化配置、保存配置、执行脚本、终止脚本、悬浮菜单控制脚本执行……"
                    textColor="blue"
                    textSize="12sp"
                    gravity="left"
                  />
                  <text
                    text="你要做的就是修改、删除、添加即可,将自己的代码封装成一个函数,放在主程序函数体里"
                    textColor="blue"
                    textSize="12sp"
                    gravity="left"
                  />
                </vertical>
              </card>
              <horizontal h="30"></horizontal>
            </vertical>
          </ScrollView>
          <horizontal w="*" h="auto" layout_gravity="bottom">
            <button id="start" text="开始" layout_weight="1" style="Widget.AppCompat.Button.Colored" />
            <button id="save" text="保存配置" layout_weight="1" style="Widget.AppCompat.Button.Colored" />
            <button id="stop" text="停止" layout_weight="1" style="Widget.AppCompat.Button.Colored" />
          </horizontal>
        </vertical>
      </frame>
      <frame>
        <globalconsole id="globalconsole" w="*" h="*" />
        <fab
          id="fab"
          src="@drawable/ic_close_black_48dp"
          w="auto"
          h="auto"
          layout_gravity="bottom|right"
          margin="16"
          tint="#ffffff"
        />
      </frame>
    </viewpager>
    <bottomnaviagtion id="navigation" bg="#ffffff" />
  </vertical>
);

ui.globalconsole.setColor("D", "#000000");
/* 设置底部导航栏的内容 */
let menuItems = [];
let menu = ui.navigation.menu;
menuItems.push(buildMenuItem(menu, "权限设置", ui.R.drawable.ic_build_black_48dp));
menuItems.push(buildMenuItem(menu, "脚本配置", ui.R.drawable.ic_view_comfy_black_48dp));
menuItems.push(buildMenuItem(menu, "运行日志", ui.R.drawable.ic_description_black_48dp));
/* 当底部按钮被选中时,切换ViewPager页面为相应位置的页面 */
ui.navigation.setOnNavigationItemSelectedListener(function (item) {
  ui.viewPager.currentItem = menuItems.indexOf(item);
  return true;
});

/* 当ViewPager页面切换时,切换底部按钮的状态 */
ui.viewPager.addOnPageChangeListener(
  new Packages.androidx.viewpager.widget.ViewPager.OnPageChangeListener({
    onPageSelected: function (position) {
      menuItems[position].setChecked(true);
    },
  })
);

/* fab点击时清空全局日志界面 */
ui.fab.on("click", () => {
  ui.globalconsole.clear();
});

/* 创建选项菜单(右上角) */
ui.emitter.on("create_options_menu", (menu) => {
  menu.add("清空日志文件");
  menu.add("官网");
  menu.add("帮助");
  menu.add("关于");
  menu.add("客服");
});
/* 监听选项菜单点击 */
ui.emitter.on("options_item_selected", (e, item) => {
  switch (item.getTitle()) {
    case "清空日志文件":
      console.clear();
      ui.globalconsole.clear();
      break;
    case "官网":
      /*跳转 浏览器打开网址 */
      app.openUrl("http://botdevhub.com");
      break;
    case "帮助":
      alert("帮助", "遇到问题联系客服");
      break;

    case "关于":
      $dialogs.setDefaultDialogType("foreground-or-overlay");
      dialogs
        .build({
          title: "关于UI",
          content: "CloudControl Pro 内测版",
          positive: "确定",
          titleColor: "#000000",
          positiveColor: "#000000",
        })
        .on("negative", function () {
          console.log("negative");
        })
        .show();
      break;

    case "客服":
      try {
        /* 此处应该先判断手机有没有安装QQ应用,有QQ应用再跳转 否则会报错*/

        var appName = "QQ";
        /* 判断手机上是否已经安装了这个应用 */
        if (app.getPackageName(appName) != null) {
          app.startActivity({
            action: "android.intent.action.VIEW",
            data: "mqq://im/chat?chat_type=wpa&version=1&src_type=web&uin=" + 本地存储.get("QQ"),
            packageName: "com.tencent.mobileqq",
          });
        } else {
          /*每安装,就弹出提示对话框 */
          alert("温馨提示", "你未安装此应用,请先下载安装:" + appName + ",然后就可以联系QQ客服了");
          console.log("请下载安装" + appName);
        }
      } catch (error) {
        console.error(error);
      }

      break;
  }
  e.consumed = true;
});
activity.setSupportActionBar(ui.toolbar);
function buildMenuItem(menu, title, icon) {
  let menuItem = menu.add(title);
  menuItem.setIcon(icon);
  return menuItem;
}
/* *************************************下面代码是权限开关按钮的点击事件监听******************************************* */
refreshState();
/* 当用户回到本界面时,resume事件会被触发 */
ui.emitter.on("resume", function () {
  refreshState();
});
/* 刷新UI权限状态 */
function refreshState() {
  ui.autoService.checked = auto.service != null;
  ui.floatyPermission.checked = floaty.checkPermission();
  ui.foregroundService.checked = $settings.isEnabled("foreground_service");
  ui.stableMode.checked = $settings.isEnabled("stable_mode");
  try {
    let pm = context.getSystemService(context.POWER_SERVICE);
    ui.battery.checked = pm.isIgnoringBatteryOptimizations(context.getPackageName());
  } catch (e) {
    toastLog("当前系统无法读取是否忽略省电策略,请在系统设置中设置即可");
    ui.battery.checked = false;
  }
}
ui.autoService.on("check", function (checked) {
  setAutoService(checked);
});
ui.floatyPermission.on("check", (isChecked) => {
  if (isChecked && !new android.provider.Settings().canDrawOverlays(context)) {
    importClass(android.content.Intent);
    importClass(android.net.Uri);

    var intent = new Intent(
      android.provider.Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
      Uri.parse("package:" + context.getPackageName())
    );
    app.startActivity(intent);
  }
  if (isChecked && new android.provider.Settings().canDrawOverlays(context)) {
    // toastLog("请手动关闭");
  }
});
ui.battery.on("check", function (checked) {
  if (checked) {
    /* 跳转到电池优化设置界面 */
    let intent = new Intent();
    intent.setAction("android.settings.IGNORE_BATTERY_OPTIMIZATION_SETTINGS");
    intent.putExtra("extra_pkgname", context.getPackageName());
    app.startActivity(intent);
  }
});
ui.stableMode.on("check", function (checked) {
  if (checked) {
    /*这个设置后需要重启下无障碍 */
    $settings.setEnabled("stable_mode", checked);
    toastLog("需重新打开无障碍");
    ui.autoService.setChecked(false);
  }
});
ui.screenCapturePermission.on("check", function (checked) {
  threads.start(function () {
    if (checked) {
      console.show();
    } else {
      console.hide();
    }
  });
});
ui.floatyWindow.on("check", function (checked) {
  本地存储.put("floatyWindow", checked);
  if (checked) {
    if (window) {
      window.close();
    }
    threads.start(function () {
      ShowFrame();
    });
  } else if (window) {
    window.close();
    window = null;
  }
});
ui.foregroundService.on("check", function (checked) {
  $settings.setEnabled("foreground_service", checked);
});
ui.VideoTutorials.on("click", () => {
  /* 对话框 随意修改颜色 内容 按钮事件 */
  dialogs
    .build({
      title: "Bot.js有全套的视频教程🎈🎈✨🎉",
      titleColor: "#b2f400",
      content: "教程不断更新\n" + "🏃🏃🏄🏊💃🚶🏀🏈🎾🏁 \n" + "Bot.js App 永久免费使用!\n遇到问题联系客服",
      contentColor: "#12f8bf",
      cancelable: true,
      positive: "确认",
      positiveColor: "#2a7fff",
      neutral: "取消",
      neutralColor: "#d1d1d1",
      negative: "其他",
      negativeColor: "#2bff44",
    })
    .on("positive", () => {
      toast("确认的事件处理");
      console.log("打开 Bot.js 官网");
      app.openUrl("http://jsdevhub.com");
    })
    .on("negative", () => {
      toastLog("其他的事件处理");
    })
    .on("neutral", () => {
      toastLog("取消的事件处理");
    })
    .show();
});

/*在UI线程不能进行阻塞操作,涉及到阻塞的操作务必在子线程中进行  */
threads.start(function () {
  if (本地存储.get("floatyWindow")) {
    ShowFrame();
  }
  /*   开启子线程判断是否有悬浮窗权限,没有的话就跳转并终止脚本  */
  if (!$floaty.checkPermission()) {
    toastLog("本软件需要悬浮窗权限来显示悬浮窗,请在随后的界面中允许并重新运行本软件。");
    $floaty.requestPermission();
    exit();
  }
  /* 网络操作也需要在子线程中进行,比如初始化配置时,有的配置数据需要从服务器获取数据,然后更新到UI上 */
});

/* *************************************在UI线程中更新配置,每次运行脚本时将之前保存的配置更新到UI界面中******************************************* */
ui.run(() => {
  /* 通过组件的ID修改值的几种方式 */
  ui.floatyWindow.checked = 本地存储.get("floatyWindow");
  ui.屏幕宽度.attr("text", device.width);
  ui.屏幕高度.attr("text", device.height);
  ui.当前版本号.attr("text", app.versionCode);
  ui.单行文本输入框.attr("text", 本地存储.get("单行文本输入框"));
  ui.多行文本输入框.setText(本地存储.get("多行文本输入框"));
  ui.数字输入框.setText(String(本地存储.get("数字输入框"))); /* 如果是数字类型 需要转换为字符串类型 */
  ui.密码输入框.setText(本地存储.get("密码输入框"));
  ui.下拉菜单.setSelection(本地存储.get("下拉菜单"));
  ui.单选框1.checked = 本地存储.get("单选框1");
  ui.单选框2.checked = 本地存储.get("单选框2");
  ui.单选框3.checked = 本地存储.get("单选框3");
  ui.复选框1.checked = 本地存储.get("复选框1");
  ui.复选框2.checked = 本地存储.get("复选框2");
  ui.复选框3.attr("checked", 本地存储.get("复选框3"));
  ui.进度条.setProgress(本地存储.get("进度条"));
});
/* *************************************下面代码是各种组件的事件监听******************************************* */
/* 下拉菜单组件的监听事件 */
ui.下拉菜单.setOnItemSelectedListener(
  new AdapterView.OnItemSelectedListener({
    onItemSelected: function (parent, view, pos, id) {
      console.log("你选择了:" + view.attr("text"));
    },
    onNothingSelected: function (parent) {
      console.log("detached" + parent);
    },
  })
);
/* 单选框组件的监听事件 */
ui.单选框组合.setOnCheckedChangeListener((group, checkedId) => {
  // 根据整数id获取勾选的radio控件
  let checkedRadio = ui.单选框组合.findViewById(checkedId);
  switch (checkedRadio) {
    case ui["单选框1"]:
      toastLog("单选框1被勾选");
      break;
    case ui["单选框2"]:
      toastLog("单选框2被勾选");
      break;
    case ui["单选框3"]:
      toastLog("单选框3被勾选");
      break;
    default:
      toastLog("没有任何单选框被勾选");
      break;
  }
});
/* 监听单个单选组件的选中 */
ui.单选框1.on("check", (checked) => {
  if (checked) {
    toastLog("单选框1被勾选了");
  } else {
    toastLog("单选框1被取消勾选了");
  }
});
ui.单选框2.on("check", (checked) => {
  if (checked) {
    toastLog("单选框2被勾选了");
  } else {
    toastLog("单选框2被取消勾选了");
  }
});
ui.单选框3.on("check", (checked) => {
  if (checked) {
    toastLog("单选框3被勾选了");
  } else {
    toastLog("单选框3被取消勾选了");
  }
});
/* 复选框组件的监听事件 */
ui.复选框1.on("check", (checked) => {
  if (checked) {
    toast("复选框1被勾选了");
  } else {
    toast("复选框1被取消勾选了");
  }
});
ui.复选框2.on("check", (checked) => {
  if (checked) {
    toast("复选框2被勾选了");
  } else {
    toast("复选框2被取消勾选了");
  }
});
ui.复选框3.on("check", (checked) => {
  if (checked) {
    toast("复选框3被勾选了");
  } else {
    toast("复选框3被取消勾选了");
  }
});
/* 进度条组件的监听事件 */
ui.进度条.setOnSeekBarChangeListener({
  onProgressChanged: (seekbar, progress, fromUser) => {
    console.log("进度: ", progress);
  },
});

/* 开始按钮的点击事件 */
ui.start.on("click", (c) => {
  start();
});
/* 保存按钮的点击事件 */
ui.save.on("click", (c) => {
  var res = 配置效验并保存();
  if (res) {
    toastLog("配置保存成功");
  } else {
    toastLog("配置保存失败");
  }
});
/* 停止按钮的点击事件 */
ui.stop.on("click", (c) => {
  stop();
});
function 配置效验并保存() {
  try {
    /* 通过组件的ID获取值 并进行效验 是否符合开发者的要求*/
    if (ui.单行文本输入框.text().length === 0) {
      toastLog("单行文本输入框必须要输入内容");
      return false;
    }
    if (ui.数字输入框.text().length === 0) {
      toastLog("数字输入框必须要输入数字");
      return false;
    }
    /* 获取单选框组合勾选的单选框ID */
    let checkedId = ui.单选框组合.getCheckedRadioButtonId();
    if (checkedId === -1) {
      toastLog("没有任何单选框被勾选,必须勾选一个");
      return false;
    } else {
      /*根据id获取勾选的单选框 */
      let checkedRadio = ui.单选框组合.findViewById(checkedId);
      /*获取勾选的位置 */
      let checkedIndex = ui.单选框组合.indexOfChild(checkedRadio);
      toastLog("当前勾选的单选框的文本: " + checkedRadio.getText().toString() + ", 位置: " + checkedIndex);
    }

    /* 通过组件的ID获取值的几种方式 并将值保存到本地存储*/
    本地存储.put("单行文本输入框", ui.单行文本输入框.text());
    本地存储.put("多行文本输入框", ui.多行文本输入框.text());
    var 数字输入框 = parseInt(ui.数字输入框.text()); /* text()的到的是字符串,需要将字符串转换为整数 */
    if (!isNaN(数字输入框)) {
      本地存储.put("数字输入框", 数字输入框); /* 保存数字类型  */
    } else {
      console.log("非数字字符串,不保存");
      return false;
    }
    本地存储.put("密码输入框", ui.密码输入框.attr("text"));
    本地存储.put("下拉菜单", ui.下拉菜单.getSelectedItemPosition());
    本地存储.put("单选框1", ui.单选框1.checked);
    本地存储.put("单选框2", ui.单选框2.checked);
    本地存储.put("单选框3", ui.单选框3.checked);
    本地存储.put("复选框1", ui.复选框1.checked);
    本地存储.put("复选框2", ui.复选框2.checked);
    本地存储.put("复选框3", ui.复选框3.checked);
    本地存储.put("进度条", ui.进度条.getProgress());
    return true;
  } catch (error) {
    /* 处理异常的代码块 */
    console.error(error);
    return null;
  }
}
function start() {
  var res = 配置效验并保存();
  if (!res) {
    console.log("配置效验失败,不符合要求");
    return false;
  }

  toastLog("如果你的程序以无障碍模式实现自动化 ,请需要在程序开始运行之前判断无障碍服务");
  if (auto.service == null) {
    toastLog("请先开启无障碍服务!");
    app.startActivity({
      action: "android.settings.ACCESSIBILITY_SETTINGS",
    });
    return;
  }
  /*  判断是否已经运行,如果已经运行了就先结束后再开始运行 */
  if (isRun) {
    console.log("已经运行,先结束后再开始运行");
    isRun.interrupt();
    console.hide();
    threads.shutDownAll();
  }

  /*在UI线程不能进行阻塞操作,涉及到阻塞的操作务必在子线程中进行 所以需要开启一个子线程运行 */
  isRun = threads.start(function () {
    /* 在子线程里启动主程序 */
    主程序();
  });
}
function stop() {
  if (isRun) {
    toastLog("停止运行");
    isRun.interrupt();
    console.hide();
    threads.shutDownAll();
    isRun = null;
  } else {
    toastLog("未运行,无需终止");
  }
}

/*注册broadcast广播 接收悬浮窗发来的广播 */
events.broadcast.on("FloatingWindow", function (type) {
  if (type == "开始运行") {
    start();
  } else {
    stop();
  }
});

/* *************************************主程序函数 将你的代码入口放入主程序内即可******************************************* */
function 主程序() {
  toastLog("如果你的脚本需要截图和文字识别需要申请截图权限");
  申请截图权限();
  sleep(1000);
  toastLog("你好");
  sleep(1000);
  toastLog("请把你的脚本代码放在此处");
  sleep(1000);
  toastLog("我运行完了");
}

/* *************************************最简单的一个悬浮窗按钮来控制脚本运行和结束******************************************* */
function ShowFrame() {
  window = floaty.window(
    <frame>
      <com.google.android.material.floatingactionbutton.FloatingActionButton
        layout_width="wrap_content"
        backgroundTint="#000000"
        tint="#FFFFFFFF"
        id="action"
        layout_height="wrap_content"
        src="@drawable/ic_play_arrow_black_48dp"
      />
      <img id="me" w="50" h="50" padding="16" src="@drawable/ic_play_arrow_black_48dp" tint="#FFFFFFFF" />
    </frame>
  );

  /* 设置悬浮窗位置*/
  window.setPosition(本地存储.get("windowX"), 本地存储.get("windowY"));
  /* 记录按键被按下时的触摸坐标 */
  var x = 0,
    y = 0;
  /* 记录按键被按下时的悬浮窗位置 */
  var windowX, windowY;
  /* 记录按键被按下的时间以便判断长按等动作 */
  var downTime;

  window.action.setAlpha(0.5);
  window.action.setOnTouchListener(function (view, event) {
    switch (event.getAction()) {
      case event.ACTION_DOWN:
        x = event.getRawX();
        y = event.getRawY();
        windowX = window.getX();
        windowY = window.getY();
        downTime = new Date().getTime();
        return true;
      case event.ACTION_MOVE:
        //移动手指时调整悬浮窗位置
        var positionX = windowX + (event.getRawX() - x);
        var positionY = windowY + (event.getRawY() - y);
        本地存储.put("windowX", positionX);
        本地存储.put("windowY", positionY);
        window.setPosition(positionX, positionY);
        /* 如果按下的时间超过1.5秒判断为长按,退出脚本 */
        if (new Date().getTime() - downTime > 1500) {
          exit();
        }
        return true;
      case event.ACTION_UP:
        //手指弹起时如果偏移很小则判断为点击
        if (Math.abs(event.getRawY() - y) < 5 && Math.abs(event.getRawX() - x) < 5) {
          onClick();
        }
        return true;
    }
    return true;
  });
}
function onClick() {
  let getIcon = window.action.attr("src");
  let itemColor = [
    ["#000000", "#FFFFFFFF"],
    ["#FF6666", "#FFFFFFFF"],
  ];
  let itemIcon = ["@drawable/ic_play_arrow_black_48dp", "@drawable/ic_stop_black_48dp"];
  let setIcon = itemIcon[getIcon == itemIcon[0] ? 1 : 0];
  window.action.attr("src", setIcon);
  window.action.attr("backgroundTint", itemColor[getIcon == itemIcon[0] ? 1 : 0][0]);
  window.action.attr("tint", itemColor[getIcon == itemIcon[0] ? 1 : 0][1]);

  if (getIcon == itemIcon[0] ? 1 : 0) {
    /* 发送一个广播 */
    events.broadcast.emit("FloatingWindow", "开始运行");
  } else {
    /* 发送一个广播 */
    events.broadcast.emit("FloatingWindow", "停止运行");
  }
}
/* *************************************下面代码无需修改******************************************* */
/*注意: 一个app里同时只能有一个js脚本文件使用截图权限*/
function 申请截图权限() {
  images.stopScreenCapture();
  if (!requestScreenCapture()) {
    alert("请求截图失败,请重新开始");
    console.hide();
    threads.shutDownAll();
    exit();
  } else {
    toastLog("请求截屏成功");
  }
}

function setAutoService(checked) {
  if (checked) {
    if (checkPermission("android.permission.WRITE_SECURE_SETTINGS")) {
      openAccessibility();
    } else {
      if ($shell.checkAccess("adb")) {
        shell("pm grant " + context.getPackageName() + " android.permission.WRITE_SECURE_SETTINGS", {
          adb: true,
        });
        toastLog("adb授权成功");
        openAccessibility();
      } else {
        if ($shell.checkAccess("root")) {
          shell("pm grant " + context.getPackageName() + " android.permission.WRITE_SECURE_SETTINGS", {
            root: true,
          });
          toastLog("root授权成功");
          openAccessibility();
        } else {
          console.info("\n也可使用WRITE_SECURE_SETTINGS权限开启无障碍服务\n授权代码已复制,使用adb激活");
          setClip("adb shell pm grant " + context.getPackageName() + " android.permission.WRITE_SECURE_SETTINGS");
          /* 用户勾选无障碍服务的选项时,跳转到页面让用户去开启 */
          app.startActivity({
            action: "android.settings.ACCESSIBILITY_SETTINGS",
          });
        }
      }
    }
  } else if (auto.service != null) {
    auto.service.disableSelf();
  }
}
function openAccessibility() {
  let mServices = ":" + context.getPackageName() + "/com.template.cn.AccessibilityService";
  let enabledServices = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
  enabledServices = enabledServices ? enabledServices.replace(new RegExp(mServices, "g"), "") : "";
  Settings.Secure.putString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "");
  Settings.Secure.putString(
    context.getContentResolver(),
    Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
    enabledServices + mServices
  );
}

function checkPermission(permission) {
  pm = context.getPackageManager();
  return PackageManager.PERMISSION_GRANTED == pm.checkPermission(permission, context.getPackageName().toString());
}