require "import"
import "android.app.*"
import "android.os.*"
import "android.widget.*"
import "android.view.*"
import "androidx.appcompat.widget.LinearLayoutCompat"
import "androidx.recyclerview.widget.RecyclerView"
import "androidx.recyclerview.widget.LinearLayoutManager"

local FastScrollerBuilder = luajava.bindClass("me.zhanghai.android.fastscroll.FastScrollerBuilder")
local PopupRecyclerAdapter = luajava.bindClass("github.znzsofficial.adapter.PopupRecyclerAdapter")
local LuaCustRecyclerHolder = luajava.bindClass("github.znzsofficial.adapter.LuaCustRecyclerHolder")

MDC_R = luajava.bindClass "com.google.android.material.R"

activity
.setTheme(MDC_R.style.Theme_Material3_DynamicColors_DayNight)
.setTitle("FastScroller使用示例")

local data = {}
for i = 1, 60 do
  table.insert(data, "第 " .. i .. " 项")
end

-- 直接在代码里创建布局
local root = LinearLayoutCompat(activity)
root.setOrientation(LinearLayoutCompat.VERTICAL)
root.setLayoutParams(LinearLayoutCompat.LayoutParams(-1, -1))

local recyclerView = RecyclerView(activity)
recyclerView.setLayoutParams(LinearLayoutCompat.LayoutParams(-1, -1))
recyclerView.setId(android.R.id.list)  -- 随便给个ID
root.addView(recyclerView)

activity.setContentView(root)

local adapter = PopupRecyclerAdapter(activity, PopupRecyclerAdapter.PopupCreator({
  getItemCount = function()
    return #data
  end,
  getItemViewType = function()
    return 0
  end,
  getPopupText = function(view, position)
    return tostring(data[position + 1])
  end,
  onCreateViewHolder = function(parent, viewType)
    local views = {}
    local holder = LuaCustRecyclerHolder(loadlayout({
      LinearLayout;
      layout_width="fill";
      layout_height="wrap_content";
      padding="16dp";
      {
        TextView;
        id="textView";
        textSize="16sp";
        layout_width="fill";
      };
    }, views))
    holder.Tag = views
    return holder
  end,
  onBindViewHolder = function(holder, position)
    local views = holder.Tag
    views.textView.setText(data[position + 1])
  end
}))

recyclerView.setAdapter(adapter)
recyclerView.setLayoutManager(LinearLayoutManager(activity))

FastScrollerBuilder(recyclerView)
.useMd2Style()
.disableScrollbarAutoHide()
.build()



--[[
  ============================================================
  灵动岛悬浮窗 - AndroLua+ 完整版
  ============================================================
]]

require "import"
import "android.app.*"
import "android.os.*"
import "android.widget.*"
import "android.view.*"
import "android.content.*"
import "android.graphics.*"
import "android.graphics.drawable.*"
import "android.provider.Settings"
import "android.net.Uri"
import "java.text.SimpleDateFormat"
import "java.util.Date"
import "java.util.Locale"
import "android.animation.ValueAnimator"
import "android.animation.Animator"
import "android.animation.ObjectAnimator"
import "android.animation.AnimatorSet"


-- ============================================================
-- 全局配置
-- ============================================================
local PREFS_NAME = "modern_float_prefs"
local VALID_CODE = "xxxnb"

local KEY_ISLAND_MODE = "island_mode"
local KEY_SNAP_EDGE = "snap_edge"
local KEY_AUTO_COLLAPSE = "auto_collapse"
local KEY_THEME_COLOR = "theme_color"
local KEY_ALPHA = "float_alpha"
local KEY_LAST_X = "last_x"
local KEY_LAST_Y = "last_y"
local KEY_LAUNCH_COUNT = "launch_count"
local KEY_VOLUME_KEY = "volume_key"

local THEME_INDIGO = 0
local THEME_CYAN = 1
local THEME_PURPLE = 2

local ACTION_PREFS_CHANGED = "com.nbnb.nbnb.PREFS_CHANGED"

local THEME_COLORS = {
  {0xFFB9C3FF, 0xFF424A8E, 0xFFDEE0FF},
  {0xFF86D1E0, 0xFF004E5A, 0xFFA2EDFB},
  {0xFFD0BCFF, 0xFF4F378B, 0xFFEADDFF},
}

-- 悬浮窗全局变量
local floatWindow = nil
local windowManager = nil
local floatParams = nil
local floatView = nil
local isExpanded = true
local currentPage = 0
local animating = false
local timeHandler = nil

-- ============================================================
-- 工具函数
-- ============================================================
local function getSp(ctx)
  return ctx.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
end

local function getBoolean(ctx, key, def)
  return getSp(ctx).getBoolean(key, def)
end

local function putBoolean(ctx, key, value)
  getSp(ctx).edit().putBoolean(key, value).apply()
end

local function getInt(ctx, key, def)
  return getSp(ctx).getInt(key, def)
end

local function putInt(ctx, key, value)
  getSp(ctx).edit().putInt(key, value).apply()
end

local function dp(v)
  return math.floor(v * activity.getResources().getDisplayMetrics().density + 0.5)
end

local function getPrimary(ctx)
  local t = getInt(ctx or activity, KEY_THEME_COLOR, THEME_INDIGO)
  t = math.max(0, math.min(2, t))
  return THEME_COLORS[t + 1][1]
end

local function getContainer(ctx)
  local t = getInt(ctx or activity, KEY_THEME_COLOR, THEME_INDIGO)
  t = math.max(0, math.min(2, t))
  return THEME_COLORS[t + 1][2]
end

-- 创建圆角矩形Drawable
local function roundRect(color, radius)
  local d = GradientDrawable()
  d.setShape(GradientDrawable.RECTANGLE)
  d.setColor(color)
  d.setCornerRadius(radius)
  return d
end

-- 创建圆形Drawable
local function oval(color)
  local d = GradientDrawable()
  d.setShape(GradientDrawable.OVAL)
  d.setColor(color)
  return d
end

-- ============================================================
-- 动画工具
-- ============================================================
local function shakeRotate(view)
  local shake = ObjectAnimator.ofFloat(view, "translationX", {0, -15, 15, -12, 12, -8, 8, -4, 4, 0})
  local rotate = ObjectAnimator.ofFloat(view, "rotation", {0, -3, 3, -2, 2, -1, 1, 0})
  local set = AnimatorSet()
  set.playTogether({shake, rotate})
  set.setDuration(500)
  set.start()
end

-- ============================================================
-- 页面1: 启动页 / 卡密验证
-- ============================================================
local function showLoading()
  activity.setContentView(loadlayout {
    LinearLayout,
    layout_width = "fill",
    layout_height = "fill",
    orientation = "vertical",
    backgroundColor = 0xFF131318,
    gravity = "center",

    {
      View,
      layout_width = "fill",
      layout_height = "320dp",
      background = (function()
        local d = GradientDrawable()
        d.setShape(GradientDrawable.RECTANGLE)
        d.setGradientType(GradientDrawable.RADIAL_GRADIENT)
        d.setGradientRadius(dp(400))
        d.setColors({0xFF1A1A2E, 0xFF131318})
        return d
      end)(),
    },

    {
      CardView,
      id = "card",
      layout_width = "fill",
      layout_height = "wrap",
      layout_margin = "24dp",
      layout_marginTop = "-160dp",
      cardBackgroundColor = 0xFF1F1F25,
      radius = "28dp",
      elevation = "4dp",

      {
        LinearLayout,
        orientation = "vertical",
        padding = "24dp",
        gravity = "center",

        {
          TextView,
          text = "灵动岛",
          textSize = "36sp",
          textColor = 0xFFE4E1E9,
          textStyle = "bold",
        },
        {
          TextView,
          text = "悬浮窗控制中心",
          textSize = "14sp",
          textColor = 0xFFC7C5D0,
          layout_marginTop = "8dp",
          layout_marginBottom = "24dp",
        },
        {
          EditText,
          id = "etCode",
          layout_width = "fill",
          layout_height = "48dp",
          background = roundRect(0xFF2A2A30, dp(16)),
          textColor = 0xFFE4E1E9,
          hint = "请输入卡密",
          hintTextColor = 0xFF918F9A,
          paddingLeft = "16dp",
          paddingRight = "16dp",
        },
        {
          Button,
          id = "btnVerify",
          layout_width = "fill",
          layout_height = "56dp",
          layout_marginTop = "16dp",
          background = roundRect(getContainer(), dp(20)),
          text = "验证并进入",
          textColor = 0xFFDEE0FF,
          textSize = "16sp",
          textStyle = "bold",
        },
        {
          ProgressBar,
          id = "progressBar",
          layout_width = "fill",
          layout_height = "4dp",
          style = "?android:attr/progressBarStyleHorizontal",
          visibility = View.GONE,
          layout_marginTop = "16dp",
          progressDrawable = (function()
            local d = GradientDrawable()
            d.setShape(GradientDrawable.RECTANGLE)
            d.setColor(getPrimary())
            d.setCornerRadius(dp(2))
            return d
          end)(),
        },
        {
          CardView,
          id = "snackbar",
          layout_width = "fill",
          layout_height = "wrap",
          visibility = View.GONE,
          layout_marginTop = "16dp",
          cardBackgroundColor = 0xFF2A2A30,
          radius = "12dp",

          {
            LinearLayout,
            orientation = "horizontal",
            gravity = "center_vertical",
            padding = "12dp",

            {
              View,
              id = "snackIndicator",
              layout_width = "8dp",
              layout_height = "8dp",
              background = oval(getPrimary()),
              layout_marginRight = "8dp",
            },
            {
              TextView,
              id = "snackText",
              textSize = "14sp",
              textColor = 0xFFE4E1E9,
            },
          },
        },
      },
    },
  })

  local function showMsg(msg, isError)
    snackText.setText(msg)
    snackIndicator.setBackground(oval(isError and 0xFFCF6679 or 0xFF4CAF50))
    snackbar.setVisibility(View.VISIBLE)
    snackbar.setAlpha(0)
    snackbar.setTranslationY(dp(20))
    snackbar.animate()
      .alpha(1)
      .translationY(0)
      .setDuration(300)
      .start()

    task(3000, function()
      snackbar.animate()
        .alpha(0)
        .setDuration(200)
        .withEndAction(function()
          snackbar.setVisibility(View.GONE)
        end)
        .start()
    end)
  end

  local function verifyCode()
    local code = tostring(etCode.getText() or "")
    if code == "" then
      showMsg("请输入卡密", true)
      shakeRotate(etCode)
      return
    end

    if code == VALID_CODE then
      showMsg("验证成功，正在进入...", false)
      progressBar.setVisibility(View.VISIBLE)
      progressBar.setProgress(0)

          local animator = ValueAnimator.ofInt({0, 100})
      animator.setDuration(1500)
      animator.addUpdateListener(ValueAnimator.AnimatorUpdateListener{
        onAnimationUpdate = function(a)
          progressBar.setProgress(a.getAnimatedValue())
        end
      })
      animator.addListener(Animator.AnimatorListener{
        onAnimationEnd = function()
          showMain()
        end,
        onAnimationStart = function() end,
        onAnimationCancel = function() end,
        onAnimationRepeat = function() end
      })
      animator.start()
    else
      showMsg("卡密错误，请重试", true)
      shakeRotate(etCode)
      etCode.setText("")
    end
  end

  btnVerify.onClick = function()
    local imm = activity.getSystemService(Context.INPUT_METHOD_SERVICE)
    imm.hideSoftInputFromWindow(etCode.getWindowToken(), 0)
    verifyCode()
  end

  -- 入场动画 (使用全局变量，因为loadlayout会创建全局id)
  -- card, etCode, btnVerify 等已由 loadlayout 自动创建为全局变量
  if card then
    card.animate()
      .alpha(0)
      .translationY(dp(60))
      .setDuration(0)
      .withEndAction(function()
        card.animate()
          .alpha(1)
          .translationY(0)
          .setDuration(600)
          .setStartDelay(200)
          .start()
      end)
      .start()
  end
end

-- ============================================================
-- 页面2: 主界面 / 控制中心
-- ============================================================
function showMain()
  activity.setContentView(loadlayout {
    LinearLayout,
    layout_width = "fill",
    layout_height = "fill",
    orientation = "vertical",
    backgroundColor = 0xFF131318,

    {
      View,
      layout_width = "fill",
      layout_height = "320dp",
      background = (function()
        local d = GradientDrawable()
        d.setShape(GradientDrawable.RECTANGLE)
        d.setGradientType(GradientDrawable.RADIAL_GRADIENT)
        d.setGradientRadius(dp(400))
        d.setColors({0xFF1A1A2E, 0xFF131318})
        return d
      end)(),
    },

    {
      LinearLayout,
      layout_width = "fill",
      layout_height = "wrap",
      orientation = "horizontal",
      gravity = "center_vertical",
      paddingLeft = "24dp",
      paddingRight = "16dp",
      paddingTop = "24dp",
      paddingBottom = "8dp",
      layout_marginTop = "-280dp",

      {
        LinearLayout,
        layout_width = 0,
        layout_height = "wrap",
        layout_weight = 1,
        orientation = "vertical",

        {
          TextView,
          text = "灵动岛",
          textSize = "36sp",
          textColor = 0xFFE4E1E9,
          textStyle = "bold",
        },
        {
          TextView,
          text = "悬浮窗控制中心",
          textSize = "14sp",
          textColor = 0xFFC7C5D0,
          layout_marginTop = "8dp",
        },
      },

      {
        ImageButton,
        id = "btnSettings",
        layout_width = "48dp",
        layout_height = "48dp",
        background = roundRect(0xFF2A2A30, dp(12)),
        src = "res/drawable/ic_settings.xml",
        padding = "12dp",
      },
    },

    {
      CardView,
      id = "statusCard",
      layout_width = "fill",
      layout_height = "wrap",
      layout_margin = "24dp",
      layout_marginTop = "16dp",
      cardBackgroundColor = 0xFF1F1F25,
      radius = "28dp",
      elevation = "2dp",

      {
        LinearLayout,
        orientation = "vertical",
        padding = "20dp",

        {
          LinearLayout,
          orientation = "horizontal",
          gravity = "center_vertical",

          {
            View,
            id = "statusDot",
            layout_width = "8dp",
            layout_height = "8dp",
            background = oval(0xFF918F9A),
            layout_marginRight = "8dp",
          },
          {
            TextView,
            id = "tvStatus",
            text = "服务未运行",
            textSize = "14sp",
            textColor = 0xFFC7C5D0,
          },
        },
      },
    },

    {
      CardView,
      layout_width = "fill",
      layout_height = "wrap",
      layout_marginLeft = "24dp",
      layout_marginRight = "24dp",
      layout_marginTop = "16dp",
      cardBackgroundColor = 0xFF1F1F25,
      radius = "20dp",

      {
        LinearLayout,
        orientation = "horizontal",
        padding = "16dp",

        {
          LinearLayout,
          layout_width = 0,
          layout_height = "wrap",
          layout_weight = 1,
          orientation = "vertical",
          gravity = "center",

          {
            TextView,
            id = "tvLaunchCount",
            text = "0",
            textSize = "24sp",
            textColor = 0xFFE4E1E9,
            textStyle = "bold",
          },
          {
            TextView,
            text = "累计启动",
            textSize = "12sp",
            textColor = 0xFF918F9A,
          },
        },

        {
          View,
          layout_width = "1dp",
          layout_height = "40dp",
          backgroundColor = 0xFF35343A,
        },

        {
          LinearLayout,
          layout_width = 0,
          layout_height = "wrap",
          layout_weight = 1,
          orientation = "vertical",
          gravity = "center",

          {
            TextView,
            id = "tvMode",
            text = "Island",
            textSize = "24sp",
            textColor = 0xFFE4E1E9,
            textStyle = "bold",
          },
          {
            TextView,
            text = "当前模式",
            textSize = "12sp",
            textColor = 0xFF918F9A,
          },
        },
      },
    },

    {
      LinearLayout,
      layout_width = "fill",
      layout_height = "wrap",
      orientation = "horizontal",
      padding = "24dp",

      {
        Button,
        id = "btnFloat",
        layout_width = 0,
        layout_height = "56dp",
        layout_weight = 1,
        layout_marginRight = "8dp",
        background = roundRect(getContainer(), dp(20)),
        text = "开启悬浮窗",
        textColor = 0xFFDEE0FF,
        textSize = "16sp",
        textStyle = "bold",
      },

      {
        Button,
        id = "btnStop",
        layout_width = 0,
        layout_height = "56dp",
        layout_weight = 1,
        layout_marginLeft = "8dp",
        background = roundRect(0xFF2A2A30, dp(20)),
        text = "停止服务",
        textColor = 0xFF918F9A,
        textSize = "16sp",
      },
    },
  })

  local function refreshStats()
    local count = getInt(activity, KEY_LAUNCH_COUNT, 0)
    tvLaunchCount.setText(tostring(count))
    local islandMode = getBoolean(activity, KEY_ISLAND_MODE, true)
    tvMode.setText(islandMode and "Island" or "Panel")
  end

  local function canDrawOverlays()
    if Build.VERSION.SDK_INT >= 23 then
      return Settings.canDrawOverlays(activity)
    end
    return true
  end

  local function updateStatus(active)
    if active then
      statusDot.setBackground(oval(0xFF4CAF50))
      tvStatus.setText("服务运行中")
      tvStatus.setTextColor(0xFF4CAF50)
    else
      statusDot.setBackground(oval(0xFF918F9A))
      tvStatus.setText("服务未运行")
      tvStatus.setTextColor(0xFFC7C5D0)
    end
  end

  btnFloat.onClick = function()
    if not canDrawOverlays() then
      local intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION)
      intent.setData(Uri.parse("package:" .. activity.getPackageName()))
      activity.startActivityForResult(intent, 1001)
      return
    end

    showFloatingWindow()

    local count = getInt(activity, KEY_LAUNCH_COUNT, 0)
    putInt(activity, KEY_LAUNCH_COUNT, count + 1)

    updateStatus(true)
    refreshStats()
  end

  btnStop.onClick = function()
    stopFloatingWindow()
    updateStatus(false)
  end

  btnSettings.onClick = function()
    showSettings()
  end

  refreshStats()
end

-- ============================================================
-- 页面3: 设置页
-- ============================================================
function showSettings()
  activity.setContentView(loadlayout {
    LinearLayout,
    layout_width = "fill",
    layout_height = "fill",
    orientation = "vertical",
    backgroundColor = 0xFF131318,

    {
      LinearLayout,
      layout_width = "fill",
      layout_height = "wrap",
      orientation = "horizontal",
      gravity = "center_vertical",
      padding = "16dp",

      {
        ImageButton,
        id = "btnBack",
        layout_width = "48dp",
        layout_height = "48dp",
        background = roundRect(0xFF2A2A30, dp(12)),
        src = "res/drawable/ic_close.xml",
        padding = "12dp",
      },

      {
        TextView,
        text = "设置",
        textSize = "20sp",
        textColor = 0xFFE4E1E9,
        textStyle = "bold",
        layout_width = 0,
        layout_weight = 1,
        gravity = "center",
      },

      {
        View,
        layout_width = "48dp",
        layout_height = "48dp",
      },
    },

    {
      ScrollView,
      layout_width = "fill",
      layout_height = "fill",

      {
        LinearLayout,
        orientation = "vertical",
        padding = "16dp",

        {
          TextView,
          text = "灵动岛模式",
          textSize = "14sp",
          textColor = 0xFF918F9A,
          layout_marginBottom = "8dp",
        },
        {
          LinearLayout,
          layout_width = "fill",
          layout_height = "wrap",
          orientation = "horizontal",
          gravity = "center_vertical",
          padding = "16dp",
          background = roundRect(0xFF1F1F25, dp(16)),
          layout_marginBottom = "8dp",

          {
            TextView,
            text = "空闲时收缩为胶囊",
            textSize = "16sp",
            textColor = 0xFFE4E1E9,
            layout_width = 0,
            layout_weight = 1,
          },
          {
            Switch,
            id = "swIsland",
            checked = getBoolean(activity, KEY_ISLAND_MODE, true),
          },
        },

        {
          LinearLayout,
          layout_width = "fill",
          layout_height = "wrap",
          orientation = "horizontal",
          gravity = "center_vertical",
          padding = "16dp",
          background = roundRect(0xFF1F1F25, dp(16)),
          layout_marginBottom = "8dp",

          {
            TextView,
            text = "边缘吸附",
            textSize = "16sp",
            textColor = 0xFFE4E1E9,
            layout_width = 0,
            layout_weight = 1,
          },
          {
            Switch,
            id = "swSnap",
            checked = getBoolean(activity, KEY_SNAP_EDGE, true),
          },
        },

        {
          LinearLayout,
          layout_width = "fill",
          layout_height = "wrap",
          orientation = "horizontal",
          gravity = "center_vertical",
          padding = "16dp",
          background = roundRect(0xFF1F1F25, dp(16)),
          layout_marginBottom = "8dp",

          {
            TextView,
            text = "5秒无操作自动收缩",
            textSize = "16sp",
            textColor = 0xFFE4E1E9,
            layout_width = 0,
            layout_weight = 1,
          },
          {
            Switch,
            id = "swCollapse",
            checked = getBoolean(activity, KEY_AUTO_COLLAPSE, false),
          },
        },

        {
          LinearLayout,
          layout_width = "fill",
          layout_height = "wrap",
          orientation = "horizontal",
          gravity = "center_vertical",
          padding = "16dp",
          background = roundRect(0xFF1F1F25, dp(16)),
          layout_marginBottom = "16dp",

          {
            TextView,
            text = "音量键控制显示/隐藏",
            textSize = "16sp",
            textColor = 0xFFE4E1E9,
            layout_width = 0,
            layout_weight = 1,
          },
          {
            Switch,
            id = "swVolume",
            checked = getBoolean(activity, KEY_VOLUME_KEY, false),
          },
        },

        {
          TextView,
          text = "主题色",
          textSize = "14sp",
          textColor = 0xFF918F9A,
          layout_marginBottom = "8dp",
        },
        {
          LinearLayout,
          layout_width = "fill",
          layout_height = "wrap",
          orientation = "horizontal",
          padding = "16dp",
          background = roundRect(0xFF1F1F25, dp(16)),
          layout_marginBottom = "16dp",

          {
            FrameLayout,
            id = "themeIndigo",
            layout_width = "48dp",
            layout_height = "48dp",
            layout_marginRight = "16dp",
            background = oval(0xFFB9C3FF),

            {
              ImageView,
              id = "checkIndigo",
              layout_width = "24dp",
              layout_height = "24dp",
              layout_gravity = "center",
              src = "res/drawable/ic_check.xml",
              visibility = View.GONE,
            },
          },

          {
            FrameLayout,
            id = "themeCyan",
            layout_width = "48dp",
            layout_height = "48dp",
            layout_marginRight = "16dp",
            background = oval(0xFF86D1E0),

            {
              ImageView,
              id = "checkCyan",
              layout_width = "24dp",
              layout_height = "24dp",
              layout_gravity = "center",
              src = "res/drawable/ic_check.xml",
              visibility = View.GONE,
            },
          },

          {
            FrameLayout,
            id = "themePurple",
            layout_width = "48dp",
            layout_height = "48dp",
            background = oval(0xFFD0BCFF),

            {
              ImageView,
              id = "checkPurple",
              layout_width = "24dp",
              layout_height = "24dp",
              layout_gravity = "center",
              src = "res/drawable/ic_check.xml",
              visibility = View.GONE,
            },
          },
        },

        {
          TextView,
          text = "悬浮窗透明度",
          textSize = "14sp",
          textColor = 0xFF918F9A,
          layout_marginBottom = "8dp",
        },
        {
          LinearLayout,
          layout_width = "fill",
          layout_height = "wrap",
          orientation = "vertical",
          padding = "16dp",
          background = roundRect(0xFF1F1F25, dp(16)),

          {
            TextView,
            id = "tvAlphaValue",
            text = "95%",
            textSize = "14sp",
            textColor = 0xFFE4E1E9,
            layout_marginBottom = "8dp",
          },
          {
            SeekBar,
            id = "sbAlpha",
            layout_width = "fill",
            layout_height = "wrap",
            max = 100,
            progress = getInt(activity, KEY_ALPHA, 95),
          },
        },
      },
    },
  })

  -- 开关监听器
  swIsland.onCheckedChangeListener = function(checked)
    putBoolean(activity, KEY_ISLAND_MODE, checked)
    local intent = Intent(ACTION_PREFS_CHANGED)
    activity.sendBroadcast(intent)
  end

  swSnap.onCheckedChangeListener = function(checked)
    putBoolean(activity, KEY_SNAP_EDGE, checked)
    local intent = Intent(ACTION_PREFS_CHANGED)
    activity.sendBroadcast(intent)
  end

  swCollapse.onCheckedChangeListener = function(checked)
    putBoolean(activity, KEY_AUTO_COLLAPSE, checked)
    local intent = Intent(ACTION_PREFS_CHANGED)
    activity.sendBroadcast(intent)
  end

  swVolume.onCheckedChangeListener = function(checked)
    putBoolean(activity, KEY_VOLUME_KEY, checked)
    local intent = Intent(ACTION_PREFS_CHANGED)
    activity.sendBroadcast(intent)
  end

  -- 透明度滑块
  sbAlpha.onProgressChanged = function(progress)
    tvAlphaValue.setText(progress .. "%")
  end

  sbAlpha.onStopTrackingTouch = function(progress)
    putInt(activity, KEY_ALPHA, progress)
    local intent = Intent(ACTION_PREFS_CHANGED)
    activity.sendBroadcast(intent)
  end

  -- 主题色选择
  local currentTheme = getInt(activity, KEY_THEME_COLOR, THEME_INDIGO)
  if currentTheme == THEME_INDIGO then
    checkIndigo.setVisibility(View.VISIBLE)
  elseif currentTheme == THEME_CYAN then
    checkCyan.setVisibility(View.VISIBLE)
  else
    checkPurple.setVisibility(View.VISIBLE)
  end

  themeIndigo.onClick = function()
    putInt(activity, KEY_THEME_COLOR, THEME_INDIGO)
    checkIndigo.setVisibility(View.VISIBLE)
    checkCyan.setVisibility(View.GONE)
    checkPurple.setVisibility(View.GONE)
    local intent = Intent(ACTION_PREFS_CHANGED)
    activity.sendBroadcast(intent)
  end

  themeCyan.onClick = function()
    putInt(activity, KEY_THEME_COLOR, THEME_CYAN)
    checkIndigo.setVisibility(View.GONE)
    checkCyan.setVisibility(View.VISIBLE)
    checkPurple.setVisibility(View.GONE)
    local intent = Intent(ACTION_PREFS_CHANGED)
    activity.sendBroadcast(intent)
  end

  themePurple.onClick = function()
    putInt(activity, KEY_THEME_COLOR, THEME_PURPLE)
    checkIndigo.setVisibility(View.GONE)
    checkCyan.setVisibility(View.GONE)
    checkPurple.setVisibility(View.VISIBLE)
    local intent = Intent(ACTION_PREFS_CHANGED)
    activity.sendBroadcast(intent)
  end

  btnBack.onClick = function()
    activity.finish()
  end
end

-- ============================================================
-- 悬浮窗服务
-- ============================================================
function showFloatingWindow()
  local ctx = activity

  if floatView and windowManager then
    return
  end

  windowManager = ctx.getSystemService(Context.WINDOW_SERVICE)

  floatParams = WindowManager.LayoutParams()
  if Build.VERSION.SDK_INT >= 26 then
    floatParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
  else
    floatParams.type = WindowManager.LayoutParams.TYPE_PHONE
  end
  floatParams.format = PixelFormat.TRANSLUCENT
  floatParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
  floatParams.gravity = Gravity.LEFT | Gravity.TOP
  floatParams.width = WindowManager.LayoutParams.WRAP_CONTENT
  floatParams.height = WindowManager.LayoutParams.WRAP_CONTENT
  floatParams.x = getInt(ctx, KEY_LAST_X, 100)
  floatParams.y = getInt(ctx, KEY_LAST_Y, 100)

  local alpha = getInt(ctx, KEY_ALPHA, 95)
  local primary = getPrimary(ctx)
  local container = getContainer(ctx)

  -- 使用 AndroLua+ 的 loadlayout 创建悬浮窗
  local layout = {
    FrameLayout,
    layout_width = "wrap",
    layout_height = "wrap",

    {
      CardView,
      id = "panelCard",
      layout_width = "wrap",
      layout_height = "wrap",
      cardBackgroundColor = 0xFF1F1F25,
      radius = "24dp",
      elevation = "8dp",
      alpha = alpha / 100,

      {
        LinearLayout,
        layout_width = "wrap",
        layout_height = "wrap",
        orientation = "horizontal",

        {
          LinearLayout,
          layout_width = "wrap",
          layout_height = "fill",
          orientation = "vertical",
          gravity = "center_horizontal",
          padding = "8dp",
          background = roundRect(0xFF19191F, 0),

          {
            ImageButton,
            id = "tabHome",
            layout_width = "40dp",
            layout_height = "40dp",
            background = roundRect(container, dp(12)),
            src = "res/drawable/ic_home.xml",
            padding = "8dp",
          },
          {
            View,
            layout_width = "1dp",
            layout_height = "8dp",
          },
          {
            ImageButton,
            id = "tabControl",
            layout_width = "40dp",
            layout_height = "40dp",
            background = roundRect(0xFF2A2A30, dp(12)),
            src = "res/drawable/ic_control.xml",
            padding = "8dp",
          },
          {
            View,
            layout_width = "1dp",
            layout_height = "8dp",
          },
          {
            ImageButton,
            id = "tabInfo",
            layout_width = "40dp",
            layout_height = "40dp",
            background = roundRect(0xFF2A2A30, dp(12)),
            src = "res/drawable/ic_info.xml",
            padding = "8dp",
          },
          {
            View,
            layout_width = "1dp",
            layout_height = "8dp",
          },
          {
            ImageButton,
            id = "tabSettings",
            layout_width = "40dp",
            layout_height = "40dp",
            background = roundRect(0xFF2A2A30, dp(12)),
            src = "res/drawable/ic_settings.xml",
            padding = "8dp",
          },
        },

        {
          View,
          layout_width = "1dp",
          layout_height = "fill",
          backgroundColor = 0xFF35343A,
        },

        {
          FrameLayout,
          id = "contentArea",
          layout_width = "240dp",
          layout_height = "wrap",
          padding = "16dp",

          -- 页面1: 主页
          {
            LinearLayout,
            id = "pageHome",
            layout_width = "fill",
            layout_height = "wrap",
            orientation = "vertical",

            {
              TextView,
              id = "tvTime",
              textSize = "24sp",
              textColor = 0xFFE4E1E9,
              textStyle = "bold",
            },
            {
              TextView,
              id = "tvDate",
              textSize = "12sp",
              textColor = 0xFFC7C5D0,
              layout_marginTop = "4dp",
            },
            {
              LinearLayout,
              layout_width = "wrap",
              layout_height = "wrap",
              orientation = "horizontal",
              gravity = "center_vertical",
              layout_marginTop = "16dp",

              {
                View,
                layout_width = "24dp",
                layout_height = "24dp",
                background = roundRect(primary, dp(4)),
              },
              {
                TextView,
                id = "tvBattery",
                textSize = "14sp",
                textColor = 0xFFC7C5D0,
                layout_marginLeft = "8dp",
              },
            },
          },

          -- 页面2: 控制
          {
            LinearLayout,
            id = "pageControl",
            layout_width = "fill",
            layout_height = "wrap",
            orientation = "vertical",
            gravity = "center",
            visibility = View.GONE,

            {
              Button,
              id = "btnToggle",
              layout_width = "fill",
              layout_height = "48dp",
              background = roundRect(container, dp(16)),
              text = "显示/隐藏面板",
              textColor = 0xFFDEE0FF,
            },
          },

          -- 页面3: 信息
          {
            LinearLayout,
            id = "pageInfo",
            layout_width = "fill",
            layout_height = "wrap",
            orientation = "vertical",
            visibility = View.GONE,

            {
              TextView,
              text = "灵动岛 v1.0\n\n悬浮窗控制工具\n支持多种主题和动画效果",
              textSize = "14sp",
              textColor = 0xFFC7C5D0,
                          },
          },

          -- 页面4: 设置快捷
          {
            LinearLayout,
            id = "pageSettings",
            layout_width = "fill",
            layout_height = "wrap",
            orientation = "vertical",
            visibility = View.GONE,

            {
              Button,
              id = "btnOpenSettings",
              layout_width = "fill",
              layout_height = "48dp",
              background = roundRect(container, dp(16)),
              text = "打开完整设置",
              textColor = 0xFFDEE0FF,
            },
          },
        },
      },
    },

    {
      ImageButton,
      id = "btnClose",
      layout_width = "32dp",
      layout_height = "32dp",
      layout_gravity = "top|right",
      layout_marginTop = "8dp",
      layout_marginRight = "8dp",
      background = oval(0xFFCF6679),
      src = "res/drawable/ic_close.xml",
      padding = "6dp",
    },
  }

  floatView = loadlayout(layout, nil)
  windowManager.addView(floatView, floatParams)

  -- 更新时间
  local function updateTime()
    local sdf = SimpleDateFormat("HH:mm", Locale.getDefault())
    local dateSdf = SimpleDateFormat("MM月dd日 EEEE", Locale.getDefault())
    tvTime.setText(sdf.format(Date()))
    tvDate.setText(dateSdf.format(Date()))
  end

  -- 更新电池
  local function updateBattery()
    local batIntent = ctx.registerReceiver(nil, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
    if batIntent then
      local level = batIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
      local scale = batIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 100)
      local pct = math.floor(level * 100 / scale)
      tvBattery.setText(pct .. "%")
    end
  end

  -- 定时更新
  timeHandler = Handler(Looper.myLooper())
  local function timeRunnable()
    updateTime()
    updateBattery()
    timeHandler.postDelayed(timeRunnable, 60000)
  end
  timeHandler.post(timeRunnable)

  -- Tab切换
  local pages = {pageHome, pageControl, pageInfo, pageSettings}
  local tabs = {tabHome, tabControl, tabInfo, tabSettings}

  local function switchPage(pageIndex)
    if animating or pageIndex == currentPage then return end
    animating = true

    for i, tab in ipairs(tabs) do
      tab.setBackground(roundRect(i - 1 == pageIndex and container or 0xFF2A2A30, dp(12)))
    end

    pages[currentPage + 1].setVisibility(View.GONE)
    pages[pageIndex + 1].setVisibility(View.VISIBLE)

    currentPage = pageIndex
    animating = false
  end

  tabHome.onClick = function() switchPage(0) end
  tabControl.onClick = function() switchPage(1) end
  tabInfo.onClick = function() switchPage(2) end
  tabSettings.onClick = function() switchPage(3) end

  -- 显示/隐藏
  btnToggle.onClick = function()
    isExpanded = not isExpanded
    if isExpanded then
      panelCard.animate()
        .scaleX(1)
        .scaleY(1)
        .alpha(alpha / 100)
        .setDuration(300)
        .start()
    else
      panelCard.animate()
        .scaleX(0.3)
        .scaleY(0.3)
        .alpha(0.7)
        .setDuration(300)
        .start()
    end
  end

  -- 打开设置
  btnOpenSettings.onClick = function()
    showSettings()
  end

  -- 关闭
  btnClose.onClick = function()
    stopFloatingWindow()
  end

  -- 拖动
  local lastX, lastY = 0, 0
  floatView.setOnTouchListener(View.OnTouchListener{
    onTouch = function(v, event)
      local action = event.getAction()
      if action == MotionEvent.ACTION_DOWN then
        lastX = event.getRawX()
        lastY = event.getRawY()
        return true
      elseif action == MotionEvent.ACTION_MOVE then
        local dx = event.getRawX() - lastX
        local dy = event.getRawY() - lastY
        floatParams.x = floatParams.x + dx
        floatParams.y = floatParams.y + dy
        windowManager.updateViewLayout(floatView, floatParams)
        lastX = event.getRawX()
        lastY = event.getRawY()
        return true
      elseif action == MotionEvent.ACTION_UP then
        if getBoolean(ctx, KEY_SNAP_EDGE, true) then
          local metrics = ctx.getResources().getDisplayMetrics()
          local screenWidth = metrics.widthPixels
          local centerX = floatParams.x + panelCard.getWidth() / 2
          if centerX < screenWidth / 2 then
            floatParams.x = 0
          else
            floatParams.x = screenWidth - panelCard.getWidth()
          end
          windowManager.updateViewLayout(floatView, floatParams)
        end
        putInt(ctx, KEY_LAST_X, floatParams.x)
        putInt(ctx, KEY_LAST_Y, floatParams.y)
        return true
      end
      return false
    end
  })
end

function stopFloatingWindow()
  if floatView and windowManager then
    windowManager.removeView(floatView)
    floatView = nil
    windowManager = nil
  end
  if timeHandler then
    timeHandler.removeCallbacksAndMessages(nil)
    timeHandler = nil
  end
end

-- ============================================================
-- 入口
-- ============================================================
showLoading()



--不要发布无意义垃圾/病毒/不文明用语/违反当地法律法规内容
--搬运帖注明来源且不可设置为付费require "import"
import "android.app.*"
import "android.os.*"
import "android.widget.*"
import "android.view.*"
import "layout"
import "AndLua"
activity.setTheme(R.Theme_Blue)
activity.setTitle("热更新")
activity.setContentView(loadlayout(layout))
--以下是放进那个收藏或者其他的事例
--[[

 by可叮
 
【开关:开】
【当前版本:1.0】
【更新公告:密码可叮牛逼】
【蓝奏云链接:https://wwp.lanzoup.com/iJMYP14u31ej】

]]


--QQ收藏笔记
Http.get("https://sharechain.qq.com/2c103fe7188d93e83b061d0dfb21cc8a",nil,"utf8",nil,function(code,content,cookie,header)
  import "android.content.pm.PackageManager"
  local 版本=activity.getPackageManager().getPackageInfo(activity.getPackageName(), 0).versionName--获取当前版本
  content=content:gsub("amp;","") or content;--过滤
  开关=content:match("【开关:(.-)】")--状态
  当前版本=content:match("【当前版本:(.-)】")--版本
  更新公告=content:match("【更新公告:(.-)】")--公告
  蓝奏云链接=content:match("【蓝奏云链接:(.-)】")--链接
  if 开关=="开"
    if 版本==当前版本--判断是不是最新版本
      提示"当前为最新版本"
     else
      dialog=AlertDialog.Builder(this)
      .setTitle("有新版本！")
      .setMessage(更新公告)
      .setPositiveButton("立即更新",{onClick=function(v)
          Http.get("https://yuanxiapi.cn/api/lanzou/?url="..蓝奏云链接.."&pwd=可叮牛逼",function(a,b)--使用API解析链接
            --&pwd=这里是密码不需要密码的可以.."&pwd=可叮牛逼"以这种形式删掉
            获取直链=b:match('"download": "(.-)"')--截取
            Http.get(获取直链,function(code,data)
              更新包路径="/storage/emulated/0/Download/更新包.apk"--更新包路径
              io.open(更新包路径,"w"):write(data):close()--下载更新包
              activity.installApk(更新包路径);--获取更新包路径并安装
              dialog=AlertDialog.Builder(this)
              .setMessage("正在安装.....")
              .show()
              dialog.create()
              .setCanceledOnTouchOutside(false)
              .setCancelable(false)
            end)
          end)
        end})
      .setNegativeButton("跳转更新",{onClick=function(v)
          import "android.content.Intent"
          import "android.net.Uri"
          activity.startActivity(Intent("android.intent.action.VIEW",Uri.parse(蓝奏云链接)))--跳转蓝奏云链接
          结束程序()
        end})
      .show()
      dialog.create()
      .setCanceledOnTouchOutside(false)
      .setCancelable(false)
      .show()dialog.getButton(dialog.BUTTON_POSITIVE).setTextColor(0xFF000000)
      dialog.getButton(dialog.BUTTON_NEGATIVE).setTextColor(0xFF000000)
    end
  end
end)



--不要发布无意义垃圾/病毒/不文明用语/违反当地法律法规内容
--搬运帖注明来源且不可设置为付费require "import"
import "android.app.*"
import "android.os.*"
import "android.widget.*"
import "android.view.*"
import "androidx.appcompat.widget.LinearLayoutCompat"

activity
.setTheme(R.style.Theme_Material3_Blue)
.setTitle("公告示例")
.setContentView(loadlayout("layout"))
--远程公告
url="https://sharechain.qq.com/29e9e1aed9a2e0065cd4294780d32822?qq_aio_chat_type=3"--这里是链接QQ收藏和其他的都可以
Http.get(url,function(a,b)
  公告开关=b:match("公告开关【(.-)】")
  if a==200 then--再加一个if语句操控公告
    if 公告开关=="开" then
      内容=b:match("公告【(.-)】")
      --加一个弹窗让他显示出来
      AlertDialog.Builder(this)
      .setTitle("公告")
      .setMessage(内容)--这里是消息
      .setPositiveButton("退出软件",function()
        os.exit()--关闭软件
      end)
      .setNeutralButton("知道了",nil)
      .setCancelable(false)--点击空白无法关闭，强制退出软件！
      .show();
     else
      --这里没有公告

      Toast.makeText(activity,"公告关闭", Toast.LENGTH_LONG).show()
    end
   else

  end
end)



--不要发布无意义垃圾/病毒/不文明用语/违反当地法律法规内容
--搬运帖注明来源且不可设置为付费
--Ai

require "import"
import "android.app.*"
import "android.os.*"
import "android.widget.*"
import "android.view.*"
import "android.graphics.drawable.GradientDrawable"
import "androidx.appcompat.widget.LinearLayoutCompat"

activity.setTheme(R.style.Theme_Material3_Blue)
activity.setTitle("按键映射示例")

local layout = {
  LinearLayoutCompat;
  layout_width = "match_parent";
  gravity = "center";
  layout_height = "match_parent";
  orientation = "vertical";
  {
    LinearLayout;
    layout_width = "match_parent";
    layout_weight = "1";
    layout_height = "0dp";
    id = "theAccusedArea";
  };
  {
    LinearLayout;
    layout_width = "match_parent";
    layout_weight = "1";
    layout_height = "0dp";
    id = "controlZone";
  };
}
activity.setContentView(loadlayout(layout))

-- DP转像素工具
local function dpToPx(dp)
  local dm = activity.getResources().getDisplayMetrics()
  return math.floor(dp * dm.density + 0.5)
end

local dp40 = dpToPx(40)
local dp80 = dpToPx(80)
local dp120 = dpToPx(120)
local speed = 1.8 -- 移动速度倍率

-- ======================
-- 1. 上半区 可移动方块
-- ======================
local squareSize = dpToPx(50)
local square = View(activity)
local squareLp = LinearLayout.LayoutParams(squareSize, squareSize)
square.setLayoutParams(squareLp)
square.setBackgroundColor(0xFF2196F3)
square.setX(0)
square.setY(0)
theAccusedArea.addView(square)

-- 获取父容器边界，防止方块移出可视区域
local function getAreaBound()
  return {
    w = theAccusedArea.getWidth(),
    h = theAccusedArea.getHeight(),
    s = squareSize
  }
end

-- 限制方块不超出容器
local function clampSquare()
  local b = getAreaBound()
  if b.w==0 or b.h==0 then return end
  local nx = math.max(0, math.min(square.getX(), b.w - b.s))
  local ny = math.max(0, math.min(square.getY(), b.h - b.s))
  square.setX(nx)
  square.setY(ny)
end

-- ======================
-- 2. 下半区 摇杆底座容器(LinearLayout) + 摇杆圆球，整体居中
-- ======================
-- 摇杆底座（改用LinearLayout容器，才能addView）
local rockerBase = LinearLayout(activity)
local baseLp = LinearLayout.LayoutParams(dp120, dp120)
rockerBase.setLayoutParams(baseLp)
rockerBase.setGravity(Gravity.CENTER)
-- 底座圆形样式
local baseDraw = GradientDrawable()
baseDraw.setShape(GradientDrawable.OVAL)
baseDraw.setStroke(dpToPx(3),0xFFAAAAAA)
baseDraw.setColor(0xFF333333)
rockerBase.setBackground(baseDraw)
-- 底座放到controlZone并居中
controlZone.addView(rockerBase)
controlZone.setGravity(Gravity.CENTER)

-- 摇杆圆球
local rocker = View(activity)
local rockLp = LinearLayout.LayoutParams(dp40, dp40)
rocker.setLayoutParams(rockLp)
local rockDraw = GradientDrawable()
rockDraw.setShape(GradientDrawable.OVAL)
rockDraw.setColor(0xFFFFFFFF)
rocker.setBackground(rockDraw)
-- 放入底座容器
rockerBase.addView(rocker)

-- ======================
-- 3. WASD摇杆逻辑
-- ======================
local baseRadius = dp80 / 2
local touchX0, touchY0
local rockOriginX, rockOriginY

rocker.setOnTouchListener{
  onTouch = function(v,event)
    local act = event.getAction()
    local x = event.getX()
    local y = event.getY()
    local centerX = rockerBase.getWidth()/2
    local centerY = rockerBase.getHeight()/2

    if act == MotionEvent.ACTION_DOWN then
      touchX0 = x
      touchY0 = y
      rockOriginX = rocker.getX()
      rockOriginY = rocker.getY()

    elseif act == MotionEvent.ACTION_MOVE then
      -- 摇杆偏移计算
      local dx = x - touchX0
      local dy = y - touchY0
      local targetX = rockOriginX + dx
      local targetY = rockOriginY + dy

      -- 限制摇杆不超出外圈底座
      local dist = math.sqrt((targetX-centerX)^2 + (targetY-centerY)^2)
      if dist > baseRadius then
        local scale = baseRadius / dist
        targetX = centerX + (targetX-centerX)*scale
        targetY = centerY + (targetY-centerY)*scale
      end
      rocker.setX(targetX)
      rocker.setY(targetY)

      -- WASD映射：左右上下控制方块
      local moveX = (targetX - centerX) * speed
      local moveY = (targetY - centerY) * speed
      square.setX(square.getX() + moveX / 15)
      square.setY(square.getY() + moveY / 15)
      clampSquare()

    elseif act == MotionEvent.ACTION_UP or act == MotionEvent.ACTION_CANCEL then
      -- 松手回弹中心
      rocker.setX(centerX - dp40/2)
      rocker.setY(centerY - dp40/2)
    end
    return true
  end
}



require "import"
import "android.app.*"
import "android.os.*"
import "android.widget.*"
import "android.view.*"
import "android.content.pm.*"
import "android.graphics.*"
import"android.os.SystemClock"

--可以多点触控
--音量上开关墙体
--音量下重置点

-- gc
collectgarbage("setpause", 200)
collectgarbage("setstepmul", 500)

-- keep screen on & hide navigation bar
activity.setRequestedOrientation(1)
local window = activity.getWindow()
window.addFlags( WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON )
window.addFlags( WindowManager.LayoutParams.FLAG_FULLSCREEN )
window.addFlags( WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION )
window.getDecorView().setSystemUiVisibility((
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)|(View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY ))
pcall(function()
  local layoutParams = window.getAttributes()
  layoutParams.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
  window.setAttributes(layoutParams)
end)
window.getDecorView().setOnSystemUiVisibilityChangeListener({
  onSystemUiVisibilityChange = function()
    window.getDecorView().setSystemUiVisibility((
    View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)|(View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY ))
  end
})

-- layout
layout = LinearLayout(activity)
activity.setContentView(layout)

-- initialize
points = {}
scr_w = activity.getWidth()
scr_h = activity.getHeight()
if scr_h < scr_w then
  scr_w, scr_h = scr_h, scr_w
end
connect_range = scr_w / 7
split_range = connect_range / 4

-- 帧率显示
local fps_text = TextView(activity)
fps_text.setTextColor(0xFFFF0000)
fps_text.setTextSize(20)
fps_text.setX(scr_w - 400) -- 右上角位置
fps_text.setY(1000)
layout.addView(fps_text)

local last_time = SystemClock.elapsedRealtime()
local frame_count = 0
local current_fps = 0

-- func
function distance(x, y)
    return math.sqrt(x * x + y * y)
end

-- draw
paint_point = Paint()
.setColor(0xffffff)
.setStrokeWidth(7)
.setStrokeCap(Paint.Cap.ROUND)
.setDither(true)

paint_line = Paint()
.setColor(0xffffff)
.setStrokeWidth(0.8)
.setStrokeCap(Paint.Cap.ROUND)
.setDither(true)

wall = true
touch_pos = {}

layout.setLayerType(View.LAYER_TYPE_HARDWARE, nil)
paint_line.setAntiAlias(true)
paint_line.setDither(true)

layout.setBackground(LuaDrawable(
function(canvas, paint, drawable)

  -- 帧率计算
  frame_count = frame_count + 1
  local now = SystemClock.elapsedRealtime()
  if now - last_time >= 1000 then -- 改为每1秒更新一次更流畅
    current_fps = frame_count / ((now - last_time) / 1000.0)
    frame_count = 0
    last_time = now
    fps_text.setText(string.format("FPS: %.1f", current_fps))
    fps_text.bringToFront() -- 确保文本在最上层
  end

  canvas.drawColor(0xff000000)
  for k, v in pairs(touch_pos) do
    for k, point in ipairs(points) do
      local tx, ty = v.x, v.y
      local x, y = point.x, point.y
      local sx, sy = point.sv.x, point.sv.y
      local dx, dy = tx - x, ty - y
      local distance = distance(dx, dy)
      dx, dy = dx / distance, dy / distance
      local force = 1 - distance / scr_h / 2
      force = math.pow(math.max(0, force), 2) * 3
      sx = sx + dx * force
      sy = sy + dy * force
      point.sv.x, point.sv.y = sx, sy
    end
  end
  for k, point in ipairs(points) do
    local x, y = point.x, point.y
    local sx, sy = point.sv.x, point.sv.y
    x = x + sx
    y = y + sy
    if distance(sx, sy) > 4 then
      sy = sy * 0.97
      sx = sx * 0.97
     else
      sx = sx / 0.97
      sy = sy / 0.97
    end
    if wall then
      if (x < 0) or (x > scr_w) then
        x = math.max(0, math.min(x, scr_w))
        sx = sx * 0.8
        sx = -sx
      end
      if (y < 0) or (y > scr_h) then
        y = math.max(0, math.min(y, scr_h))
        sy = sy * 0.8
        sy = -sy
      end
     else
      x = x % scr_w
      y = y % scr_h
    end
    point.x, point.y = x, y
    point.sv.x, point.sv.y = sx, sy
  end
  for k, point in ipairs(points) do
    local x, y = point.x, point.y
    local sx, sy = point.sv.x, point.sv.y
    local connect_strength = 1
    local connected = {}
    for r, spoint in ipairs(points) do
      if r == k then
        goto loop
      end
::loop::
      local dx, dy = spoint.x - x, spoint.y - y
      local distance = distance(dx, dy)
      if distance < connect_range then
        if #connected < 4 then
          table.insert(connected, spoint)
        end
        local pstrength = ( 1 - distance / connect_range ) ^ 2
        connect_strength = connect_strength + pstrength
        if connect_strength > 0.5 then
          break
        end
      end
    end
    local alpha = connect_strength * 512
    alpha = math.min(alpha, 255)
    for r, spoint in ipairs(connected) do
      paint_line.setAlpha(alpha / 3)
      canvas.drawLine(x, y, spoint.x, spoint.y, paint_line)
    end
    paint_point.setAlpha(alpha)
    canvas.drawPoint(x, y, paint_point)
    point.x, point.y = x, y
    point.sv.x, point.sv.y = sx, sy
  end
  drawable.invalidateSelf()
end))

function onTouchEvent(e)
  local action = e.getActionMasked()
  if action == MotionEvent.ACTION_UP then
    touch_pos = {}
   else
    for i = 1, e.getPointerCount() do
      local t = touch_pos[i]
      if not t then
        t = {}; touch_pos[i] = t
      end
      t.x = e.getRawX(i - 1)
      t.y = e.getRawY(i - 1)
    end
    for i = e.getPointerCount() + 1, #touch_pos do
      touch_pos[i] = nil
    end
  end
  return true
end

function onKeyDown(code)
  if code == KeyEvent.KEYCODE_VOLUME_UP then
    wall = not wall
    print("wall: "..tostring(wall))
    return true
   elseif code == KeyEvent.KEYCODE_VOLUME_DOWN then
    points = {}
    for i = 1, 500 do
      points[i] = {
        sv = { x = math.random() * 4 - 2, y = math.random() * 4 - 2 },
        x = math.random(0, scr_w),
        y = math.random(0, scr_h),
      }
    end
    return true
  end
end

onKeyDown(KeyEvent.KEYCODE_VOLUME_DOWN)



--Material3编辑框 多种样式&功能
--填充式，边框式，含图标，限制字数等
--官方默认的那个就不放上来了
--外面一层TextInputLayout里面TextInputEditText即可搞定
--Made By 小猪
--小猪制作
--官方QQ群 967848360
--入群申请信息填 Lua源码

require "import"
import "android.app.*"
import "android.os.*"
import "android.widget.*"
import "android.view.*"
import "androidx.appcompat.widget.LinearLayoutCompat"
import "com.google.android.material.textfield.*"
import "androidx.coordinatorlayout.widget.CoordinatorLayout"
import "com.google.android.material.appbar.AppBarLayout"
import "androidx.appcompat.widget.Toolbar"
import "androidx.core.widget.NestedScrollView"
import "androidx.appcompat.widget.LinearLayoutCompat"

material = {}
material.R = luajava.bindClass("com.google.android.material.R")
local layout = {
      ScrollView;
      layout_width="fill";
      layout_height="fill";
      {
        LinearLayout;
        layout_width="fill";
        layout_height="wrap_content";
        orientation="vertical";
        {
          TextInputLayout;
          layout_width="fill";
          layout_margin="16dp";
          style=material.R.attr.textInputFilledStyle;
          {
            TextInputEditText;
            layout_width="fill";
            id="edit1";
            hint="填充式输入框";
            theme=material.R.style.Widget_Material3_TextInputLayout_FilledBox;
          };
        };
        {
          TextInputLayout;
          layout_width="fill";
          layout_margin="16dp";
          style=material.R.attr.textInputOutlinedStyle;
          {
            TextInputEditText;
            layout_width="fill";
            id="edit2";
            hint="边框式输入框";
            theme=material.R.style.Widget_Material3_TextInputLayout_OutlinedBox;
          };
        };
        {
          TextInputLayout;
          layout_width="fill";
          layout_margin="16dp";
          style=material.R.attr.textInputFilledStyle;
          startIconDrawable=androidx.drawable.abc_ic_voice_search_api_material;
          startIconContentDescription="语音";
          {
            TextInputEditText;
            layout_width="fill";
            id="edit3";
            hint="填充式输入框（带图标）";
            theme=material.R.style.Widget_Material3_TextInputLayout_FilledBox;
          };
        };
        {
          TextInputLayout;
          layout_width="fill";
          layout_margin="16dp";
          style=material.R.attr.textInputOutlinedStyle;
          startIconDrawable=androidx.drawable.abc_ic_voice_search_api_material;
          startIconContentDescription="语音";
          {
            TextInputEditText;
            layout_width="fill";
            id="edit4";
            hint="边框式输入框（带图标）";
            theme=material.R.style.Widget_Material3_TextInputLayout_OutlinedBox;
          };
        };
        {
          TextInputLayout;
          layout_width="fill";
          layout_margin="16dp";
          style=material.R.attr.textInputOutlinedStyle;
          counterEnabled=true;
          counterMaxLength=10;
          {
            TextInputEditText;
            layout_width="fill";
            id="edit5";
            hint="输入框-文字限制10个字";
            theme=material.R.style.Widget_Material3_TextInputLayout_OutlinedBox;
          };
        };
      };
  };
  
  
activity
.setContentView(loadlayout(layout))
.setTheme(R.style.Theme_Material3_Blue)
.setTitle("Material3编辑框")



--弹窗配置请求请在465行修改
require "import"
import "android.app.*"
import "android.os.*"
import "android.widget.*"
import "android.view.*"
import "android.content.Context"
import "com.google.android.material.dialog.MaterialAlertDialogBuilder"
import "com.google.android.material.textview.MaterialTextView"
import "android.graphics.Typeface"
import "android.text.method.ScrollingMovementMethod"
import "android.content.res.Configuration"
import "java.net.URL"
import "java.net.HttpURLConnection"
import "java.io.BufferedReader"
import "java.io.InputStreamReader"
import "org.json.JSONObject"
import "org.json.JSONArray"
import "android.os.Handler"
import "android.os.Looper"
import "java.lang.StringBuilder"
import "android.util.TypedValue"
import "android.content.Intent"
import "android.net.Uri"
import "android.net.ConnectivityManager"
import "android.net.NetworkCapabilities"



-- 判断当前是否为暗色模式
local isDarkMode = function()
  local nightModeFlags = activity.resources.configuration.uiMode and 
                         activity.resources.configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK
  return nightModeFlags == Configuration.UI_MODE_NIGHT_YES
end

-- 获取主题色
local function getThemeColor()
  local typedValue = TypedValue()
  activity.getTheme().resolveAttribute(android.R.attr.colorPrimary, typedValue, true)
  return typedValue.data
end

-- dp转px
local function dp2px(dp)
  return math.floor(dp * activity.getResources().getDisplayMetrics().density + 0.5)
end

-- 获取当前应用版本号
local function getCurrentVersion()
  local success, version = pcall(function()
    local pm = activity.getPackageManager()
    local info = pm.getPackageInfo(activity.getPackageName(), 0)
    return info.versionName
  end)
  return success and version or "1.0.0"
end

-- 比较版本号
local function compareVersion(v1, v2)
  local a = {}
  local b = {}
  for num in v1:gmatch("%d+") do table.insert(a, tonumber(num)) end
  for num in v2:gmatch("%d+") do table.insert(b, tonumber(num)) end
  for i = 1, math.max(#a, #b) do
    local x = a[i] or 0
    local y = b[i] or 0
    if x > y then return 1 end
    if x < y then return -1 end
  end
  return 0
end

-- 退出软件
local function exitApp()
  activity.finish()
  Handler(Looper.getMainLooper()).postDelayed(function()
    System.exit(0)
  end, 100)
end

-- 跳转链接
local function openUrl(url)
  if url and #url > 0 then
    local intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
    activity.startActivity(intent)
  end
end

-- 判断网络是否连接
local function isNetworkConnected()
  local success, result = pcall(function()
    local cm = activity.getSystemService(Context.CONNECTIVITY_SERVICE)
    local network = cm.getActiveNetwork()
    if network == nil then
      return false
    end
    local capabilities = cm.getNetworkCapabilities(network)
    if capabilities == nil then
      return false
    end
    return capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
  end)
  return success and result or false
end

-- 声明 fetchConfigFromNetwork 函数（先声明，后定义）
local fetchConfigFromNetwork

-- 显示普通弹窗
local function showNormalDialog(item, onDismiss)
  local layout = LinearLayout(activity)
  layout.setOrientation(LinearLayout.VERTICAL)
  layout.setPadding(dp2px(24), dp2px(20), dp2px(24), dp2px(16))
  
  -- 标题
  local titleView = MaterialTextView(activity)
  titleView.setText(item.标题 or "提示")
  titleView.setTextSize(18)
  titleView.setTypeface(Typeface.DEFAULT_BOLD)
  titleView.setPadding(0, 0, 0, dp2px(12))
  
  if isDarkMode() then titleView.setTextColor(0xFFFFFFFF)
  else titleView.setTextColor(0xFF000000) end
  
  layout.addView(titleView)
  
  -- 内容
  local messageView = MaterialTextView(activity)
  messageView.setText(item.内容 or "")
  messageView.setTextSize(14)
  messageView.setLineSpacing(dp2px(4), 1.0)
  
  if isDarkMode() then messageView.setTextColor(0xFFCCCCCC)
  else messageView.setTextColor(0xFF666666) end
  
  local maxHeight = dp2px(400)
  messageView.measure(
    View.MeasureSpec.makeMeasureSpec(dp2px(280), View.MeasureSpec.EXACTLY),
    View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
  )
  
  if messageView.getMeasuredHeight() > maxHeight then
    local scrollView = ScrollView(activity)
    scrollView.setLayoutParams(LinearLayout.LayoutParams(
      ViewGroup.LayoutParams.MATCH_PARENT, maxHeight
    ))
    scrollView.addView(messageView)
    layout.addView(scrollView)
  else
    layout.addView(messageView)
  end
  
  -- 创建对话框
  local builder = MaterialAlertDialogBuilder(activity)
  builder.setView(layout)
  
  local 是否强制 = item.强制 == true
  local 点击退出 = item.退出 == true
  local 空白关闭 = item.空白关闭 ~= false -- 默认true
  local 跳转链接 = item.链接 or ""
  local 重试 = item.重试 == true
  
  -- 处理取消按钮：nil或空字符串都不显示
  local 取消按钮文字 = item.取消按钮
  local 显示取消按钮 = 取消按钮文字 and #取消按钮文字 > 0
  
  if 是否强制 then
    builder.setCancelable(false)
    builder.setPositiveButton(item.确定按钮 or "确定", nil)
  else
    builder.setCancelable(空白关闭)
    builder.setPositiveButton(item.确定按钮 or "确定", nil)
    -- 只有明确有文字时才添加取消按钮
    if 显示取消按钮 then
      builder.setNegativeButton(取消按钮文字, nil)
    end
  end
  
  local dialog = builder.create()
  dialog.setCanceledOnTouchOutside(not 是否强制 and 空白关闭)
  
  -- 标记弹窗是否通过按钮关闭
  local isClosedByButton = false
  
  dialog.setOnShowListener(function(d)
    local positiveBtn = d.getButton(AlertDialog.BUTTON_POSITIVE)
    local negativeBtn = d.getButton(AlertDialog.BUTTON_NEGATIVE)
    
    positiveBtn.setTextColor(getThemeColor())
    positiveBtn.setTypeface(Typeface.DEFAULT_BOLD)
    positiveBtn.setAllCaps(false)
    
    -- 统一处理确定按钮点击事件（修复后的逻辑）
    positiveBtn.setOnClickListener(function(v)
      isClosedByButton = true
      
      -- 重试逻辑：优先处理
      if 重试 then
        if isNetworkConnected() then
          d.dismiss()
          fetchConfigFromNetwork()
        else
          Toast.makeText(activity, "请检查网络连接喵🥺", Toast.LENGTH_SHORT).show()
        end
        return
      end
      
      -- 显示提示（如果有）
      if item.提示 and #item.提示 > 0 then
        Toast.makeText(activity, item.提示, Toast.LENGTH_SHORT).show()
      end
      
      -- 处理链接跳转（如果有）
      if 跳转链接 and #跳转链接 > 0 then
        openUrl(跳转链接)
        -- 【修复】跳转链接后不关闭弹窗，保持显示
        return
      end
      
      -- 【修复】处理退出（无论是否有链接，只要点击退出就执行）
      if 点击退出 then
        exitApp()
        return  -- 立即返回，不再执行后续代码
      end
      
      -- 处理弹窗关闭（非强制模式下关闭，强制模式下保持）
      if not 是否强制 then
        d.dismiss()
      end
      -- 强制模式下：不关闭弹窗，保持强制显示
    end)
    
    -- 只有存在取消按钮时才设置点击事件
    if negativeBtn then
      negativeBtn.setTextColor(0xFF888888)
      negativeBtn.setAllCaps(false)
      negativeBtn.setOnClickListener(function(v)
        isClosedByButton = true
        d.dismiss()
      end)
    end
  end)
  
  -- 监听弹窗关闭事件
  dialog.setOnDismissListener(function()
    if onDismiss then
      onDismiss()
    end
  end)
  
  -- 强制模式下拦截返回键
  if 是否强制 then
    dialog.setOnKeyListener(function(d, keyCode, event)
      if keyCode == KeyEvent.KEYCODE_BACK then
        return true  -- 拦截返回键，不执行任何操作
      end
      return false
    end)
  end
  
  dialog.show()
  return dialog
end

-- 显示更新弹窗
local function showUpdateDialog(item, onDismiss)
  local 当前版本 = getCurrentVersion()
  local 新版本 = item.版本 or "1.0.0"
  
  if compareVersion(当前版本, 新版本) >= 0 then
    print("当前已是最新版本: " .. 当前版本)
    if onDismiss then onDismiss() end
    return nil
  end
  
  local layout = LinearLayout(activity)
  layout.setOrientation(LinearLayout.VERTICAL)
  layout.setPadding(dp2px(24), dp2px(20), dp2px(24), dp2px(16))
  
  local titleView = MaterialTextView(activity)
  titleView.setText("发现新版本 " .. 新版本)
  titleView.setTextSize(18)
  titleView.setTypeface(Typeface.DEFAULT_BOLD)
  titleView.setPadding(0, 0, 0, dp2px(4))
  
  local currentView = MaterialTextView(activity)
  currentView.setText("当前版本: " .. 当前版本)
  currentView.setTextSize(12)
  currentView.setPadding(0, 0, 0, dp2px(16))
  
  local logTitleView = MaterialTextView(activity)
  logTitleView.setText("更新内容")
  logTitleView.setTextSize(14)
  logTitleView.setTypeface(Typeface.DEFAULT_BOLD)
  logTitleView.setPadding(0, 0, 0, dp2px(8))
  
  local logView = MaterialTextView(activity)
  logView.setText(item.更新内容 or "")
  logView.setTextSize(13)
  logView.setLineSpacing(dp2px(4), 1.0)
  
  if isDarkMode() then
    titleView.setTextColor(0xFFFFFFFF)
    currentView.setTextColor(0xFF888888)
    logTitleView.setTextColor(0xFFFFFFFF)
    logView.setTextColor(0xFFCCCCCC)
  else
    titleView.setTextColor(0xFF000000)
    currentView.setTextColor(0xFF888888)
    logTitleView.setTextColor(0xFF000000)
    logView.setTextColor(0xFF666666)
  end
  
  layout.addView(titleView)
  layout.addView(currentView)
  layout.addView(logTitleView)
  
  local maxHeight = dp2px(300)
  logView.measure(
    View.MeasureSpec.makeMeasureSpec(dp2px(280), View.MeasureSpec.EXACTLY),
    View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
  )
  
  if logView.getMeasuredHeight() > maxHeight then
    local scrollView = ScrollView(activity)
    scrollView.setLayoutParams(LinearLayout.LayoutParams(
      ViewGroup.LayoutParams.MATCH_PARENT, maxHeight
    ))
    scrollView.addView(logView)
    layout.addView(scrollView)
  else
    layout.addView(logView)
  end
  
  local builder = MaterialAlertDialogBuilder(activity)
  builder.setView(layout)
  
  local 是否强制 = item.强制更新 == true
  local 空白关闭 = item.空白关闭 ~= false
  
  -- 处理取消按钮
  local 取消按钮文字 = item.取消按钮
  local 显示取消按钮 = 取消按钮文字 and #取消按钮文字 > 0
  
  if 是否强制 then
    builder.setCancelable(false)
    builder.setPositiveButton(item.确定按钮 or "立即更新", nil)
  else
    builder.setCancelable(空白关闭)
    builder.setPositiveButton(item.确定按钮 or "立即更新", nil)
    if 显示取消按钮 then
      builder.setNegativeButton(取消按钮文字, nil)
    end
  end
  
  local dialog = builder.create()
  dialog.setCanceledOnTouchOutside(not 是否强制 and 空白关闭)
  
  dialog.setOnShowListener(function(d)
    local positiveBtn = d.getButton(AlertDialog.BUTTON_POSITIVE)
    local negativeBtn = d.getButton(AlertDialog.BUTTON_NEGATIVE)
    
    positiveBtn.setTextColor(getThemeColor())
    positiveBtn.setTypeface(Typeface.DEFAULT_BOLD)
    positiveBtn.setAllCaps(false)
    
    positiveBtn.setOnClickListener(function(v)
      openUrl(item.链接)
    end)
    
    if negativeBtn then
      negativeBtn.setTextColor(0xFF888888)
      negativeBtn.setAllCaps(false)
      negativeBtn.setOnClickListener(function(v)
        d.dismiss()
      end)
    end
  end)
  
  dialog.setOnDismissListener(function()
    if onDismiss then
      onDismiss()
    end
  end)
  
  -- 强制更新模式下拦截返回键
  if 是否强制 then
    dialog.setOnKeyListener(function(d, keyCode, event)
      if keyCode == KeyEvent.KEYCODE_BACK then
        return true  -- 拦截返回键
      end
      return false
    end)
  end
  
  dialog.show()
  return dialog
end

-- 显示单个弹窗
local function showSingleDialog(item, onDismiss)
  local 弹窗类型 = item.类型 or "普通"
  
  if 弹窗类型 == "更新" then
    return showUpdateDialog(item, onDismiss)
  else
    return showNormalDialog(item, onDismiss)
  end
end

-- 顺序显示多个弹窗
local function showDialogsSequentially(dialogs, index)
  index = index or 1
  if index > #dialogs then return end
  
  showSingleDialog(dialogs[index], function()
    showDialogsSequentially(dialogs, index + 1)
  end)
end

-- 解析配置
local function parseConfig(jsonStr)
  local json = JSONObject(jsonStr)
  local 弹窗列表 = {}
  
  if json.has("更新") then
    local u = json.getJSONObject("更新")
    table.insert(弹窗列表, {
      类型 = "更新",
      版本 = u.optString("版本", ""),
      更新内容 = u.optString("更新内容", ""),
      链接 = u.optString("链接", ""),
      确定按钮 = u.optString("确定按钮", "立即更新"),
      取消按钮 = u.optString("取消按钮", ""),
      强制更新 = u.optBoolean("强制更新", false),
      空白关闭 = u.optBoolean("空白关闭", true)
    })
  end
  
  if json.has("弹窗列表") then
    local arr = json.getJSONArray("弹窗列表")
    for i = 0, arr.length() - 1 do
      local d = arr.getJSONObject(i)
      table.insert(弹窗列表, {
        类型 = d.optString("类型", "普通"),
        标题 = d.optString("标题", "提示"),
        内容 = d.optString("内容", ""),
        确定按钮 = d.optString("确定按钮", "确定"),
        取消按钮 = d.optString("取消按钮", ""),
        提示 = d.optString("提示", ""),
        链接 = d.optString("链接", ""),
        强制 = d.optBoolean("强制", false),
        退出 = d.optBoolean("退出", false),
        空白关闭 = d.optBoolean("空白关闭", true)
      })
    end
  end
  
  return 弹窗列表
end

-- 定义 fetchConfigFromNetwork 函数
fetchConfigFromNetwork = function()
  local configUrl = "这里填你的远程链接例如http://xxx.top/弹窗配置.json"
  
  Thread(function()
    local success, result = pcall(function()
      local url = URL(configUrl)
      local connection = url.openConnection()
      connection.setRequestMethod("GET")
      connection.setConnectTimeout(10000)
      connection.setReadTimeout(10000)
      connection.setRequestProperty("Accept", "application/json")
      
      local responseCode = connection.getResponseCode()
      if responseCode == HttpURLConnection.HTTP_OK then
        local reader = BufferedReader(InputStreamReader(connection.getInputStream(), "UTF-8"))
        local sb = StringBuilder()
        local line = reader.readLine()
        while line do
          sb.append(line)
          line = reader.readLine()
        end
        reader.close()
        connection.disconnect()
        return sb.toString()
      else
        connection.disconnect()
        error("HTTP " .. responseCode)
      end
    end)
    
    Handler(Looper.getMainLooper()).post(function()
      if not success then
        -- 网络请求失败 - 强制弹窗，添加重试功能
        showNormalDialog({
          类型 = "普通",
          标题 = "提示",
          内容 = "获取内容失败了喵🥺",
          确定按钮 = "重试",
          强制 = true,  -- 强制弹窗
          空白关闭 = false,  -- 禁止空白处关闭
          重试 = true  -- 启用重试功能
        }, nil)
        return
      end
      
      local parseSuccess, 弹窗列表 = pcall(function()
        return parseConfig(result)
      end)
      
      if parseSuccess and #弹窗列表 > 0 then
        print("成功解析 " .. #弹窗列表 .. " 个弹窗")
        showDialogsSequentially(弹窗列表)
      else
        -- 配置解析失败 - 强制弹窗，添加重试功能
        print("配置解析失败或为空")
        showNormalDialog({
          类型 = "普通",
          标题 = "提示",
          内容 = "获取内容失败了喵🥺",
          确定按钮 = "重试",
          强制 = true,  -- 强制弹窗
          空白关闭 = false,  -- 禁止空白处关闭
          重试 = true  -- 启用重试功能
        }, nil)
      end
    end)
  end).start()
end

-- 启动
fetchConfigFromNetwork()



require "import"
import "android.app.*"
import "android.os.*"
import "android.widget.*"
import "android.view.*"
import "com.google.android.material.textfield.*"
import "androidx.coordinatorlayout.widget.CoordinatorLayout"
import "com.google.android.material.appbar.AppBarLayout"
import "androidx.appcompat.widget.Toolbar"
import "androidx.core.widget.NestedScrollView"
import "androidx.appcompat.widget.LinearLayoutCompat"

material = {}
material.R = luajava.bindClass("com.google.android.material.R")

local layout = {
  LinearLayout;
  orientation="vertical";
  layout_width="fill";
  layout_height="fill";
  {
    Toolbar;
    layout_width="fill";
    layout_height="56dp";
    title="Material Design 输入框示例";
  };
  {
    ScrollView,
    layout_width="fill";
    layout_height="fill";
    {
      LinearLayout;
      layout_width="fill";
      layout_height="fill";
      orientation="vertical";
      {
        TextInputLayout;
        layout_width="fill";
        layout_margin="16dp";
        style=material.R.attr.textInputFilledStyle;
        {
          TextInputEditText;
          layout_width="fill";
          id="edit";
          hint="输入";
          theme=material.R.style.Widget_Material3_TextInputLayout_FilledBox;
        };
      };
    };
  };
};

activity.setContentView(loadlayout(layout))
activity.setSupportActionBar(toolbar)



-- 转载: Lua代码手册
---@author Xiayu

local DynamicColorsOptions = luajava.bindClass "com.google.android.material.color.DynamicColorsOptions"
local DynamicColors = luajava.bindClass "com.google.android.material.color.DynamicColors"
local LinearLayoutCompat = luajava.bindClass "androidx.appcompat.widget.LinearLayoutCompat"
local MaterialButton = luajava.bindClass "com.google.android.material.button.MaterialButton"
local MaterialColors = luajava.bindClass "com.google.android.material.color.MaterialColors"
local ExtendedFloatingActionButton = luajava.bindClass "com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton"
local CoordinatorLayout = luajava.bindClass "androidx.coordinatorlayout.widget.CoordinatorLayout"

local loadlayout = require "loadlayout"

math.randomseed(tostring(os.time()):reverse():sub(1, 6))

activity.setTheme(material.style.Theme_Material3_DayNight_NoActionBar)

local builder = DynamicColorsOptions.Builder()
builder.setThemeOverlay(material.style.Theme_Material3_DayNight_NoActionBar)
.setContentBasedSource(math.random(0xFF000000, 0xFFFFFFFF)) -- 传入Bitmap或者Int
DynamicColors.applyToActivityIfAvailable(activity, builder.build())

local colorPrimary = MaterialColors.getColorOrNull(activity, androidx.attr.colorPrimary)
local colorSecondary = MaterialColors.getColorOrNull(activity, material.attr.colorSecondary)
local colorTertiary = MaterialColors.getColorOrNull(activity, material.attr.colorTertiary)

local layout = {
  CoordinatorLayout,
  layout_width = "match",
  layout_height = "match",
  {
    LinearLayoutCompat,
    layout_width = "match",
    layout_height = "match",
    gravity = "center",
    orientation = "vertical",
    {
      MaterialButton,
      text = "colorPrimary",
      backgroundColor = colorPrimary,
    },
    {
      MaterialButton,
      text = "colorSecondary",
      backgroundColor = colorSecondary,
      layout_marginTop = "16dp",
      layout_marginBottom = "16dp",
    },
    {
      MaterialButton,
      text = "colorTertiary",
      backgroundColor = colorTertiary,
    },
  },
  {
    ExtendedFloatingActionButton,
    text = "recreate",
    layout_margin = "16dp",
    layout_gravity = "end|bottom",
    onClick = function()
      activity.recreate()
    end,
  },
}

activity.setContentView(loadlayout(layout))



import "android.graphics.Color"
import "android.content.res.ColorStateList"
import "com.google.android.material.shape.MaterialShapeDrawable"
import "com.google.android.material.shape.CornerFamily"
import "com.google.android.material.shape.ShapeAppearanceModel"
-- 创建自定义形状外观
shapeAppearance = ShapeAppearanceModel.builder()
.setAllCorners(CornerFamily.ROUNDED, 16)
.setTopLeftCorner(CornerFamily.CUT, 8)
.setBottomRightCorner(CornerFamily.CUT, 8)
.build()

-- 应用到MaterialShapeDrawable
drawable = MaterialShapeDrawable(shapeAppearance)
drawable.fillColor = ColorStateList.valueOf(Color.BLUE)
drawable.strokeWidth = 2
drawable.strokeColor = ColorStateList.valueOf(Color.GRAY)

-- 应用到视图
--functionid.background = drawable
--[[
Material形状系统支持基于状态的变化，可以通过StateListShapeAppearanceModel实现不同状态下的形状变化：
local StateListShapeAppearanceModel = luajava.bindClass "com.google.android.material.shape.StateListShapeAppearanceModel"
stateListShape = StateListShapeAppearanceModel.Builder()
.addStateShapeAppearanceModel(
intArrayOf(android.R.attr.state_pressed),
ShapeAppearanceModel.builder()
.setAllCorners(CornerFamily.ROUNDED, 12)
.build()
)
.setDefaultShape(
ShapeAppearanceModel.builder()
.setAllCorners(CornerFamily.ROUNDED, 8)
.build()
)
.build()

]]



require"import"
import"android.widget.*"
import"android.view.*"

local data={}
for i=1,10 do data[i]="Test "..i.." ABCD" end

local adapter=ArrayAdapter(activity,android.R.layout.simple_list_item_multiple_choice,data)
local lv=ListView(activity)
lv.setAdapter(adapter)
lv.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE)
activity.setContentView(lv)

local base
lv.setOnItemClickListener(AdapterView.OnItemClickListener{
  onItemClick=function(_,_,p) lv.setItemChecked(p,not lv.isItemChecked(p)) end
})

lv.setOnTouchListener(View.OnTouchListener{
  onTouch=function(_,e)
    local a=e.getActionMasked()
    local p=lv.pointToPosition(e.getX(),e.getY())
    if a==MotionEvent.ACTION_DOWN and p>=0 then
      base=not lv.isItemChecked(p)
      lv.setItemChecked(p,base)
      return true
    elseif a==MotionEvent.ACTION_MOVE and base~=nil and p>=0 then
      lv.setItemChecked(p,base)
    elseif a==MotionEvent.ACTION_UP or a==MotionEvent.ACTION_CANCEL then
      base=nil
    end
    return false
  end
})



--Source：氕-Protium 2748966266
--OpenSource:Apache License Version

local Spinner = luajava.bindClass "android.widget.Spinner"
local AppCompatSpinner = luajava.bindClass "androidx.appcompat.widget.AppCompatSpinner"
local LinearLayoutCompat = luajava.bindClass "androidx.appcompat.widget.LinearLayoutCompat"
local MaterialTextView = luajava.bindClass "com.google.android.material.textview.MaterialTextView"

activity.setContentView(loadlayout({
  LinearLayoutCompat;
  orientation='vertical';
  layout_width='fill';
  layout_height='fill';
  gravity='center';
  {
    AppCompatSpinner;
    id="spinner1";
  };
  {
    Space;
    layout_width='fill';
    layout_height='32dp';
  };
  {
    Spinner;
    id="spinner2";
  }
}))

local datas1, datas2 = {}, {}
for i = 1, 520 do
  datas2[#datas2+1] = "item-"..i
  table.insert(datas1, {
    title = {
      text = "item-"..i
    }
  })
end

local ArrayAdapter = luajava.bindClass "android.widget.ArrayAdapter"
local LuaAdapter = luajava.bindClass "com.androlua.LuaAdapter"
spinner1.setAdapter(LuaAdapter(activity, datas1, {
  LinearLayoutCompat;--线性控件
  orientation='vertical';--布局方向
  layout_width='fill';--布局宽度
  layout_height='wrap';--布局高度
  {
    MaterialTextView;--文本控件
    layout_width='fill';--控件宽度
    layout_height='wrap';--控件高度
    id='title';--设置控件ID
    singleLine=true;--设置单行输入
    gravity='center';--重力
  }
}))
spinner1.onItemSelected = function(parent, view, pos, id)
  print("选中 "..(pos+1).." "..view.tag.title.text)
end

spinner2.setAdapter(ArrayAdapter(activity, android.R.layout.simple_spinner_item, datas2))
spinner2.onItemSelected = function(parent, view, pos, id)
  print("选中 "..(pos+1).." "..datas2[pos+1])
end



local LinearLayoutCompat = luajava.bindClass "androidx.appcompat.widget.LinearLayoutCompat"
local FileTreeView = luajava.bindClass "com.luaappx.views.FileTreeView"
local ContextCompat = luajava.bindClass "androidx.core.content.ContextCompat"

activity.setContentView(loadlayout({
  LinearLayoutCompat,
  layout_width="fill",
  layout_height="fill",
  {
    FileTreeView,
    id = "treeView",
    layout_width="fill"
  }
}))


-- 初始化根目录；返回 false 表示无存储权限
local initOk = treeView.init("/sdcard") -- 或 Environment.getExternalStorageDirectory().path
if (not initOk) then
  -- 处理无权限
  return
end

-- 设置文件点击/长按监听
treeView.setOnFileClickListener(function(view, path)
  print(path)
end)

treeView.setOnLongFileClickListener(function(view, path)
  print(path)
  return true
end)

-- 动态设置文件图标（三种重载形式）
--a 使用资源 id
treeView.setFileIcon("lua", R.drawable.ic_language_lua)
treeView.setFileIcon("json", R.drawable.ic_code_json)

--b 使用 Drawable 对象
local pdfDrawable = ContextCompat.getDrawable(this, R.drawable.ic_zip_box_outline)
treeView.setFileIcon("zip", pdfDrawable)

--c 使用路径字符串或 Drawable 的通用重载
treeView.setFileIcon("alp", activity.getLuaDir("res/drawable/ic_zip_box_outline.png"))

-- 获取节点信息
local allNodes = treeView.getNodes()
local node = treeView.getNode("/sdcard/Download") -- 找不到返回 null

-- 排序设置
treeView.setSortType(FileTreeView.SORT_BY_NAME) -- 或 SORT_BY_TIME / SORT_AUTO
treeView.setSortDescending(false) -- 是否降序

-- 显示控制
treeView.setShowHidden(false) -- 不显示隐藏文件
treeView.setNodeIndent(16) -- 缩进
treeView.setLineSpace(4) -- 行距
treeView.setFolderIconSize(22) -- 图标大小

-- 刷新
treeView.refresh("/sdcard/Download") -- 只刷新指定目录节点



import "androidx.appcompat.widget.LinearLayoutCompat"
import "androidx.recyclerview.widget.RecyclerView"
import "androidx.recyclerview.widget.LinearLayoutManager"
import "com.google.android.material.button.MaterialButton"
import "LuaRecyclerAdapter"

activity.setContentView(loadlayout({
  RecyclerView;
  id = "recycler_view",
  layout_width = -1,
  layout_height = -1
}))

local data = {}

for i=1,50 do
  table.insert(data, { key = i })
end

local item = {
  LinearLayoutCompat;
  layout_width="fill";
  gravity="center";
  {
    MaterialButton;
    id="button";
  }
}

local adapter = LuaRecyclerAdapter(data, item,
{
  onBindViewHolder = function(holder, position)
    local view = holder.view.getTag()
    local data = data[position+1]

    view.button.setText(tostring(data.key))

  end,
})

recycler_view
.setAdapter(adapter)
.setLayoutManager(LinearLayoutManager(activity))



--[[
ERNIE系列-旗舰模型
ernie-4.5-turbo-128k
ernie-4.5-turbo-128k-preview
ernie-4.5-turbo-32k
ernie-4.5-turbo-latest
ernie-4.5-turbo-vl-preview
ernie-4.5-turbo-vl
ernie-4.5-turbo-vl-32k
ernie-4.5-turbo-vl-32k-preview
ernie-4.5-turbo-vl-latest
ernie-4.5-8k-preview

ERNIE系列-主力模型
ernie-speed-128k
ernie-speed-8k
ernie-speed-pro-128k
ernie-lite-8k
ernie-lite-pro-128k

ERNIE系列-轻量模型
ernie-tiny-8k

ERNIE系列-垂直场景模型
ernie-char-8k
ernie-char-8k-1010
ernie-char-fiction-8k
ernie-char-fiction-8k-preview
ernie-novel-8k

ERNIE系列-开源模型
ernie-4.5-0.3b
ernie-4.5-21b-a3b
ernie-4.5-vl-28b-a3b

QianFan系列
qianfan-lightning-128b-a19b
qianfan-8b
qianfan-70b
qianfan-agent-intent-32k
qianfan-agent-lite-8k
qianfan-agent-speed-32k
qianfan-agent-speed-8k
qianfan-chinese-llama-2-13b
qianfan-sug-8k
qianfan-correct
qianfan-toytalk

DeepSeek系列
deepseek-v3.1-250821
deepseek-v3

其他
kimi-k2-instruct
qwen3-coder-480b-a35b-instruct
qwen3-coder-30b-a3b-instruct
qwen3-next-80b-a3b-instruct
qwen3-235b-a22b-instruct-2507
qwen3-30b-a3b-instruct-2507
qwen3-235b-a22b
qwen3-30b-a3b
qwen3-32b
qwen3-14b
qwen3-8b
qwen3-4b
qwen3-1.7b
qwen3-0.6b
qwen2.5-7b-instruct
glm-4-32b-0414
llama-4-maverick-17b-128e-instruct
llama-4-scout-17b-16e-instruct
meta-llama-3-70b
meta-llama-3-8b
]]

local Http = luajava.bindClass "com.androlua.Http"
local cjson = require "cjson"

-- 配置项
local CONFIG = {
  API_KEY = "bce-v3/ALTAK-z3g9tWxhVfCGDY2b6iUQN/60708d68b1cf16b1883a8e00a42401ed2122ed1d",
  API_URL = "https://qianfan.baidubce.com/v2/chat/completions",
  MODEL = "ernie-tiny-8k"
}

-- 问题内容
local USER_QUESTION = "LuaAppX是什么?"

local function create_request_body(question)
  return {
    model = CONFIG.MODEL,
    messages = {
      {
        role = "user",
        content = {
          {
            type = "text",
            text = question
          }
        }
      }
    },
    web_search = {
      enable = false,
      enable_citation = false,
      enable_trace = false
    },
    plugin_options = {}
  }
end

local function create_headers()
  return {
    ["Content-Type"] = "application/json; charset=utf-8",
    ["Authorization"] = "Bearer " .. CONFIG.API_KEY
  }
end

local function handle_response(code, resp)
  if code == 200 then
    local ok, data = pcall(cjson.decode, resp)
    if ok then
      if data.choices and data.choices[1] and data.choices[1].message then
        print("AI回复：")
        print(data.choices[1].message.content)
       else
        print("响应格式异常")
        print("原始响应：", resp)
      end
     else
      print("JSON解析失败")
      print("错误响应：", resp)
    end
   else
    print("HTTP错误 " .. code)
    print("错误信息：", resp)
  end
end

-- 主执行逻辑
local request_body = create_request_body(USER_QUESTION)
local json_data = cjson.encode(request_body)
local headers = create_headers()

print("提问：" .. USER_QUESTION)
print("请求发送中...")

Http.post(CONFIG.API_URL, json_data, headers, handle_response)



require "import"
import "android.app.*"
import "android.os.*"
import "android.widget.*"
import "android.view.*"
import "layout"
import "android.widget.*"
import "android.view.*"

activity.setTitle("MyApp5")
activity.setContentView(loadlayout({
  LinearLayout,
  orientation="vertical",
  padding="16dp",
  {
    TextView,----这个是显示的ip文本
    id="tvIp",
    textSize="18sp",
    text="正在获取…",
  },
  {
    Button,---这个是按钮
    id="btnRefresh",
    text="刷新",
    layout_marginTop="12dp",
  }
}))
--------获取本地IP 
function getLocalIp()
  local pipe = io.popen("ip route get 1")
  local result = pipe:read("*a")
  pipe:close()
  local ip = result:match("src%s+([%d%.]+)")
  if not ip or ip == "" then
    return "获取失败"
  end
  return ip
end
-----刷新
function refreshIp()
  tvIp.text = "正在刷新…"
  task(100, function()
    tvIp.text = "本地IP地址: " .. getLocalIp()
  end)
end

refreshIp()

btnRefresh.onClick = refreshIp

function getLocalIp()
  import "java.net.*"
  local en = NetworkInterface.getNetworkInterfaces()
  while en.hasMoreElements() do
    local intf = en.nextElement()
    local addrs = intf.getInetAddresses()
    while addrs.hasMoreElements() do
      local addr = addrs.nextElement()
      if not addr.isLoopbackAddress() and not addr.getHostAddress():find(":") then
        return addr.getHostAddress()
      end
    end
  end
  return "获取失败"
end
-------*公网IP 
local oldRefresh = refreshIp
refreshIp = function()
  oldRefresh()
  task(100, function()
    tvIp.text = tvIp.text .. "\n公网 IP: " .. getPublicIp()
  end)
end



require "import"
import "android.widget.*"
import "android.view.*"

activity.setTitle("ADB Wi-Fi 开关")
activity.setContentView(loadlayout({
  LinearLayout,
  orientation="vertical",
  padding="24dp",
  {
    TextView,
    text="ADB 端口：",
    textSize="16sp",
  },
  {
    EditText,
    id="etPort",
    text="5555",
    inputType="number",
    singleLine=true,
  },
  {
    Switch,
    id="swAdb",
    text="ADB Wi-Fi",
    textSize="18sp",
    layout_marginTop="16dp",
  },
}))

-- 判断某端口是否正在监听
function isPortListening(port)
  local p=io.popen(string.format("netstat -lnt | grep -q :%d && echo 1 || echo 0", port))
  local ok=p:read("*n")==1
  p:close()
  return ok
end

-- 刷新开关状态
function refreshSwitch()
  local port=tonumber(etPort.text) or 5555
  swAdb.checked=isPortListening(port)
end

-- 立即执行一次
refreshSwitch()

-- 监听开关变化
swAdb.setOnCheckedChangeListener{
  onCheckedChanged=function(v, isChecked)
    local port=tonumber(etPort.text) or 5555
    if isChecked then
      -- 开启
      os.execute(string.format("setprop service.adb.tcp.port %d", port))
      os.execute("stop adbd")
      os.execute("start adbd")
      Toast.makeText(activity, "ADB Wi-Fi 已开启（端口"..port.."）", 0).show()
    else
      -- 关闭
      os.execute("setprop service.adb.tcp.port -1")
      os.execute("stop adbd")
      os.execute("start adbd")
      Toast.makeText(activity, "ADB Wi-Fi 已关闭", 0).show()
    end
    -- 延迟 1 秒再次确认状态，避免快速切换时 UI 不同步
    task(1000, refreshSwitch)
  end
}

-- 端口输入框变化后自动刷新
etPort.addTextChangedListener{
  onTextChanged=function(s,start,before,count)
    refreshSwitch()
  end
}



--Source：氕-Protium 2748966266
--OpenSource:Apache License Version

--md5摘要函数
function md5(data)
  local StringBuilder = luajava.bindClass "java.lang.StringBuilder"
  local String = luajava.bindClass "java.lang.String"
  local MessageDigest = luajava.bindClass "java.security.MessageDigest"
  local md = MessageDigest.getInstance("MD5")
  local bytes = md.digest(String(data).getBytes())
  local result = StringBuilder()
  for i = 0, #bytes-1 do
    result.append(string.format("%02x", (bytes[i]&0xff)))
  end
  return result.toString()
end



--Source：氕-Protium 2748966266
--OpenSource:Apache License Version

-- 尺寸转换函数工具类
-- 各种尺寸转换

local _M = {}

function _M.dp2px(dpValue)
  local scale = activity.getResources().getDisplayMetrics().scaledDensity
  return dpValue * scale + 0.5
end

function _M.px2dp(pxValue)
  local scale = activity.getResources().getDisplayMetrics().scaledDensity
  return pxValue / scale + 0.5
end

function _M.px2sp(pxValue)
  local scale = activity.getResources().getDisplayMetrics().scaledDensity;
  return pxValue / scale + 0.5
end

function _M.sp2px(spValue)
  local scale = activity.getResources().getDisplayMetrics().scaledDensity
  return spValue * scale + 0.5
end

return _M



--Source：氕-Protium 2748966266
--OpenSource:Apache License Version

--DES 加解密函数工具类

local _M={}

local String = luajava.bindClass "java.lang.String"
local SecretKeySpec = luajava.bindClass "javax.crypto.spec.SecretKeySpec"
local Cipher = luajava.bindClass "javax.crypto.Cipher"
local Base64 = luajava.bindClass "android.util.Base64"

--加密函数
function _M.encrypt(Str, key)
  local raw = String(key).getBytes();
  local skey = SecretKeySpec(raw, "DES");
  local cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
  cipher.init(Cipher.ENCRYPT_MODE, skey);
  local byte_content = String(Str).getBytes("utf-8");
  local encode_content = cipher.doFinal(byte_content);
  return Base64.encodeToString(encode_content,Base64.DEFAULT);
end

--解密函数
function _M.decrypt(key,Str)
  local raw = String(key).getBytes();
  local skey = SecretKeySpec(raw, "DES");
  local cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
  cipher.init(Cipher.DECRYPT_MODE, skey);
  local encode_content = Base64.decode(Str,Base64.DEFAULT);
  local byte_content = cipher.doFinal(encode_content);
  return tostring(String(byte_content,"utf-8"))
end


return _M



--AES 加解密函数工具类

local _M={}

local Cipher = luajava.bindClass "javax.crypto.Cipher"
local SecretKeySpec = luajava.bindClass "javax.crypto.spec.SecretKeySpec"
local Base64 = luajava.bindClass "android.util.Base64"
local MessageDigest = luajava.bindClass "java.security.MessageDigest"
local String = luajava.bindClass "java.lang.String"

function generateKey(key)
  local md = MessageDigest.getInstance("SHA-256")
  local keyBytes = md.digest(String(key).getBytes("UTF-8"))
  if #keyBytes == 32 then
    return keyBytes
   elseif #keyBytes >= 24 then
    return keyBytes:sub(1,24)
   else
    return keyBytes:sub(1,16)
  end
end

function _M.encrypt(key, data)
  local cipher = Cipher.getInstance("AES/ECB/PKCS5Padding")
  local keySpec = SecretKeySpec(generateKey(key), "AES")
  cipher.init(Cipher.ENCRYPT_MODE, keySpec)
  local encryptedBytes = cipher.doFinal(String(data).getBytes("UTF-8"))
  return tostring(Base64.encodeToString(encryptedBytes, Base64.DEFAULT))
end

function _M.decrypt(key, data)
  local cipher = Cipher.getInstance("AES/ECB/PKCS5Padding")
  local keySpec = SecretKeySpec(generateKey(key), "AES")
  cipher.init(Cipher.DECRYPT_MODE, keySpec)
  local encryptedBytes = Base64.decode(data, Base64.DEFAULT)
  local decryptedBytes = cipher.doFinal(encryptedBytes)
  return tostring(String(decryptedBytes, "UTF-8"))
end

return _M



-- RSA 加解密函数工具类

local _M={}

function doLongerCipherFinal(opMode, cipher, source)
  local ByteArrayOutputStream = luajava.bindClass "java.io.ByteArrayOutputStream"
  local Cipher = luajava.bindClass "javax.crypto.Cipher"

  local out = ByteArrayOutputStream()
  if (opMode == Cipher.DECRYPT_MODE) then
    out.write(cipher.doFinal(source))
   else
    local offset = 0
    local totalSize = #source
    while (totalSize - offset > 0) do
      local size = math.min(cipher.getOutputSize(0) - 11, totalSize - offset)
      out.write(cipher.doFinal(source, offset, size))
      offset = offset + size
    end
  end
  out.close()
  return out.toByteArray()
end

--获取公钥和私钥
--publicKeyString, privateKeyString
function _M.generateKeyPair()
  local KeyPairGenerator = luajava.bindClass "java.security.KeyPairGenerator"
  local Base64 = luajava.bindClass "android.util.Base64"

  local keyPairGenerator = KeyPairGenerator.getInstance("RSA")
  keyPairGenerator.initialize(1024)
  local keyPair = keyPairGenerator.generateKeyPair()
  local rsaPublicKey = keyPair.getPublic()
  local rsaPrivateKey = keyPair.getPrivate()
  local publicKeyString = Base64.encodeToString(rsaPublicKey.getEncoded(),Base64.DEFAULT)
  local privateKeyString = Base64.encodeToString(rsaPrivateKey.getEncoded(),Base64.DEFAULT)
  return publicKeyString, privateKeyString
end

--使用公钥加密
function _M.encryptByPublicKey(publicKeyText, text)
  local X509EncodedKeySpec = luajava.bindClass "java.security.spec.X509EncodedKeySpec"
  local Base64 = luajava.bindClass "android.util.Base64"
  local KeyFactory = luajava.bindClass "java.security.KeyFactory"
  local Cipher = luajava.bindClass "javax.crypto.Cipher"
  local String = luajava.bindClass "java.lang.String"

  local x509EncodedKeySpec = X509EncodedKeySpec(Base64.decode(publicKeyText,Base64.DEFAULT))
  local keyFactory = KeyFactory.getInstance("RSA")
  local publicKey = keyFactory.generatePublic(x509EncodedKeySpec)
  local cipher = Cipher.getInstance("RSA")
  cipher.init(Cipher.ENCRYPT_MODE, publicKey)
  local result = doLongerCipherFinal(Cipher.ENCRYPT_MODE, cipher, String(text).getBytes())
  return Base64.encodeToString(result,Base64.DEFAULT)
end
--使用公钥解密
function _M.decryptByPublicKey(publicKeyText, text)
  local X509EncodedKeySpec = luajava.bindClass "java.security.spec.X509EncodedKeySpec"
  local Base64 = luajava.bindClass "android.util.Base64"
  local KeyFactory = luajava.bindClass "java.security.KeyFactory"
  local Cipher = luajava.bindClass "javax.crypto.Cipher"
  local String = luajava.bindClass "java.lang.String"

  local x509EncodedKeySpec = X509EncodedKeySpec(Base64.decode(publicKeyText,Base64.DEFAULT))
  local keyFactory = KeyFactory.getInstance("RSA")
  local publicKey = keyFactory.generatePublic(x509EncodedKeySpec)
  local cipher = Cipher.getInstance("RSA")
  cipher.init(Cipher.DECRYPT_MODE, publicKey)
  local result = doLongerCipherFinal(Cipher.DECRYPT_MODE, cipher, Base64.decode(text,Base64.DEFAULT))
  return String(result)
end

--使用私钥解密
function _M.decryptByPrivateKey(privateKeyText, text)
  local PKCS8EncodedKeySpec = luajava.bindClass "java.security.spec.PKCS8EncodedKeySpec"
  local Base64 = luajava.bindClass "android.util.Base64"
  local KeyFactory = luajava.bindClass "java.security.KeyFactory"
  local Cipher = luajava.bindClass "javax.crypto.Cipher"
  local String = luajava.bindClass "java.lang.String"

  local pkcs8EncodedKeySpec = PKCS8EncodedKeySpec(Base64.decode(privateKeyText,Base64.DEFAULT))
  local keyFactory = KeyFactory.getInstance("RSA")
  local privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec)
  local cipher = Cipher.getInstance("RSA")
  cipher.init(Cipher.DECRYPT_MODE, privateKey)
  local result = doLongerCipherFinal(Cipher.DECRYPT_MODE, cipher, Base64.decode(text,Base64.DEFAULT))
  return String(result)
end

--使用私钥加密
function _M.encryptByPrivateKey(privateKeyText, text)
  local PKCS8EncodedKeySpec = luajava.bindClass "java.security.spec.PKCS8EncodedKeySpec"
  local Base64 = luajava.bindClass "android.util.Base64"
  local KeyFactory = luajava.bindClass "java.security.KeyFactory"
  local Cipher = luajava.bindClass "javax.crypto.Cipher"
  local String = luajava.bindClass "java.lang.String"

  local pkcs8EncodedKeySpec = PKCS8EncodedKeySpec(Base64.decode(privateKeyText,Base64.DEFAULT))
  local keyFactory = KeyFactory.getInstance("RSA")
  local privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec)
  local cipher = Cipher.getInstance("RSA")
  cipher.init(Cipher.ENCRYPT_MODE, privateKey)
  local result = doLongerCipherFinal(Cipher.ENCRYPT_MODE, cipher, String(text).getBytes())
  return Base64.encodeToString(result,Base64.DEFAULT)
end




return _M



--Source：氕-Protium 2748966266
--OpenSource:Apache License Version

-- ChineseNumberUtils
-- 一个简单的中文数字工具类
--

local _M = {}

string.toNumber = tonumber

_M.NORMAl_NUMBERS = {
  "一", "二", "三", "四", "五",
  "六", "七", "八", "九"
}

_M.NORMAL_UNITS = {
  "", "十", "百", "千", "万",
  "十", "百", "千", "亿",
  "十", "百", "千", "兆",
  "十", "百", "千", "京", -- 大概不会有人数字大到需要用更大的吧（）
}

_M.CAPITAL_NUMBERS = {
  "壹", "贰", "叁", "肆", "伍",
  "陆", "柒", "捌", "玖"
}

_M.CAPITAL_UNITS = {
  "", "拾", "佰", "仟", "万",
  "拾", "佰", "仟", "亿",
  "拾", "佰", "仟", "兆",
  "拾", "佰", "仟", "京",
}

_M.convertWithoutUnits =
function(num, capital, useCircleZero)
  if (type(num) ~= "number" and !tonumber(num)) then
    error(num.." is not a number! ", 2)
  end
    
  local NUMBERS = capital and
    _M.CAPITAL_NUMBERS or _M.NORMAl_NUMBERS
  NUMBERS[0] = useCircleZero and "〇" or "零"
 
  local result = ""
  local str = tostring(num)
  local len = str:len()  
  local hasZero = false

  for i = 1, len do
    local n = str:sub(i, i):toNumber()
    result = result..NUMBERS[n]    
  end
  return result
end

_M.convert = function(num, capital)
  if (type(num) ~= "number" and !tonumber(num)) then
    error(num.." is not a number! ", 2)
  end
  
  local NUMBERS = capital and
    _M.CAPITAL_NUMBERS or _M.NORMAl_NUMBERS
  local UNITS = capital and
    _M.CAPITAL_UNITS or _M.NORMAL_UNITS

  local result = ""
  local str, decimal = tostring(num):match(
     "(%d+)%.?(%d-)$")
  local len = str:len()  
  local hasZero = false

  for i = 1, len do
    local n = str:sub(i, i):toNumber()
    local p = len - i + 1
    if (n > 0 and hasZero == true) then --连续多个零只显示一个
      result = result.."零"
      hasZero = false
    end

    if (p % 4 == 2 and n == 1) then --十位数如果是首位则不显示一十这样的
      if len > p then
        result = result..NUMBERS[n]
      end
      result = result..UNITS[p]
 
     elseif n > 0 then 
      result = result..NUMBERS[n]..UNITS[p]
     
     elseif n == 0 then
      if p % 4 == 1 then --个位是零则补单位
        result = result..UNITS[p]
       else
        hasZero = true
      end
    end
  end

  if str == "0" then
    result = "零"
  end

  if decimal ~= "" then
    result = result.."点"..
      _M.convertWithoutUnits(decimal, capital)
  end

  return result
end

return _M



--不要发布无意义垃圾/病毒/不文明用语/违反当地法律法规内容
--搬运帖注明来源且不可设置为付费
require "import"
import "android.app.Activity"
import "android.view.WindowManager"
import "android.widget.LinearLayout"
import "android.widget.TextView"
import "android.widget.Switch"
import "android.widget.HorizontalScrollView"
import "android.widget.FrameLayout"
import "android.graphics.Color"
import "android.graphics.drawable.GradientDrawable"
import "android.widget.Toast"
import "android.provider.Settings"
import "android.content.Intent"
import "android.view.MotionEvent"
import "android.view.View"
import "android.view.Gravity"
import "android.animation.ObjectAnimator"
import "android.animation.AnimatorSet"
import "android.view.animation.OvershootInterpolator"
import "android.view.animation.AccelerateInterpolator"
import "android.util.TypedValue"

local activity = activity
local lastX, lastY
local floatView, miniBall, wm, params, miniParams
local isMinimized = false

if activity.getApplicationInfo().targetSdkVersion >= 23 then
    if not Settings.canDrawOverlays(activity) then
        Toast.makeText(activity, "请开启悬浮窗权限", Toast.LENGTH_LONG).show()
        activity.startActivity(Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION))
        return
    end
end

wm = activity.getSystemService(Activity.WINDOW_SERVICE)

-- 横向长条：宽一些，矮一些
local winWidth = 320   -- dp
local winHeight = 120  -- dp 矮

local function dp2px(dp)
    return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, activity.getResources().getDisplayMetrics())
end

params = WindowManager.LayoutParams()
params.width = dp2px(winWidth)
params.height = dp2px(winHeight)
if activity.getApplicationInfo().targetSdkVersion >= 26 then
    params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
else
    params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
end
params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
params.gravity = Gravity.TOP + Gravity.LEFT
params.x = 80
params.y = 300

miniParams = WindowManager.LayoutParams()
miniParams.width = 70
miniParams.height = 70
miniParams.type = params.type
miniParams.flags = params.flags
miniParams.gravity = Gravity.TOP + Gravity.LEFT
miniParams.x = 80
miniParams.y = 300

local function roundBg(color, radius, borderColor, borderWidth)
    local d = GradientDrawable()
    d.setShape(GradientDrawable.RECTANGLE)
    d.setCornerRadius(radius)
    d.setColor(Color.parseColor(color))
    if borderColor and borderWidth then
        d.setStroke(borderWidth, Color.parseColor(borderColor))
    end
    return d
end

local switchColors = {
    "#C084FC", "#FF6B6B", "#51CF66", "#FF922B", "#BE4BDB",
    "#20C997", "#F783AC", "#74C0FC", "#FCC419", "#FF6B6B"
}

-- 横向布局：每个功能项是竖向小卡片（图标+文字+开关）
local function createFuncCard(emoji, text, descOn, descOff, statusLabel, colorIndex)
    local card = LinearLayout(activity)
    card.setOrientation(LinearLayout.VERTICAL)
    card.setGravity(Gravity.CENTER)
    card.setPadding(dp2px(6), dp2px(6), dp2px(6), dp2px(6))
    card.setBackgroundDrawable(roundBg("#33FFFFFF", 12, "#44FFFFFF", 1))
    local cardWidth = dp2px(70)
    card.setLayoutParams(LinearLayout.LayoutParams(cardWidth, LinearLayout.LayoutParams.WRAP_CONTENT))

    local iconTv = TextView(activity)
    iconTv.setText(emoji)
    iconTv.setTextSize(16)
    iconTv.setGravity(Gravity.CENTER)
    card.addView(iconTv)

    local label = TextView(activity)
    label.setText(text)
    label.setTextSize(10)
    label.setTextColor(Color.parseColor("#FFFFFF"))
    label.setGravity(Gravity.CENTER)
    card.addView(label)

    local sw = Switch(activity)
    sw.setChecked(false)
    sw.setScaleX(0.75)
    sw.setScaleY(0.75)
    local accentColor = switchColors[colorIndex] or "#C084FC"

    sw.setOnCheckedChangeListener({
        onCheckedChanged = function(btn, isChecked)
            if isChecked then
                statusLabel.setText(descOn)
                card.setBackgroundDrawable(roundBg("#44FFFFFF", 12, accentColor, 1.5))
            else
                statusLabel.setText(descOff)
                card.setBackgroundDrawable(roundBg("#33FFFFFF", 12, "#44FFFFFF", 1))
            end
        end
    })
    card.addView(sw)
    return card
end

-- 迷你球
miniBall = FrameLayout(activity)
miniBall.setBackgroundDrawable(roundBg("#BB6366F1", 35, "#C084FC", 2))
miniBall.setElevation(18)
miniBall.setAlpha(0.9)

local miniIcon = TextView(activity)
miniIcon.setText("⚡")
miniIcon.setTextSize(18)
miniIcon.setGravity(Gravity.CENTER)
miniIcon.setLayoutParams(FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT))
miniBall.addView(miniIcon)

miniBall.setOnTouchListener(View.OnTouchListener {
    onTouch = function(v, event)
        local action = event.getAction()
        if action == MotionEvent.ACTION_DOWN then
            lastX = event.getRawX()
            lastY = event.getRawY()
            v.animate().scaleX(0.8).scaleY(0.8).setDuration(80).start()
        elseif action == MotionEvent.ACTION_MOVE then
            local dx = event.getRawX() - lastX
            local dy = event.getRawY() - lastY
            miniParams.x = miniParams.x + dx
            miniParams.y = miniParams.y + dy
            wm.updateViewLayout(miniBall, miniParams)
            lastX = event.getRawX()
            lastY = event.getRawY()
        elseif action == MotionEvent.ACTION_UP then
            v.animate().scaleX(1).scaleY(1).setDuration(120).start()
            if math.abs(event.getRawX() - lastX) < 12 and math.abs(event.getRawY() - lastY) < 12 then
                expandFromMini()
            end
        end
        return true
    end
})

-- 动画
function showWithAnim(view)
    view.setAlpha(0)
    view.setScaleX(0.5)
    view.setScaleY(0.5)
    view.post(function()
        local set = AnimatorSet()
        set.playTogether({
            ObjectAnimator.ofFloat(view, "alpha", {0, 1}),
            ObjectAnimator.ofFloat(view, "scaleX", {0.5, 1}),
            ObjectAnimator.ofFloat(view, "scaleY", {0.5, 1})
        })
        set.setDuration(350)
        set.setInterpolator(OvershootInterpolator(1.1))
        set.start()
    end)
end

function dismissWithAnim(view)
    local set = AnimatorSet()
    set.playTogether({
        ObjectAnimator.ofFloat(view, "alpha", {1, 0}),
        ObjectAnimator.ofFloat(view, "scaleX", {1, 0.6}),
        ObjectAnimator.ofFloat(view, "scaleY", {1, 0.6})
    })
    set.setDuration(180)
    set.setInterpolator(AccelerateInterpolator())
    set.addListener({
        onAnimationEnd = function()
            if miniBall and miniBall.isAttachedToWindow() then
                wm.removeView(miniBall)
            end
            wm.removeView(view)
        end
    })
    set.start()
end

function minimizeToBall()
    if isMinimized then return end
    isMinimized = true
    miniParams.x = params.x + params.width / 2 - 35
    miniParams.y = params.y

    local set = AnimatorSet()
    set.playTogether({
        ObjectAnimator.ofFloat(floatView, "alpha", {1, 0}),
        ObjectAnimator.ofFloat(floatView, "scaleX", {1, 0.2}),
        ObjectAnimator.ofFloat(floatView, "scaleY", {1, 0.2})
    })
    set.setDuration(180)
    set.setInterpolator(AccelerateInterpolator())
    set.addListener({
        onAnimationEnd = function()
            wm.removeView(floatView)
            wm.addView(miniBall, miniParams)
            miniBall.setAlpha(0)
            miniBall.setScaleX(0.2)
            miniBall.setScaleY(0.2)
            miniBall.post(function()
                local ms = AnimatorSet()
                ms.playTogether({
                    ObjectAnimator.ofFloat(miniBall, "alpha", {0, 0.9}),
                    ObjectAnimator.ofFloat(miniBall, "scaleX", {0.2, 1}),
                    ObjectAnimator.ofFloat(miniBall, "scaleY", {0.2, 1})
                })
                ms.setDuration(280)
                ms.setInterpolator(OvershootInterpolator(1.2))
                ms.start()
            end)
        end
    })
    set.start()
end

function expandFromMini()
    if not isMinimized then return end
    isMinimized = false
    params.x = miniParams.x - params.width / 2 + 35
    params.y = miniParams.y

    local set = AnimatorSet()
    set.playTogether({
        ObjectAnimator.ofFloat(miniBall, "alpha", {0.9, 0}),
        ObjectAnimator.ofFloat(miniBall, "scaleX", {1, 0.2}),
        ObjectAnimator.ofFloat(miniBall, "scaleY", {1, 0.2})
    })
    set.setDuration(130)
    set.addListener({
        onAnimationEnd = function()
            wm.removeView(miniBall)
            wm.addView(floatView, params)
            showWithAnim(floatView)
        end
    })
    set.start()
end

-- 构建主悬浮窗（横向布局）
floatView = LinearLayout(activity)
floatView.setOrientation(LinearLayout.VERTICAL)  -- 整个窗口竖向，但内部是横向滚动
floatView.setBackgroundDrawable(roundBg("#CC1E1B4B", 20, "#88A78BFA", 2))
floatView.setElevation(16)
floatView.setPadding(dp2px(8), dp2px(6), dp2px(8), dp2px(6))

-- 顶部小标题栏
local headerBar = LinearLayout(activity)
headerBar.setOrientation(LinearLayout.HORIZONTAL)
headerBar.setGravity(Gravity.CENTER_VERTICAL)

local iconView = TextView(activity)
iconView.setText("⚡")
iconView.setTextSize(14)
headerBar.addView(iconView)

local status = TextView(activity)
status.setText("滑动开关启用")
status.setTextSize(10)
status.setTextColor(Color.parseColor("#BBBBCC"))
status.setLayoutParams(LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1))
headerBar.addView(status)

local minBtn = TextView(activity)
minBtn.setText("—")
minBtn.setTextSize(14)
minBtn.setTextColor(Color.parseColor("#AAAACC"))
minBtn.setPadding(4, 2, 4, 2)
minBtn.setOnClickListener(function() minimizeToBall() end)
headerBar.addView(minBtn)

local closeBtn = TextView(activity)
closeBtn.setText("✕")
closeBtn.setTextSize(12)
closeBtn.setTextColor(Color.parseColor("#FF6B6B"))
closeBtn.setPadding(2, 2, 0, 2)
closeBtn.setOnClickListener(function() dismissWithAnim(floatView) end)
headerBar.addView(closeBtn)

floatView.addView(headerBar)

-- 横向滚动区域（功能卡片水平排列）
local hScroll = HorizontalScrollView(activity)
hScroll.setLayoutParams(LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT))
hScroll.setHorizontalScrollBarEnabled(false)

local cardsContainer = LinearLayout(activity)
cardsContainer.setOrientation(LinearLayout.HORIZONTAL)
cardsContainer.setPadding(dp2px(2), dp2px(2), dp2px(2), dp2px(2))

local function addFuncCard(emoji, text, descOn, descOff, idx)
    local card = createFuncCard(emoji, text, descOn, descOff, status, idx)
    local lp = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT)
    lp.setMargins(dp2px(4), 0, dp2px(4), 0)
    card.setLayoutParams(lp)
    cardsContainer.addView(card)
end

addFuncCard("📍", "模拟定位", "✅ 精度A+", "✨ 开关启用", 1)
addFuncCard("🎯", "目标锁定", "✅ 1.2x",   "✨ 开关启用", 2)
addFuncCard("👁", "场景透视", "✅ 800m",   "✨ 开关启用", 3)
addFuncCard("🕊", "伪飞行",  "✅ 5m/s",    "✨ 开关启用", 4)
addFuncCard("⚙", "属性调节", "✅ 等级A",  "✨ 开关启用", 5)
addFuncCard("⏱", "无CD",    "✅ 0.1s",    "✨ 开关启用", 6)
addFuncCard("🔄", "动态DRC", "✅ 响应D",   "✨ 开关启用", 7)
addFuncCard("🎨", "美化",    "✅ 128计数", "✨ 开关启用", 8)
addFuncCard("🧲", "自动吸附", "✅ 10m",    "✨ 开关启用", 9)
addFuncCard("🛡", "无敌",    "✅ 3s",      "✨ 开关启用", 10)

hScroll.addView(cardsContainer)
floatView.addView(hScroll)

-- 底部小字
local footer = TextView(activity)
footer.setText("绎世")
footer.setTextSize(9)
footer.setTextColor(Color.parseColor("#555577"))
footer.setGravity(Gravity.CENTER)
floatView.addView(footer)

-- 拖动（标题栏）
headerBar.setOnTouchListener(View.OnTouchListener {
    onTouch = function(v, event)
        local action = event.getAction()
        if action == MotionEvent.ACTION_DOWN then
            lastX = event.getRawX()
            lastY = event.getRawY()
        elseif action == MotionEvent.ACTION_MOVE then
            local dx = event.getRawX() - lastX
            local dy = event.getRawY() - lastY
            params.x = params.x + dx
            params.y = params.y + dy
            wm.updateViewLayout(floatView, params)
            lastX = event.getRawX()
            lastY = event.getRawY()
        end
        return false
    end
})

wm.addView(floatView, params)
showWithAnim(floatView)



--by FreneticWind60
--Q 2679200979

require "import"
import "android.app.WallpaperManager"
import "android.content.Context"
import "android.content.res.Configuration"
import "android.os.Build"
import "androidx.appcompat.app.AppCompatDelegate"

--算是FA2的uiMgr的残血版

uiManager={}
--用于储存函数，便于调用

function uiManager.isDarkMode()
  --判断是否为黑暗模式的函数，返回布尔值
  if Build.VERSION.SDK_INT>=29 then
    --安卓10及以上可以直接用configuration判断
    local configuration=activity.getResources().getConfiguration()
    return (configuration.uiMode&Configuration.UI_MODE_NIGHT_MASK)==Configuration.UI_MODE_NIGHT_YES
   else
    --低版本安卓需要用AppCompatDelegate兼容一下
    local nightMode=AppCompatDelegate.getDefaultNightMode()
    if nightMode==AppCompatDelegate.MODE_NIGHT_YES then return true
     elseif nightMode==AppCompatDelegate.MODE_NIGHT_NO then return false
     elseif nightMode==AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM then return false
    end end end

function uiManager.addDarkModeListener(callback)
  --监听黑暗/白天模式切换的函数，参数callback为Lua函数，其中第一个参数为布尔值
  activity.registerComponentCallbacks({
    onConfigurationChanged=function(newConfig)
      local isDark=(newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)==Configuration.UI_MODE_NIGHT_YES
      if callback then callback(isDark) end end}) end
--使用示例
--[[
uiManager.addDarkModeListener(function(isDark)
    if isDark then --应用深色主题
    else --应用浅色主题
    end end)
]]

function uiManager.getCurrentModeText()
  --将当前模式转化为中文输出的函数，返回文本
  --没啥用，建议用糖代替
  if uiManager.isDarkMode() then return "黑暗模式"
   else return "浅色模式"
  end end

--主题颜色
function uiManager.adjustColor(mode,hexColor,targetLuminance)
  --颜色处理函数，其中参数mode为调整模式，参数hexColor为包含#的颜色值文本，targetLuminance为调整目标，输出一个6位含#颜色值文本
  local r=tonumber(string.sub(hexColor,2,3),16) local g=tonumber(string.sub(hexColor,4,5),16) local b=tonumber(string.sub(hexColor,6,7),16) local gray=0.299*r+0.587*g+0.114*b switch(mode)
   case 1 return "#"..string.format("%02X",math.floor((r+255*targetLuminance)/(targetLuminance+1)))..string.format("%02X",math.floor((g+255*targetLuminance)/(targetLuminance+1)))..string.format("%02X",math.floor((b+255*targetLuminance)/(targetLuminance+1))) --加亮
   case 2 return "#"..string.format("%02X",math.floor(r/(targetLuminance+1)))..string.format("%02X",math.floor(g/(targetLuminance+1)))..string.format("%02X",math.floor(b/(targetLuminance+1))) --减暗
   case 3
    local newR=math.max(0,math.min(255,gray+targetLuminance*(tonumber(string.sub(hexColor,2,3),16)-gray)))
    local newG=math.max(0,math.min(255,gray+targetLuminance*(tonumber(string.sub(hexColor,2,3),16)-gray)))
    local newB=math.max(0,math.min(255,gray+targetLuminance*(tonumber(string.sub(hexColor,2,3),16)-gray)))
    return string.format("#%02X%02X%02X",math.floor(newR),math.floor(newG),math.floor(newB))
  end end
  --模式1（mode==1）→将颜色调亮，targetLuminance越大，颜色越亮
  --模式2（mode==2）→将颜色调暗，targetLuminance越大，颜色越暗
  --模式3（mode==3）→将颜色调为灰度，再根据targetLuminance调整饱和度，targetLuminance越大颜色越深
  
function uiManager.getColors(isCollectWallpaperColor)
  --获取壁纸主题色函数，返回包含以强调色accent及其衍生的4个颜色的表
  colors=uiManager.isDarkMode() and {primary="#111118",secondary="#24242B",accent="#2983BB",textPrimary="#F8F9FA",textSecondary="#ADB5BD"} or {primary="#FBFBFF",secondary="#EAEAEE",accent="#5CB3CC",textPrimary="#1A1A2E",textSecondary="#6B7280"}
  --默认颜色
  if isCollectWallpaperColor and Build.VERSION.SDK_INT>=27 then
    --需要安卓8.1及以上
    colors.accent=string.format("#%06X",0xFFFFFF & activity.getSystemService(Context.WALLPAPER_SERVICE).getWallpaperColors(WallpaperManager.FLAG_SYSTEM).getPrimaryColor().toArgb())
    colors.primary=uiManager.adjustColor(uiManager.isDarkMode() and 2 or 1,colors.accent,10)
    colors.secondary=uiManager.adjustColor(uiManager.isDarkMode() and 2 or 1,colors.accent,5)
    colors.textPrimary=uiManager.adjustColor(uiManager.isDarkMode() and 1 or 2,colors.accent,2)
    colors.textSecondary=uiManager.adjustColor(uiManager.isDarkMode() and 3 or 2,colors.accent,uiManager.isDarkMode() and -1 or .25)
    colors.accent=uiManager.adjustColor(1,colors.accent,.25)
  end return colors end
  
  --下面可以补充一点布局相关函数
  
  
  
--[[
🐁植物大战僵尸 第二版🐁

  这个是无图片是文字的版本

  有图片的版本我保存在其他地方了
  
基础版本，包括：
  关卡
  植物
  僵尸
  基础逻辑(无详细生成器)

🐁森佑药西🐁
]]--👀

require "import"
import "android.app.*"
import "android.os.*"
import "android.widget.*"
import "android.view.*"
import "android.graphics.*"
import "java.util.*"
import "android.content.*"
import "com.google.android.material.dialog.MaterialAlertDialogBuilder"
import "java.io.File"
import "android.content.pm.ActivityInfo"
local function parseJSON(str)
  str = str:gsub("[\r\n]", ""):gsub("%s+", " ")
  local idx = 1
  local len = #str
  local function skipSpaces()
    while idx <= len and str:sub(idx, idx):match("%s") do
      idx = idx + 1
    end
  end
  local parseValue, parseObject, parseArray, parseString, parseNumber
  parseString = function()
    idx = idx + 1 
    local result = ""
    while idx <= len do
      local char = str:sub(idx, idx)
      if char == '"' then
        idx = idx + 1
        return result
       elseif char == "\\" then
        idx = idx + 1
        char = str:sub(idx, idx)
        if char == "n" then
          result = result .. "\n"
         else
          result = result .. char
        end
       else
        result = result .. char
      end
      idx = idx + 1
    end
    error("Unterminated string")
  end
  parseNumber = function()
    local start = idx
    if str:sub(idx, idx) == "-" then
      idx = idx + 1
    end
    while idx <= len and str:sub(idx, idx):match("%d") do
      idx = idx + 1
    end
    if str:sub(idx, idx) == "." then
      idx = idx + 1
      while idx <= len and str:sub(idx, idx):match("%d") do
        idx = idx + 1
      end
    end
    local numStr = str:sub(start, idx - 1)
    return tonumber(numStr)
  end
  parseArray = function()
    idx = idx + 1 
    local arr = {}
    skipSpaces()
    while idx <= len and str:sub(idx, idx) ~= "]" do
      local value = parseValue()
      table.insert(arr, value)
      skipSpaces()
      if str:sub(idx, idx) == "," then
        idx = idx + 1
        skipSpaces()
      end
    end
    idx = idx + 1 
    return arr
  end
  parseObject = function()
    idx = idx + 1 
    local obj = {}
    skipSpaces()
    while idx <= len and str:sub(idx, idx) ~= "}" do
      local key = parseString()
      skipSpaces()
      if str:sub(idx, idx) ~= ":" then
        error("Expected colon")
      end
      idx = idx + 1
      local value = parseValue()
      obj[key] = value
      skipSpaces()
      if str:sub(idx, idx) == "," then
        idx = idx + 1
        skipSpaces()
      end
    end
    idx = idx + 1 
    return obj
  end
  parseValue = function()
    skipSpaces()
    local char = str:sub(idx, idx)
    if char == "{" then
      return parseObject()
     elseif char == "[" then
      return parseArray()
     elseif char == '"' then
      return parseString()
     elseif char:match("%d") or char == "-" then
      return parseNumber()
     elseif str:sub(idx, idx + 3) == "true" then
      idx = idx + 4
      return true
     elseif str:sub(idx, idx + 4) == "false" then
      idx = idx + 5
      return false
     elseif str:sub(idx, idx + 3) == "null" then
      idx = idx + 4
      return nil
    end
    error("Unexpected character: " .. char)
  end
  return parseValue()
end
local function loadJSONFromFile(filename)
  local file = File(activity.getFilesDir(), filename)
  if file.exists() then
    local input = activity.openFileInput(filename)
    local isr = InputStreamReader(input)
    local reader = BufferedReader(isr)
    local content = reader.readLine()
    local jsonStr = ""
    while content do
      jsonStr = jsonStr .. content
      content = reader.readLine()
    end
    reader.close()
    return parseJSON(jsonStr)
  end
  return nil
end
local function saveJSONToFile(filename, data)
  local jsonStr = JSON.stringify(data) 
  local output = activity.openFileOutput(filename, Context.MODE_PRIVATE)
  output.write(jsonStr)
  output.close()
end
local levelConfigs = {
  ["1-1"] = [[
{
    "id": "1-1",
    "name": "草地 1-1",
    "description": "111",
    "waves": 3,
    "startSun": 150,
    "availablePlants": ["peashooter"],
    "availableRows": [1],
    "zombieTypes": ["normal"],
    "waveConfigs": [
        {
            "wave": 1,
            "totalZombies": 3,
            "spawnInterval": 8000,
            "zombieDistribution": {"normal": 1}
        },
        {
            "wave": 2, 
            "totalZombies": 4,
            "spawnInterval": 6000,
            "zombieDistribution": {"normal": 1}
        },
        {
            "wave": 3,
            "totalZombies": 5, 
            "spawnInterval": 5000,
            "zombieDistribution": {"normal": 1}
        }
    ],
    "flagWaves": [3],
    "reward": {
        "type": "unlockPlant",
        "plant": "sunflower",
        "message": "解锁向日葵了"
    },
    "background": "lawn_day",
    "music": "grasswalk"
}
    ]],
  ["1-2"] = [[
{
    "id": "1-2", 
    "name": "草地 1-2",
    "description": "使用向日葵产生阳光",
    "waves": 4,
    "startSun": 150,
    "availablePlants": ["peashooter", "sunflower"],
    "availableRows": [1, 2],
    "zombieTypes": ["normal"],
    "waveConfigs": [
        {
            "wave": 1,
            "totalZombies": 4,
            "spawnInterval": 7000,
            "zombieDistribution": {"normal": 1}
        },
        {
            "wave": 2,
            "totalZombies": 5, 
            "spawnInterval": 6000,
            "zombieDistribution": {"normal": 1}
        },
        {
            "wave": 3,
            "totalZombies": 6,
            "spawnInterval": 5000, 
            "zombieDistribution": {"normal": 1}
        },
        {
            "wave": 4,
            "totalZombies": 8,
            "spawnInterval": 4000,
            "zombieDistribution": {"normal": 1}
        }
    ],
    "flagWaves": [2, 4],
    "reward": {
        "type": "unlockPlant", 
        "plant": "wallnut",
        "message": "解锁坚果墙了"
    },
    "background": "lawn_day",
    "music": "grasswalk"
}
    ]],
  ["1-3"] = [[
{
    "id": "1-3",
    "name": "草地 1-3", 
    "description": "面对更强的僵尸攻势",
    "waves": 5,
    "startSun": 175,
    "availablePlants": ["peashooter", "sunflower", "wallnut"],
    "availableRows": [1, 2, 3],
    "zombieTypes": ["normal", "buckethead"],
    "waveConfigs": [
        {
            "wave": 1,
            "totalZombies": 5,
            "spawnInterval": 6000,
            "zombieDistribution": {"normal": 0.8, "buckethead": 0.2}
        },
        {
            "wave": 2,
            "totalZombies": 6,
            "spawnInterval": 5000,
            "zombieDistribution": {"normal": 0.7, "buckethead": 0.3}
        },
        {
            "wave": 3,
            "totalZombies": 8,
            "spawnInterval": 4000,
            "zombieDistribution": {"normal": 0.6, "buckethead": 0.4}
        },
        {
            "wave": 4,
            "totalZombies": 10,
            "spawnInterval": 3000, 
            "zombieDistribution": {"normal": 0.5, "buckethead": 0.5}
        },
        {
            "wave": 5,
            "totalZombies": 12,
            "spawnInterval": 2500,
            "zombieDistribution": {"normal": 0.4, "buckethead": 0.6}
        }
    ],
    "flagWaves": [3, 5],
    "reward": {
        "type": "unlockPlant",
        "plant": "cherrybomb", 
        "message": "解锁樱桃炸弹了"
    },
    "background": "lawn_night",
    "music": "moongrains"
}
    ]]
}
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
local window = activity.getWindow()
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS | WindowManager.LayoutParams.FLAG_FULLSCREEN)
window.getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
)
window.setNavigationBarColor(Color.TRANSPARENT)
window.setStatusBarColor(Color.TRANSPARENT)
pcall(function()
  local lp = window.getAttributes()
  lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
  window.setAttributes(lp)
end)
if activity.getSupportActionBar() then
  activity.getSupportActionBar().hide()
end
local displayMetrics = activity.getResources().getDisplayMetrics()
local density = displayMetrics.density
local function dp2px(dp)
  return math.floor(dp * density + 0.5)
end
local SCREEN_WIDTH = math.max(activity.getWidth() or 1280, activity.getHeight() or 720)
local SCREEN_HEIGHT = math.min(activity.getWidth() or 1280, activity.getHeight() or 720)
local UI_TOP_HEIGHT = dp2px(80)
local GRID_MARGIN_BOTTOM = dp2px(20)
local GRID_MARGIN_SIDE = dp2px(30)
local function calculateGridSize()
  local availableHeight = SCREEN_HEIGHT - UI_TOP_HEIGHT - GRID_MARGIN_BOTTOM
  local availableWidth = SCREEN_WIDTH - GRID_MARGIN_SIDE * 2
  local maxCellHeight = math.floor(availableHeight / 5)
  local maxCellWidth = math.floor(availableWidth / 9)
  local cellSize = math.min(maxCellHeight, maxCellWidth)
  cellSize = math.max(dp2px(40), math.min(dp2px(80), cellSize))
  return cellSize
end
local CELL_SIZE = calculateGridSize()
local camera = {
  x = 0,
  y = 0,
  scale = 1.0,
  minScale = 0.8,
  maxScale = 1.5,
  targetX = 0,
  targetY = 0,
  targetScale = 1.0,
  smoothing = 0.15,
  worldMinX = -200,
  worldMaxX = SCREEN_WIDTH + 200,
  worldMinY = -100,
  worldMaxY = SCREEN_HEIGHT + 100
}
local function updateCamera()
  camera.targetX = math.max(camera.worldMinX, math.min(camera.worldMaxX - SCREEN_WIDTH/camera.scale, camera.targetX))
  camera.targetY = math.max(camera.worldMinY, math.min(camera.worldMaxY - SCREEN_HEIGHT/camera.scale, camera.targetY))
  camera.x = camera.x + (camera.targetX - camera.x) * camera.smoothing
  camera.y = camera.y + (camera.targetY - camera.y) * camera.smoothing
  camera.scale = camera.scale + (camera.targetScale - camera.scale) * camera.smoothing
end
local function worldToScreen(x, y)
  return (x - camera.x) * camera.scale, (y - camera.y) * camera.scale
end
local function screenToWorld(x, y)
  return x / camera.scale + camera.x, y / camera.scale + camera.y
end
local config = {
  screen = {width = SCREEN_WIDTH, height = SCREEN_HEIGHT, fps = 120},
  grid = {
    rows = 5,
    cols = 10,
    cellWidth = CELL_SIZE,
    cellHeight = CELL_SIZE,
    startX = (SCREEN_WIDTH - CELL_SIZE * 10) / 2,
    startY = UI_TOP_HEIGHT + (SCREEN_HEIGHT - UI_TOP_HEIGHT - GRID_MARGIN_BOTTOM - CELL_SIZE * 5) / 2
  },
  plant = {
    peashooter = {名字 = "豌豆\n射手", 阳光消耗 = 100, 伤害 = 20, 攻击速度 = 1000, 血量 = 150, 主颜色 = 0xFF00AA00},
    sunflower = {名字 = "向日\n葵", 阳光消耗 = 50, 阳光数量 = 25, interval = 8000, 血量 = 250, 主颜色 = 0xFFFFAA00},
    wallnut = {名字 = "坚果\n墙", 阳光消耗 = 50, 血量 = 1500, 主颜色 = 0xFFD2B68C},
    cherrybomb = {名字 = "樱桃\n炸弹", 阳光消耗 = 150, 伤害 = 200, radius = 120, 血量 = 50, 主颜色 = 0xFFFF0000}
  },
  zombie = {
    normal = {名字 = "普通\n僵尸", 血量 = 100, 速度 = 15, 伤害 = 50, score = 10, 主颜色 = 0xFF797979},
    buckethead = {名字 = "铁桶\n僵尸", 血量 = 300, 速度 = 20, 伤害 = 50, score = 30, 主颜色 = 0xFFB0C4DE},
    football = {名字 = "橄榄\n球僵\n尸", 血量 = 700, 速度 = 40, 伤害 = 50, score = 25, 主颜色 = 0xFF2F4F4F}
  },
  wave = {刷新间隔 = 15000, 刷新间隔1 = 3000, 难度升级 = 0.95},
  bullet = {width = 15, height = 8, 速度 = 200, 主颜色 = 0xFF00FF00},
  sun = {
    baseAmount = 50,
    fallspeed = 50,
    value = 25,
    spawnInterval = 12000,
    size = dp2px(40),
    textSize = dp2px(14)
  },
}
local buttonSize = dp2px(80)
local buttonSpacing = dp2px(10)
local plantButtons = {
  {plant = "sunflower", x = dp2px(20), y = dp2px(15), width = buttonSize, height = buttonSize},
  {plant = "peashooter", x = dp2px(20) + (buttonSize + buttonSpacing) * 1, y = dp2px(15), width = buttonSize, height = buttonSize},
  {plant = "wallnut", x = dp2px(20) + (buttonSize + buttonSpacing) * 2, y = dp2px(15), width = buttonSize, height = buttonSize},
  {plant = "cherrybomb", x = dp2px(20) + (buttonSize + buttonSpacing) * 3, y = dp2px(15), width = buttonSize, height = buttonSize},
  {plant = "shovel", x = dp2px(20) + (buttonSize + buttonSpacing) * 4, y = dp2px(15), width = buttonSize, height = buttonSize}
}
local grid = {}
for row = 1, config.grid.rows do
  grid[row] = {}
  for col = 1, config.grid.cols do
    grid[row][col] = {
      x = config.grid.startX + (col - 1) * config.grid.cellWidth,
      y = config.grid.startY + (row - 1) * config.grid.cellHeight,
      plant = nil,
      zombie = nil,
      isZombieSpawnZone = (col == 10)
    }
  end
end
local plants = {}
local zombies = {}
local bullets = {}
local suns = {}
local explosions = {}
local lastTime = System.currentTimeMillis()
local sunSpawnTimer = 0
local waveTimer = 0
local zombieSpawnTimer = 0
local gameView = SurfaceView(activity)
local holder = gameView.getHolder()
local touchState = {
  lastX = 0,
  lastY = 0,
  lastDistance = 0,
  mode = "none",
  pointerCount = 0,
  hasMoved = false,
  downTime = 0
}
local function getDistance(event)
  if event.getPointerCount() < 2 then return 0 end
  local x = event.getX(0) - event.getX(1)
  local y = event.getY(0) - event.getY(1)
  return math.sqrt(x * x + y * y)
end
local function drawMultilineText(canvas, paint, text, x, y, lineHeight)
  local lines = {}
  for line in text:gmatch("[^\n]+") do
    table.insert(lines, line)
  end
  local startY = y - (#lines - 1) * lineHeight / 2
  for i, line in ipairs(lines) do
    canvas.drawText(line, x, startY + (i - 1) * lineHeight, paint)
  end
end
local dragState = {
  isDragging = false,
  draggedPlant = nil,
  dragX = 0,
  dragY = 0,
  startX = 0,
  startY = 0
}
local parsedLevelConfigs = {}
for levelId, jsonStr in pairs(levelConfigs) do
  parsedLevelConfigs[levelId] = parseJSON(jsonStr)
end
local playerProgress = {
  unlockedPlants = {"peashooter"}, 
  completedLevels = {}, 
  currentLevel = "1-1", 
  totalSunCollected = 0,
  totalZombiesKilled = 0
}
local gameState = {
  state = "menu", 
  score = 0,
  level = 1,
  sun = 150,
  selectedPlant = nil,
  wave = 1,
  zombiesKilled = 0,
  gameMode = "adventure",
  currentLevelId = "1-1",
  currentLevelConfig = nil,
  waveProgress = {
    currentWave = 1,
    zombiesSpawnedThisWave = 0,
    waveTimer = 0,
    spawnTimer = 0,
    isFlagWave = false
  },
  gameSpeed = 1.0, 
  isDoubleSpeed = false
}
local speedButton = {
  x = SCREEN_WIDTH - dp2px(100),
  y = UI_TOP_HEIGHT + dp2px(10),
  width = dp2px(80),
  height = dp2px(40)
}
local function applyLevelConfig(levelId)
  local config = parsedLevelConfigs[levelId]
  if not config then return false end
  gameState.currentLevelId = levelId
  gameState.currentLevelConfig = config
  gameState.sun = config.startSun
  gameState.wave = 1
  gameState.zombiesKilled = 0
  gameState.score = 0
  gameState.gameSpeed = 1.0
  gameState.isDoubleSpeed = false
  gameState.waveProgress = {
    currentWave = 1,
    zombiesSpawnedThisWave = 0,
    waveTimer = 0,
    spawnTimer = 0,
    isFlagWave = false
  }
  local validatedRows = {}
  for _, row in ipairs(config.availableRows) do
    if row >= 1 and row <= #grid then
      table.insert(validatedRows, row)
    end
  end
  if #validatedRows == 0 then
    validatedRows = {1}
  end
  for row = 1, #grid do
    for col = 1, #grid[row] do
      local isAvailableRow = false
      for _, availableRow in ipairs(validatedRows) do
        if row == availableRow then
          isAvailableRow = true
          break
        end
      end
      grid[row][col].isZombieSpawnZone = (col == 10) and isAvailableRow
      grid[row][col].plant = nil
      grid[row][col].zombie = nil
    end
  end
  plants = {}
  zombies = {}
  bullets = {}
  suns = {}
  explosions = {}
  return true
end
local function isPlantUnlocked(plantType)
  for _, unlockedPlant in ipairs(playerProgress.unlockedPlants) do
    if unlockedPlant == plantType then
      return true
    end
  end
  return false
end
local function unlockPlant(plantType)
  if not isPlantUnlocked(plantType) then
    table.insert(playerProgress.unlockedPlants, plantType)
    return true
  end
  return false
end
local function completeLevel(levelId)
  if not table.contains(playerProgress.completedLevels, levelId) then
    table.insert(playerProgress.completedLevels, levelId)
    local config = parsedLevelConfigs[levelId]
    if config and config.reward then
      if config.reward.type == "unlockPlant" then
        unlockPlant(config.reward.plant)
      end
    end
    return true
  end
  return false
end
local function getCurrentWaveConfig()
  local levelConfig = gameState.currentLevelConfig
  if not levelConfig then return nil end
  for _, waveConfig in ipairs(levelConfig.waveConfigs) do
    if waveConfig.wave == gameState.waveProgress.currentWave then
      return waveConfig
    end
  end
  return nil
end
local function isFlagWave(waveNumber)
  local levelConfig = gameState.currentLevelConfig
  if not levelConfig then return false end
  for _, flagWave in ipairs(levelConfig.flagWaves) do
    if flagWave == waveNumber then
      return true
    end
  end
  return false
end
local function getRandomZombieType(waveConfig)
  if not waveConfig or not waveConfig.zombieDistribution then
    return "normal"
  end
  local rand = math.random()
  local cumulative = 0
  for zombieType, probability in pairs(waveConfig.zombieDistribution) do
    cumulative = cumulative + probability
    if rand <= cumulative then
      return zombieType
    end
  end
  return "normal" 
end
function 生成僵尸()
  local waveConfig = getCurrentWaveConfig()
  if not waveConfig then return end
  if gameState.waveProgress.zombiesSpawnedThisWave >= waveConfig.totalZombies then
    return
  end
  local levelConfig = gameState.currentLevelConfig
  local zombieType = getRandomZombieType(waveConfig)
  local zombieConfig = config.zombie[zombieType]
  if not zombieConfig then
    zombieType = "normal"
    zombieConfig = config.zombie.normal
  end
  local availableRows = levelConfig.availableRows or {1, 2, 3, 4, 5}
  local row = availableRows[math.random(#availableRows)]
  if row < 1 or row > #grid then
    row = 1 
  end
  local spawnCell = grid[row][10]
  if spawnCell and not spawnCell.zombie then
    local zombie = {
      type = zombieType,
      x = spawnCell.x,
      y = spawnCell.y,
      血量 = zombieConfig.血量,
      maxHealth = zombieConfig.血量,
      速度 = zombieConfig.速度,
      damage = zombieConfig.伤害,
      score = zombieConfig.score,
      row = row,
      col = 10,
      attackTimer = 0,
      moveTimer = 0
    }
    table.insert(zombies, zombie)
    spawnCell.zombie = zombie
    gameState.waveProgress.zombiesSpawnedThisWave = gameState.waveProgress.zombiesSpawnedThisWave + 1
  end
end
function 更新生成(deltaTime)
  local levelConfig = gameState.currentLevelConfig
  if not levelConfig then return end
  local waveConfig = getCurrentWaveConfig()
  if not waveConfig then return end
  gameState.waveProgress.waveTimer = gameState.waveProgress.waveTimer + deltaTime
  gameState.waveProgress.spawnTimer = gameState.waveProgress.spawnTimer + deltaTime
  gameState.waveProgress.isFlagWave = isFlagWave(gameState.waveProgress.currentWave)
  if gameState.waveProgress.spawnTimer >= waveConfig.spawnInterval then
    if gameState.waveProgress.zombiesSpawnedThisWave < waveConfig.totalZombies then
      if #zombies < (levelConfig.maxZombies or 10) then
        生成僵尸()
      end
    end
    gameState.waveProgress.spawnTimer = 0
  end
  if #zombies == 0 and
    gameState.waveProgress.zombiesSpawnedThisWave >= waveConfig.totalZombies and
    gameState.waveProgress.currentWave < levelConfig.waves then
    gameState.waveProgress.currentWave = gameState.waveProgress.currentWave + 1
    gameState.waveProgress.zombiesSpawnedThisWave = 0
    gameState.waveProgress.spawnTimer = 0
    gameState.waveProgress.waveTimer = 0
    gameState.wave = gameState.waveProgress.currentWave
  end
  if #zombies == 0 and
    gameState.waveProgress.currentWave >= levelConfig.waves and
    gameState.waveProgress.zombiesSpawnedThisWave >= waveConfig.totalZombies then
    gameState.state = "victory"
    completeLevel(gameState.currentLevelId)
  end
end
function 更新阳光(deltaTime)
  sunSpawnTimer = sunSpawnTimer + deltaTime
  local levelConfig = gameState.currentLevelConfig
  local spawnInterval = levelConfig and levelConfig.sunSpawnInterval or config.sun.spawnInterval
  if sunSpawnTimer >= spawnInterval then
    生成阳光(math.random(100, SCREEN_WIDTH - 100), -50, "falling")
    sunSpawnTimer = 0
  end
  for i = #suns, 1, -1 do
    local sun = suns[i]
    if sun.type == "falling" then
      sun.y = sun.y + config.sun.fallspeed * (deltaTime / 16.67)
      if sun.y > SCREEN_HEIGHT - 200 then
        sun.type = "ground"
        sun.lifeTime = 8000
      end
     elseif sun.type == "ground" or sun.type == "produced" then
      sun.lifeTime = sun.lifeTime - deltaTime
      if sun.lifeTime <= 0 then
        table.remove(suns, i)
      end
    end
  end
end
local levelButtons = {}
local function updateLevelButtonPositions()
  levelButtons = {}
  local buttonWidth = dp2px(120)
  local buttonHeight = dp2px(80)
  local buttonSpacing = dp2px(20)
  local startX = dp2px(50)
  local startY = SCREEN_HEIGHT / 4
  local levels = {
    "1-1", "1-2", "1-3", "1-4", "1-5",
    "2-1", "2-2", "2-3", "2-4", "2-5"
  }
  for i, levelId in ipairs(levels) do
    local row = math.floor((i - 1) / 5)
    local col = (i - 1) % 5
    local button = {
      levelId = levelId,
      x = startX + col * (buttonWidth + buttonSpacing),
      y = startY + row * (buttonHeight + buttonSpacing),
      width = buttonWidth,
      height = buttonHeight,
      isUnlocked = table.contains(playerProgress.completedLevels, levelId) or
      (i == 1) or 
      (i > 1 and table.contains(playerProgress.completedLevels, levels[i-1]))
    }
    table.insert(levelButtons, button)
  end
end
function 更新游戏(deltaTime)
  更新阳光(deltaTime)
  更新植物(deltaTime)
  更新僵尸(deltaTime)
  更新子弹(deltaTime)
  更新爆炸(deltaTime)
  更新生成(deltaTime)
  检查子弹()
  检查游戏结束()
end
function 更新植物(deltaTime)
  for row = 1, config.grid.rows do
    for col = 1, config.grid.cols do
      local cell = grid[row][col]
      if cell.plant then
        local plant = cell.plant
        if plant.type == "sunflower" then
          plant.sunTimer = plant.sunTimer + deltaTime
          if plant.sunTimer >= config.plant.sunflower.interval then
            生成阳光(cell.x + config.grid.cellWidth/2, cell.y + config.grid.cellHeight/2, "produced")
            plant.sunTimer = 0
          end
        end
        if plant.type == "peashooter" then
          plant.fireTimer = plant.fireTimer + deltaTime
          if plant.fireTimer >= config.plant.peashooter.攻击速度 then
            local nearestZombie = 获取最近僵尸角色(row)
            if nearestZombie then
              local bulletX = cell.x + config.grid.cellWidth - 10
              local bulletY = cell.y + config.grid.cellHeight / 2
              fireBullet(bulletX, bulletY, row)
              plant.fireTimer = 0
            end
          end
        end
        if plant.type == "cherrybomb" then
          plant.fuseTimer = plant.fuseTimer + deltaTime
          if plant.fuseTimer >= 1500 then
            引爆樱桃炸弹(cell.x, cell.y, row, col)
          end
        end
      end
    end
  end
end
function 更新僵尸(deltaTime)
  for i = #zombies, 1, -1 do
    local zombie = zombies[i]
    if not zombie or not zombie.速度 or not zombie.血量 then
      table.remove(zombies, i)
      goto continue
    end
    if zombie.血量 <= 0 then
      if zombie.row and zombie.col and grid[zombie.row] and grid[zombie.row][zombie.col] then
        grid[zombie.row][zombie.col].zombie = nil
      end
      local scoreToAdd = zombie.scoreValue or zombie.score or 10
      gameState.score = gameState.score + scoreToAdd
      gameState.zombiesKilled = gameState.zombiesKilled + 1
      table.remove(zombies, i)
      goto continue
    end
    local currentCol = math.floor((zombie.x - config.grid.startX) / config.grid.cellWidth) + 1
    local frontCol = currentCol - 1
    if frontCol >= 1 then
      local frontCell = grid[zombie.row][frontCol]
      if frontCell and frontCell.plant then
        zombie.attackTimer = (zombie.attackTimer or 0) + deltaTime
        if zombie.attackTimer >= 1500 then
          frontCell.plant.血量 = frontCell.plant.血量 - (zombie.damage or 50)
          zombie.attackTimer = 0
          if frontCell.plant.血量 <= 0 then
            frontCell.plant = nil
          end
        end
        goto continue
      end
    end
    local moveSpeed = zombie.速度 or 15
    local moveDistance = moveSpeed * (deltaTime / 1000)
    zombie.x = zombie.x - moveDistance
    local newCol = math.floor((zombie.x - config.grid.startX) / config.grid.cellWidth) + 1
    if newCol ~= zombie.col then
      if grid[zombie.row] and grid[zombie.row][zombie.col] then
        grid[zombie.row][zombie.col].zombie = nil
      end
      zombie.col = newCol
      if newCol >= 1 and grid[zombie.row] and grid[zombie.row][newCol] then
        grid[zombie.row][newCol].zombie = zombie
      end
    end
    if zombie.x < config.grid.startX then
      gameState.state = "gameover"
      break
    end
::continue::
  end
end
function 更新子弹(deltaTime)
  for i = #bullets, 1, -1 do
    local bullet = bullets[i]
    bullet.x = bullet.x + config.bullet.速度 * (deltaTime / 1000)
    if bullet.x > SCREEN_WIDTH + 50 then
      table.remove(bullets, i)
    end
  end
end
function 更新爆炸(deltaTime)
  for i = #explosions, 1, -1 do
    local explosion = explosions[i]
    explosion.life = explosion.life - deltaTime
    explosion.radius = explosion.radius + 2
    if explosion.life <= 0 then
      table.remove(explosions, i)
    end
  end
end
function 获取最近僵尸角色(row)
  local nearestZombie = nil
  local minDistance = math.huge
  for _, zombie in ipairs(zombies) do
    if zombie.row == row then
      local distance = zombie.x
      if distance < minDistance then
        minDistance = distance
        nearestZombie = zombie
      end
    end
  end
  return nearestZombie
end
function 检查子弹()
  for i = #bullets, 1, -1 do
    local bullet = bullets[i]
    if not bullet then goto continue_bullet end
    for j = #zombies, 1, -1 do
      local zombie = zombies[j]
      if not zombie then goto continue_zombie end
      if bullet.row == zombie.row and
        bullet.x >= zombie.x - 10 and bullet.x <= zombie.x + 60 then
        local rowY = config.grid.startY + (zombie.row - 1) * config.grid.cellHeight
        local zombieCenterY = rowY + config.grid.cellHeight / 2
        if math.abs(bullet.y - zombieCenterY) < config.grid.cellHeight / 2 then
          zombie.血量 = zombie.血量 - config.plant.peashooter.伤害
          生成爆炸效果(bullet.x, bullet.y, "small")
          table.remove(bullets, i)
          if zombie.血量 <= 0 then
            gameState.score = gameState.score + (zombie.score or 0)
            gameState.zombiesKilled = gameState.zombiesKilled + 1
            生成爆炸效果(zombie.x + 25, zombie.y + 40, "normal")
            table.remove(zombies, j)
          end
          goto continue_bullet
        end
      end
::continue_zombie::
    end
::continue_bullet::
  end
end
function 绘制拖拽植物(canvas, paint)
  local plantType = dragState.draggedPlant
  local plantConfig = config.plant[plantType]
  if not plantConfig then return end
  paint.reset()
  paint.setAlpha(180)
  paint.setColor(plantConfig.主颜色)
  paint.setStyle(Paint.Style.FILL)
  local size = buttonSize * 1.2
  canvas.drawRect(
  dragState.dragX - size/2,
  dragState.dragY - size/2,
  dragState.dragX + size/2,
  dragState.dragY + size/2,
  paint
  )
  paint.reset()
  paint.setColor(0xFFFFFFFF)
  paint.setTextSize(dp2px(16))
  paint.setTextAlign(Paint.Align.CENTER)
  paint.setFakeBoldText(true)
  drawMultilineText(
  canvas, paint,
  plantConfig.名字,
  dragState.dragX,
  dragState.dragY,
  dp2px(18)
  )
end
function 渲染关卡选择()
  local canvas = holder.lockCanvas()
  if not canvas then return end
  local paint = Paint()
  canvas.drawColor(0xFF87CEEB)
  paint.setColor(0xFF2F4F4F)
  paint.setTextSize(dp2px(36))
  paint.setTextAlign(Paint.Align.CENTER)
  paint.setFakeBoldText(true)
  canvas.drawText(
  "选择关卡",
  SCREEN_WIDTH / 2,
  SCREEN_HEIGHT / 6,
  paint
  )
  local backButtonWidth = dp2px(120)
  local backButtonHeight = dp2px(50)
  local backButtonX = dp2px(20)
  local backButtonY = dp2px(20)
  paint.reset()
  paint.setColor(0xFF666666)
  paint.setStyle(Paint.Style.FILL)
  canvas.drawRect(
  backButtonX, backButtonY,
  backButtonX + backButtonWidth, backButtonY + backButtonHeight,
  paint
  )
  paint.reset()
  paint.setColor(0xFFFFFFFF)
  paint.setTextSize(dp2px(18))
  paint.setTextAlign(Paint.Align.CENTER)
  canvas.drawText(
  "返回",
  backButtonX + backButtonWidth / 2,
  backButtonY + backButtonHeight / 2 + dp2px(6),
  paint
  )
  for _, button in ipairs(levelButtons) do
    local levelConfig = parsedLevelConfigs[button.levelId]
    local isCompleted = table.contains(playerProgress.completedLevels, button.levelId)
    paint.reset()
    if not button.isUnlocked then
      paint.setColor(0xFF333333) 
     elseif isCompleted then
      paint.setColor(0xFF4CAF50) 
     else
      paint.setColor(0xFFFF9800) 
    end
    paint.setStyle(Paint.Style.FILL)
    canvas.drawRect(
    button.x, button.y,
    button.x + button.width, button.y + button.height,
    paint
    )
    paint.reset()
    paint.setColor(0xFFFFFFFF)
    paint.setTextSize(dp2px(16))
    paint.setTextAlign(Paint.Align.CENTER)
    paint.setFakeBoldText(true)
    canvas.drawText(
    button.levelId,
    button.x + button.width / 2,
    button.y + button.height / 2 - dp2px(8),
    paint
    )
    paint.reset()
    paint.setColor(0xFFFFFFFF)
    paint.setTextSize(dp2px(12))
    if not button.isUnlocked then
      canvas.drawText(
      "锁定",
      button.x + button.width / 2,
      button.y + button.height / 2 + dp2px(12),
      paint
      )
     elseif isCompleted then
      canvas.drawText(
      "已完成",
      button.x + button.width / 2,
      button.y + button.height / 2 + dp2px(12),
      paint
      )
     else
      canvas.drawText(
      "开始",
      button.x + button.width / 2,
      button.y + button.height / 2 + dp2px(12),
      paint
      )
    end
    paint.reset()
    paint.setColor(0xFF000000)
    paint.setStyle(Paint.Style.STROKE)
    paint.setStrokeWidth(2)
    canvas.drawRect(
    button.x, button.y,
    button.x + button.width, button.y + button.height,
    paint
    )
  end
  paint.reset()
  paint.setColor(0xFF2F4F4F)
  paint.setTextSize(dp2px(18))
  paint.setTextAlign(Paint.Align.LEFT)
  canvas.drawText(
  "已解锁植物:",
  dp2px(20),
  SCREEN_HEIGHT - dp2px(100),
  paint
  )
  local plantText = ""
  for i, plantType in ipairs(playerProgress.unlockedPlants) do
    if i > 1 then
      plantText = plantText .. ", "
    end
    plantText = plantText .. config.plant[plantType].名字
  end
  paint.setTextSize(dp2px(14))
  canvas.drawText(
  plantText,
  dp2px(20),
  SCREEN_HEIGHT - dp2px(70),
  paint
  )
  holder.unlockCanvasAndPost(canvas)
end
local function gameLoop()
  local currentTime = System.currentTimeMillis()
  local deltaTime = currentTime - lastTime
  lastTime = currentTime
  local adjustedDeltaTime = deltaTime * gameState.gameSpeed
  if gameState.state == "playing" then
    updateCamera()
    更新游戏(adjustedDeltaTime)
    渲染游戏()
   elseif gameState.state == "menu" then
    渲染菜单()
   elseif gameState.state == "levelSelect" then
    渲染关卡选择()
   elseif gameState.state == "victory" then
    渲染胜利界面()
   elseif gameState.state == "gameover" then
    渲染游戏结束界面()
  end
  task(1000 / config.screen.fps, gameLoop)
end
function 渲染胜利界面()
  local canvas = holder.lockCanvas()
  if not canvas then return end
  local paint = Paint()
  canvas.drawColor(0x88000000) 
  local panelWidth = dp2px(400)
  local panelHeight = dp2px(300)
  local panelX = (SCREEN_WIDTH - panelWidth) / 2
  local panelY = (SCREEN_HEIGHT - panelHeight) / 2
  paint.reset()
  paint.setColor(0xFF4CAF50)
  paint.setStyle(Paint.Style.FILL)
  canvas.drawRect(panelX, panelY, panelX + panelWidth, panelY + panelHeight, paint)
  paint.reset()
  paint.setColor(0xFF000000)
  paint.setStyle(Paint.Style.STROKE)
  paint.setStrokeWidth(4)
  canvas.drawRect(panelX, panelY, panelX + panelWidth, panelY + panelHeight, paint)
  paint.reset()
  paint.setColor(0xFFFFFFFF)
  paint.setTextSize(dp2px(32))
  paint.setTextAlign(Paint.Align.CENTER)
  paint.setFakeBoldText(true)
  canvas.drawText(
  "关卡胜利！",
  SCREEN_WIDTH / 2,
  panelY + dp2px(50),
  paint
  )
  local levelConfig = gameState.currentLevelConfig
  if levelConfig then
    paint.setTextSize(dp2px(20))
    canvas.drawText(
    levelConfig.name,
    SCREEN_WIDTH / 2,
    panelY + dp2px(90),
    paint
    )
  end
  paint.setTextSize(dp2px(18))
  canvas.drawText(
  "分数: " .. gameState.score,
  SCREEN_WIDTH / 2,
  panelY + dp2px(130),
  paint
  )
  canvas.drawText(
  "击杀僵尸: " .. gameState.zombiesKilled,
  SCREEN_WIDTH / 2,
  panelY + dp2px(160),
  paint
  )
  if levelConfig and levelConfig.reward then
    local reward = levelConfig.reward
    if reward.type == "unlockPlant" then
      local plantConfig = config.plant[reward.plant]
      if plantConfig then
        paint.setColor(0xFFFFFF00) 
        paint.setTextSize(dp2px(20))
        canvas.drawText(
        "新植物解锁！",
        SCREEN_WIDTH / 2,
        panelY + dp2px(200),
        paint
        )
        paint.setColor(0xFFFFFFFF)
        paint.setTextSize(dp2px(16))
        canvas.drawText(
        plantConfig.名字,
        SCREEN_WIDTH / 2,
        panelY + dp2px(230),
        paint
        )
      end
    end
  end
  local continueButtonWidth = dp2px(150)
  local continueButtonHeight = dp2px(50)
  local continueButtonX = (SCREEN_WIDTH - continueButtonWidth) / 2
  local continueButtonY = panelY + panelHeight + dp2px(20)
  paint.reset()
  paint.setColor(0xFF2196F3)
  paint.setStyle(Paint.Style.FILL)
  canvas.drawRect(
  continueButtonX, continueButtonY,
  continueButtonX + continueButtonWidth, continueButtonY + continueButtonHeight,
  paint
  )
  paint.reset()
  paint.setColor(0xFFFFFFFF)
  paint.setTextSize(dp2px(18))
  paint.setTextAlign(Paint.Align.CENTER)
  canvas.drawText(
  "继续",
  SCREEN_WIDTH / 2,
  continueButtonY + continueButtonHeight / 2 + dp2px(6),
  paint
  )
  holder.unlockCanvasAndPost(canvas)
end
function 获取网格单元格(x, y)
  for row = 1, config.grid.rows do
    for col = 1, config.grid.cols do
      local cell = grid[row][col]
      if x >= cell.x and x < cell.x + config.grid.cellWidth and
        y >= cell.y and y < cell.y + config.grid.cellHeight then
        return cell
      end
    end
  end
  return nil
end
function 生成阳光(x, y, type)
  table.insert(suns, {
    x = x,
    y = y,
    type = type,
    value = config.sun.value,
    lifeTime = type == "produced" and 8000 or nil,
    sunTimer = 0
  })
end
function fireBullet(x, y, row)
  local rowY = config.grid.startY + (row - 1) * config.grid.cellHeight
  local bulletY = rowY + config.grid.cellHeight / 2
  table.insert(bullets, {
    x = x,
    y = bulletY,
    row = row
  })
end
function 生成爆炸效果(x, y, type)
  local explosionType = type or "normal"
  table.insert(explosions, {
    x = x,
    y = y,
    radius = explosionType == "big" and 20 or (explosionType == "small" and 5 or 10),
    maxRadius = explosionType == "big" and 100 or (explosionType == "small" and 15 or 40),
    life = explosionType == "small" and 200 or 500,
    type = explosionType
  })
end
function 引爆樱桃炸弹(x, y, row, col)
  for i = #zombies, 1, -1 do
    local zombie = zombies[i]
    local distance = math.sqrt((zombie.x - x)^2 + (zombie.y - y)^2)
    if distance <= config.plant.cherrybomb.radius then
      gameState.score = gameState.score + zombie.score
      gameState.zombiesKilled = gameState.zombiesKilled + 1
      table.remove(zombies, i)
    end
  end
  生成爆炸效果(x, y, "big")
  grid[row][col].plant = nil
end
function drawGrid(canvas, paint)
  paint.reset()
  paint.setColor(0x33000000)
  paint.setStyle(Paint.Style.STROKE)
  paint.setStrokeWidth(2)
  for row = 1, config.grid.rows do
    for col = 1, config.grid.cols do
      local cell = grid[row][col]
      canvas.drawRect(
      cell.x, cell.y,
      cell.x + config.grid.cellWidth,
      cell.y + config.grid.cellHeight,
      paint
      )
    end
  end
end
function 绘制植物(canvas, paint)
  for row = 1, config.grid.rows do
    for col = 1, config.grid.cols do
      local cell = grid[row][col]
      if cell.plant then
        local plant = cell.plant
        local plantConfig = config.plant[plant.type]
        paint.reset()
        paint.setColor(plantConfig.主颜色)
        paint.setStyle(Paint.Style.FILL)
        canvas.drawRect(
        cell.x + 5, cell.y + 5,
        cell.x + config.grid.cellWidth - 5,
        cell.y + config.grid.cellHeight - 5,
        paint
        )
        paint.reset()
        paint.setColor(0xFFFFFFFF)
        paint.setTextSize(dp2px(12))
        paint.setTextAlign(Paint.Align.CENTER)
        paint.setFakeBoldText(true)
        drawMultilineText(
        canvas, paint,
        plantConfig.名字,
        cell.x + config.grid.cellWidth / 2,
        cell.y + config.grid.cellHeight / 2,
        dp2px(14)
        )
        if plant.血量 and plantConfig.血量 then
          local healthPercent = plant.血量 / plantConfig.血量
          paint.reset()
          paint.setStyle(Paint.Style.FILL)
          paint.setColor(0x88FF0000)
          canvas.drawRect(
          cell.x + 5, cell.y + config.grid.cellHeight - 15,
          cell.x + config.grid.cellWidth - 5, cell.y + config.grid.cellHeight - 5,
          paint
          )
          paint.setColor(0xFF00FF00)
          canvas.drawRect(
          cell.x + 5, cell.y + config.grid.cellHeight - 15,
          cell.x + 5 + (config.grid.cellWidth - 10) * healthPercent,
          cell.y + config.grid.cellHeight - 5,
          paint
          )
        end
      end
    end
  end
end
function 绘制僵尸(canvas, paint)
  for _, zombie in ipairs(zombies) do
    local zombieConfig = config.zombie[zombie.type]
    paint.reset()
    paint.setColor(zombieConfig.主颜色)
    paint.setStyle(Paint.Style.FILL)
    local zombieWidth = config.grid.cellWidth - 10
    local zombieHeight = config.grid.cellHeight - 10
    canvas.drawRect(
    zombie.x, zombie.y + 5,
    zombie.x + zombieWidth, zombie.y + zombieHeight,
    paint
    )
    paint.reset()
    paint.setColor(0xFFFFFFFF)
    paint.setTextSize(dp2px(10))
    paint.setTextAlign(Paint.Align.CENTER)
    paint.setFakeBoldText(true)
    drawMultilineText(
    canvas, paint,
    zombieConfig.名字,
    zombie.x + zombieWidth / 2,
    zombie.y + zombieHeight / 2,
    dp2px(12)
    )
    local healthPercent = zombie.血量 / zombie.maxHealth
    paint.reset()
    paint.setStyle(Paint.Style.FILL)
    paint.setColor(0x88FF0000)
    canvas.drawRect(
    zombie.x, zombie.y,
    zombie.x + zombieWidth, zombie.y + 8,
    paint
    )
    paint.setColor(0xFF00FF00)
    canvas.drawRect(
    zombie.x, zombie.y,
    zombie.x + zombieWidth * healthPercent, zombie.y + 8,
    paint
    )
  end
end
function 绘制子弹(canvas, paint)
  paint.reset()
  paint.setColor(config.bullet.主颜色)
  paint.setStyle(Paint.Style.FILL)
  for _, bullet in ipairs(bullets) do
    canvas.drawCircle(bullet.x, bullet.y, config.bullet.width / 2, paint)
  end
end
function 绘制阳光(canvas, paint)
  for _, sun in ipairs(suns) do
    paint.reset()
    paint.setColor(0xFFFFD700)
    paint.setStyle(Paint.Style.FILL)
    canvas.drawCircle(sun.x, sun.y, config.sun.size / 2, paint)
    paint.reset()
    paint.setColor(0xFFFFAA00)
    paint.setStyle(Paint.Style.STROKE)
    paint.setStrokeWidth(3)
    canvas.drawCircle(sun.x, sun.y, config.sun.size / 2, paint)
    paint.reset()
    paint.setColor(0xFFFFFFFF)
    paint.setTextSize(config.sun.textSize)
    paint.setTextAlign(Paint.Align.CENTER)
    paint.setFakeBoldText(true)
    canvas.drawText(
    tostring(sun.value),
    sun.x,
    sun.y + config.sun.textSize / 3,
    paint
    )
  end
end
function 绘制爆炸效果(canvas, paint)
  for _, explosion in ipairs(explosions) do
    local alpha = math.floor(255 * (explosion.life / 500))
    paint.reset()
    if explosion.type == "big" then
      paint.setColor(Color.argb(alpha, 255, 100, 0))
     elseif explosion.type == "small" then
      paint.setColor(Color.argb(alpha, 255, 255, 0))
     else
      paint.setColor(Color.argb(alpha, 255, 150, 0))
    end
    paint.setStyle(Paint.Style.STROKE)
    paint.setStrokeWidth(5)
    canvas.drawCircle(explosion.x, explosion.y, explosion.radius, paint)
  end
end
function 绘制UI(canvas, paint)
  paint.reset()
  paint.setColor(0xDD2F4F4F)
  paint.setStyle(Paint.Style.FILL)
  canvas.drawRect(0, 0, SCREEN_WIDTH, UI_TOP_HEIGHT, paint)
  paint.reset()
  paint.setColor(0xFFFFD700)
  paint.setTextSize(dp2px(24))
  paint.setTextAlign(Paint.Align.LEFT)
  paint.setFakeBoldText(true)
  canvas.drawText(
  "阳光: " .. gameState.sun,
  SCREEN_WIDTH - dp2px(200),
  dp2px(35),
  paint
  )
  paint.reset()
  paint.setColor(0xFFFFFFFF)
  paint.setTextSize(dp2px(18))
  canvas.drawText(
  "波数: " .. gameState.wave,
  SCREEN_WIDTH - dp2px(200),
  dp2px(60),
  paint
  )
  canvas.drawText(
  "分数: " .. gameState.score,
  SCREEN_WIDTH - dp2px(200),
  UI_TOP_HEIGHT - dp2px(10),
  paint
  )
  绘制倍速按钮(canvas, paint)
end
function 绘制倍速按钮(canvas, paint)
  paint.reset()
  if gameState.isDoubleSpeed then
    paint.setColor(0xFFFFFF00) 
   else
    paint.setColor(0xFF666666) 
  end
  paint.setStyle(Paint.Style.FILL)
  canvas.drawRect(
  speedButton.x, speedButton.y,
  speedButton.x + speedButton.width, speedButton.y + speedButton.height,
  paint
  )
  paint.reset()
  paint.setColor(0xFFFFFFFF)
  paint.setTextSize(dp2px(14))
  paint.setTextAlign(Paint.Align.CENTER)
  paint.setFakeBoldText(true)
  local speedText = gameState.isDoubleSpeed and "2x" or "1x"
  canvas.drawText(
  speedText,
  speedButton.x + speedButton.width / 2,
  speedButton.y + speedButton.height / 2 + dp2px(5),
  paint
  )
  paint.reset()
  paint.setColor(0xFF000000)
  paint.setStyle(Paint.Style.STROKE)
  paint.setStrokeWidth(2)
  canvas.drawRect(
  speedButton.x, speedButton.y,
  speedButton.x + speedButton.width, speedButton.y + speedButton.height,
  paint
  )
end
function 渲染菜单()
  local canvas = holder.lockCanvas()
  if not canvas then return end
  local paint = Paint()
  canvas.drawColor(0xFF87CEEB)
  paint.setColor(0xFF2F4F4F)
  paint.setTextSize(dp2px(48))
  paint.setTextAlign(Paint.Align.CENTER)
  paint.setFakeBoldText(true)
  canvas.drawText(
  "植物大战僵尸",
  SCREEN_WIDTH / 2,
  SCREEN_HEIGHT / 3,
  paint
  )
  local buttonWidth = dp2px(200)
  local buttonHeight = dp2px(60)
  local buttonX = (SCREEN_WIDTH - buttonWidth) / 2
  local buttonY = SCREEN_HEIGHT / 2
  paint.reset()
  paint.setColor(0xFF4CAF50)
  paint.setStyle(Paint.Style.FILL)
  canvas.drawRect(
  buttonX, buttonY,
  buttonX + buttonWidth, buttonY + buttonHeight,
  paint
  )
  paint.reset()
  paint.setColor(0xFFFFFFFF)
  paint.setTextSize(dp2px(24))
  paint.setTextAlign(Paint.Align.CENTER)
  canvas.drawText(
  "开始游戏",
  SCREEN_WIDTH / 2,
  buttonY + buttonHeight / 2 + dp2px(8),
  paint
  )
  holder.unlockCanvasAndPost(canvas)
end
function 绘制植物卡牌(canvas, paint)
  local availableButtons = {}
  for _, button in ipairs(plantButtons) do
    if button.plant == "shovel" then
      table.insert(availableButtons, button)
     elseif isPlantUnlocked(button.plant) then
      table.insert(availableButtons, button)
    end
  end
  for _, button in ipairs(availableButtons) do
    local plantType = button.plant
    if plantType == "shovel" then
      paint.reset()
      if gameState.selectedPlant == "shovel" then
        paint.setColor(0xFFCCCCCC)
       else
        paint.setColor(0xFF999999)
      end
      paint.setStyle(Paint.Style.FILL)
      canvas.drawRect(
      button.x, button.y,
      button.x + button.width, button.y + button.height,
      paint
      )
      paint.reset()
      paint.setColor(0xFFFFFFFF)
      paint.setTextSize(dp2px(18))
      paint.setTextAlign(Paint.Align.CENTER)
      paint.setFakeBoldText(true)
      canvas.drawText(
      "铲子",
      button.x + button.width / 2,
      button.y + button.height / 2 + dp2px(6),
      paint
      )
     else
      local plantConfig = config.plant[plantType]
      local isAvailable = gameState.sun >= plantConfig.阳光消耗
      paint.reset()
      if gameState.selectedPlant == plantType then
        paint.setColor(0xFFFFFF00)
       elseif isAvailable then
        paint.setColor(plantConfig.主颜色)
       else
        paint.setColor(0xFF666666)
      end
      paint.setStyle(Paint.Style.FILL)
      canvas.drawRect(
      button.x, button.y,
      button.x + button.width, button.y + button.height,
      paint
      )
      paint.reset()
      paint.setColor(0xFFFFFFFF)
      paint.setTextSize(dp2px(14))
      paint.setTextAlign(Paint.Align.CENTER)
      paint.setFakeBoldText(true)
      drawMultilineText(
      canvas, paint,
      plantConfig.名字,
      button.x + button.width / 2,
      button.y + button.height / 2 - dp2px(10),
      dp2px(16)
      )
      paint.reset()
      paint.setColor(0xFFFFD700)
      paint.setTextSize(dp2px(16))
      canvas.drawText(
      tostring(plantConfig.阳光消耗),
      button.x + button.width / 2,
      button.y + button.height - dp2px(10),
      paint
      )
    end
    paint.reset()
    paint.setColor(0xFF000000)
    paint.setStyle(Paint.Style.STROKE)
    paint.setStrokeWidth(3)
    canvas.drawRect(
    button.x, button.y,
    button.x + button.width, button.y + button.height,
    paint
    )
  end
end
gameView.setOnTouchListener(View.OnTouchListener{
  onTouch = function(v, event)
    local action = event.getAction()
    local screenX = event.getX()
    local screenY = event.getY()
    if gameState.state == "menu" then
      if action == MotionEvent.ACTION_UP then
        local buttonWidth = dp2px(200)
        local buttonHeight = dp2px(60)
        local buttonX = (SCREEN_WIDTH - buttonWidth) / 2
        local buttonY = SCREEN_HEIGHT / 2
        if screenX >= buttonX and screenX <= buttonX + buttonWidth and
          screenY >= buttonY and screenY <= buttonY + buttonHeight then
          gameState.state = "levelSelect"
          updateLevelButtonPositions()
        end
      end
      return true
    end
    if gameState.state == "levelSelect" then
      if action == MotionEvent.ACTION_UP then
        local backButtonWidth = dp2px(120)
        local backButtonHeight = dp2px(50)
        local backButtonX = dp2px(20)
        local backButtonY = dp2px(20)
        if screenX >= backButtonX and screenX <= backButtonX + backButtonWidth and
          screenY >= backButtonY and screenY <= backButtonY + backButtonHeight then
          gameState.state = "menu"
          return true
        end
        for _, button in ipairs(levelButtons) do
          if screenX >= button.x and screenX <= button.x + button.width and
            screenY >= button.y and screenY <= button.y + button.height then
            if button.isUnlocked then
              if applyLevelConfig(button.levelId) then
                gameState.state = "playing"
                lastTime = System.currentTimeMillis()
              end
            end
            return true
          end
        end
      end
      return true
    end
    if gameState.state == "victory" then
      if action == MotionEvent.ACTION_UP then
        local continueButtonWidth = dp2px(150)
        local continueButtonHeight = dp2px(50)
        local continueButtonX = (SCREEN_WIDTH - continueButtonWidth) / 2
        local continueButtonY = (SCREEN_HEIGHT - continueButtonHeight) / 2 + dp2px(180)
        if screenX >= continueButtonX and screenX <= continueButtonX + continueButtonWidth and
          screenY >= continueButtonY and screenY <= continueButtonY + continueButtonHeight then
          gameState.state = "levelSelect"
          updateLevelButtonPositions()
        end
      end
      return true
    end
    return true
  end
})
if not table.contains then
  table.contains = function(t, value)
    for _, v in ipairs(t) do
      if v == value then
        return true
      end
    end
    return false
  end
end
updateLevelButtonPositions()
local function gameLoop()
  local currentTime = System.currentTimeMillis()
  local deltaTime = currentTime - lastTime
  lastTime = currentTime
  if gameState.state == "playing" then
    updateCamera()
    更新游戏(deltaTime)
    渲染游戏()
   elseif gameState.state == "menu" then
    渲染菜单()
   elseif gameState.state == "levelSelect" then
    渲染关卡选择()
   elseif gameState.state == "victory" then
    渲染胜利界面()
   elseif gameState.state == "gameover" then
    渲染游戏结束界面()
  end
  task(1000 / config.screen.fps, gameLoop)
end
function 检查游戏结束()
  for _, zombie in ipairs(zombies) do
    if zombie.x < config.grid.startX then
      gameState.state = "gameover"
      return
    end
  end
end
function 渲染游戏结束界面()
  local canvas = holder.lockCanvas()
  if not canvas then return end
  local paint = Paint()
  canvas.drawColor(0x88000000) 
  local panelWidth = dp2px(400)
  local panelHeight = dp2px(300)
  local panelX = (SCREEN_WIDTH - panelWidth) / 2
  local panelY = (SCREEN_HEIGHT - panelHeight) / 2
  paint.reset()
  paint.setColor(0xFFF44336)
  paint.setStyle(Paint.Style.FILL)
  canvas.drawRect(panelX, panelY, panelX + panelWidth, panelY + panelHeight, paint)
  paint.reset()
  paint.setColor(0xFF000000)
  paint.setStyle(Paint.Style.STROKE)
  paint.setStrokeWidth(4)
  canvas.drawRect(panelX, panelY, panelX + panelWidth, panelY + panelHeight, paint)
  paint.reset()
  paint.setColor(0xFFFFFFFF)
  paint.setTextSize(dp2px(32))
  paint.setTextAlign(Paint.Align.CENTER)
  paint.setFakeBoldText(true)
  canvas.drawText(
  "游戏结束",
  SCREEN_WIDTH / 2,
  panelY + dp2px(50),
  paint
  )
  paint.setTextSize(dp2px(20))
  canvas.drawText(
  "最终分数: " .. gameState.score,
  SCREEN_WIDTH / 2,
  panelY + dp2px(100),
  paint
  )
  canvas.drawText(
  "击杀僵尸: " .. gameState.zombiesKilled,
  SCREEN_WIDTH / 2,
  panelY + dp2px(140),
  paint
  )
  canvas.drawText(
  "波次: " .. gameState.waveProgress.currentWave,
  SCREEN_WIDTH / 2,
  panelY + dp2px(180),
  paint
  )
  local restartButtonWidth = dp2px(150)
  local restartButtonHeight = dp2px(50)
  local restartButtonX = (SCREEN_WIDTH - restartButtonWidth) / 2
  local restartButtonY = panelY + panelHeight + dp2px(20)
  paint.reset()
  paint.setColor(0xFF2196F3)
  paint.setStyle(Paint.Style.FILL)
  canvas.drawRect(
  restartButtonX, restartButtonY,
  restartButtonX + restartButtonWidth, restartButtonY + restartButtonHeight,
  paint
  )
  paint.reset()
  paint.setColor(0xFFFFFFFF)
  paint.setTextSize(dp2px(18))
  paint.setTextAlign(Paint.Align.CENTER)
  canvas.drawText(
  "重新开始",
  SCREEN_WIDTH / 2,
  restartButtonY + restartButtonHeight / 2 + dp2px(6),
  paint
  )
  holder.unlockCanvasAndPost(canvas)
end
local function getCellRow(cell)
  for row = 1, config.grid.rows do
    for col = 1, config.grid.cols do
      if grid[row][col] == cell then
        return row
      end
    end
  end
  return nil
end
gameView.setOnTouchListener(View.OnTouchListener{
  onTouch = function(v, event)
    local action = event.getAction()
    local screenX = event.getX()
    local screenY = event.getY()
    if gameState.state == "menu" then
      if action == MotionEvent.ACTION_UP then
        local buttonWidth = dp2px(200)
        local buttonHeight = dp2px(60)
        local buttonX = (SCREEN_WIDTH - buttonWidth) / 2
        local buttonY = SCREEN_HEIGHT / 2
        if screenX >= buttonX and screenX <= buttonX + buttonWidth and
          screenY >= buttonY and screenY <= buttonY + buttonHeight then
          gameState.state = "levelSelect"
          updateLevelButtonPositions()
        end
      end
      return true
    end
    if gameState.state == "levelSelect" then
      if action == MotionEvent.ACTION_UP then
        local backButtonWidth = dp2px(120)
        local backButtonHeight = dp2px(50)
        local backButtonX = dp2px(20)
        local backButtonY = dp2px(20)
        if screenX >= backButtonX and screenX <= backButtonX + backButtonWidth and
          screenY >= backButtonY and screenY <= backButtonY + backButtonHeight then
          gameState.state = "menu"
          return true
        end
        for _, button in ipairs(levelButtons) do
          if screenX >= button.x and screenX <= button.x + button.width and
            screenY >= button.y and screenY <= button.y + button.height then
            if button.isUnlocked then
              if applyLevelConfig(button.levelId) then
                gameState.state = "playing"
                lastTime = System.currentTimeMillis()
              end
            end
            return true
          end
        end
      end
      return true
    end
    if gameState.state == "victory" then
      if action == MotionEvent.ACTION_UP then
        local continueButtonWidth = dp2px(150)
        local continueButtonHeight = dp2px(50)
        local continueButtonX = (SCREEN_WIDTH - continueButtonWidth) / 2
        local continueButtonY = (SCREEN_HEIGHT - continueButtonHeight) / 2 + dp2px(180)
        if screenX >= continueButtonX and screenX <= continueButtonX + continueButtonWidth and
          screenY >= continueButtonY and screenY <= continueButtonY + continueButtonHeight then
          gameState.state = "levelSelect"
          updateLevelButtonPositions()
        end
      end
      return true
    end
    if gameState.state == "gameover" then
      if action == MotionEvent.ACTION_UP then
        local restartButtonWidth = dp2px(150)
        local restartButtonHeight = dp2px(50)
        local restartButtonX = (SCREEN_WIDTH - restartButtonWidth) / 2
        local restartButtonY = (SCREEN_HEIGHT - restartButtonHeight) / 2 + dp2px(180)
        if screenX >= restartButtonX and screenX <= restartButtonX + restartButtonWidth and
          screenY >= restartButtonY and screenY <= restartButtonY + restartButtonHeight then
          if applyLevelConfig(gameState.currentLevelId) then
            gameState.state = "playing"
            lastTime = System.currentTimeMillis()
          end
        end
      end
      return true
    end
    if gameState.state == "playing" then
      if action == MotionEvent.ACTION_DOWN then
        touchState.mode = "down"
        touchState.lastX = screenX
        touchState.lastY = screenY
        touchState.pointerCount = 1
        touchState.hasMoved = false
        touchState.downTime = System.currentTimeMillis()
        if screenX >= speedButton.x and screenX <= speedButton.x + speedButton.width and
          screenY >= speedButton.y and screenY <= speedButton.y + speedButton.height then
          if gameState.isDoubleSpeed then
            gameState.gameSpeed = 1.0
            gameState.isDoubleSpeed = false
           else
            gameState.gameSpeed = 2.0
            gameState.isDoubleSpeed = true
          end
          return true
        end
        local worldX, worldY = screenToWorld(screenX, screenY)
        for i = #suns, 1, -1 do
          local sun = suns[i]
          local distance = math.sqrt((worldX - sun.x)^2 + (worldY - sun.y)^2)
          local touchRadius = config.sun.size / 2 / camera.scale
          if distance <= touchRadius then
            gameState.sun = gameState.sun + sun.value
            生成爆炸效果(sun.x, sun.y, "small")
            table.remove(suns, i)
            return true 
          end
        end
        for _, button in ipairs(plantButtons) do
          if screenX >= button.x and screenX <= button.x + button.width and
            screenY >= button.y and screenY <= button.y + button.height then
            if button.plant == "shovel" then
              gameState.selectedPlant = "shovel"
              return true
            end
            if not isPlantUnlocked(button.plant) then
              return true 
            end
            local plantConfig = config.plant[button.plant]
            if gameState.sun >= plantConfig.阳光消耗 then
              dragState.isDragging = true
              dragState.draggedPlant = button.plant
              dragState.dragX = screenX
              dragState.dragY = screenY
              dragState.startX = screenX
              dragState.startY = screenY
              gameState.selectedPlant = button.plant
            end
            return true
          end
        end
       elseif action == MotionEvent.ACTION_POINTER_DOWN then
        touchState.pointerCount = event.getPointerCount()
        if touchState.pointerCount == 2 then
          touchState.mode = "zoom"
          touchState.lastDistance = getDistance(event)
        end
       elseif action == MotionEvent.ACTION_MOVE then
        local moveDistance = math.sqrt((screenX - touchState.lastX)^2 + (screenY - touchState.lastY)^2)
        if moveDistance > 10 then
          touchState.hasMoved = true
        end
        if dragState.isDragging then
          dragState.dragX = screenX
          dragState.dragY = screenY
         elseif touchState.mode == "zoom" and event.getPointerCount() == 2 then
          local newDistance = getDistance(event)
          if touchState.lastDistance > 0 then
            local scaleFactor = newDistance / touchState.lastDistance
            camera.targetScale = math.max(camera.minScale,
            math.min(camera.maxScale,
            camera.scale * scaleFactor))
          end
          touchState.lastDistance = newDistance
         elseif touchState.mode == "down" and event.getPointerCount() == 1 and not dragState.isDragging then
          local dx = (screenX - touchState.lastX) / camera.scale
          local dy = (screenY - touchState.lastY) / camera.scale
          camera.targetX = camera.targetX - dx
          camera.targetY = camera.targetY - dy
          touchState.lastX = screenX
          touchState.lastY = screenY
        end
       elseif action == MotionEvent.ACTION_UP then
        if dragState.isDragging then
          local worldX, worldY = screenToWorld(screenX, screenY)
          local cell = 获取网格单元格(worldX, worldY)
          local canPlantHere = false
          if cell then
            local cellRow = getCellRow(cell)
            local levelConfig = gameState.currentLevelConfig
            local availableRows = levelConfig and levelConfig.availableRows or {1, 2, 3, 4, 5}
            for _, availableRow in ipairs(availableRows) do
              if cellRow == availableRow then
                canPlantHere = true
                break
              end
            end
          end
          if cell and not cell.plant and not cell.isZombieSpawnZone and canPlantHere then
            local plantConfig = config.plant[dragState.draggedPlant]
            if gameState.sun >= plantConfig.阳光消耗 then
              cell.plant = {
                type = dragState.draggedPlant,
                血量 = plantConfig.血量,
                sunTimer = 0,
                fireTimer = 0,
                fuseTimer = 0
              }
              gameState.sun = gameState.sun - plantConfig.阳光消耗
            end
           elseif cell and not canPlantHere then
            生成爆炸效果(worldX, worldY, "small")
          end
          dragState.isDragging = false
          dragState.draggedPlant = nil
          gameState.selectedPlant = nil
         elseif gameState.selectedPlant == "shovel" and not touchState.hasMoved then
          local worldX, worldY = screenToWorld(screenX, screenY)
          local cell = 获取网格单元格(worldX, worldY)
          if cell and cell.plant then
            cell.plant = nil
          end
          gameState.selectedPlant = nil
        end
        touchState.mode = "none"
        touchState.pointerCount = 0
       elseif action == MotionEvent.ACTION_POINTER_UP then
        touchState.pointerCount = event.getPointerCount() - 1
        if touchState.pointerCount < 2 then
          touchState.mode = "down"
          touchState.lastX = screenX
          touchState.lastY = screenY
        end
      end
    end
    return true
  end
})
function 渲染游戏()
  local canvas = holder.lockCanvas()
  if not canvas then return end
  local paint = Paint()
  canvas.drawColor(0xFF87CEEB)
  canvas.save()
  canvas.scale(camera.scale, camera.scale)
  canvas.translate(-camera.x, -camera.y)
  local levelConfig = gameState.currentLevelConfig
  local availableRows = levelConfig and levelConfig.availableRows or {1, 2, 3, 4, 5}
  for row = 1, config.grid.rows do
    local isAvailableRow = false
    for _, availableRow in ipairs(availableRows) do
      if row == availableRow then
        isAvailableRow = true
        break
      end
    end
    local rowY = config.grid.startY + (row - 1) * config.grid.cellHeight
    local rowHeight = config.grid.cellHeight
    paint.reset()
    if isAvailableRow then
      paint.setColor(0xFF228B22)
     else
      paint.setColor(0xFF8B4513)
    end
    paint.setStyle(Paint.Style.FILL)
    canvas.drawRect(0, rowY, SCREEN_WIDTH, rowY + rowHeight, paint)
  end
  paint.reset()
  paint.setColor(0xFFFF0000)
  paint.setStyle(Paint.Style.STROKE)
  paint.setStrokeWidth(5)
  canvas.drawRect(camera.worldMinX, camera.worldMinY,
  camera.worldMaxX, camera.worldMaxY, paint)
  drawGrid(canvas, paint)
  if gameState.selectedPlant == "shovel" then
    paint.reset()
    paint.setColor(0x44FF0000)
    paint.setStyle(Paint.Style.FILL)
    for row = 1, config.grid.rows do
      for col = 1, config.grid.cols do
        local cell = grid[row][col]
        if cell.plant then
          canvas.drawRect(cell.x, cell.y,
          cell.x + config.grid.cellWidth,
          cell.y + config.grid.cellHeight, paint)
        end
      end
    end
  end
  绘制植物(canvas, paint)
  绘制僵尸(canvas, paint)
  绘制子弹(canvas, paint)
  绘制阳光(canvas, paint)
  绘制爆炸效果(canvas, paint)
  canvas.restore()
  绘制UI(canvas, paint)
  绘制植物卡牌(canvas, paint)
  绘制关卡信息(canvas, paint)
  if dragState.isDragging and dragState.draggedPlant then
    绘制拖拽植物(canvas, paint)
  end
  holder.unlockCanvasAndPost(canvas)
end
function 绘制关卡信息(canvas, paint)
  local levelConfig = gameState.currentLevelConfig
  if not levelConfig then return end
  paint.reset()
  paint.setColor(0xFF2F4F4F)
  paint.setTextSize(dp2px(20))
  paint.setTextAlign(Paint.Align.LEFT)
  paint.setFakeBoldText(true)
  canvas.drawText(
  levelConfig.name,
  dp2px(20),
  UI_TOP_HEIGHT - dp2px(10),
  paint
  )
  local waveText = "波次: " .. gameState.waveProgress.currentWave .. "/" .. levelConfig.waves
  if gameState.waveProgress.isFlagWave then
    paint.setColor(0xFFFFFF00) 
    waveText = waveText .. " 🚩"
   else
    paint.setColor(0xFFFFFFFF)
  end
  paint.setTextSize(dp2px(18))
  canvas.drawText(
  waveText,
  SCREEN_WIDTH - dp2px(400),
  dp2px(60),
  paint
  )
end
local function initializeGame()
  gameState.state = "menu"
  gameState.gameSpeed = 1.0
  gameState.isDoubleSpeed = false
  if not playerProgress then
    playerProgress = {
      unlockedPlants = {"peashooter"},
      completedLevels = {},
      currentLevel = "1-1",
      totalSunCollected = 0,
      totalZombiesKilled = 0
    }
  end
  updateLevelButtonPositions()
  gameLoop()
end
activity.setContentView(gameView)
initializeGame()
--跳了🐧



function read(target) --target文件位置
  local file=io.open(target,"a") --以防文件不存在
  io.close(file)
  file=io.open(target,"r")
  io.input(file)
  local result=io.read("*a")
  io.close(file)
  return result
  end
  
function input(target,content) --target文件位置;content写入内容
  local file=io.open(target,"a") --以防文件不存在
  io.close(file)
  file=io.open(target,"w+")
  io.output(file)
  io.write(content)
  io.flush()
  io.close(file)
  end
  
  
  
import "java.lang.Throwable"

local function printStackTrace()
  local stackTrace = Throwable().stackTrace
  for _,v in pairs(luajava.astable(stackTrace)) do
    print("at "..tostring(v))
  end
end

printStackTrace()



--Source：氚-Tritium 2957148920
--OpenSource：Apache License Version

-- YouTube解析演示用例实例
-- https://m.youtube.com/shorts/Y6IiTkL_sA8
-- https://m.youtube.com/watch?v=eou_g_cYCew&pp=ugUEEgJlbg==

-- 尝试更多，以体验解析效果

-- 初始化解析函数
local PARSE_YOUTUBE_MODE_LIST = {
  {
    { value="144", name="MP4 (144p)"},
    { value="240", name="MP4 (240p)"},
    { value="360", name="MP4 (360p)"},
    { value="480", name="MP4 (480p)"},
    { value="720", name="MP4 (720p)"},
    { value="1080", name="MP4 (1080p)"},
    { value="1440", name="MP4 (1440p)"},
    { value="4k", name="WEBM (4K)"}
  },
  {
    { value="mp3", name="MP3"},
    { value="m4a", name="M4A"},
    { value="webm", name="WEBM"},
    { value="aac", name="AAC"},
    { value="flac", name="FLAC"},
    { value="opus", name="OPUS"},
    { value="ogg", name="OGG"},
    { value="wav", name="WAV"}
  }
}


function getParseMode(t, row)
  local success, result = pcall(function ()
    local data = PARSE_YOUTUBE_MODE_LIST[t]
    return data[row]
  end)
  return (success and result) and result or PARSE_YOUTUBE_MODE_LIST
end

function selectModeAlertDialog(callback)
  local data = {{},{}}
  for _, v in pairs(PARSE_YOUTUBE_MODE_LIST) do
    for _, val in pairs(v) do
      table.insert(data[1], val.name)
      table.insert(data[2], val.value)
    end
  end
  luajava.newInstance("com.google.android.material.dialog.MaterialAlertDialogBuilder", this)
  .setTitle("选择解析模式")
  .setNegativeButton("取消", nil)
  .setSingleChoiceItems(data[1], -1, {
    onClick = function(v, pos)
      callback(data[2][pos+1])
      v.dismiss()
    end
  }).show()
end

function handleVideoParse(format, url, callback)
  local result = {success=false}
  local api = "https://p.savenow.to/ajax/download.php?copyright=0&api=dfcb6d76f2f6a9894gjkege8a4ab232222&format=%s&url=%s"
  Http.get(string.format(api, format, url),cookie,charset,header,function(code,content,cookie,heafer)
    if code == 200 and content then
      local success, resultback = pcall(function ()
        return require('cjson').decode(content)
      end)
      if success and resultback and type(resultback) == "table" then
        result = table.clone(resultback)
      end
    end
    callback(result)
  end)
end

function handleVideoProgress(id, callback)
  local api = "https://p.savenow.to/api/progress?id=%s"
  Http.get(string.format(api, id),cookie,charset,header,function(code,content,cookie,heafer)
    if code == 200 and content then
      local success, resultback = pcall(function ()
        return require('cjson').decode(content)
      end)
      if success and resultback and type(resultback) == "table" then
        result = table.clone(resultback)
      end
    end
    callback(result)
  end)
end

-- 演示布局
activity.getWindow().setSoftInputMode(32)
activity.setContentView(loadlayout({
  LinearLayout;
  orientation='vertical';
  layout_width='fill';
  layout_height='fill';
  {
    ScrollView;
    layout_width='fill';
    layout_height='0dp';
    layout_weight='1';
    {
      LinearLayout;--线性控件
      orientation='vertical';--布局方向
      layout_width='fill';--布局宽度
      layout_height='fill';--布局高度

      {
        TextView;
        layout_width='fill';
        layout_height='wrap';
        layout_marginTop='16dp';
        layout_marginBottom='56dp';
        layout_marginLeft='12dp';
        layout_marginRight='12dp';
        textSize='16sp';
        textColor='#888888';
        id='contentView';
        text="输入地址点击parse开始解析",
        textIsSelectable=true;
        gravity='left|center';
      }
    }
  };
  {
    TextView;
    layout_width='fill';
    layout_height='1dp';
    layout_gravity='center';
    backgroundColor='#33888888';
  };


  {
    LinearLayout;
    orientation='vertical';
    layout_width='fill';
    layout_height='wrap';
    layout_margin='6dp';

    {
      ProgressBar,
      layout_width="fill";
      layout_height="wrap";
      layout_margin='6dp';
      Max='1000';
      Progress='0';
      indeterminate=false;
      id='progress';
      Visibility=8;
      style='?android:attr/progressBarStyleHorizontal';--长形进度条
    };

    {
      LinearLayout;
      layout_width='fill';
      layout_height='wrap';
      {
        EditText;
        layout_width='wrap';
        layout_height='wrap';
        layout_weight='1';
        singleLine=true;
        id='input';
      };
      {
        Button;
        layout_width='wrap';
        layout_height='wrap';
        text='parse';
        id='btn';
        gravity='center';
      };
    };
  }
}))

dataValue = {
  value = {
    start = false,
    nexts = true,
    parseId = nil,
  }
}

function initDemo(url, mode)
  progress.setVisibility(0)
  progress.setIndeterminate(true)
  contentView.setText(mode..' 模式解析：'..url)
  handleVideoParse(mode, url, function(data)
    if data.success then
      if data.id then
        dataValue.value.start = true
        dataValue.value.parseId = data.id
        contentView.setText(string.format("任务提交成功，解析即将开始处理\n%s", contentView.text))
      end
     else
      btn.setEnabled(true)
      contentView.setText('解析出现异常'..dump(data))
    end
  end)
end

btn.setOnClickListener(View.OnClickListener{
  onClick = function(view)
    selectModeAlertDialog(function(mode)
      initDemo(tostring(input.getText()), mode)
      view.setEnabled(false)
    end)
  end
})


-- 测试用例
input.setText('https://www.youtube.com/watch?v=k5atuC9JLdM')
input.setText('https://m.youtube.com/shorts/zkEs_YSx6lo')
input.setText('https://m.youtube.com/shorts/v90KgKSFgmU')

-- 声明全局变量
ti = luajava.newInstance("com.androlua.Ticker")
ti.setPeriod(1000)
ti.setOnTickListener(luajava.bindClass("com.androlua.Ticker$OnTickListener"){
  onTick = function()
    if dataValue.value.start and dataValue.value.nexts then
      pcall(function()
        dataValue.value.nexts = false
        progress.setIndeterminate(false)
        handleVideoProgress(dataValue.value.parseId, function(data)
          if data.success == 0 then
            progress.setProgress(tointeger(data.progress or 0) or 0)
            -- 进度汇报
            contentView.setText(string.format("[%s] 正在处理，进度：%d/1000\n%s", os.time(), tointeger(data.progress) , contentView.text))
           else
            contentView.setText(string.format("处理完成，下载地址：%s\n%s", data.download_url, contentView.text))
            progress.setVisibility(8)
            btn.setEnabled(true)
            dataValue.value.start = false
          end
          dataValue.value.nexts = true
        end)
      end)
    end
  end
})
ti.start()

function onDestroy()
  if ti then
    ti.stop()
    ti = nil
  end
end



--Source：氚-Tritium 2957148920
--OpenSource：Apache License Version

-- SearchView 简单的搜索提示实例
-- 请确保您的material库版本足够高

-- 如果您不是基于本手册编写测试用例
-- 主题和layout_behavior可能需要修改

-- bing：https://cn.bing.com/AS/Suggestions?pt=page.home&qry=%s&cp=2&csr=1&pths=1&cvid=E0DA42E05C7940BF8A8AA46E33C04F4C


import "androidx.coordinatorlayout.widget.CoordinatorLayout"
import "androidx.recyclerview.widget.LinearLayoutManager"
import "androidx.appcompat.widget.LinearLayoutCompat"

import "com.google.android.material.textview.MaterialTextView"
import "com.google.android.material.appbar.AppBarLayout"
import "com.google.android.material.search.SearchView"
import "com.google.android.material.search.SearchBar"

activity.setTheme(R.style.Theme_Material3_Blue)
activity.supportActionBar.hide()
activity.setContentView(loadlayout({
  CoordinatorLayout,
  layout_width = "fill",
  layout_height = "fill",
  background="#ffffff",

  {
    AppBarLayout,
    id = "appbar",
    layout_width = "fill",
    layout_height = "wrap",
    background="#ffffff",

    {
      SearchBar,
      id = "searchbar",
      layout_width = "fill",
      layout_height = "wrap",
      hint = "输入关键词以搜索内容",
    },
  },
  {
    SearchView,
    id = "searchview",
    layout_width = "fill",
    layout_height = "wrap",
    hint = "输入关键词以搜索内容",
    layout_anchor = "searchbar",

    {
      ListView;--列表适配器
      layout_width='fill';--宽度
      layout_height='fill';--高度
      id="listSugrec";
      DividerHeight=3;
      verticalScrollBarEnabled=false;--隐藏滑条
    };
  };

  {
    LinearLayoutCompat;--线性控件
    orientation='vertical';--布局方向
    layout_width='fill';--布局宽度
    layout_height='fill';--布局高度
    layout_behavior = "appbar_scrolling_view_behavior",

    {
      LuaWebView;--浏览器控件
      layout_width='fill';--浏览器宽度
      layout_height='fill';--浏览器高度
      id='webView';--控件ID
    }
  }
}))

listSugrec.setAdapter(LuaAdapter(activity, {
  LinearLayoutCompat;--线性控件
  orientation='vertical';--布局方向
  layout_width='fill';--布局宽度
  layout_height='wrap';--布局高度

  {
    MaterialTextView;--文本控件
    layout_width='fill';--控件宽度
    layout_height='wrap';--控件高度
    layout_marginLeft='16dp';--布局左距
    layout_marginRight='16dp';--布局右距
    layout_marginTop='12dp';--布局顶距
    layout_marginBottom='12dp';--布局底距
    textSize='16sp';--文字大小
    id='q';--设置控件ID
    singleLine=true;--设置单行输入
    ellipsize='middle';--多余文字用省略号显示
    gravity='left|center';--重力
  };
}))

function handleSearchSugrecData(keyword)
  local datas = {}
  local url = string.format("https://www.baidu.com/sugrec?prod=pc&wd=%s", keyword)
  Http.get(url,cookie,charset,header,function(code,content,cookie,heafer)
    if(code == 200 and content)then
      local success, resultback = pcall(require("cjson").decode, content)
      if success and resultback and resultback.g and #resultback.g > 0 then
        datas = table.clone(resultback.g)
      end
    end
    listSugrec.getAdapter().clear()
    listSugrec.getAdapter().addAll(#datas > 0 and datas or (#keyword > 0 and {{g=keyword}} or {}))
  end)
end

searchview.getEditText().addTextChangedListener({
  onTextChanged = function(str)
    if #str > 0 and #str < 256 then
      handleSearchSugrecData(tostring(str))
     else
      listSugrec.getAdapter().clear()
    end
  end
})


listSugrec.setOnItemClickListener(ListView.OnItemClickListener{
  onItemClick=function(l, view, pos, id)
    local keyword = listSugrec.getAdapter().getData()[pos+1].q
    local url = string.format("https://www.bing.com/search?q=%s&search=&form=QBLH", keyword)
    searchbar.setHint(keyword)
    searchview.hide()
    webView.loadUrl(url)
  end
})

listSugrec.setOnItemLongClickListener(ListView.OnItemLongClickListener{
  onItemLongClick=function(l, view, pos, id)
    local keyword = listSugrec.getAdapter().getData()[pos+1].q
    print(keyword)
    return true
  end
})



--Source：氚-Tritium 2957148920
--OpenSource：Apache License Version

-- 需要JXDocument，Xpath库自行处理

-- Pexels无版权4K图片解析封装
-- 界面展示暂时还没写，搜索/详情/图片地址

local dataValue = {
  config = {
    api = 'https://pexels.com/zh-cn',
  }
}

import "org.xpath.JXDocument"

function handlePhotoSearch(keyword, page, callback)
  local datas, url = {}, string.format("%s/search/%s/?page=%d", dataValue.config.api, keyword, page)
  Http.get(url, cookie, charset, header, function(code,content,cookie,heafer)
    if(code == 200 and content)then
      local jxdoc = JXDocument(content)
      local images = jxdoc.sel('//a[@data-testid="next-link"]/img/@src')
      local titles = jxdoc.sel('//a[@data-testid="next-link"]/img/@alt')

      if images and images.size() > 0 and titles and titles.size() > 0 then
        for i=0, math.min(images.size(), titles.size())-1 do
          local title = utf8.match(titles.get(i), "^免费 (.-) 素材图片$")
          local photoId = utf8.match(images.get(i), "/photos/(.-)/")
          if photoId and utf8.len(photoId) > 0 then
            table.insert(datas, {
              image = images.get(i),
              title = title or titles.get(i) or '暂无标题',
              photoId = photoId,
            })
          end
        end
      end
    end

    if datas and type(datas) == "table" then
      callback(true, datas)
     else
      callback(false, {})
    end
  end)
end


function handlePhotosDetail(photoId, callback)
  local datas, url = "", string.format("%s/photo/%s", dataValue.config.api, photoId)
  Http.get(url, cookie, charset, header, function(code,content,cookie,heafer)
    if(code == 200 and content)then
      local jxdoc = JXDocument(content)
      local jsons = jxdoc.sel('//script[@type="application/ld+json"]//text()')

      if jsons and jsons.size() > 0 then
        for i=0, jsons.size()-1 do
          local success, resultback = pcall(function()
            return require('cjson').decode(jsons.get(i))
          end)
          if success and resultback and type(resultback)=="table" then
            if resultback["@type"] == "ImageObject" then
              datas = table.clone(resultback) break
            end
          end
        end
      end
    end

    if datas and type(datas) == "table" then
      callback(true, datas)
     else
      callback(false, {})
    end
  end)
end

function handlePhotosUrl(datas, width, hight)
  local function formatMeasure(max, size)
    if type(size) ~= 'number' then
      if tonumber(size) == nil then
        size = max
       else
        size = tonumber(size)
      end
    end
    return tointeger(size > max and max or size)
  end
  local url = nil
  if datas and type(datas)=="table" then
    if datas["@type"] == "ImageObject" then
      -- 读取数据
      local contentUrl = datas.contentUrl
      local maxWidth, maxHeight = datas.width, datas.height

      -- 格式化参数
      width = formatMeasure(maxWidth, width)
      height = formatMeasure(maxHeight, height)

      -- 拼接地址
      url = string.format('%s?w=%d&h=%d', contentUrl, width, height)
    end
  end
  if url then
    return true, url
  end
  return false, ''
end


-- demo
handlePhotoSearch("雪", 1, function(success, datas)
  -- 搜索列表
  print(dump(datas))

  handlePhotosDetail(datas[1].photoId, function(success, datas)
    -- 详情数据
    print(dump(datas))

    -- 原图获取
    print(handlePhotosUrl(datas, width, hight))
  end)
end)



--Source：氚-Tritium 2957148920
--OpenSource:Apache License Version

-- @integer[string] timestamp[date]
local function diff(timestamp)
  local function time2Unix(time)
    local ok, val = pcall(function ()
      local y, m, d, h, M, s = utf8.match(time, "^(%d+)-(%d+)-(%d+)%s+(%d+):(%d+):(%d+)")
      return os.time({
        year = y, month = m, day = d,
        hour = h, min = M, sec = s
      })
    end)
    if ok then
      return val or os.time()
    end
    return os.time()
  end
  if type(timestamp) == "string" then
    if tointeger(timestamp) then
      timestamp = tointeger(timestamp)
     else
      timestamp = time2Unix(timestamp)
    end
  end
  local diffs = math.abs(os.time() - (timestamp or os.time()))
  for _, unit in ipairs({
      {31536000, "年"}, {86400, "天"},
      {3600, "小时"}, {60, "分钟"}
    }) do
    if diffs >= unit[1] then
      return string.format("%.0f%s", diffs / unit[1], unit[2])
    end
  end
  return diffs <= 1 and "刚刚更新" or string.format("%.0f秒", diffs)
end


-- demo test
print(
diff(),
diff(0),
diff("🤔🌿"),
diff("502EF7J8GK"),
diff(math.maxinteger),
diff(os.time()+114514),
diff(os.time()-114514),
diff("2022-02-22 22:22:22"))



--Source：氚-Tritium 2957148920
--OpenSource:Apache License Version

-- 跑不了，别试了
-- 没有SmartRefreshLayout

-- 本项目需要以下依赖，请不要问为什么跑不了
-- appcompat/material/Glide/SmartRefreshLayout
-- 举例的依赖不是全部而是需要但您可能没有的

-- 数据有1440分钟缓存，超时进入自动刷新
-- 数据由SQLiteDatabase储存管理

-- 基于Android SQLiteDatabase
-- 需要一个数据库操作工具，此处是ID-1的数据操作封装

-- LuaSQLite数据库连贯操作
-- By:夜色未央

-- update：2025年10月10日 氚-Tritium
-- 更高效的游标数据处理和字面量装箱函数
-- 值table类型自动encode为JSON字符串


local LuaSQLite = {}

local TRUE_BOOLEAN = luajava.newInstance("java.lang.Boolean", true)
local FALSE_BOOLEAN = luajava.newInstance("java.lang.Boolean", false)
local EMPTY_STRING = luajava.newInstance("java.lang.String", "")

local Cursor = luajava.bindClass "android.database.Cursor"

--创建数据库对象
function LuaSQLite:new(databaseName)
  local obj = {}
  obj.options = {}
  local SQLiteDatabase = luajava.bindClass("android.database.sqlite.SQLiteDatabase")
  obj.db = SQLiteDatabase.openOrCreateDatabase(databaseName, nil)

  obj._split = function(str, delimiter)
    local result = {}
    for part in (str..delimiter):gmatch("(.-)%"..delimiter) do
      table.insert(result, part)
    end
    return result
  end

  obj._createContentValues = function(args)
    local ContentValues = luajava.bindClass("android.content.ContentValues")
    local function toValue(value)
      if value == nil then return nil end

      local t = type(value)

      if t == "number" then
        if math.floor(value) == value and value >= -2^31 and value < 2^31 then
          return luajava.newInstance("java.lang.Long", value)
        end
        return luajava.newInstance("java.lang.Double", value)
      end

      if t == "boolean" then
        return value and TRUE_BOOLEAN or FALSE_BOOLEAN
      end

      if t == "table" then
        local success, json = pcall(require("cjson").encode, value)
        if success then
          return luajava.newInstance("java.lang.String", json)
        end
        return luajava.newInstance("java.lang.String", "{}")
      end

      if t == "string" then
        return luajava.newInstance("java.lang.String", value)
      end
      return luajava.newInstance("java.lang.String", tostring(value))
    end

    local dataArgs = luajava.newInstance("android.content.ContentValues")
    local success, error = pcall(function()
      for k, v in pairs(args) do
        if k then
          dataArgs.put(tostring(k), toValue(v))
        end
      end
    end)

    return dataArgs
  end

  obj._processCondition = function(field, operator, param)
    local upperOperator = string.upper(tostring(operator))--转为大写
    -- 字段名处理
    local fieldName = tostring(field)
    if not fieldName:find("`") and not fieldName:find("%(") and not fieldName:find("[%+%-%*/]") then
      fieldName = "`" .. fieldName .. "`"
    end

    if type(param) == "string" and param:upper() == "NULL" then
      -- 特殊处理NULL值
      if operator == "=" then
        return fieldName .. " IS NULL", {}
       elseif operator == "!=" or operator == "<>" then
        return fieldName .. " IS NOT NULL", {}
      end
     elseif upperOperator == "BETWEEN" or upperOperator == "NOT BETWEEN" then
      -- BETWEEN查询条件
      local value1, value2 = param:match("^%s*(%w+)%s*,%s*(%w+)%s*$")
      if value1 and value2 then
        return fieldName .. " " .. upperOperator .. " ? AND ?", {value1, value2}
       else
        return fieldName .. " " .. upperOperator .. " ? AND ?", param
      end
     elseif upperOperator == "IN" or upperOperator == "NOT IN" then
      -- IN查询条件
      local placeholders = obj._split(param, ",")
      local fields = {}
      local params = {}
      for _, p in ipairs(placeholders) do
        table.insert(fields, "?")
        table.insert(params, p)
      end
      return fieldName .. " " .. upperOperator .. " (" .. table.concat(fields, ",") .. ")", params
    end
    return fieldName .. " " .. operator .. " ?", param
  end

  -- 创建条件函数
  obj._createCondition = function(fields, operator, param)
    local conditionSql = ""
    local conditionParam = {}
    -- 快捷查询处理
    if type(fields) == "string" and (fields:find("%|") or fields:find("&")) then
      local logicOperator = "AND"
      local fieldList = {}
      if fields:find("&") then
        logicOperator = "AND" -- 使用 & 符号表示 AND 连接
        fieldList = obj._split(fields, "&")
       else
        logicOperator = "OR" -- 使用 | 符号表示 OR 连接
        fieldList = obj._split(fields, "|")
      end
      local args, params = {}, {}
      for _, value in ipairs(fieldList) do
        local condition, paramValue = obj._processCondition(value, operator, param)
        table.insert(args, condition)
        if type(paramValue) == "table" then
          for _, p in ipairs(paramValue) do
            table.insert(params, p)
          end
         else
          table.insert(params, paramValue)
        end
      end
      conditionSql = string.format("( %s )", table.concat(args, " " .. logicOperator .. " "))
      conditionParam = params
     else
      conditionSql, conditionParam = obj._processCondition(fields, operator, param)
    end
    return {sql = conditionSql, param = conditionParam}
  end

  setmetatable(obj, self)
  self.__index = self
  return obj
end

--事务方法
function LuaSQLite:transaction(callback)
  if self.db.inTransaction() then
    return callback()-- 如果已经在事务中，直接执行回调函数而不开始新事务
  end
  self.db.beginTransaction()--在EXCLUSIVE模式下开始事务
  local success, result = pcall(callback)
  if success then
    self.db.setTransactionSuccessful()--标记事务成功（等待提交）
  end
  self.db.endTransaction()--结束事务（成功则提交，失败则回滚）
  if not success then
    error(result)
  end
  return result
end

--指定表名
function LuaSQLite:name(tableName)
  if not tableName or tableName == "" then
    error("name方法必须指定表名")
  end
  local query = {} -- 创建新的查询实例
  query.db = self.db
  query.options = {}
  query.options["name"] = tostring(tableName)
  setmetatable(query, {__index = self})
  return query
end

-- 执行闭包并获取结果结构
function LuaSQLite:_execClosure(closureFunc)
  local tempQuery = {} -- 创建临时查询对象
  tempQuery.options = { where = {} }
  setmetatable(tempQuery, {__index = self})
  closureFunc(tempQuery)
  -- 获取闭包生成的where结构
  local closureWhere = tempQuery.options["where"]
  if closureWhere and #closureWhere > 0 then
    local sqlParts, allParams = {}, {}
    for _, conditionGroup in ipairs(closureWhere) do
      if #conditionGroup > 0 then
        local groupSqlParts, groupParams = {}, {}
        for _, condition in ipairs(conditionGroup) do
          table.insert(groupSqlParts, condition.sql)
          if type(condition.param) == "table" then
            for _, paramValue in ipairs(condition.param) do
              table.insert(groupParams, paramValue)
            end
           else
            table.insert(groupParams, condition.param)
          end
        end
        local groupCondition = table.concat(groupSqlParts, " AND ")
        if groupCondition:find(" AND ") then
          table.insert(sqlParts, "(" .. groupCondition .. ")")
         else
          table.insert(sqlParts, groupCondition)
        end
        for _, param in ipairs(groupParams) do
          table.insert(allParams, param)
        end
      end
    end
    local finalSql = table.concat(sqlParts, " OR ")
    return {sql = finalSql, param = allParams}
  end
  return nil
end

-- 解析where条件的辅助函数
function LuaSQLite:_parseWhereArgs(fields,operator,param)
  local whereArgs = {}
  if type(fields) == "function" then--闭包查询
    local closureWhere = self:_execClosure(fields)
    if closureWhere then
      table.insert(whereArgs, closureWhere)
    end
   elseif type(fields) == "table" and operator == nil then --批量查询
    for k, v in pairs(fields) do
      if type(v) == "table" and #v == 3 then
        table.insert(whereArgs, self._createCondition(v[1], v[2], v[3]))
       elseif type(v) ~= "table" then
        table.insert(whereArgs, self._createCondition(k, "=", v))
      end
    end
   elseif fields and param == nil then--单条查询
    table.insert(whereArgs, self._createCondition(fields, "=", operator))
   elseif fields and operator and param then
    table.insert(whereArgs, self._createCondition(fields, operator, param))
  end
  return whereArgs
end

--设置AND查询条件
function LuaSQLite:where(fields,operator,param)
  if self.options["where"] == nil then
    self.options["where"] = {}
    table.insert(self.options["where"], {}) -- 必须预留一个空条件组给where方法
  end
  local whereArgs = self:_parseWhereArgs(fields,operator,param)
  if #whereArgs > 0 then
    for _, condition in ipairs(whereArgs) do
      table.insert(self.options["where"][1], condition)
    end
  end
  return self
end

--设置OR查询条件
function LuaSQLite:whereOr(fields,operator,param)
  if self.options["where"] == nil then
    self.options["where"] = {}
    table.insert(self.options["where"], {}) -- 必须预留一个空条件组给where方法
  end
  local whereArgs = self:_parseWhereArgs(fields,operator,param)
  if #whereArgs > 0 then
    table.insert(self.options["where"], whereArgs)
  end
  return self
end

--设置自定义查询条件
function LuaSQLite:whereRaw(str,args,logic)
  if self.options["where"] == nil then
    self.options["where"] = {}
    table.insert(self.options["where"], {}) -- 必须预留一个空条件组给where方法
  end
  if logic and logic:upper() == "OR" then
    table.insert(self.options["where"], {{sql = tostring(str), param = args}})
   else
    table.insert(self.options["where"][1], {sql = tostring(str), param = args})
  end
  return self
end

--指定IN查询条件
function LuaSQLite:whereIn(field, condition, logic)
  -- 如果是数组，先转换成字符串
  if type(condition) == "table" then
    condition = table.concat(condition, ",")
  end
  if logic and logic:upper() == "OR" then
    return self:whereOr(field, "IN", condition)
   else
    return self:where(field, "IN", condition)
  end
end

--指定NOT IN查询条件
function LuaSQLite:whereNotIn(field, condition, logic)
  -- 如果是数组，先转换成字符串
  if type(condition) == "table" then
    condition = table.concat(condition, ",")
  end
  if logic and logic:upper() == "OR" then
    return self:whereOr(field, "NOT IN", condition)
   else
    return self:where(field, "NOT IN", condition)
  end
end

--指定NULL查询条件
function LuaSQLite:whereNull(field, logic)
  if logic and logic:upper() == "OR" then
    return self:whereOr(field, "NULL", nil)
   else
    return self:where(field, "NULL", nil)
  end
end

--指定NOT NULL查询条件
function LuaSQLite:whereNotNull(field, logic)
  if logic and logic:upper() == "OR" then
    return self:whereOr(field, "NOT NULL", nil)
   else
    return self:where(field, "NOT NULL", nil)
  end
end

--指定LIKE查询条件
function LuaSQLite:whereLike(field, condition, logic)
  if logic and logic:upper() == "OR" then
    return self:whereOr(field, "LIKE", condition)
   else
    return self:where(field, "LIKE", condition)
  end
end

--指定NOT LIKE查询条件
function LuaSQLite:whereNotLike(field, condition, logic)
  if logic and logic:upper() == "OR" then
    return self:whereOr(field, "NOT LIKE", condition)
   else
    return self:where(field, "NOT LIKE", condition)
  end
end

--指定BETWEEN查询条件
function LuaSQLite:whereBetween(field, condition, logic)
  if logic and logic:upper() == "OR" then
    return self:whereOr(field, "BETWEEN", condition)
   else
    return self:where(field, "BETWEEN", condition)
  end
end

--指定NOT BETWEEN查询条件
function LuaSQLite:whereNotBetween(field, condition, logic)
  if logic and logic:upper() == "OR" then
    return self:whereOr(field, "NOT BETWEEN", condition)
   else
    return self:where(field, "NOT BETWEEN", condition)
  end
end

-- 构建WHERE条件的辅助函数
function LuaSQLite:_buildWhereConditions()
  if not self.options["where"] or #self.options["where"] == 0 then
    return nil, nil
  end
  local whereClause = ""
  local whereArgs = {}
  local groupConditions = {}
  -- 遍历所有条件组
  for groupIndex, conditionGroup in ipairs(self.options["where"]) do
    if #conditionGroup > 0 then--如果条件组不为空
      local groupSqlParts = {}
      local groupParams = {}
      for _, condition in ipairs(conditionGroup) do
        table.insert(groupSqlParts, condition.sql)
        if type(condition.param) == "table" then
          for _, paramValue in ipairs(condition.param) do
            table.insert(groupParams, paramValue)
          end
         else
          table.insert(groupParams, condition.param)
        end
      end
      -- 构建组内条件（AND连接）
      local groupCondition = table.concat(groupSqlParts, " AND ")
      table.insert(groupConditions, {
        sql = groupCondition,
        params = groupParams
      })
    end
  end
  -- 构建最终的WHERE子句
  if #groupConditions > 0 then
    local sqlParts = {}
    for _, group in ipairs(groupConditions) do
      if group.sql:find(" AND ") then
        table.insert(sqlParts, "(" .. group.sql .. ")")
       else
        table.insert(sqlParts, group.sql)
      end
      for _, param in ipairs(group.params) do
        table.insert(whereArgs, param)
      end
    end
    -- 组间用OR连接
    whereClause = table.concat(sqlParts, " OR ")
  end
  return whereClause, whereArgs
end

--指定返回的列名
function LuaSQLite:field(value)
  if type(value) == "string" then
    value=self._split(value,",")
  end
  self.options["field"] = value
  return self
end

--设置分组方式
function LuaSQLite:group(value)
  self.options["group"] = tostring(value)
  return self
end

--筛选分组结果
function LuaSQLite:having(value)
  self.options["having"] = tostring(value)
  return self
end

--设置排序方式
function LuaSQLite:order(value)
  self.options["order"] = tostring(value)
  return self
end

--指定返回结果的数量
function LuaSQLite:limit(value)
  self.options["limit"] = tostring(value)
  return self
end

--插入数据
function LuaSQLite:insert(values)
  local tableName = self.options["name"]
  local dataArgs = self._createContentValues(values)
  return self.db.insert(tableName, nil, dataArgs)
end

--批量插入数据（使用事务提高效率）
function LuaSQLite:insertAll(dataList)
  local tableName = self.options["name"]
  return self:transaction(function()
    local insertedIds = {}
    for _, values in ipairs(dataList) do
      local rowId = self:insert(values)
      table.insert(insertedIds, rowId)
    end
    return insertedIds
  end)
end

--根据id自动判断是新增还是更新数据
function LuaSQLite:save(values)
  local tableName=self.options["name"]
  local dataArgs = self._createContentValues(values)
  return self.db.replace(tableName, nil, dataArgs)
end

--删除数据
function LuaSQLite:delete(isder)
  local tableName=self.options["name"]
  local whereClause, whereArgs = self:_buildWhereConditions()
  --isder为true表示强制删除，不检查条件
  if (whereClause == nil or whereClause == "") and not isder then
    error("delete方法必须带条件")
  end
  return self.db.delete(tableName, whereClause, whereArgs)
end

--修改数据
function LuaSQLite:update(values)
  local tableName=self.options["name"]
  local dataArgs = self._createContentValues(values)
  local whereClause, whereArgs = self:_buildWhereConditions()
  return self.db.update(tableName, dataArgs, whereClause, whereArgs)
end

--字段自增
function LuaSQLite:setInc(field, step)
  step = step or 1
  local tableName=self.options["name"]
  local whereClause, whereArgs = self:_buildWhereConditions()
  local sql = string.format("UPDATE %s SET %s = %s + %d WHERE %s", tableName, field, field, step, whereClause or "1=1")
  if whereClause and whereClause:find("%?") and whereArgs and #whereArgs > 0 then
    return self:execSQL(sql, whereArgs)
   else
    return self:execSQL(sql)
  end
end

--字段自减
function LuaSQLite:setDec(field, step)
  step = step or 1
  return self:setInc(field, -step)
end

--游标查询
function LuaSQLite:cursor()
  local tableName=self.options["name"]
  local columns=self.options["field"] or nil
  local whereClause, whereArgs = self:_buildWhereConditions()
  local groupBy=self.options["group"] or nil
  local having=self.options["having"] or nil
  local orderBy=self.options["order"] or nil
  local limit=self.options["limit"] or nil
  local cursor = self.db.query(tableName, columns, whereClause, whereArgs, groupBy, having, orderBy, limit)
  return cursor
end

--数据集转table表
--[[
function LuaSQLite:toArray(cursor)
  local records = {}
  while cursor.moveToNext() do
    local record = {}
    for i = 0, cursor.getColumnCount() - 1 do
      local columnName = cursor.getColumnName(i)
      local columnType = cursor.getType(i)
      if columnType == 1 then
        record[columnName] = cursor.getInt(i)
       elseif columnType == 3 then
        record[columnName] = cursor.getString(i)
        --暂时不考虑其他类型
      end
    end
    table.insert(records, record)
  end
  cursor.close()
  return records
end
--]]
function LuaSQLite:toArray(cursor)
  -- 异常拦截
  if not cursor then return {} end

  -- 移动到第一行
  if not cursor.moveToFirst() then
    cursor.close() return {}
  end

  -- 初始化结果集
  local datas = {}
  local columnCount = cursor.getColumnCount()

  -- 获取总行数
  local totalCount = cursor.getCount()
  if totalCount == 0 then
    cursor.close() return {}
  end

  -- 预分配结果
  for i = 1, totalCount do datas[i] = {} end

  -- 列信息缓存
  local columnNames = {}
  local columnTypes = {}
  local operateData = function(columnName, columnType, dataIndex, data)
    if columnType then
      switch columnType do
       case Cursor.FIELD_TYPE_INTEGER
        data[columnName] = cursor.getLong(dataIndex)
       case Cursor.FIELD_TYPE_FLOAT
        data[columnName] = cursor.getFloat(dataIndex)
       case Cursor.FIELD_TYPE_STRING
        data[columnName] = cursor.getString(dataIndex)
       case Cursor.FIELD_TYPE_BLOB
        data[columnName] = cursor.getBlob(dataIndex)
       default
        data[columnName] = nil
      end
    end
  end

  -- 在第一行位置获取列信息
  for i = 0, columnCount - 1 do
    local name = cursor.getColumnName(i)
    columnNames[i + 1] = name
    columnTypes[name] = cursor.getType(i)
  end

  -- 处理第一行
  local index = 1
  for columnIndex = 1, columnCount do
    local columnName = columnNames[columnIndex]
    local columnType = columnTypes[columnName]
    local dataIndex = columnIndex - 1

    operateData(columnName, columnType, dataIndex, datas[index])
  end

  -- 处理剩余行
  index = index + 1
  while cursor.moveToNext() and index <= totalCount do
    for columnIndex = 1, columnCount do
      local columnName = columnNames[columnIndex]
      local columnType = columnTypes[columnName]
      local dataIndex = columnIndex - 1

      operateData(columnName, columnType, dataIndex, datas[index])
    end
    index = index + 1
  end

  cursor.close()
  return datas
end


--查询数据
function LuaSQLite:select()
  local cursor=self:cursor()
  return self:toArray(cursor)
end

--查询某一列的值
function LuaSQLite:column(name)
  local data = self:field(name):select()
  local result = {}
  for _, row in ipairs(data) do
    table.insert(result, row[name])
  end
  return result
end

--查询单行数据
function LuaSQLite:find()
  local data=self:limit(1):select()
  if #data>0 then
    return data[1]
  end
  return nil
end

--查询某个字段的值
function LuaSQLite:value(name)
  local data=self:field(name):find()
  if data and data[name] ~= nil then
    return data[name]
  end
  return nil
end

--execSQL()方法可以执行insert、delete、update和CREATE TABLE之类有更改行为的SQL语句
function LuaSQLite:execSQL(sql, args)
  if args then
    return self.db.execSQL(tostring(sql),args)
   else
    return self.db.execSQL(tostring(sql),{})
  end
end

--rawQuery()方法用于执行select语句。
function LuaSQLite:rawQuery(sql, args)
  if args then
    return self.db.rawQuery(tostring(sql),args)
   else
    return self.db.rawQuery(tostring(sql),{})
  end
end

--关闭数据库
function LuaSQLite:close()
  self.db.close()
end

--return LuaSQLite

-- LuaSQLite依赖部分结束
import "com.androlua.Http"
import "android.graphics.Typeface"
import "android.animation.LayoutTransition"
import "android.content.res.ColorStateList"

-- androidx
import "androidx.coordinatorlayout.widget.CoordinatorLayout"
import "androidx.viewpager2.widget.ViewPager2"
import "androidx.recyclerview.widget.RecyclerView"
import "androidx.recyclerview.widget.LinearLayoutManager"
import "androidx.appcompat.widget.AppCompatImageView"
import "androidx.appcompat.widget.LinearLayoutCompat"

-- material
import "com.google.android.material.card.MaterialCardView"
import "com.google.android.material.tabs.TabLayout"
import "com.google.android.material.dialog.MaterialAlertDialogBuilder"
import "com.google.android.material.textview.MaterialTextView"
import "com.google.android.material.appbar.MaterialToolbar"
import "com.google.android.material.appbar.AppBarLayout"

-- SmartRefreshLayout
import "com.scwang.smartrefresh.layout.SmartRefreshLayout"
import "com.scwang.smartrefresh.header.MaterialHeader"

-- Glide
import "com.bumptech.glide.Glide"


function onLongClick(id, callback)
  if Build.VERSION.SDK_INT <34 then
    id.setOnLongClickListener(View.OnLongClickListener{
      onLongClick=function(v)
        callback(v)
        return true
      end
    })
   else
    id.setOnLongClickListener(View.OnLongClickListener{
      onLongClick=function(v)
        callback(v)
        return true
      end,
      onLongClickUseDefaultHapticFeedback=function()
        return true
      end
    })
  end
end

--配置状态栏颜色
pcall(function()
  local View = luajava.bindClass "android.view.View"
  local WindowManager = luajava.bindClass "android.view.WindowManager"
  local window = activity.getWindow()
  window.setStatusBarColor(0xffffffff)
  window.setNavigationBarColor(0xffffffff)
  window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
  window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
  window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR)
end)


-- 设置布局
activity.setContentView(loadlayout({
  LinearLayoutCompat;--线性控件
  orientation='vertical';--布局方向
  layout_width='fill';--布局宽度
  layout_height='fill';--布局高度
  background='#ffffff';--布局背景

  {
    LinearLayoutCompat;--线性控件
    orientation='vertical';--布局方向
    layout_width='fill';--布局宽度
    layout_height='wrap';--布局高度
    --layout_marginTop=dataValue.config.margins[1];--布局顶距

    {
      MaterialToolbar,--工具栏控件
      id="toolbar",
      layout_width='fill';--布局宽度
      layout_height='wrap';--布局高度
      background='#ffffff';--布局背景
      navigationIcon=luajava.bindClass "com.google.android.material.R".drawable.ic_arrow_back_black_24,
      --navigationIcon=luajava.bindClass "com.google.android.material.R".drawable.abc_ic_ab_back_material,
      popupTheme=R.style.AppTheme,
      title="番剧周更表",
    };
    --[[
    {
      MaterialTextView;--横向分割线
      layout_width='fill';--分割线宽度
      layout_height='4px';--分割线厚度
      layout_gravity='center';--高度居中
      backgroundColor='#1f888888';--分割线颜色
    }
    --]]
  },
  {
    CoordinatorLayout;
    layout_width='fill'; -- 宽度填满
    layout_height='fill'; -- 高度填满
    background="#ffffff"; -- 背景颜色
    {
      AppBarLayout;
      layout_width='fill'; -- 宽度填满
      layout_height='wrap'; -- 高度包裹内容
      background="#ffffff"; -- 背景颜色
      {
        LinearLayoutCompat;--线性控件
        orientation='vertical'; -- 垂直方向
        layout_width='fill'; -- 宽度填满
        layout_height='wrap'; -- 高度包裹内容
        layout_scrollFlags="scroll|enterAlways"; -- 设置滚动行为
        {
          MaterialTextView;--横向分割线
          layout_width='fill';--分割线宽度
          layout_height='4px';--分割线厚度
          layout_gravity='center';--高度居中
          backgroundColor='#1f888888';--分割线颜色
        };
        {
          TabLayout;
          id="weektab",
          layout_width='fill';--布局宽度
          layout_height='wrap';--布局高度
          background='#ffffff';--布局背景
          tabGravity=TabLayout.GRAVITY_START;
          tabMode=TabLayout.MODE_SCROLLABLE;
          layoutTransition=LayoutTransition().enableTransitionType(LayoutTransition.CHANGING),
        }
      }
    };

    {
      LinearLayoutCompat;--线性控件
      orientation='vertical';--布局方向
      layout_width='fill';--布局宽度
      layout_height='fill';--布局高度
      layout_behavior="appbar_scrolling_view_behavior",
      {
        SmartRefreshLayout,--拉动刷新
        layout_width='fill';--宽度
        layout_height='fill';--高度
        id="weekRefresh";
        {
          ViewPager2,--翻页控件
          id="weekpage",
          layout_width='fill';--控件宽度
          layout_height='fill';--控件高度
        }
      }
    }
  }
}))

-- 初始化
dataValue = { value = {}, config = {}}
dataValue.value.datas = {}
dataValue.config.database = {
  "tritium-cache.db",
  "week",
  "CREATE TABLE IF NOT EXISTS week (time INTEGER, weekday TEXT, items TEXT)"
}
dataValue.value.lang = {
  index = 1,
  value = {
    "cn", "en", "ja"
  },
  title = {
    "简体中文", "English", "日本語"
  }
}

-- 标签和页面联动
weektab.setTabRippleColor(ColorStateList.valueOf(0x08000000))
weektab.setTabTextColors(ColorStateList({{android.R.attr.state_selected},{}},{0xff000000, 0xff888888}))
weektab.setSelectedTabIndicatorColor(0xff000000)
weektab.addOnTabSelectedListener(TabLayout.OnTabSelectedListener{
  onTabSelected = function(Tab)
    weekpage.setCurrentItem(Tab.getPosition())
  end
})
weekpage.setOffscreenPageLimit(7)
weekpage.registerOnPageChangeCallback(ViewPager2.OnPageChangeCallback({
  onPageSelected = function(position)
    weektab.selectTab(weektab.getTabAt(position))--设置到 TabLayout
  end,
  onPageScrolled = function(position, positionOffset, positionOffsetPixels)
    weektab.setScrollPosition(position, positionOffset, false)
  end
}))

-- 配置适配器
weekpage.setAdapter(luajava.override(RecyclerView.Adapter,{
  getItemCount = function()
    return int(#dataValue.value.datas)
  end,
  getItemViewType = function(super, position)
    return int(dataValue.value.datas[position+1]["type"] or 0)
  end,
  onCreateViewHolder = function(super, parent, viewType)
    local views = {}
    local holder = luajava.override(RecyclerView.ViewHolder, {}, loadlayout({
      LinearLayoutCompat;--线性控件
      orientation='vertical';--布局方向
      layout_width='fill';--布局宽度
      layout_height='fill';--布局高度
      {
        RecyclerView;
        layout_width='fill';
        layout_height='fill';
        id="recycler";
      };
      {
        LinearLayoutCompat;--线性控件
        orientation='vertical';--布局方向
        layout_width='fill';--布局宽度
        layout_height='fill';--布局高度
        gravity='center';--控件内容的重力方向
        id='emptylayout';--控件ID
        {
          AppCompatImageView;--图片控件
          layout_width='160dp';--图片宽度
          layout_height='140dp';--图片高度
          --src='img/empty.png';--图片路径
          src='https://s21.ax1x.com/2025/10/15/pVqkWiq.md.jpg';--图片路径
          scaleType='fitXY';--图片拉伸
          layout_gravity='center';--重力
        };
        {
          MaterialTextView,
          text="这里什么都没有～",
          id='empty';--控件ID
        }
      }
    }, views))
    holder.itemView.setTag(views)
    return holder
  end,
  onBindViewHolder = function(super, holder,position)
    -- 初始化视图
    local itemView = holder.itemView.getTag()
    local PageData = dataValue.value.datas[position+1]

    itemView.recycler.setVisibility(#PageData == 0 and 8 or 0)
    itemView.recycler.setAdapter(luajava.override(RecyclerView.Adapter,{
      getItemCount = function()
        return int(#PageData)
      end,
      getItemViewType = function(super, pos)
        return int(PageData[pos+1]["type"] or 0)
      end,
      onCreateViewHolder = function(super, parent, viewType)
        local views = {}
        local holder = luajava.override(RecyclerView.ViewHolder, {}, loadlayout({
          LinearLayoutCompat;--线性控件
          orientation='vertical';--布局方向
          layout_width='fill';--布局宽度
          layout_height='wrap';--布局高度

          {
            MaterialCardView;--卡片控件
            layout_width='fill';--卡片宽度
            layout_height='wrap';--卡片高度
            cardBackgroundColor='#ffffff';--卡片颜色
            layout_marginTop='8dp';--布局顶距
            layout_marginLeft='12dp';--布局左距
            layout_marginRight='12dp';--布局右距
            layout_marginBottom='0dp';--布局底距
            cardElevation='0dp';--卡片阴影
            strokeWidth="1dp", --边框宽度
            strokeColor="#1f888888", --边框颜色
            clickable=false;--点击效果
            radius='6dp';--卡片圆角
            id='mainView';--控件ID

            {
              LinearLayoutCompat;--线性控件
              orientation='horizontal';--布局方向
              layout_width='fill';--布局宽度
              layout_height='wrap';--布局高度
              layout_marginLeft='12dp';--布局左距
              layout_marginRight='12dp';--布局右距
              layout_marginTop='12dp';--布局顶距
              layout_marginBottom='12dp';--布局底距

              {
                MaterialCardView;--卡片控件
                layout_width='56dp';--卡片宽度
                layout_height='78dp';--卡片高度
                cardBackgroundColor='#ffffff';--卡片颜色
                layout_margin='0dp';--卡片边距
                cardElevation='0dp';--卡片阴影
                strokeWidth="0dp", --边框宽度
                strokeColor="#ffffff", --边框颜色
                clickable=true;--点击效果
                radius='4dp';--卡片圆角

                {
                  AppCompatImageView;--图片控件
                  layout_width='fill';--图片宽度
                  layout_height='fill';--图片高度
                  id='image';--设置控件ID
                  scaleType='centerCrop';--图片拉伸
                  --adjustViewBounds="true",
                  layout_gravity='center';--重力
                };
              };

              {
                Space;--空隙控件
                layout_width='12dp';--控件宽度
                layout_height='wrap';--控件高度
              };

              {
                LinearLayoutCompat;--线性控件
                layout_weight='1';--权重值
                orientation='vertical';--布局方向
                layout_width='fill';--布局宽度
                layout_height='fill';--布局高度

                {
                  MaterialTextView;--文本控件
                  layout_width='fill';--控件宽度
                  layout_height='wrap';--控件高度
                  layout_marginRight='16dp';--布局右距
                  textSize='16sp';--文字大小
                  textColor='#333333';--文字颜色
                  id='title';--设置控件ID
                  singleLine=true;--设置单行输入
                  ellipsize='middle';--多余文字用省略号显示
                  gravity='left|center';--重力
                };
                {
                  LinearLayoutCompat;--线性控件
                  layout_weight='1';--权重值
                  layout_width='fill';--布局宽度
                  layout_height='0dp';--控件高度
                  {
                    MaterialTextView;--文本控件
                    layout_width='fill';--控件宽度
                    layout_height='fill';--控件高度
                    textSize='14sp';--文字大小
                    textColor='#888888';--文字颜色
                    id='subtitle';--设置控件ID
                    singleLine=true;--设置单行输入
                    ellipsize='end';--多余文字用省略号显示
                    gravity='left';--重力
                  }
                };
                {
                  MaterialTextView;--文本控件
                  layout_width='fill';--控件宽度
                  layout_height='wrap';--控件高度
                  textSize='10sp';--文字大小
                  textColor='#666666';--文字颜色
                  typeface=Typeface.DEFAULT_BOLD,
                  id='infoData';--设置控件ID
                  singleLine=true;--设置单行输入
                  ellipsize='end';--多余文字用省略号显示
                  gravity='right|bottom';--重力
                };
              }
            }
          }
        }, views))
        holder.itemView.setTag(views)
        return holder
      end,
      onBindViewHolder = function(super, holder, pos)
        -- 初始化视图
        local itemView = holder.itemView.getTag()
        local itemData = PageData[pos+1]

        -- 设置内容
        itemView.title.setText(itemData.title)
        itemView.subtitle.setText(itemData.subtitle)
        itemView.infoData.setText(itemData.rank and string.format("NO.%d", itemData.rank) or itemData.date)

        -- 配置图片加载
        Glide.with(activity)
        .asBitmap()
        .load(itemData.image)
        .into(itemView.image)

        -- 图片点击
        itemView.image.getParent().setOnClickListener(View.OnClickListener{
          onClick = function(view)
            --[[
            activity.newActivity("activitys/PhotosActivity/",{
              itemView.title.text,
              itemData.image
            })
            --]]
          end
        })

        itemView.mainView.setOnClickListener(View.OnClickListener{
          onClick = function(view)
            local dialog = MaterialAlertDialogBuilder(this)
            .setTitle("提示信息")
            .setMessage(string.format("接下来的操作即将搜索番剧「%s」关键词\n\n是否跳转至搜索界面？", itemView.title.text))
            .setNegativeButton("取消",nil)
            .setPositiveButton("确定",{
              onClick=function(a)
                --[[
                activity.newActivity("activitys/SearchActivity", {
                  itemView.title.text
                })
                --]]
              end
            }).show()
          end
        })

        onLongClick(itemView.mainView, function(view)--控件长按事件

        end)
      end
    })).setLayoutManager(LinearLayoutManager(this))
  end
}))

-- 数据处理
function handleWeekData(config)
  local function safeDecode(data)
    if type(data) == "string" then
      local success, resultback = pcall(function()
        return require('cjson').decode(data)
      end)
      if success and resultback and type(resultback) == "table" then
        return resultback
      end
    end
    return data
  end
  local language = dataValue.value.lang.value[dataValue.value.lang.index]
  toolbar.setTitle(({
    cn = "番剧周更表", en = "Timetable", ja = "アニメ周更表"
  })[language])
  weektab.removeAllTabs()
  for k, v in ipairs(config) do
    dataValue.value.datas[k] = {}
    pcall(function()
      local weekday = safeDecode(v.weekday)[language]
      local tabs = weektab.newTab().setText(weekday)
      weektab.addTab(tabs)
    end)
    for _, value in ipairs(safeDecode(v.items)) do
      table.insert(dataValue.value.datas[k], {
        title = utf8.len(value.name_cn or "") > 0 and value.name_cn or value.name,
        subtitle = utf8.len(value.name or "") > 0 and value.name or value.name_cn,
        image = value.images.large,
        rank = tointeger(value.rank),
        date = value.air_date,
      })
    end
  end
  weekpage.adapter.notifyDataSetChanged()
  task(450, function ()
    weektab.selectTab(weektab.getTabAt(tonumber(os.date("%w") == '0' and 7 or os.date("%w"))-1))
  end)
end

-- 初始化信息
function initWeekData()
  local file, name, sql = table.unpack(dataValue.config.database)
  local Database = LuaSQLite:new(activity.getDatabasePath(file))
  Database:execSQL(sql)
  local resultback = Database:name(name):field("*"):select()

  if resultback and type(resultback) == "table" and table.maxn(resultback) > 0 then
    if resultback[1].time - os.time() > 86400 then
      weekRefresh.autoRefresh()
     else
      handleWeekData(resultback)
    end
   else
    weekRefresh.autoRefresh()
  end
  Database:close()
end

-- 数据加载
--weekRefresh.setRefreshHeader(luajava.newInstance("com.scwang.smartrefresh.header.MaterialHeader", this))
weekRefresh.setRefreshHeader(MaterialHeader(this))
weekRefresh.setOnMultiPurposeListener({
  onRefresh = function()
    Http.get("https://api.bgm.tv/calendar", cookie, charset, header, function(code,content,cookie,heafer)
      if(code == 200 and content)then
        local success, resultback = pcall(function()
          return require('cjson').decode(content)
        end)
        if success and resultback and type(resultback) == "table" and table.maxn(resultback) > 0 then
          pcall(function()
            local file, name, sql = table.unpack(dataValue.config.database)
            local Database = LuaSQLite:new(activity.getDatabasePath(file))
            Database:execSQL(sql)
            for k, v in ipairs(resultback) do
              resultback[k].time = os.time()
            end
            Database:execSQL(string.format("delete from %s", name))
            Database:name(name):insertAll(resultback)
            Database:close()
          end)
          handleWeekData(resultback)
         else
          activity.showToast("数据解析错误")
        end
       else
        activity.showToast("网络或服务器异常 -"..code)
      end
      weekRefresh.finishRefresh()
    end)
  end
})

-- 开始加载信息
initWeekData()

-- 顶栏菜单
toolbar.menu.add(0, 0, 0, "选择语言")
-- 此处是一个图标
--.setIcon(Utils.getIconDrawable(activity.getLuaDir("icon/24dp/translate.png"), 20)).setShowAsAction(1)
toolbar.setOnMenuItemClickListener(MaterialToolbar.OnMenuItemClickListener{
  onMenuItemClick = function(item)
    local dialog = MaterialAlertDialogBuilder(this)
    .setTitle(({
      cn = "选择语言", en = "Select Language", ja = "言語選択"
    })[dataValue.value.lang.value[dataValue.value.lang.index]])
    .setNegativeButton("取消",nil)
    .setSingleChoiceItems(dataValue.value.lang.title, dataValue.value.lang.index-1, {
      onClick = function(v, pos)
        dataValue.value.lang.index = pos+1
        initWeekData()
        v.dismiss()
      end
    }).show()
  end
})

--设置导航键点击监听
toolbar.setNavigationOnClickListener(View.OnClickListener{
  onClick = function(view)
    activity.finish()
  end
})



--Source：氚-Tritium 2957148920 pid-5088
--OpenSource:Apache License Version

-- 调用1DM+下载文件函数
-- 包名为： "idm.internet.download.manager.plus"
-- https://play.google.com/store/apps/details/1DM_Browser_Video_Download?id=idm.internet.download.manager.plus&hl=en_SG


-- A 不支持列表
function downloadByIDM(params)
  local packageName = "idm.internet.download.manager.plus"
  if not pcall(function() activity.getPackageManager().getPackageInfo(packageName, 0) end) then
    activity.showToast("IDM下载器未安装") else
    --复制链接文件名
    --activity.getSystemService(luajava.bindClass("android.content.Context").CLIPBOARD_SERVICE).setText(tostring(params[2]))
    xpcall(function()
      local Intent = luajava.bindClass "android.content.Intent"
      local intent = Intent(Intent.ACTION_VIEW);
      local Uri = luajava.bindClass "android.net.Uri"
      intent.setData(Uri.parse(params[1] or ""));
      intent.putExtra("filename", params[2]);
      intent.setClassName(packageName, "idm.internet.download.manager.Downloader");
      activity.startActivity(intent);
    end, function(e) print(e) end)
  end
end

-- 传入一个数组 下载地址和文件名称
--[[
downloadByIDM({
  "https://8.bf8bf.com/video/xianni/第103集/index.m3u8",
  "仙逆103集.mp4",
})
--]]

-- B 支持列表
function downloadByIDM(data, expand)
  local packageName = "idm.internet.download.manager.plus"
  if not pcall(function() activity.getPackageManager().getPackageInfo(packageName, 0) end) then
    activity.showToast("IDM下载器未安装") else
    if type(data) ~= "table" and type(data) == "string" then
      data = {
        {
          name = expand,
          url = data
        }
      }
    end
    xpcall(function()
      local Intent = luajava.bindClass "android.content.Intent"
      local Uri = luajava.bindClass "android.net.Uri"
      local intent = Intent(Intent.ACTION_VIEW)
      local names, urls = ArrayList(), ArrayList()
      intent.setClassName(packageName, "idm.internet.download.manager.Downloader");
      intent.putExtra("secure_uri", false);
      for k, v in ipairs(data) do
        if v.url then
          urls.add(v.url)
          names.add(v.name or "")
        end
      end
      intent.setData(Uri.parse(urls.get(0)))
      intent.putExtra("url_list", urls)
      intent.putExtra("url_list.filename", names)
      activity.startActivity(intent)
    end, function(e) print(e) end)
  end
end


-- 传入两个字符串 下载一个链接
--downloadByIDM("https://8.bf8bf.com/video/xianni/第103集/index.m3u8", "仙逆103集.mp4")
-- 当然，一个字符串也可以
--downloadByIDM("https://8.bf8bf.com/video/xianni/第103集/index.m3u8")


-- 您可以下载一个列表
--[[
-- 可以只有一项，这并不碍事
downloadByIDM({
  {
    name = "仙逆103集.mp4",
    url = "https://8.bf8bf.com/video/xianni/第103集/index.m3u8",
  }
})
--]]

-- 一个多条不同类型项目的列表
downloadByIDM({
  {
    -- 不填写文件名称
    url = "https://8.bf8bf.com/video/xianni/第95集/index.m3u8",
  },
  {
    name = "仙逆96集.mp4",
    -- 不填写下载地址
  },
  {
    name = "仙逆97集.mp4",
    url = "https://8.bf8bf.com/video/xianni/第97集/index.m3u8",
  },
  {
    -- 全部都删掉
  },
  {
    name = "仙逆99集.mp4",
    url = "https://8.bf8bf.com/video/xianni/第99集/index.m3u8",
  },
  {
    name = "仙逆100集.mp4",
    url = "https://8.bf8bf.com/video/xianni/第100集/index.m3u8",
  },
  {
    name = "仙逆101集.mp4",
    url = "https://8.bf8bf.com/video/xianni/第101集/index.m3u8",
  },
  {
    name = "仙逆102集.mp4",
    url = "https://8.bf8bf.com/video/xianni/第102集/index.m3u8",
  },
  {
    name = "仙逆103集.mp4",
    url = "https://8.bf8bf.com/video/xianni/第103集/index.m3u8",
  }
}, true)



--转载自 Lua代码手册 @TiAmo

--应该挺多人知道的，分享给那些不知道的，需要root权限

--这是设置电量，80是电量百分比
os.execute("su -c dumpsys battery set level 80")

--这是恢复默认
os.execute("su -c dumpsys battery reset")



local LuaWebView = luajava.bindClass "com.androlua.LuaWebView"

activity.setContentView(loadlayout({
  LuaWebView,
  id="webView",
  layout_width="match_parent",
  layout_height="match_parent"
}))
activity.getSupportActionBar().hide()

-- 配置WebView设置
local webSettings = webView.getSettings()
webSettings.setJavaScriptEnabled(true) -- 启用JavaScript
webSettings.setDomStorageEnabled(true) -- 启用DOM存储
webSettings.setLoadWithOverviewMode(true) -- 缩放至屏幕宽度
webSettings.setUseWideViewPort(true) -- 支持视口元标签

local htmlContent = [[
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0>
    <title>Markdown预览器</title>
    <link rel="stylesheet" href="https://unpkg.com/mdui@2/mdui.css">
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet">
    <script src="https://unpkg.com/mdui@2/mdui.global.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/showdown@2.1.0/dist/showdown.min.js"></script>
    <style>
        html, body {
            height: 100%;
            margin: 0;
            padding: 0;
            background-color: var(--mdui-color-surface);
            overflow: hidden;
        }

        html::-webkit-scrollbar,
        body::-webkit-scrollbar,
        #md-preview::-webkit-scrollbar {
            display: none !important;
        }
        html, body, #md-preview {
            scrollbar-width: none !important;
            -ms-overflow-style: none !important;
        }
        
        #md-preview {
            height: 100vh;
            padding: 24px;
            overflow-y: auto;
            overflow-x: auto;
            line-height: 1.7;
            background-color: var(--mdui-color-surface);
            box-sizing: border-box;
        }

        #md-preview h1, #md-preview h2, #md-preview h3 {
            color: var(--mdui-color-primary);
            margin: 1.8em 0 0.8em;
            font-weight: 500;
        }
        #md-preview h1 {
            border-bottom: 1px solid var(--mdui-color-outline);
            padding-bottom: 0.5em;
        }
        #md-preview code {
            background-color: var(--mdui-color-surface-container-high);
            padding: 2px 6px;
            border-radius: 4px;
            font-size: 0.9em;
            font-family: 'Roboto Mono', 'Courier New', monospace;
        }
        #md-preview pre {
            background-color: var(--mdui-color-surface-container-highest);
            padding: 16px;
            border-radius: 8px;
            margin: 1.5em 0;
            overflow-x: auto;
            border: 1px solid var(--mdui-color-outline);
        }
        #md-preview pre code {
            background: transparent;
            padding: 0;
            font-size: 0.95em;
        }
        #md-preview blockquote {
            border-left: 4px solid var(--mdui-color-primary);
            padding-left: 16px;
            margin: 1.5em 0;
            color: var(--mdui-color-on-surface-variant);
        }
        #md-preview ul, #md-preview ol {
            padding-left: 1.5em;
            margin: 1em 0;
        }
        #md-preview table {
            width: 100%;
            border-collapse: collapse;
            margin: 1.5em 0;
        }
        #md-preview th, #md-preview td {
            border: 1px solid var(--mdui-color-outline);
            padding: 8px 12px;
            text-align: left;
        }
        #md-preview th {
            background-color: var(--mdui-color-surface-container-high);
        }
        @media (max-width: 768px) {
            #md-preview {
                padding: 16px;
            }
        }
    </style>
</head>
<body class="mdui-theme-primary-indigo mdui-theme-accent-pink">
    <textarea id="md-editor" style="display: none;">
# Markdown预览器

## 1. 标题层级

# 一级标题 (最大)
## 二级标题
### 三级标题
#### 四级标题
##### 五级标题
###### 六级标题 (最小)

---

## 2. 文本格式化

### 2.1 基础样式
- **粗体文本**：使用 `**粗体文本**` 或 `__粗体文本__`
- *斜体文本*：使用 `*斜体文本*` 或 `_斜体文本_`
- ***粗斜体文本***：使用 `***粗斜体文本***` 或 `___粗斜体文本___`
- ~~删除线文本~~：使用 `~~删除线文本~~`
- <u>下划线文本</u>：使用 `<u>下划线文本</u>` (部分编辑器支持 `++下划线++`)
- ==高亮文本==：使用 `==高亮文本==` (部分编辑器支持)

### 2.2 引用文本
> 这是一级引用文本
>> 这是二级引用文本（嵌套引用）
>>> 这是三级引用文本
>
> 引用文本中支持 **格式化**，比如 *斜体*、`代码` 等样式
>
> - 引用内的列表项 1
> - 引用内的列表项 2

---

## 3. 列表

### 3.1 无序列表
- 无序列表项 1 (使用 `-` 开头)
- 无序列表项 2
  - 嵌套无序列表项 2.1 (缩进 2 个空格或 1 个 Tab)
  - 嵌套无序列表项 2.2
    - 三级嵌套列表项 2.2.1
- 无序列表项 3

### 3.2 有序列表
1. 有序列表项 1 (使用 `1.` 开头)
2. 有序列表项 2
   1. 嵌套有序列表项 2.1
   2. 嵌套有序列表项 2.2
      1. 三级嵌套列表项 2.2.1
3. 有序列表项 3
   - 有序列表中嵌套无序列表项
   - 另一个嵌套无序列表项

### 3.3 任务列表
- [x] 已完成任务 (使用 `[x]`)
- [ ] 未完成任务 (使用 `[ ]`)
- [x] 已完成任务 2
  - [ ] 子任务 2.1 (未完成)
  - [x] 子任务 2.2 (已完成)
- [ ] 未完成任务 3

---

## 4. 代码块与代码

### 4.1 行内代码
行内代码示例：`const greeting = "Hello, Markdown!";`，可以嵌入到普通文本中，比如引用一个函数 `renderMarkdown()` 或变量 `userName`。

### 4.2 普通代码块
```
// 这是一个普通代码块（不指定语言）
function add(a, b) {
  return a + b;
}

let result = add(10, 20);
console.log("计算结果：" + result);
```

---

## 5. 链接

### 5.1 基础链接
- 普通链接：[Markdown 官方文档](https://daringfireball.net/projects/markdown/)
- 带标题的链接：[Showdown 库 (Markdown 解析器)](https://github.com/showdownjs/showdown "Showdown - 用于浏览器和服务器的 Markdown 解析器")

### 5.2 引用式链接
- 基础引用：[MDUI 框架][1]
- 带标题引用：[Google Fonts][2]

[1]: https://mdui.org/ "MDUI - 轻量级 Material Design 前端框架"
[2]: https://fonts.google.com/ "Google Fonts - 免费字体库"

### 5.3 锚点链接
- 跳转到当前文档的「图片」章节：[查看图片语法](#6-图片)
- 跳转到「表格」章节：[查看表格语法](#7-表格)

### 5.4 邮箱与自动链接
- 邮箱链接：<example@example.com>
- 自动链接（无需括号）：<https://cdn.jsdelivr.net/>

---

## 6. 图片

### 6.1 基础图片
![Markdown 图标](https://picsum.photos/id/0/400/200)

### 6.2 带标题的图片
![Material Design 图标](https://picsum.photos/id/180/600/300 "Material Design 风格图标示例")

### 6.3 带链接的图片
点击图片跳转到 MDUI 官网：
[![MDUI 标志](https://picsum.photos/id/237/200/100 "点击访问 MDUI 官网")](https://mdui.org/)

### 6.4 引用式图片
![示例图片 1][img1]
![示例图片 2][img2]

[img1]: https://picsum.photos/id/25/500/250 "示例图片 1 - 自然风景"
[img2]: https://picsum.photos/id/42/500/250 "示例图片 2 - 动物"

---

## 7. 表格

### 7.1 基础表格
| 姓名   | 年龄 | 职业       | 城市     |
|--------|------|------------|----------|
| 张三   | 28   | 前端开发   | 北京     |
| 李四   | 32   | 后端开发   | 上海     |
| 王五   | 26   | 产品经理   | 广州     |

### 7.2 对齐方式表格
| 左对齐表头 | 居中对齐表头 | 右对齐表头 |
| :--------- | :----------: | ---------: |
| 左对齐内容 |   居中内容   |   右对齐内容 |
| 文本 1     |    数值 100    |      99.99 |
| 文本 2     |    数值 200    |     199.50 |

### 7.3 复杂表格（含格式化）
| 功能         | 语法示例                  | 支持情况       |
|--------------|---------------------------|----------------|
| 粗体         | `**文本**`                | ✅ 完全支持    |
| 代码块       | ```javascript 代码 ```    | ✅ 支持语法高亮|
| 任务列表     | `- [x] 任务`              | ✅ 部分编辑器支持 |
| 数学公式     | `$E=mc^2$`                | ❌ 需扩展支持  |

---

## 8. 水平分隔线
以下是三种不同的水平分隔线语法（效果相同）：

---
***
___

---

## 9. 数学公式（需扩展支持）
> 注：原生 Markdown 不支持数学公式，需配合 MathJax 或 KaTeX 等库使用。

### 9.1 行内公式
- 质能方程：$E=mc^2$
- 勾股定理：$a^2 + b^2 = c^2$
- 圆的面积：$S = \pi r^2$

### 9.2 块级公式
$$
\sum_{i=1}^{n} i = \frac{n(n+1)}{2}
$$

$$
f(x) = \frac{1}{\sqrt{2\pi\sigma^2}} e^{-\frac{(x-\mu)^2}{2\sigma^2}}
$$

$$
\begin{bmatrix}
a_{11} & a_{12} & a_{13} \\
a_{21} & a_{22} & a_{23} \\
a_{31} & a_{32} & a_{33}
\end{bmatrix}
$$

---

## 10. 特殊元素

### 10.1 脚注
这是一个带有脚注的句子[^1]，另一个脚注示例[^2]。

[^1]: 脚注内容 1 - 用于补充说明正文内容，会显示在文档底部。
[^2]: 脚注内容 2 - 脚注支持 **格式化**，比如 *斜体* 和 `代码`。

### 10.2 定义列表
术语 1
: 术语 1 的定义说明，用于解释术语的含义。
: 术语 1 的补充说明，可以有多行定义。

术语 2
: 术语 2 的定义内容
: 术语 2 的补充内容

### 10.3 换行与空行
- 换行示例 1：在行尾添加两个空格  
  这是换行后的内容
- 换行示例 2：使用 `<br>` 标签<br>这是换行后的内容

空行：两个段落之间需要有一个或多个空行来分隔。
这是第一个段落。

这是第二个段落（与上一段有一个空行）。

### 10.4 转义字符
以下字符在 Markdown 中有特殊含义，如需显示原字符，需在前面加 `\` 转义：
- 星号：\* 斜体文本 \* → 显示为 * 斜体文本 *
- 井号：\# 这不是标题 → 显示为 # 这不是标题
- 括号：\[链接文本\] → 显示为 [链接文本]
- 反斜杠：\\ → 显示为 \

支持转义的字符：\* _ \` \[ \] \( \) \# \+ \- = \~ \! \. \,

---

## 11. 高级用法

### 11.1 折叠块（部分编辑器支持）
<details>
  <summary>点击展开查看折叠内容</summary>
  <p>这是折叠起来的内容，需要点击上方的标题才能查看。</p>
  <p>折叠块内可以包含任何 Markdown 内容，比如：</p>
  <ul>
    <li>列表项 1</li>
    <li>列表项 2</li>
  </ul>
  <p>```javascript
  // 折叠块内的代码
  console.log("折叠块示例");
  ```</p>
</details>

<details>
  <summary>Markdown 语法小贴士（点击展开）</summary>
  1. 标题层级建议不超过 3 级，避免文档结构过于复杂。
  2. 代码块建议指定语言，以获得更好的语法高亮效果。
  3. 图片建议添加描述文本（alt 属性），提升可访问性。
  4. 长文档建议使用锚点链接，方便读者快速导航。
</details>

### 11.2  emoji 表情（部分编辑器支持）
- 常用表情：😊 😎 🚀 ⭐ ✨ 📝 📚 🎨 🎯 🎉
- 功能相关：✅ ❌ ⚠️ ℹ️ ❓ ❗ 🔧 🔗 🖼️ 📊
- 情绪相关：😂 😭 😍 😡 😱 😴 😌 😏 😭 😘

### 11.3 目录（部分编辑器支持）
```markdown
# 目录
1. [标题层级](#1-标题层级)
2. [文本格式化](#2-文本格式化)
3. [列表](#3-列表)
4. [代码块与代码](#4-代码块与代码)
5. [链接](#5-链接)
6. [图片](#6-图片)
7. [表格](#7-表格)
8. [水平分隔线](#8-水平分隔线)
9. [数学公式](#9-数学公式需扩展支持)
10. [特殊元素](#10-特殊元素)
11. [高级用法](#11-高级用法)
```

---

## 12. 综合示例（Markdown 文档模板）

# 项目需求文档
## 1. 项目概述
- **项目名称**：Markdown 在线编辑器
- **项目目标**：开发一个支持实时预览、语法高亮、响应式布局的 Markdown 编辑器
- **目标用户**：程序员、内容创作者、学生

## 2. 核心功能
### 2.1 编辑功能
- [x] 实时预览（编辑区与预览区同步）
- [x] 语法高亮（支持多种编程语言）
- [ ] 自动保存（本地存储）

### 2.2 导出功能
- [ ] 导出为 HTML 文件
- [ ] 导出为 PDF 文件
- [ ] 复制 HTML 内容到剪贴板

## 3. 技术栈
| 模块         | 技术选择                  | 备注                     |
|--------------|---------------------------|--------------------------|
| 前端框架     | MDUI 2.x                  | 轻量级 Material Design 框架 |
| Markdown 解析 | Showdown.js               | 支持扩展语法             |
| 代码高亮     | Prism.js                  | 支持多种语言高亮         |
| 本地存储     | localStorage              | 用于自动保存功能         |

## 4. 界面设计
![界面设计草图](https://picsum.photos/id/160/800/400 "Markdown 编辑器界面设计草图")

## 5. 开发计划
1. **第一阶段**（1-2 周）：完成基础编辑与预览功能
2. **第二阶段**（2-3 周）：添加语法高亮与扩展功能
3. **第三阶段**（1-2 周）：实现导出功能与优化

## 6. 参考资料
- [Showdown.js 官方文档][showdown]
- [MDUI 组件库][mdui]
- [Markdown 语法指南][md-guide]

[showdown]: https://github.com/showdownjs/showdown
[mdui]: https://mdui.org/
[md-guide]: https://www.markdownguide.org/

---

[^1]: 脚注内容 1 - 用于补充说明正文内容，会显示在文档底部。
[^2]: 脚注内容 2 - 脚注支持 **格式化**，比如 *斜体* 和 `代码`。
    </textarea>

    <div id="md-preview" class="mdui-typo"></div>

    <script>
        const converter = new showdown.Converter({
            tables: true,
            tasklists: true,
            strikethrough: true,
            simplifiedAutoLink: true,
            literalMidWordUnderscores: true,
            disableForced4SpacesIndentedSublists: true,
            backslashEscapesHTMLTags: false
        });

        const mdEditor = document.getElementById('md-editor');
        const previewElement = document.getElementById('md-preview');

        function renderMarkdown() {
            previewElement.innerHTML = converter.makeHtml(mdEditor.value);
        }

        document.addEventListener('DOMContentLoaded', function() {
            renderMarkdown();
        });
    </script>
</body>
</html>
]]

webView.loadDataWithBaseURL(
"https://unpkg.com/", -- 基础URL，用于解析相对路径
htmlContent, -- HTML内容
"text/html", -- MIME类型
"UTF-8", -- 编码格式
nil -- 历史URL（可为空）
)



require "import"
local TypedValue = luajava.bindClass "android.util.TypedValue"
local RecyclerView = luajava.bindClass "androidx.recyclerview.widget.RecyclerView"
local LinearLayoutManager = luajava.bindClass "androidx.recyclerview.widget.LinearLayoutManager"
local LinearLayoutCompat = luajava.bindClass "androidx.appcompat.widget.LinearLayoutCompat"
local MaterialTextView = luajava.bindClass "com.google.android.material.textview.MaterialTextView"
local ImageView = luajava.bindClass "android.widget.ImageView"
local MaterialCardView = luajava.bindClass "com.google.android.material.card.MaterialCardView"
local SearchView = luajava.bindClass "android.widget.SearchView"
local FastScroller = luajava.bindClass "androidx.recyclerview.widget.FastScroller"
local StateListDrawable = luajava.bindClass "android.graphics.drawable.StateListDrawable"
local GradientDrawable = luajava.bindClass "android.graphics.drawable.GradientDrawable"
local Colors = require "Colors"

local filePath = activity.applicationInfo.sourceDir
-- dex/apk路径
local dexData = {}

-- 尺寸转换函数
local function dp2px(dp)
  return TypedValue.applyDimension(
  TypedValue.COMPLEX_UNIT_DIP,
  dp,
  activity.getResources().getDisplayMetrics()
  )
end

function initFastScroller(recyclerView, thumbColor, trackColor, width, radius)
  local verticalThumb = StateListDrawable()
  verticalThumb.addState({}, GradientDrawable().setColor(thumbColor).setCornerRadius(radius))

  local verticalTrack = GradientDrawable().setColor(trackColor).setCornerRadius(radius)
  local horizontalThumb = verticalThumb
  local horizontalTrack = verticalTrack

  local defaultWidth = width
  local scrollbarMinRange = dp2px(50)
  local margin = dp2px(0)

  local constructor = FastScroller.getDeclaredConstructors()[0]
  constructor.setAccessible(true)
  return constructor.newInstance({
    recyclerView,
    verticalThumb,
    verticalTrack,
    horizontalThumb,
    horizontalTrack,
    int(defaultWidth),
    int(scrollbarMinRange),
    int(margin)
  })
end

-- 解析DEX文件
local function parseDex(dexPath)
  local DexFile = luajava.bindClass("dalvik.system.DexFile")
  local classNames = {}

  local success, dexFile = pcall(function()
    return DexFile.loadDex(dexPath, nil, 0)
  end)

  if success and dexFile then
    local entries = dexFile.entries()
    while entries.hasMoreElements() do
      local className = entries.nextElement()
      table.insert(classNames, className)
    end
   else
    classNames = {}
  end

  return classNames
end

-- 从APK中提取所有DEX文件并解析
local function parseApk(apkPath)
  local classNames = {}

  xpcall(function()
    local zipFile = luajava.bindClass("java.util.zip.ZipFile")(apkPath)
    local entries = zipFile.entries()

    while entries.hasMoreElements() do
      local entry = entries.nextElement()
      local name = entry.getName()

      -- 查找DEX文件（classes.dex, classes2.dex, classes3.dex等）
      if name:match("^classes%d*%.dex$") then
        -- 创建临时目录来保存提取的DEX文件
        local tempDir = activity.getExternalCacheDir().getAbsolutePath() .. "/dex_temp/"
        local tempFile = tempDir .. name

        -- 确保目录存在
        luajava.bindClass("java.io.File")(tempDir).mkdirs()

        -- 提取DEX文件到临时目录
        ZipUtil.extractFileFromZip(apkPath, name, tempFile)

        -- 解析DEX文件
        local dexClassNames = parseDex(tempFile)
        for _, className in ipairs(dexClassNames) do
          table.insert(classNames, className)
        end

        -- 删除临时文件
        luajava.bindClass("java.io.File")(tempFile).delete()
      end
    end

    zipFile.close()
  end,function(e)
    print("解析APK时出错: " .. tostring(e))
  end)

  return classNames
end

-- 根据文件路径判断类型并解析
local function parseFile(filePath)
  if not filePath then
    return {}
  end

  local file = luajava.bindClass("java.io.File")(filePath)
  if not file.exists() then
    print("文件不存在: " .. filePath)
    return {}
  end

  local fileName = file.getName():lower()

  if fileName:match("%.apk$") then
    --print("检测到APK文件，开始解析...")
    return parseApk(filePath)
   elseif fileName:match("%.dex$") then
    --print("检测到DEX文件，开始解析...")
    return parseDex(filePath)
   else
    print("不支持的文件类型: " .. fileName)
    return {}
  end
end

if filePath then
  dexData = parseFile(filePath)
 else
  dexData = {}
end

-- 构建树形结构
local function buildTreeStructure(classNames, expandAll)
  expandAll = expandAll or false
  local root = {}

  for _, className in ipairs(classNames) do
    local parts = {}
    for part in className:gmatch("[^%.]+") do
      table.insert(parts, part)
    end

    local currentNode = root
    for i, part in ipairs(parts) do
      if not currentNode[part] then
        currentNode[part] = {
          name = part,
          fullName = table.concat(parts, ".", 1, i),
          children = {},
          isLeaf = i == #parts,
          isPackage = i < #parts
        }
      end
      currentNode = currentNode[part].children
    end
  end

  -- 优化树结构（合并单子节点）
  local function optimizeTree(node)
    if not node or type(node) ~= "table" then return end

    for name, item in pairs(node) do
      if type(item) == "table" and not item.isLeaf and item.children then
        local function mergeSingleChild(currentItem)
          if not currentItem or not currentItem.children then return end

          local childKeys = {}
          for k in pairs(currentItem.children) do
            table.insert(childKeys, k)
          end

          if #childKeys == 1 then
            local onlyChild = currentItem.children[childKeys[1]]
            if onlyChild and type(onlyChild) == "table" and not onlyChild.isLeaf then
              currentItem.name = currentItem.name .. "." .. onlyChild.name
              currentItem.fullName = onlyChild.fullName
              currentItem.children = onlyChild.children
              mergeSingleChild(currentItem)
            end
          end
        end

        mergeSingleChild(item)
        optimizeTree(item.children)
      end
    end
  end

  optimizeTree(root)

  -- 扁平化树结构用于显示
  local function flattenTree(node, level, result)
    if not node or type(node) ~= "table" then return end

    local nodes = {}
    for name, item in pairs(node) do
      if type(item) == "table" then
        table.insert(nodes, item)
      end
    end

    table.sort(nodes, function(a, b)
      if a.isPackage ~= b.isPackage then
        return a.isPackage
      end
      return a.name:lower() < b.name:lower()
    end)

    for _, item in ipairs(nodes) do
      if type(item) == "table" then
        table.insert(result, {
          name = item.name,
          fullName = item.fullName,
          isLeaf = item.isLeaf,
          isPackage = item.isPackage,
          level = level,
          opening = expandAll,
          children = item.children
        })

        if (expandAll or item.opening) and not item.isLeaf then
          flattenTree(item.children, level + 1, result)
        end
      end
    end
  end

  local result = {}
  flattenTree(root, 0, result)
  return result
end

-- 增强版搜索功能
local function performSearch(query)
  if not dexData or #dexData == 0 then return {} end

  if query == "" or query == nil then
    return buildTreeStructure(dexData, false)
  end

  query = query:lower()
  local exactMatchData = {} -- 类名精确匹配
  local classMatchData = {} -- 类名部分匹配

  for _, className in ipairs(dexData) do
    -- 提取类名（最后一个点后面的部分）
    local simpleName = className:match("([^%.]+)$") or className
    local simpleNameLower = simpleName:lower()
    local classNameLower = className:lower()

    if simpleNameLower == query then
      -- 类名精确匹配（最高优先级）
      table.insert(exactMatchData, className)
     elseif simpleNameLower:find(query, 1, true) then
      -- 类名部分匹配（中等优先级）
      table.insert(classMatchData, className)
    end
  end

  -- 合并结果，保持优先级顺序
  local filteredData = {}
  for _, v in ipairs(exactMatchData) do table.insert(filteredData, v) end
  for _, v in ipairs(classMatchData) do table.insert(filteredData, v) end

  return buildTreeStructure(filteredData, true)
end

-- 界面布局
local views = {}
local rootView = loadlayout({
  LinearLayoutCompat;
  layout_height = "match_parent";
  orientation = "vertical";
  layout_width = "match_parent";
  {
    RecyclerView;
    layout_height = "match_parent";
    layout_width = "match_parent";
    id = "rvTree";
    padding = "8dp";
    clipToPadding = false;
  };
}, views)

activity.setContentView(rootView)

-- 列表项布局
local itemLayout = {
  MaterialCardView,
  layout_width = "match",
  layout_height = "wrap",
  StrokeWidth = 0,
  {
    LinearLayoutCompat,
    layout_width = "match_parent",
    layout_height = "wrap_content",
    orientation = "horizontal",
    gravity = "center|left",
    padding = "8dp",
    {
      ImageView,
      id = "ivExpand",
      layout_width = "24dp",
      layout_height = "24dp",
      scaleType = "center",
      colorFilter = Colors.colorPrimary,
      padding = "4dp";
    };
    {
      MaterialCardView,
      StrokeWidth = 0,
      layout_width = "28dp",
      layout_height = "28dp",
      cardBackgroundColor = Colors.colorPrimary,
      {
        ImageView,
        id = "ivIcon",
        layout_width = "16dp",
        layout_height = "16dp",
        layout_gravity = "center",
      },
    };
    {
      MaterialTextView,
      id = "tvName",
      textSize = "14sp",
      layout_width = "wrap",
      layout_height = "wrap",
      paddingLeft = "8dp";
      textColor = "#333333";
    };
  };
}

local treeData = buildTreeStructure(dexData, false)
local adapter

-- 图标资源
local folderIcon = R.drawable.ic_folder
local classIcon = R.drawable.ic_language_java
local expandIcon = material.drawable.mtrl_ic_arrow_drop_down

-- 创建适配器
adapter = luajava.override(RecyclerView.Adapter, {
  getItemCount = function()
    return int(#treeData)
  end,

  onCreateViewHolder = function(parent, viewType)
    local views = {}
    local itemView = loadlayout(itemLayout, views, parent)
    itemView.setTag(views)

    local attrs = {android.R.attr.selectableItemBackground}
    local ta = activity.obtainStyledAttributes(attrs)
    itemView.setForeground(ta.getDrawable(0))
    ta.recycle()

    return luajava.override(RecyclerView.ViewHolder, {}, itemView)
  end,

  onBindViewHolder = function(_, vh, pos)
    local itemView = vh.itemView
    local views = itemView.getTag()
    local node = treeData[pos + 1]

    if not node then return end

    -- 设置缩进
    local indent = dp2px(12) * (node.level or 0)
    views.ivExpand.setTranslationX(indent)
    views.ivIcon.parent.setTranslationX(indent + dp2px(8))
    views.tvName.setTranslationX(indent + dp2px(18))

    views.tvName.setText(node.name or "")

    -- 设置图标和展开状态
    if node.isPackage then
      views.ivExpand.setVisibility(0)
      views.ivExpand.setImageResource(expandIcon)
      views.ivIcon.setImageResource(folderIcon)
      views.ivIcon.setColorFilter(0xffffffff)
      views.ivIcon.parent.setCardBackgroundColor(Colors.colorPrimary)
      views.ivExpand.animate().rotation(node.opening and -90 or 0).setDuration(0).start()
     else
      views.ivExpand.setVisibility(4)
      views.ivIcon.parent.setCardBackgroundColor(Colors.colorSurfaceContainer)
      views.ivIcon.setImageResource(classIcon)
      views.ivIcon.setColorFilter(Colors.colorPrimary)
    end

    -- 点击事件处理
    itemView.onClick = function()
      local index = vh.getAdapterPosition() + 1
      local node = treeData[index]

      if not node then return end

      if node.isPackage then
        if node.opening then
          -- 折叠节点
          local count = 0
          local i = index + 1
          while i <= #treeData and treeData[i].level > node.level do
            table.remove(treeData, i)
            count = count + 1
          end
          node.opening = false
          adapter.notifyItemChanged(index - 1)
          if count > 0 then
            adapter.notifyItemRangeRemoved(index, count)
          end
         else
          -- 展开节点
          local function addChildren(parentNode, parentIndex, parentLevel)
            local children = {}
            local childLevel = parentLevel + 1

            local childNodes = {}
            if parentNode.children and type(parentNode.children) == "table" then
              for childName, childData in pairs(parentNode.children) do
                if type(childData) == "table" then
                  table.insert(childNodes, childData)
                end
              end
            end

            table.sort(childNodes, function(a, b)
              if a.isPackage ~= b.isPackage then
                return a.isPackage
              end
              return a.name:lower() < b.name:lower()
            end)

            for _, childData in ipairs(childNodes) do
              table.insert(children, {
                name = childData.name,
                fullName = childData.fullName,
                isLeaf = childData.isLeaf,
                isPackage = childData.isPackage,
                level = childLevel,
                opening = false,
                children = childData.children
              })
            end

            for i, child in ipairs(children) do
              table.insert(treeData, parentIndex + i, child)
            end

            return #children
          end

          local addedCount = addChildren(node, index, node.level)
          node.opening = true
          adapter.notifyItemChanged(index - 1)
          if addedCount > 0 then
            adapter.notifyItemRangeInserted(index, addedCount)
          end
        end
       else
        -- 类点击事件
        print(node.fullName)
      end
    end
  end,
})

-- 设置布局管理器和适配器
local layoutManager = LinearLayoutManager(activity)
views.rvTree.setLayoutManager(layoutManager)
views.rvTree.setAdapter(adapter)
initFastScroller(views.rvTree, Colors.colorPrimary, 0x31000000, dp2px(8), dp2px(8))

-- 使用 onCreateOptionsMenu 创建搜索菜单
function onCreateOptionsMenu(menu)
  -- 创建搜索菜单项
  local searchItem = menu.add("搜索")
  .setIcon(android.R.drawable.ic_menu_search)
  .setActionView(SearchView(activity))
  .setShowAsAction(2)

  local searchView = searchItem.getActionView()
  searchView
  .setQueryHint("搜索类名...")

  -- 设置搜索监听器
  searchView.setOnQueryTextListener(luajava.createProxy("android.widget.SearchView$OnQueryTextListener", {
    onQueryTextChange = function(newText)
      -- 实时搜索
      local filteredTreeData = performSearch(newText)
      treeData = filteredTreeData
      adapter.notifyDataSetChanged()
      return true
    end
  }))
end



--[[

简易版，能用👍

]]--
require "import"
import "android.app.*"
import "android.os.*"
import "android.widget.*"
import "android.view.*"
import "android.media.MediaPlayer"

activity.setContentView(loadlayout({
  LinearLayout,
  orientation="vertical",
  layout_width="fill",
  layout_height="fill",
  background="#ffffff",
  {
    TextView,
    text="LRC歌词制作器",
    textSize="24sp",
    layout_width="fill",
    gravity="center",
    padding="15dp",
    background="#4CAF50",
    textColor="#ffffff"
  },
  {
    ScrollView,
    layout_width="fill",
    layout_height="fill",
    {
      LinearLayout,
      orientation="vertical",
      layout_width="fill",
      {
        Button,
        text="选择音频文件MP3",
        id="btnSelect",
        layout_margin="10dp",
        layout_width="fill",
        backgroundColor="#2196F3",
        textColor="#ffffff"
      },
      {
        TextView,
        text="未选择",
        id="tvFile",
        layout_margin="10dp",
        textSize="12sp"
      },
      {
        LinearLayout,
        orientation="horizontal",
        gravity="center",
        {
          Button,
          text="播放",
          id="btnPlay",
          layout_margin="5dp"
        },
        {
          Button,
          text="暂停",
          id="btnPause",
          layout_margin="5dp"
        }
      },
      {
        TextView,
        text="00:00.00",
        id="tvTime",
        textSize="24sp",
        gravity="center",
        layout_margin="10dp",
        textColor="#000000"
      },
      {
        TextView,
        text="输入全部歌词(一行一句):",
        layout_margin="10dp",
        textSize="14sp"
      },
      {
        EditText,
        hint="可以粘贴多行歌词\n每行对应一句",
        id="etLyrics",
        layout_margin="10dp",
        layout_height="150dp",
        gravity="top"
      },
      {
        Button,
        text="载入歌词(按行分割)",
        id="btnLoad",
        layout_width="fill",
        layout_margin="10dp",
        backgroundColor="#9C27B0",
        textColor="#ffffff"
      },
      {
        TextView,
        text="当前行:",
        id="tvCurrent",
        layout_margin="10dp",
        textSize="16sp",
        textColor="#FF5722"
      },
      {
        Button,
        text="添加当前行(时间戳)",
        layout_width="fill",
        id="btnAdd",
        layout_margin="10dp",
        backgroundColor="#FF9800",
        textColor="#ffffff"
      },
      {
        TextView,
        text="已添加时间戳的歌词:",
        layout_margin="10dp",
        textSize="14sp"
      },
      {
        ListView,
        id="lvList",
        layout_width="fill",
        layout_height="300dp"
      },
      {
        LinearLayout,
        orientation="horizontal",
        layout_width="fill",
        {
          Button,
          text="清空",
          id="btnClear",
          layout_margin="5dp",
          backgroundColor="0xFFAF0000",
          layout_width="fill",
          textColor="#ffffff",
          layout_weight="1"
        },
        {
          Button,
          text="保存LRC",
          id="btnSave",
          layout_margin="5dp",
          layout_width="fill",
          layout_weight="1",
          backgroundColor="#4CAF50",
          textColor="#ffffff"
        },
      }
    }
  }
}))

mp = nil
lyrics = {}
lyricLines = {}
currentIndex = 0
running = false

adapter = ArrayAdapter(activity, android.R.layout.simple_list_item_1, {})
lvList.setAdapter(adapter)

function fmt(ms)
  local m = math.floor(ms/60000)
  local s = math.floor((ms%60000)/1000)
  local c = math.floor((ms%1000)/10)
  return string.format("%02d:%02d.%02d", m, s, c)
end

function updateCurrentLine()
  if currentIndex > 0 and currentIndex <= #lyricLines then
    tvCurrent.setText("当前行 [" .. currentIndex .. "/" .. #lyricLines .. "]: " .. lyricLines[currentIndex])
   else
    tvCurrent.setText("已完成所有歌词")
  end
end

btnSelect.onClick = function()
  import "android.content.Intent"
  i = Intent(Intent.ACTION_GET_CONTENT)
  i.setType("audio/*")
  activity.startActivityForResult(i, 100)
end

function onActivityResult(req, res, data)
  if req == 100 and res == -1 then
    uri = data.getData()
    import "android.provider.OpenableColumns"
    cursor = activity.getContentResolver().query(uri, nil, nil, nil, nil)
    if cursor then
      if cursor.moveToFirst() then
        idx = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
        if idx >= 0 then
          tvFile.setText(cursor.getString(idx))
        end
      end
      cursor.close()
    end
    if mp then
      mp.release()
    end
    mp = MediaPlayer()
    mp.setOnPreparedListener({
      onPrepared = function()
        print("音频准备完成")
      end
    })
    mp.setOnErrorListener({
      onError = function(a,b,c)
        print("错误:" .. b)
        return true
      end
    })
    fd = activity.getContentResolver().openFileDescriptor(uri, "r")
    mp.setDataSource(fd.getFileDescriptor())
    fd.close()
    mp.prepare()
  end
end

btnPlay.onClick = function()
  if mp then
    mp.start()
    running = true
    task(function()
      while running do
        t = mp.getCurrentPosition()
        activity.runOnUiThread(function()
          tvTime.setText(fmt(t))
        end)
        sleep(50)
      end
    end)
  end
end

btnPause.onClick = function()
  if mp then
    mp.pause()
    running = false
  end
end

btnLoad.onClick = function()
  local txt = etLyrics.getText().toString()
  if txt == "" then
    print("请先输入歌词")
    return
  end
  lyricLines = {}
  for line in txt:gmatch("[^\r\n]+") do
    line = line:match("^%s*(.-)%s*$") or ""
    if line ~= "" then
      table.insert(lyricLines, line)
    end
  end
  if #lyricLines > 0 then
    currentIndex = 1
    updateCurrentLine()
    print("载入了 " .. #lyricLines .. " 行歌词")
   else
    print("没有找到有效歌词")
  end
end

btnAdd.onClick = function()
  if not mp then
    print("请先选择音频")
    return
  end
  if currentIndex < 1 or currentIndex > #lyricLines then
    print("请先载入歌词")
    return
  end
  local t = mp.getCurrentPosition()
  local txt = lyricLines[currentIndex]
  table.insert(lyrics, {t=t, txt=txt})
  table.sort(lyrics, function(a,b) return a.t < b.t end)
  adapter.clear()
  for i,v in ipairs(lyrics) do
    adapter.add("[" .. fmt(v.t) .. "] " .. v.txt)
  end
  lvList.setSelection(adapter.getCount() - 1)
  currentIndex = currentIndex + 1
  updateCurrentLine()
end

btnSave.onClick = function()
  if #lyrics == 0 then
    print("没有歌词可保存")
    return
  end
  local content = "[ti:]\n[ar:]\n[al:]\n\n"
  for i, v in ipairs(lyrics) do
    content = content .. "[" .. fmt(v.t) .. "]" .. v.txt .. "\n"
  end
  local fileName = "output.lrc"
  pcall(function()
    local fileText = tvFile.getText()
    if fileText then
      local fn = fileText.toString()
      if fn and fn ~= "" and fn ~= "未选择" then
        fileName = fn:gsub("%.%w+$", "") .. ".lrc"
      end
    end
  end)
  local savePath = "/sdcard/Download/" .. fileName
  local file = io.open(savePath, "w")
  if file then
    file:write(content)
    file:close()
    print("保存成功: " .. savePath)
   else
    savePath = "/sdcard/" .. fileName
    file = io.open(savePath, "w")
    if file then
      file:write(content)
      file:close()
      print("保存成功: " .. savePath)
     else
      print("保存失败,请检查存储权限")
    end
  end
end


btnClear.onClick = function()
  lyrics = {}
  lyricLines = {}
  currentIndex = 0
  adapter.clear()
  tvCurrent.setText("当前行:")
  print("已清空")
end



local bindClass = luajava.bindClass
local Float = bindClass "java.lang.Float"
local Array = bindClass "java.lang.reflect.Array"

local luaNums = {0, 360}        
local floatArr = Array.newInstance(Float.TYPE, #luaNums)

for i = 1, #luaNums do
  floatArr[i-1] = luaNums[i]     
end

print(floatArr)



local Byte = luajava.bindClass "java.lang.Byte"
local Array = luajava.bindClass "java.lang.reflect.Array"

local seed = "Test String"
local bytes = Array.newInstance(Byte.TYPE, #seed)

for i = 1, #seed do
  bytes[i-1] = string.byte(seed, i)
end

print(bytes)



local bindClass = luajava.bindClass
local CoordinatorLayout = bindClass "androidx.coordinatorlayout.widget.CoordinatorLayout"
local LinearLayoutCompat = bindClass "androidx.appcompat.widget.LinearLayoutCompat"
local RecyclerView = bindClass "androidx.recyclerview.widget.RecyclerView"
local Integer = bindClass "java.lang.Integer"
local Color = bindClass "android.graphics.Color"
local View = bindClass "android.view.View"
local TextView = bindClass "android.widget.TextView"
local MaterialCardView = bindClass "com.google.android.material.card.MaterialCardView"
local TabLayout = bindClass "com.google.android.material.tabs.TabLayout"
local ViewPager = bindClass "androidx.viewpager.widget.ViewPager"
local LuaPagerAdapter = bindClass "github.daisukiKaffuChino.LuaPagerAdapter"
local ArrayList = bindClass "java.util.ArrayList"
local StaggeredGridLayoutManager = bindClass "androidx.recyclerview.widget.StaggeredGridLayoutManager"
local ContextCompat = bindClass "androidx.core.content.ContextCompat"

local LuaRecyclerAdapter = require "LuaRecyclerAdapter"
local loadlayout = require "loadlayout"

activity.setContentView(loadlayout({
  CoordinatorLayout,
  layout_width = -1,
  layout_height = -1,
  {
    LinearLayoutCompat,
    layout_width = -1,
    layout_height = -1,
    orientation = "vertical",
    {
      TabLayout,
      id = "tabLayout",
      layout_width = -1,
      layout_height = "wrap_content",
    },
    {
      ViewPager,
      id = "viewPager",
      layout_width = -1,
      layout_height = -1,
    },
  },
}))

local targetClasses = {
  {name = "Material", classPath = "com.google.android.material.R$color"},
  {name = "AppCompat", classPath = "androidx.appcompat.R$color"},
  {name = "LuaAppX Pro",classPath = "com.luaappx.R$color"},
  {name = "Android", classPath = "android.R$color"},
}

local allColorResources = {}

for _, classInfo in ipairs(targetClasses) do
  local success, colorClass = pcall(bindClass, classInfo.classPath)
  if success then
    local fields = luajava.astable(colorClass.getDeclaredFields())
    for _, field in ipairs(fields) do
      if field.getType() == Integer.TYPE then
        table.insert(allColorResources, {
          name = field.getName(),
          id = field.getInt(nil),
          source = classInfo.name,
        })
      end
    end
   else
    print("无法加载类: " .. classInfo.classPath)
  end
end

table.sort(allColorResources, function(a, b) return a.name < b.name end)

local tabData = {
  all = allColorResources,
  material = {},
  appcompat = {},
  app = {},
  android = {},
}

for _, res in ipairs(allColorResources) do
  if res.source == "Material" then
    table.insert(tabData.material, res)
   elseif res.source == "AppCompat" then
    table.insert(tabData.appcompat, res)
   elseif res.source == "LuaAppX Pro" then
    table.insert(tabData.app, res)
   elseif res.source == "Android" then
    table.insert(tabData.android, res)
  end
end

-- 更紧凑的布局
local itemLayout = {
  LinearLayoutCompat,
  layout_width = "match_parent",
  {
    MaterialCardView,
    layout_width = "match_parent",
    layout_height = "wrap_content",
    layout_margin = "4dp",
    id = "card",
    {
      LinearLayoutCompat,
      layout_width = "match_parent",
      layout_height = "wrap_content",
      orientation = "vertical",
      {
        View,
        id = "colorPreview",
        layout_width = "match_parent",
        layout_height = "60dp",
      },
      {
        LinearLayoutCompat,
        layout_width = "match_parent",
        layout_height = "wrap_content",
        orientation = "vertical",
        padding = "8dp",
        {
          TextView,
          id = "text_name",
          layout_width = "match_parent",
          layout_height = "wrap_content",
          textSize = "12sp",
          textColor = 0xFF000000,
        },
        {
          TextView,
          id = "text_value",
          layout_width = "match_parent",
          layout_height = "wrap_content",
          textSize = "10sp",
          textColor = 0xFF666666,
          paddingTop = "2dp",
        },
        {
          TextView,
          id = "text_source",
          layout_width = "match_parent",
          layout_height = "wrap_content",
          textSize = "9sp",
          textColor = 0xFF888888,
          paddingTop = "1dp",
        },
      },
    }
  }
}

local pagerList = ArrayList()
local adapters = {}

local tabOrder = {"all", "material", "appcompat", "app", "android"}

for _, tabName in ipairs(tabOrder) do
  local recyclerView = RecyclerView(activity)

  -- 使用网格布局以增加紧凑性
  local spanCount = 2 -- 改为两列布局
  local staggeredLayoutManager = StaggeredGridLayoutManager(spanCount, StaggeredGridLayoutManager.VERTICAL)
  recyclerView.setLayoutManager(staggeredLayoutManager)

  -- 添加项目间距
  local dimension = bindClass("android.util.TypedValue")
  local dip = 4
  local px = math.floor(dip * activity.getResources().getDisplayMetrics().density + 0.5)
  recyclerView.setPadding(px, px, px, px)
  recyclerView.setClipToPadding(false)

  adapters[tabName] = LuaRecyclerAdapter(tabData[tabName], itemLayout, {
    onBindViewHolder = function(holder, position)
      local data = tabData[tabName][position + 1]
      local view = holder.view.getTag()

      view.text_name.setText(data.name)
      view.text_source.setText("来源: " .. data.source)

      -- 尝试获取颜色值
      local colorValue = ""
      pcall(function()
        local colorInt = ContextCompat.getColor(activity, data.id)
        view.colorPreview.setBackgroundColor(colorInt)
        colorValue = string.format("#%06X", 0xFFFFFF & colorInt)
        view.text_value.setText("值: " .. colorValue)
      end)

      view.card.onClick = function()
        local Context = bindClass "android.content.Context"
        local clipboard = activity.getSystemService(Context.CLIPBOARD_SERVICE)
        local clipData = bindClass("android.content.ClipData").newPlainText("颜色名称", data.name)
        clipboard.setPrimaryClip(clipData)

        local Snackbar = bindClass "com.google.android.material.snackbar.Snackbar"
        Snackbar.make(holder.view, "已复制: " .. data.name, Snackbar.LENGTH_SHORT).show()
      end
    end,
  })

  recyclerView.setAdapter(adapters[tabName])
  pagerList.add(recyclerView)
end

viewPager.setAdapter(LuaPagerAdapter(pagerList))
tabLayout.setupWithViewPager(viewPager)

local titles = {"全部", "Material", "AppCompat", "LuaAppX Pro", "Android"}
for i = 1, #titles do
  local tab = tabLayout.getTabAt(i - 1)
  if tab then tab.setText(titles[i]) end
end

tabLayout.getTabAt(0).select()



import "android.widget.*"
import "android.view.*"
import "android.os.*"
import "android.graphics.*"
import "android.view.ViewGroup"
import "com.luaappx.utils.UiUtil"

activity.setTitle("UiUtil Demo")

-- 创建滚动布局
local scrollView = ScrollView(activity)
local layout = LinearLayout(activity)
layout.setOrientation(LinearLayout.VERTICAL)
layout.setPadding(30, 30, 30, 30)
scrollView.addView(layout)

-- 添加标题函数
local function addTitle(text)
  local tv = TextView(activity)
  tv.setText(text)
  tv.setTextSize(18)
  tv.setTextColor(Color.BLUE)
  tv.setTypeface(nil, Typeface.BOLD)
  tv.setPadding(0, 30, 0, 10)
  layout.addView(tv)
end

-- 添加文本函数
local function addText(label, value)
  local tv = TextView(activity)
  tv.setText(label .. ": " .. tostring(value))
  tv.setTextSize(14)
  tv.setPadding(0, 5, 0, 5)
  layout.addView(tv)
end

-- 添加按钮函数
local function addButton(text, onClick)
  local btn = Button(activity)
  btn.setText(text)
  btn.setOnClickListener(onClick)
  layout.addView(btn)
end

-- 添加分隔线
local function addDivider()
  local view = View(activity)
  view.setBackgroundColor(Color.LTGRAY)
  layout.addView(view, ViewGroup.LayoutParams(-1, 1))
end

-- 屏幕信息
addTitle("屏幕信息")
addText("屏幕宽度", UiUtil.getScreenWidth(activity) .. "px")
addText("屏幕高度", UiUtil.getScreenHeight(activity) .. "px")
addText("真实屏幕高度", UiUtil.getRealScreenHeight(activity) .. "px")
addText("屏幕宽度(dp)", UiUtil.getScreenWidthDp(activity) .. "dp")
addText("屏幕高度(dp)", UiUtil.getScreenHeightDp(activity) .. "dp")
addText("最小屏幕宽度(dp)", UiUtil.getSmallestScreenWidthDp(activity) .. "dp")
addText("屏幕宽高比", string.format("%.2f", UiUtil.getScreenAspectRatio(activity)))
addText("屏幕密度", UiUtil.getDensity(activity))
addText("屏幕DPI", UiUtil.getDensityDpi(activity))
addText("缩放密度", UiUtil.getScaledDensity(activity))

addDivider()

-- 方向信息
addTitle("方向信息")
addText("是否横屏", UiUtil.isLandscape(activity))
addText("是否竖屏", UiUtil.isPortrait(activity))
addText("是否平板", UiUtil.isTablet(activity))
addText("是否RTL布局", UiUtil.isRtl(activity))

addDivider()

-- 尺寸信息
addTitle("尺寸信息")
addText("状态栏高度", UiUtil.getStatusBarHeight(activity) .. "px")
addText("导航栏高度", UiUtil.getNavigationBarHeight(activity) .. "px")
addText("ActionBar大小", UiUtil.getActionBarSize(activity) .. "px")
addText("折叠工具栏中等大小", UiUtil.getCollapsingToolbarLayoutMediumSize(activity) .. "px")

addDivider()

-- 单位转换
addTitle("单位转换")
addText("100dp转px", UiUtil.dp2px(activity, 100) .. "px")
addText("100px转dp", string.format("%.2f", UiUtil.px2dp(activity, 100)) .. "dp")
addText("20sp转px", UiUtil.sp2px(activity, 20) .. "px")

addDivider()

-- 颜色相关
addTitle("颜色相关")
local testColor = Color.BLUE
addText("测试颜色(#0000FF)是否深色", UiUtil.isColorDark(testColor))
addText("对比色", string.format("#%06X", 0xFFFFFF and UiUtil.getContrastColor(testColor)))
addText("半透明颜色", string.format("#%08X", UiUtil.adjustAlpha(testColor, 0.5)))
addText("指定透明度颜色", string.format("#%08X", UiUtil.getColorWithAlpha(testColor, 128)))

addDivider()

-- 模式检测
addTitle("模式检测")
addText("是否深色模式", UiUtil.isNightMode(activity))
addText("是否有刘海屏", UiUtil.hasNotch(activity))

addDivider()

-- 显示指标
addTitle("显示指标")
local metrics = UiUtil.getDisplayMetrics(activity)
addText("显示指标宽度", metrics.widthPixels .. "px")
addText("显示指标高度", metrics.heightPixels .. "px")
addText("显示指标密度", metrics.density)
addText("显示指标DPI", metrics.densityDpi)

addDivider()

-- 状态栏控制
addTitle("状态栏控制")
addButton("设置状态栏颜色为蓝色", function()
  UiUtil.setStatusBarColor(activity, 0xFF0077FF)
  Toast.makeText(activity, "状态栏颜色已设置为蓝色", Toast.LENGTH_SHORT).show()
end)

addButton("设置状态栏颜色为红色", function()
  UiUtil.setStatusBarColor(activity, 0xFFFF0000)
  Toast.makeText(activity, "状态栏颜色已设置为红色", Toast.LENGTH_SHORT).show()
end)

addButton("切换状态栏亮色模式", function()
  local isLight = not UiUtil.isColorDark(activity.window.statusBarColor)
  UiUtil.setLightStatusBar(activity, isLight)
  Toast.makeText(activity, "状态栏亮色模式: " .. tostring(isLight), Toast.LENGTH_SHORT).show()
end)

addDivider()

-- 导航栏控制
addTitle("导航栏控制")
addButton("设置导航栏颜色为绿色", function()
  UiUtil.setNavigationBarColor(activity, 0xFF00FF00)
  Toast.makeText(activity, "导航栏颜色已设置为绿色", Toast.LENGTH_SHORT).show()
end)

addButton("切换导航栏亮色模式", function()
  local isLight = not UiUtil.isColorDark(activity.window.navigationBarColor)
  UiUtil.setLightNavigationBar(activity, isLight)
  Toast.makeText(activity, "导航栏亮色模式: " .. tostring(isLight), Toast.LENGTH_SHORT).show()
end)

addDivider()

-- 全屏控制
addTitle("全屏控制")
addText("当前是否全屏", UiUtil.isFullScreen(activity))

addButton("切换全屏模式", function()
  local isFullScreen = not UiUtil.isFullScreen(activity)
  UiUtil.setFullScreen(activity, isFullScreen)
  Toast.makeText(activity, "全屏模式: " .. tostring(isFullScreen), Toast.LENGTH_SHORT).show()
  
  -- 刷新显示
  local handler = Handler()
  handler.postDelayed(function()
    local tv = TextView(activity)
    tv.setText("当前是否全屏: " .. tostring(UiUtil.isFullScreen(activity)))
    tv.setTextSize(14)
    tv.setPadding(0, 5, 0, 5)
    layout.addView(tv, layout.getChildCount() - 3) -- 插入到按钮之前
  end, 300)
end)

-- 添加底部空间
local bottomSpace = View(activity)
layout.addView(bottomSpace, ViewGroup.LayoutParams(-1, 50))

activity.setContentView(scrollView)



local bindClass = luajava.bindClass
local CoordinatorLayout = bindClass "androidx.coordinatorlayout.widget.CoordinatorLayout"
local LinearLayoutCompat = bindClass "androidx.appcompat.widget.LinearLayoutCompat"
local RecyclerView = bindClass "androidx.recyclerview.widget.RecyclerView"
local Integer = bindClass "java.lang.Integer"
local Color = bindClass "android.graphics.Color"
local TextView = bindClass "android.widget.TextView"
local ImageView = bindClass "android.widget.ImageView"
local MaterialCardView = bindClass "com.google.android.material.card.MaterialCardView"
local TabLayout = bindClass "com.google.android.material.tabs.TabLayout"
local ViewPager = bindClass "androidx.viewpager.widget.ViewPager"
local LuaPagerAdapter = bindClass "github.daisukiKaffuChino.LuaPagerAdapter"
local ArrayList = bindClass "java.util.ArrayList"
local StaggeredGridLayoutManager = bindClass "androidx.recyclerview.widget.StaggeredGridLayoutManager"
local SearchView = bindClass "androidx.appcompat.widget.SearchView"

local LuaRecyclerAdapter = require "LuaRecyclerAdapter"
local loadlayout = require "loadlayout"

-- 启用 SupportActionBar
activity.getSupportActionBar().show()
activity.getSupportActionBar().setTitle("Drawable 资源浏览器")
activity.supportActionBar.setElevation(0)

activity.setContentView(loadlayout({
  CoordinatorLayout,
  layout_width = -1,
  layout_height = -1,
  {
    LinearLayoutCompat,
    layout_width = -1,
    layout_height = -1,
    orientation = "vertical",
    {
      TabLayout,
      id = "tabLayout",
      layout_width = -1,
      layout_height = "wrap_content",
    },
    {
      ViewPager,
      id = "viewPager",
      layout_width = -1,
      layout_height = -1,
    },
  },
}))

local targetClasses = {
  {name = "Material",   classPath = "com.google.android.material.R$drawable"},
  {name = "AppCompat",  classPath = "androidx.appcompat.R$drawable"},
  {name = "LuaAppX Pro",classPath = "com.luaappx.R$drawable"},
  {name = "Android",    classPath = "android.R$drawable"},
}

local allDrawableResources = {}

for _, classInfo in ipairs(targetClasses) do
  local success, drawableClass = pcall(bindClass, classInfo.classPath)
  if success then
    local fields = luajava.astable(drawableClass.getDeclaredFields())
    for _, field in ipairs(fields) do
      if field.getType() == Integer.TYPE then
        table.insert(allDrawableResources, {
          name = field.getName(),
          id = field.getInt(nil),
          source = classInfo.name,
        })
      end
    end
  else
    print("无法加载类: " .. classInfo.classPath)
  end
end

table.sort(allDrawableResources, function(a, b) return a.name < b.name end)

local tabData = {
  all = allDrawableResources,
  material = {},
  appcompat = {},
  app = {},
  android = {},
}

for _, res in ipairs(allDrawableResources) do
  if res.source == "Material" then
    table.insert(tabData.material, res)
  elseif res.source == "AppCompat" then
    table.insert(tabData.appcompat, res)
  elseif res.source == "LuaAppX Pro" then
    table.insert(tabData.app, res)
  elseif res.source == "Android" then
    table.insert(tabData.android, res)
  end
end

local itemLayout = {
  LinearLayoutCompat,
  layout_height = "wrap_content",
  layout_width = "match_parent",
  {
    MaterialCardView,
    layout_width = "match_parent",
    layout_marginLeft = "8dp",
    layout_marginRight = "8dp",
    layout_marginTop = "8dp",
    layout_marginBottom = "8dp",
    id = "card",
    {
      LinearLayoutCompat,
      layout_width = "match_parent",
      layout_height = "wrap_content",
      padding = "8dp",
      orientation = "vertical",
      gravity = "center",
      {
        ImageView,
        id = "img_preview",
        layout_width = "48dp",
        layout_height = "48dp",
        padding = "4dp",
        backgroundColor = 0xeeeeeeee,
      },
      {
        TextView,
        layout_width = "match_parent",
        layout_height = "wrap_content",
        textSize = "12sp",
        textColor = colorOnSurface,
        id = "text_name",
        gravity = "center",
        paddingTop = "4dp",
      },
    },
  }
}

local pagerList = ArrayList()
local adapters = {}
local recyclerViews = {}

local tabOrder = {"all", "material", "appcompat", "app", "android"}

-- 创建适配器函数
local function createAdapter(data)
  return LuaRecyclerAdapter(data, itemLayout, {
    onBindViewHolder = function(holder, position)
      local data = data[position + 1]
      local view = holder.view.getTag()

      view.text_name.setText(data.name)

      pcall(function()
        view.img_preview.setImageResource(data.id)
      end)

      view.card.onClick = function()
        local Context = bindClass "android.content.Context"
        local clipboard = activity.getSystemService(Context.CLIPBOARD_SERVICE)
        local clipData = bindClass("android.content.ClipData").newPlainText("Drawable名称", data.name)
        clipboard.setPrimaryClip(clipData)

        local Snackbar = bindClass "com.google.android.material.snackbar.Snackbar"
        Snackbar.make(holder.view, "已复制: " .. data.name, Snackbar.LENGTH_SHORT).show()
      end
    end,
  })
end

-- 过滤函数
local function filterData(query, data)
  if not query or #query == 0 then
    return data
  end
  
  local filtered = {}
  local lowerQuery = query:lower()
  
  for _, item in ipairs(data) do
    if item.name:lower():find(lowerQuery, 1, true) then
      table.insert(filtered, item)
    end
  end
  
  return filtered
end

-- 应用搜索过滤
local function applySearchFilter(query)
  for i, tabName in ipairs(tabOrder) do
    local filteredData = filterData(query, tabData[tabName])
    adapters[tabName] = createAdapter(filteredData)
    recyclerViews[i].setAdapter(adapters[tabName])
  end
end

-- 初始化 RecyclerViews
for i, tabName in ipairs(tabOrder) do
  local recyclerView = RecyclerView(activity)
  local spanCount = 3
  local staggeredLayoutManager = StaggeredGridLayoutManager(spanCount, StaggeredGridLayoutManager.VERTICAL)
  recyclerView.setLayoutManager(staggeredLayoutManager)
  
  adapters[tabName] = createAdapter(tabData[tabName])
  recyclerView.setAdapter(adapters[tabName])
  
  pagerList.add(recyclerView)
  recyclerViews[i] = recyclerView
end

viewPager.setAdapter(LuaPagerAdapter(pagerList))
tabLayout.setupWithViewPager(viewPager)

local titles = {"全部", "Material", "AppCompat", "LuaAppX Pro", "Android"}
for i = 1, #titles do
  local tab = tabLayout.getTabAt(i - 1)
  if tab then tab.setText(titles[i]) end
end

tabLayout.getTabAt(0).select()

-- 创建菜单和搜索功能
function onCreateOptionsMenu(menu)
  local MenuInflater = bindClass("android.view.MenuInflater")
  local inflater = MenuInflater(activity)
  
  -- 使用菜单资源文件（如果有）
  -- inflater.inflate(R.menu.main_menu, menu)
  
  -- 或者动态创建菜单项
  local searchItem = menu.add("搜索")
  searchItem.setIcon(android.R.drawable.ic_menu_search)
  searchItem.setShowAsAction(1) -- SHOW_AS_ACTION_ALWAYS
  
  -- 创建SearchView
  local searchView = SearchView(activity)
  searchItem.setActionView(searchView)
  
  -- 设置SearchView属性
  searchView.setQueryHint("搜索资源名称...")
  
  -- 监听搜索查询变化
  searchView.setOnQueryTextListener(SearchView.OnQueryTextListener{
    onQueryTextSubmit = function(query)
      return false
    end,
    onQueryTextChange = function(newText)
      applySearchFilter(newText)
      return false
    end
  })
  
  -- 监听搜索视图展开/折叠
  searchView.setOnSearchClickListener({
    onClick = function(v)
      -- 搜索视图展开时的操作
    end
  })
  
  searchView.setOnCloseListener(SearchView.OnCloseListener{
    onClose = function()
      applySearchFilter("")
      return false
    end
  })
  
  return true
end

-- 可选：处理选项菜单项点击
function onOptionsItemSelected(item)
  -- 处理其他菜单项点击
  return false
end



--内容来自AI
--需要摄像头权限
require "import"
import "android.hardware.Camera"
import "android.view.*"
import "android.widget.FrameLayout"
import "android.graphics.Rect"
import "java.util.ArrayList"

camera = Camera.open(0)
params = camera.getParameters()
local sz = params.getSupportedPreviewSizes()[1]
params.setPreviewSize(sz.width, sz.height)
camera.setParameters(params)
camera.setDisplayOrientation(90)

local dm = activity.getResources().getDisplayMetrics()
local scrW, scrH = dm.widthPixels, dm.heightPixels
local ar = sz.width / sz.height
local viewH = scrW * ar
if viewH > scrH then
  viewH = scrH
  scrW  = scrH / ar
end

sv   = SurfaceView(activity)
holder = sv.getHolder()
root = FrameLayout(activity)
root.addView(sv, FrameLayout.LayoutParams(scrW, viewH, 17))
activity.setContentView(root)

local isPreview = false
holder.addCallback({
  surfaceCreated = function(h)
    camera.setPreviewDisplay(h)
    camera.startPreview()
    isPreview = true
  end,
  surfaceDestroyed = function()
    camera.stopPreview()
    camera.release()
    isPreview = false
  end
})

local currentZoom = 0
local maxZoom     = params.getMaxZoom()
local lastTime    = 0
function writeZoom(newZ)
  if not isPreview or newZ == currentZoom then return end
  local now = os.clock()
  if now - lastTime < 0.25 then return end
  lastTime = now
  currentZoom = newZ
  pcall(function()
    local p = camera.getParameters()
    p.setZoom(currentZoom)
    camera.setParameters(p)
  end)
end

local isFocusing = false
function autoFocus(x, y)
  if isFocusing then return end
  isFocusing = true
  local p = camera.getParameters()
  if p.getMaxNumFocusAreas() > 0 then
    local areas = ArrayList()
    local s = 200
    local l = x * 2000 / scrW - 1000 - s/2
    local t = y * 2000 / viewH - 1000 - s/2
    areas.add(Camera.Area(Rect(l, t, l+s, t+s), 1000))
    p.setFocusAreas(areas)
    p.setMeteringAreas(areas)
  end
  pcall(function()
    camera.setParameters(p)
    camera.autoFocus(function(succ, cam)
      cam.cancelAutoFocus()
      isFocusing = false
    end)
  end)
  isFocusing = false
end

local scaleListener = luajava.createProxy("android.view.ScaleGestureDetector$OnScaleGestureListener", {
  onScale = function(detector)
    if not isPreview then return true end
    local scale = detector.getScaleFactor()
    local newZ  = currentZoom
    if scale > 1.01 then newZ = math.min(maxZoom, currentZoom + 1) end
    if scale < 0.99 then newZ = math.max(0, currentZoom - 1) end
    writeZoom(newZ)
    return true
  end,
  onScaleBegin = function(_) return true end,
  onScaleEnd   = function(_) end
})
local scaleDetector = ScaleGestureDetector(activity, scaleListener)

sv.setOnTouchListener(View.OnTouchListener{
  onTouch = function(v, e)
    scaleDetector.onTouchEvent(e)
    if e.getAction() == MotionEvent.ACTION_UP and e.getPointerCount() == 1 then
      autoFocus(e.getX(), e.getY())
    end
    return true
  end
})

function onKeyDown(k,e)
  if k == 4 then activity.finish(); return true end
end



local function stringToHexWithX(str)
  local hex = ""
  for i = 1, #str do
    local byte = string.byte(str, i)
    hex = hex .. "\\x" .. string.format("%02x", byte)
  end
  return hex
end

local hexASCII = stringToHexWithX("HelloWorld")
print(hexASCII)



--[[
AAAA
AAA
AA
A
空如壳
🐭🐭🐭
]]

require "import"
import "android.app.*"
import "android.os.*"
import "android.widget.*"
import "android.view.*"
import "androidx.appcompat.widget.LinearLayoutCompat"
import "android.graphics.drawable.*"
import "com.google.android.material.button.MaterialButton"
import "com.google.android.material.card.MaterialCardView"
import "com.google.android.material.textfield.TextInputLayout"
import "com.google.android.material.textfield.TextInputEditText"
import "com.google.android.material.dialog.MaterialAlertDialogBuilder"
import "android.content.Context"
import "android.content.Intent"
import "org.json.JSONObject"
import "android.widget.ListView"
import "lemon.material.textfield.MaterialTextField"
import "androidx.appcompat.widget.LinearLayoutCompat"
import "androidx.appcompat.widget.LinearLayoutCompat"

activity
.setTheme(R.style.Theme_Material3_Blue_NoActionBar)
.setTitle("王者自定义房间创建器")
.setContentView(loadlayout({
  LinearLayout,
  layout_width = "fill",
  layout_height = "fill",
  orientation = "vertical",
  padding = "16dp",
  {
    TextView,
    text = "王者自定义房间创建器",
    textSize = "24sp",
    layout_gravity = "center",
    layout_marginBottom = "16dp",
  },
  {
    ScrollView,
    layout_width = "match_parent",
    layout_height = "match_parent",
    {
      LinearLayout,
      layout_width = "fill",
      layout_height = "fill",
      orientation = "vertical",
      {
        MaterialCardView,
        layout_width = "match_parent",
        layout_height = "wrap_content",
        {
          LinearLayout,
          layout_width = "match_parent",
          layout_height = "wrap_content",
          orientation = "vertical",
          padding = "16dp",
          {
            TextView,
            text = "游戏服务器",
            textSize = "18sp",
          },
          {
            LinearLayout,
            orientation = "horizontal",
            layout_width = "match_parent",
            layout_height = "wrap_content",
            layout_marginTop = "8dp",
            {
              MaterialButton,
              id = "正式服",
              text = "正式服",
              layout_width = "0dp",
              layout_height = "48dp",
              layout_weight = "1",
            },
            {
              MaterialButton,
              id = "体验服",
              text = "体验服",
              layout_width = "0dp",
              layout_height = "48dp",
              layout_weight = "1",
              layout_marginLeft = "8dp",
            },
          },
        },
      },

      {
        MaterialCardView,
        layout_width = "match_parent",
        layout_height = "wrap_content",
        layout_marginTop = "16dp",
        {
          LinearLayout,
          layout_width = "match_parent",
          layout_height = "wrap_content",
          orientation = "vertical",
          padding = "16dp",
          {
            TextView,
            text = "地图模式",
            textSize = "18sp",
          },
          {
            MaterialTextField,
            layout_width="fill",
            hint = "请输入地图模式",
            BoxCornerRadii = "16dp",
            id="地图输入",
          },
          {
            MaterialButton,
            id = "查看地图",
            layout_width = "match_parent",
            text="查看英雄",
          },
        },
      },
      {
        MaterialCardView,
        layout_width = "match_parent",
        layout_height = "wrap_content",
        layout_marginTop = "16dp",
        {
          LinearLayout,
          layout_width = "match_parent",
          layout_height = "wrap_content",
          orientation = "vertical",
          padding = "16dp",
          {
            TextView,
            text = "禁用英雄",
            textSize = "18sp",
          },
          {
            MaterialTextField,
            layout_width="fill",
            hint = "请输入禁用英雄",
            BoxCornerRadii = "16dp",
            id="英雄输入"
          },
          {
            MaterialButton,
            id = "禁英雄",
            text="选择英雄",
            layout_width = "match_parent",
          },
        },
      },

      {
        MaterialCardView,
        layout_width = "match_parent",
        layout_height = "wrap_content",
        layout_marginTop = "16dp",
        {
          LinearLayout,
          layout_width = "match_parent",
          layout_height = "wrap_content",
          orientation = "vertical",
          padding = "16dp",
          {
            TextView,
            text = "自定义配置",
            textSize = "18sp",
          },
          {
            TextInputLayout,
            hint = "选择自定义配置",
            layout_width = "match_parent",
            layout_marginTop = "8dp",
            {
              AutoCompleteTextView,
              id = "配置输入",
              layout_width = "match_parent",
              inputType = "none",
            },
          },
          {
            ListView,
            id = "配置列表",
            layout_width = "match_parent",
            layout_height = "200dp",
          },
          {
            MaterialButton,
            id = "添加配置",
            text = "添加自定义配置",
            layout_width = "match_parent",
            layout_height = "48dp",
            layout_marginTop = "8dp",
          },
        },
      },
      {
        MaterialCardView,
        layout_width = "match_parent",
        layout_height = "wrap_content",
        layout_marginTop = "16dp",
        {
          LinearLayout,
          layout_width = "match_parent",
          layout_height = "wrap_content",
          orientation = "vertical",
          padding = "16dp",
          {
            MaterialButton,
            id = "生成链接",
            text = "生成房间链接",
            layout_width = "match_parent",
            layout_height = "48dp",
          },
          {
            TextView,
            id = "链接显示",
            text = "点击生成按钮创建链接",
            textSize = "14sp",
            layout_marginTop = "16dp",
            padding = "8dp",
            layout_width = "match_parent",
          },
          {
            MaterialButton,
            id = "打开房间",
            text = "打开房间",
            layout_width = "match_parent",
            layout_height = "48dp",
            layout_marginTop = "8dp",
          },
          {
            MaterialButton,
            id = "分享链接",
            text = "分享房间链接",
            layout_width = "match_parent",
            layout_height = "48dp",
            layout_marginTop = "8dp",
          },
        },
      },
      {
        MaterialCardView,
        layout_width = "match_parent",
        layout_height = "wrap_content",
        layout_marginTop = "16dp",
        {
          LinearLayout,
          layout_width = "match_parent",
          layout_height = "wrap_content",
          orientation = "vertical",
          padding = "16dp",
          {
            MaterialButton,
            id = "显示教程",
            text = "使用教程",
            layout_width = "match_parent",
            layout_height = "48dp",
          },
        },
      },
    }
  }
}))

-- 地图数据
local 地图数据 = {
  ["快速赛"] = {1010, 1, 10},
  ["小峡谷"] = {20031, 1, 10},
  ["1v1对抗路"] = {25001, 2, 2},
  ["1v1中路"] = {25002, 2, 2},
  ["1v1发育路"] = {25003, 2, 2},
  ["5v5征兆0ban位"] = {20910, 1, 10},
  ["火焰山大作战"] = {20009, 1, 10},
  ["克隆模式"] = {20012, 1, 10},
  ["契约之战"] = {20019, 1, 10},
  ["无限乱斗"] = {20017, 1, 10},
  ["梦境大乱斗"] = {90001, 1, 10},
  ["变身大乱斗"] = {4010, 1, 10},
  ["觉醒之战"] = {5121, 1, 10},
  ["多重施法"] = {5153, 1, 10},
  ["双人同舞"] = {5155, 1, 10},
}

-- 英雄数据
local 英雄数据 = {
  ["安琪拉"] = "10001|法师",
  ["孙悟空"] = "10002|战士",
  ["亚瑟"] = "10003|战士",
  ["鲁班七号"] = "10004|射手",
  ["妲己"] = "10005|法师",
  ["赵云"] = "10006|战士",
  ["后羿"] = "10007|射手",
  ["貂蝉"] = "10008|法师",
  ["吕布"] = "10009|战士",
  ["曹操"] = "10010|战士",
  ["典韦"] = "10011|战士",
  ["宫本武藏"] = "10012|战士",
  ["李白"] = "10013|刺客",
  ["马可波罗"] = "10014|射手",
  ["狄仁杰"] = "10015|射手",
  ["项羽"] = "10016|坦克",
  ["关羽"] = "10017|战士",
  ["露娜"] = "10018|战士",
  ["韩信"] = "10019|刺客",
  ["兰陵王"] = "10020|刺客",
}



function 查看地图.onClick()
  content={
    LinearLayoutCompat;
    layout_height="fill";
    orientation="vertical";
    layout_width="fill";
    {
      ListView;
      layout_height="match_parent";
      id="地图列表";
      layout_width="match_parent";
    };
  };
  local 对话框 = MaterialAlertDialogBuilder(activity)
  对话框.setView(loadlayout(content))
  -- 初始化地图下拉菜单
  local 地图适配器 = ArrayAdapter(activity, android.R.layout.simple_spinner_dropdown_item)
  for 地图名称, _ in pairs(地图数据) do
    地图适配器.add(地图名称)
  end
  地图列表.setAdapter(地图适配器)
  对话框.show()
  地图列表.onItemClick = function(_, _, 位置, _)
    local 地图名称 = 地图列表.getItemAtPosition(位置)
    地图输入.setText(地图名称)
    Toast.makeText(activity, "已选择: "..地图名称, Toast.LENGTH_SHORT).show()
  end
end


function 禁英雄.onClick()
  content={
    LinearLayoutCompat;
    layout_height="fill";
    orientation="vertical";
    layout_width="fill";
    {
      ListView;
      layout_height="match_parent";
      id="英雄列表";
      layout_width="match_parent";
    };
  };
  local 对话框 = MaterialAlertDialogBuilder(activity)
  对话框.setView(loadlayout(content))
  -- 初始化地图下拉菜单
  local 英雄适配器 = ArrayAdapter(activity, android.R.layout.simple_spinner_dropdown_item)
  for 英雄名称, _ in pairs(英雄数据) do
    英雄适配器.add(英雄名称)
  end
  英雄列表.setAdapter(英雄适配器)
  对话框.show()
  英雄列表.onItemClick = function(_, _, 位置, _)
    local 英雄名称 = 英雄列表.getItemAtPosition(位置)
    英雄输入.setText(英雄名称)
    Toast.makeText(activity, "已选择: "..英雄名称, Toast.LENGTH_SHORT).show()
  end
end


-- 游戏模式选择
local 游戏类型 = "正式服" -- 默认选择正式服

正式服.onClick = function()
  游戏类型 = "正式服"
  正式服.setBackgroundColor(0xFF2196F3)
  正式服.setTextColor(0xFFFFFFFF)
  体验服.setBackgroundColor(0xFFFFFFFF)
  体验服.setTextColor(0xFF000000)
  Toast.makeText(activity, "已选择正式服", Toast.LENGTH_SHORT).show()
end

体验服.onClick = function()
  游戏类型 = "体验服"
  体验服.setBackgroundColor(0xFF2196F3)
  体验服.setTextColor(0xFFFFFFFF)
  正式服.setBackgroundColor(0xFFFFFFFF)
  正式服.setTextColor(0xFF000000)
  Toast.makeText(activity, "已选择体验服", Toast.LENGTH_SHORT).show()
end



-- 生成链接
生成链接.onClick = function()
  local 游戏链接 = ""
  if 游戏类型 == "正式服" then
    游戏链接 = "tencentmsdk1104466820://?gamedata=SmobaLaunch_"
   else
    游戏链接 = "tencentmsdk1104791911://?gamedata=SmobaLaunch_"
  end

  local 地图名称 = "觉醒之战"
  local 英雄名称 = "鲁班七号"
  local 自定义配置名称 = 配置输入.getText().toString()

  if 地图名称 == "" then
    Toast.makeText(activity, "请选择地图模式", Toast.LENGTH_SHORT).show()
    return
  end

  -- 获取地图数据
  local 地图信息 = 地图数据[地图名称]
  if not 地图信息 then
    Toast.makeText(activity, "无效的地图模式: "..地图名称, Toast.LENGTH_SHORT).show()
    return
  end

  -- 创建房间配置
  local 房间配置 = JSONObject()
  房间配置.put("createType", "2")
  房间配置.put("mapID", tostring(地图信息[1]))
  房间配置.put("ullRoomid", "0")
  房间配置.put("mapType", tostring(地图信息[2]))
  房间配置.put("ullExternUid", "0")
  房间配置.put("roomName", "王者自定义房间")
  房间配置.put("teamerNum", tostring(地图信息[3]))
  房间配置.put("platType", "2")
  房间配置.put("campid", "1")
  房间配置.put("AddPos", "0")
  房间配置.put("firstCountDownTime", "6666666666")
  房间配置.put("secondCountDownTime", "17")
  房间配置.put("AddType", "2")
  房间配置.put("OfflineRelayEntityID", "")
  房间配置.put("openAICommentator", "1")

  -- 添加禁用英雄
  if 英雄名称 ~= "" then
    local 英雄信息 = 英雄数据[英雄名称]
    if 英雄信息 then
      local 英雄ID = 英雄信息:match("([^|]+)")
      local banHerosCamp = luajava.newInstance("org.json.JSONArray")
      banHerosCamp.put(英雄ID)

      房间配置.put("banHerosCamp1", banHerosCamp)
      房间配置.put("banHerosCamp2", banHerosCamp)
     else
      Toast.makeText(activity, "无效的英雄: "..英雄名称, Toast.LENGTH_SHORT).show()
    end
  end

  -- 添加自定义配置
  if 自定义配置名称 ~= "" and 自定义配置名称 ~= "无" then
    -- 从SharedPreferences中获取配置内容
    local 偏好设置 = activity.getSharedPreferences("custom_configs", Context.MODE_PRIVATE)
    local 配置内容 = 偏好设置.getString(自定义配置名称, "")

    if 配置内容 ~= "" then
      local customDefineItems = luajava.newInstance("org.json.JSONArray")
      customDefineItems.put(配置内容)
      房间配置.put("customDefineItems", customDefineItems)
    end
  end

  -- 转换为JSON并Base64编码
  local jsonStr = 房间配置.toString()
  local base64Str = luajava.bindClass("android.util.Base64").encodeToString(
  luajava.bindClass("java.lang.String")(jsonStr).getBytes("UTF-8"),
  luajava.bindClass("android.util.Base64").NO_WRAP
  )

  local 最终链接 = 游戏链接 .. base64Str
  链接显示.text = 最终链接

  -- 显示成功消息
  Toast.makeText(activity, "链接已生成!", Toast.LENGTH_SHORT).show()
end

-- 分享链接
分享链接.onClick = function()
  local 链接内容 = 链接显示.text

  if 链接内容 == "" or 链接内容 == "点击生成按钮创建链接" then
    Toast.makeText(activity, "请先生成链接", Toast.LENGTH_SHORT).show()
    return
  end

  local 分享意图 = Intent(Intent.ACTION_SEND)
  分享意图.setType("text/plain")
  分享意图.putExtra(Intent.EXTRA_TEXT, 链接内容)
  activity.startActivity(Intent.createChooser(分享意图, "分享房间链接"))
end

-- 显示教程
显示教程.onClick = function()
  local 对话框 = MaterialAlertDialogBuilder(activity)
  对话框.setTitle("使用教程")
  对话框.setMessage([[
1. 选择游戏服务器（正式服/体验服）
2. 选择地图模式
3. 选择要禁用的英雄（可选）
4. 输入自定义配置（可选）
5. 点击"生成链接"按钮
6. 点击"分享链接"将房间分享给好友

提示：
- 正式服链接：tencentmsdk1104466820
- 体验服链接：tencentmsdk1104791911
- 自定义配置需要输入有效的JSON格式
    ]])
  对话框.setPositiveButton("我知道了", nil)
  对话框.show()
end

-- 添加自定义配置
添加配置.onClick = function()
  local 视图 = loadlayout{
    LinearLayout,
    orientation = "vertical",
    padding = "16dp",
    {
      TextInputLayout,
      hint = "配置名称",
      {
        TextInputEditText,
        id = "配置名称输入",
      }
    },
    {
      TextInputLayout,
      hint = "配置内容 (JSON格式)",
      layout_marginTop = "16dp",
      {
        TextInputEditText,
        id = "配置内容输入",
        inputType = "textMultiLine",
        lines = 5,
      }
    }
  }

  local 对话框 = MaterialAlertDialogBuilder(activity)
  对话框.setTitle("添加自定义配置")
  对话框.setView(视图)
  对话框.setPositiveButton("保存", {
    onClick = function()
      local 名称 = 配置名称输入.getText().toString()
      local 内容 = 配置内容输入.getText().toString()

      if 名称 == "" or 内容 == "" then
        Toast.makeText(activity, "名称和内容不能为空", Toast.LENGTH_SHORT).show()
        return
      end

      -- 保存配置到SharedPreferences
      local 偏好设置 = activity.getSharedPreferences("custom_configs", Context.MODE_PRIVATE)
      local 编辑器 = 偏好设置.edit()
      编辑器.putString(名称, 内容)
      编辑器.apply()

      -- 更新下拉菜单
      更新配置下拉菜单()

      Toast.makeText(activity, "配置已保存: "..名称, Toast.LENGTH_SHORT).show()
    end
  })
  对话框.setNegativeButton("取消", nil)
  对话框.show()
end

-- 更新配置下拉菜单
function 更新配置下拉菜单()
  local 偏好设置 = activity.getSharedPreferences("custom_configs", Context.MODE_PRIVATE)
  local 所有配置 = 偏好设置.getAll()
  local 适配器 = ArrayAdapter(activity, android.R.layout.simple_spinner_dropdown_item)

  -- 添加默认选项
  适配器.add("无")

  -- 添加配置选项

  配置列表.setAdapter(适配器)
end

-- 配置选择事件
配置列表.onItemClick = function(_, _, 位置, _)
  local 配置名称 = 配置列表.getItemAtPosition(位置)
  配置输入.setText(配置名称)

  if 配置名称 ~= "无" then
    Toast.makeText(activity, "已选择配置: "..配置名称, Toast.LENGTH_SHORT).show()
  end
end

-- 初始化配置下拉菜单
更新配置下拉菜单()




-- 设置初始按钮状态
正式服.setBackgroundColor(0xFF2196F3)
正式服.setTextColor(0xFFFFFFFF)
体验服.setBackgroundColor(0xFFFFFFFF)
体验服.setTextColor(0xFF000000)


打开房间.onClick = function()
  local 链接内容 = 链接显示.text
  if 链接内容 == "" or 链接内容 == "点击生成按钮创建链接" then
    Toast.makeText(activity, "请先生成链接", Toast.LENGTH_SHORT).show()
    return
  end
  -- 创建Intent打开链接
  local intent = Intent(Intent.ACTION_VIEW)
  -- 检查是否有应用可以处理该Intent
  local pm = "com.tencent.tmgp.sgame"
  activity.startActivity(intent)
end



--支持所有环境
require "import"
import "android.app.*"
import "android.os.*"
import "android.widget.*"
import "android.view.*"
import "androidx.appcompat.widget.LinearLayoutCompat"
import "android.animation.ValueAnimator"
activity
.setTitle("Iswitch")
.setContentView(loadlayout(
{
  LinearLayoutCompat;
  layout_height = "fill";
  gravity = "center";
  layout_width = "fill";
  {
    CardView;
    cardBackgroundColor = "0xffe5e5e5";
    radius = "100dp";
    id = "Parent_Card";
    CardElevation = "0dp";
    layout_height = "38dp";
    layout_width = "70dp";
    {
      CardView;
      radius = "100dp";
      cardBackgroundColor = "0xfffefefe";
      layout_height = "35dp";
      id = "Move_Card";
      CardElevation = "0dp";
      layout_margin = "2dp";
      layout_width = "37dp";
    };
  };
}))
import "android.animation.ObjectAnimator"
import "android.view.animation.DecelerateInterpolator"
import "androidx.vectordrawable.graphics.drawable.ArgbEvaluator"
import "android.view.animation.Animation"
function 颜色渐变(id, color, ...)
  time = 200
  str = "cardBackgroundColor"
  if ... == nil then
   elseif type(...) == "string" then
    str = ...
   elseif type(...) == "number" then
    time = ...
  end
  ObjectAnimator.ofInt(id, str, color)
  .setInterpolator(DecelerateInterpolator())
  .setDuration(time)
  .setRepeatCount(0.5)--如果出现闪烁情况 改为-2
  .setRepeatMode(Animation.REVERSE)
  .setEvaluator(ArgbEvaluator())
  .start()
end
local state = false
function Parent_Card.onClick()
  if state == false then
    local view = Move_Card
    local view_width = Move_Card.getWidth()
    local width = Parent_Card.getWidth()
    local margin = activity.getHeight() * 0.0025
    local startX = Move_Card.getX()
    local startY = margin
    local targetX = width - margin - view_width
    local targetY = Move_Card.getY()
    local animator = ValueAnimator.ofFloat({0, 1})
    animator.setDuration(300)
    animator.setInterpolator(DecelerateInterpolator())
    animator.addUpdateListener({
      onAnimationUpdate = function(animation)
        local fraction = animation.getAnimatedValue()
        local currentX = startX + (targetX - startX) * fraction
        local currentY = startY + (targetY - startY) * fraction
        view.setX(currentX)
        view.setY(currentY)
      end
    })
    animator.addListener({
      onAnimationStart = function()
        -- 动画结束后的操作
        颜色渐变(Parent_Card, [0xff07c160])
        state = true
      end
    })
    animator.addListener({
      onAnimationEnd = function()
        -- 动画结束后的操作自己写
      end
    })
    animator.start()
   else
    local view = Move_Card
    local view_width = Move_Card.getWidth()
    local width = Parent_Card.getWidth()
    local margin = activity.getHeight() * 0.0025
    local startX = Move_Card.getX()
    local startY = margin
    local targetX = margin
    local targetY = Move_Card.getY()
    local animator = ValueAnimator.ofFloat({0, 1})
    animator.setDuration(300)
    animator.setInterpolator(DecelerateInterpolator())
    animator.addUpdateListener({
      onAnimationUpdate = function(animation)
        local fraction = animation.getAnimatedValue()
        local currentX = startX + (targetX - startX) * fraction
        local currentY = startY + (targetY - startY) * fraction
        view.setX(currentX)
        view.setY(currentY)
      end
    })
    animator.addListener({
      onAnimationStart = function()
        -- 动画结束后的操作
        颜色渐变(Parent_Card, [0xffe5e5e5])
        state = false
      end
    })
    animator.addListener({
      onAnimationEnd = function()
        -- 动画结束后的操作
      end
    })
    animator.start()
  end
end



import "android.app.*"
import "android.os.*"
import "android.widget.*"
import "android.graphics.*"
import "android.content.*"
import "android.graphics.drawable.GradientDrawable"
import "com.flask.colorpicker.*"
import "com.flask.colorpicker.builder.*"
import "com.flask.colorpicker.renderer.*"

activity.setTitle("ColorPicker")
activity.setContentView(loadlayout({
  LinearLayout,
  orientation = "vertical",
  layout_width = "fill",
  layout_height = "fill",
  padding = "16dp",
  {
    LinearLayout,
    orientation = "horizontal",
    layout_width = "fill",
    layout_height = "wrap",
    gravity = "center_vertical",
    {
      CheckBox,
      id = "cbAlpha",
      text = "Alpha 滑条",
      checked = true
    },
    {
      CheckBox,
      id = "cbLight",
      text = "亮度 滑条",
      checked = true
    },
    {
      CheckBox,
      id = "cbBorder",
      text = "外圈边框",
      checked = true
    }
  },
  {
    LinearLayout,
    orientation = "horizontal",
    layout_width = "fill",
    layout_height = "wrap",
    gravity = "center_vertical",
    {
      CheckBox,
      id = "cbEdit",
      text = "十六进制输入框",
      checked = true
    },
    {
      CheckBox,
      id = "cbPreview",
      text = "颜色预览条",
      checked = true
    }
  },
  {
    LinearLayout,
    orientation = "horizontal",
    layout_width = "fill",
    layout_height = "wrap",
    gravity = "center_vertical",
    {
      TextView,
      text = "密度："
    },
    {
      SeekBar,
      id = "sbDensity",
      max = "20",
      progress = "10",
      layout_weight = "1"
    },
    {
      TextView,
      id = "tvDensity",
      text = "10",
      width = "40dp",
      gravity = "center"
    }
  },
  {
    LinearLayout,
    orientation = "horizontal",
    layout_width = "fill",
    layout_height = "wrap",
    gravity = "center_vertical",
    {
      TextView,
      text = "转盘类型："
    },
    {
      RadioGroup,
      id = "rgWheel",
      orientation = "horizontal",
      {
        RadioButton,
        id = "rbFlower",
        text = "FLOWER",
        checked = true
      },
      {
        RadioButton,
        id = "rbCircle",
        text = "CIRCLE"
      }
    }
  },
  {
    Button,
    id = "btnPick",
    text = "打开颜色选择器",
    layout_marginTop = "16dp",
    layout_gravity = "center_horizontal"
  },
  {
    TextView,
    id = "tvColor",
    text = "当前颜色：0xFF2196F3",
    textSize = "18sp",
    layout_marginTop = "16dp",
    layout_gravity = "center_horizontal"
  },
  {
    ImageView,
    id = "ivColor",
    layout_width = "48dp",
    layout_height = "48dp",
    layout_marginTop = "8dp",
    layout_gravity = "center_horizontal",
    background = "0xFF2196F3"
  }
}))

local prefs = activity.getSharedPreferences("demo", Context.MODE_PRIVATE)
local key = "last_color"

-- 工具：int -> 十六进制
local function hexColor(c)
  return "0x" .. string.format("%08X", c):sub( - 8)
end

-- 更新预览
local function refresh()
  local c = prefs.getInt(key, 0xFF2196F3)
  tvColor.setText("当前颜色：" .. hexColor(c))
  ivColor.background = GradientDrawable()
  ivColor.background.setShape(GradientDrawable.OVAL)
  ivColor.background.setColor(c)
end
refresh()

-- SeekBar 实时显示数值
sbDensity.setOnSeekBarChangeListener(luajava.createProxy("android.widget.SeekBar$OnSeekBarChangeListener", {
  onProgressChanged = function(v, p, f)
    tvDensity.text = tostring(p)
  end
}))

-- 统一打开选择器
btnPick.onClick = function()
  -- 收集参数
  local alpha = cbAlpha.checked
  local light = cbLight.checked
  local border = cbBorder.checked
  local edit = cbEdit.checked
  local preview = cbPreview.checked
  local density = sbDensity.progress
  local wheel = rbFlower.checked and ColorPickerView.WHEEL_TYPE.FLOWER
  or ColorPickerView.WHEEL_TYPE.CIRCLE

  -- 构建
  local builder = ColorPickerDialogBuilder.with(activity)
  .setTitle("ColorPicker")
  .initialColor(prefs.getInt(key, 0xFF2196F3))
  .wheelType(wheel)
  .density(density)
  .showBorder(border)

  if alpha and light then
    -- 默认：两个滑条
   elseif alpha then
    builder.alphaSliderOnly()
   elseif light then
    builder.lightnessSliderOnly()
   else
    builder.noSliders()
  end

  builder.showColorEdit(edit)
  .showColorPreview(preview)
  .setPositiveButton("确定", luajava.createProxy("com.flask.colorpicker.builder.ColorPickerClickListener", {
    onClick = function(dialog, selectedColor, allColors)
      prefs.edit().putInt(key, selectedColor).apply()
      refresh()
    end
  }))
  .setNegativeButton("取消", nil)
  .build()
  .show()
end



-- Intent（意图）主要是解决Android应用的各项组件之间的通讯。
-- Intent负责对应用中一次操作的动作、动作涉及数据、附加数据进行描述.
--[[
Android则根据此Intent的描述，负责找到对应的组件，将 Intent传递给调用的组件，并完成组件的调用。

因此，Intent在这里起着一个媒体中介的作用
专门提供组件互相调用的相关信息
实现调用者与被调用者之间的解耦。

例如，在一个联系人维护的应用中，当我们在一个联系人列表屏幕(假设对应的Activity为listActivity)上
点击某个联系人后，希望能够跳出此联系人的详细信息屏幕(假设对应的Activity为detailActivity)
为了实现这个目的，listActivity需要构造一个 Intent
这个Intent用于告诉系统，我们要做“查看”动作，此动作对应的查看对象是“某联系人”
然后调用startActivity (Intent intent)，将构造的Intent传入

系统会根据此Intent中的描述到ManiFest中找到满足此Intent要求的Activity，系统会调用找到的 Activity，即为detailActivity，最终传入Intent，detailActivity则会根据此Intent中的描述，执行相应的操作。
--]]

import "android.net.Uri"
import "android.content.Intent"
import "android.app.SearchManager"
import "android.provider.Settings"
import "android.content.pm.PackageManager"

-- 调用浏览器搜索关键词
intent = Intent()
intent.setAction(Intent.ACTION_WEB_SEARCH)
intent.putExtra(SearchManager.QUERY,"AndroLua") 
activity.startActivity(intent)


-- 调用浏览器打开网页
url="https://baidu.com"
activity.startActivity(Intent("android.intent.action.VIEW",Uri.parse(url)))

-- 打开置顶包名应用
packageName="com.tencent.mobileqq"
manager = activity.getPackageManager()
open = manager.getLaunchIntentForPackage(packageName)
this.startActivity(open)


--安装应用程序
intent = Intent(Intent.ACTION_VIEW)
安装包路径="/sdcard/a.apk"
intent.setDataAndType(Uri.parse("file://"..安装包路径), "application/vnd.android.package-archive") 
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
activity.startActivity(intent)

-- 卸载应用程序
packageName="com.tencent.mobileqq"
uri = Uri.parse("package:"..packageName)
intent = Intent(Intent.ACTION_DELETE,uri)
activity.startActivity(intent)

-- 播放视频
intent = Intent(Intent.ACTION_VIEW)
uri = Uri.parse("file:///sdcard/a.mp4") 
intent.setDataAndType(uri, "video/mp4")
activity.startActivity(intent)

-- 播放音乐
intent = Intent(Intent.ACTION_VIEW)
uri = Uri.parse("file:///sdcard/song.mp3")
intent.setDataAndType(uri, "audio/mp3")
this.startActivity(intent)


-- 打开应用设置
intent = Intent(android.provider.Settings.ACTION_SETTINGS)
this.startActivity(intent)
--[[
部分字段列表:
ACTION_SETTINGS 系统设置
CTION_APN_SETTINGS APN设置
ACTION_LOCATION_SOURCE_SETTINGS 位置和访问信息
ACTION_WIRELESS_SETTINGS 网络设置
ACTION_AIRPLANE_MODE_SETTINGS 无线和网络热点设置
ACTION_SECURITY_SETTINGS 位置和安全设置
ACTION_WIFI_SETTINGS 无线网WIFI设置
ACTION_WIFI_IP_SETTINGS 无线网IP设置
ACTION_BLUETOOTH_SETTINGS 蓝牙设置
ACTION_DATE_SETTINGS 时间和日期设置
ACTION_SOUND_SETTINGS 声音设置
ACTION_DISPLAY_SETTINGS 显示设置——字体大小等
ACTION_LOCALE_SETTINGS 语言设置
ACTION_INPUT_METHOD_SETTINGS 输入法设置
ACTION_USER_DICTIONARY_SETTINGS 用户词典
ACTION_APPLICATION_SETTINGS 应用程序设置
ACTION_APPLICATION_DEVELOPMENT_SETTINGS 应用程序设置
ACTION_QUICK_LAUNCH_SETTINGS 快速启动设置
ACTION_MANAGE_APPLICATIONS_SETTINGS 已下载（安装）软件列表
ACTION_SYNC_SETTINGS 应用程序数据同步设置
ACTION_NETWORK_OPERATOR_SETTINGS 可用网络搜索
ACTION_DATA_ROAMING_SETTINGS 移动网络设置
ACTION_INTERNAL_STORAGE_SETTINGS 手机存储设置
--]]


-- 选择图片
import "android.content.Intent"
local intent= Intent(Intent.ACTION_PICK)
intent.setType("image/*")
this.startActivityForResult(intent, 1)
-------

-- 回调
function onActivityResult(requestCode,resultCode,intent)
  if intent then
    local cursor =this.getContentResolver ().query(intent.getData(), nil, nil, nil, nil)
    cursor.moveToFirst()
    import "android.provider.MediaStore"
    local idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA)
    fileSrc = cursor.getString(idx)
    bit=nil
    --fileSrc回调路径路径
    import "android.graphics.BitmapFactory"
    bit =BitmapFactory.decodeFile(fileSrc)
    --  iv.setImageBitmap(bit)
  end
end--nirenr


-- 选择文件
function ChooseFile()
  import "android.content.Intent"
  import "android.net.Uri"
  import "java.net.URLDecoder"
  import "java.io.File"
  intent = Intent(Intent.ACTION_GET_CONTENT)
  intent.setType("*/");
  intent.addCategory(Intent.CATEGORY_OPENABLE)
  activity.startActivityForResult(intent,1);
  function onActivityResult(requestCode,resultCode,data)
    if resultCode == Activity.RESULT_OK then
      local str = data.getData().toString()
      local decodeStr = URLDecoder.decode(str,"UTF-8")
      print(decodeStr)
    end
  end
end

ChooseFile()

-- 分享文件
function Sharing(path)
  import "android.webkit.MimeTypeMap"
  import "android.content.Intent"
  import "android.net.Uri"
  import "java.io.File"
  FileName=tostring(File(path).Name)
  ExtensionName=FileName:match("%.(.+)")
  Mime=MimeTypeMap.getSingleton().getMimeTypeFromExtension(ExtensionName)
  intent = Intent()
  intent.setAction(Intent.ACTION_SEND)
  intent.setType(Mime)
  file = File(path)
  uri = Uri.fromFile(file)
  intent.putExtra(Intent.EXTRA_STREAM,uri)
  intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
  activity.startActivity(Intent.createChooser(intent, "分享到:"))
end

Sharing(文件路径)


-- 发送短信
import "android.net.Uri"
import "android.content.Intent"
uri = Uri.parse("smsto:10010")
intent = Intent(Intent.ACTION_SENDTO, uri)
intent.putExtra("sms_body","cxll") 
intent.setAction("android.intent.action.VIEW")
activity.startActivity(intent)



-- 发送彩信
import "android.net.Uri"
import "android.content.Intent"
uri=Uri.parse("file:///sdcard/a.png") --图片路径
intent= Intent();
intent.setAction(Intent.ACTION_SEND);
intent.putExtra("address",mobile) --邮件地址
intent.putExtra("sms_body",content) --邮件内容
intent.putExtra(Intent.EXTRA_STREAM,uri)
intent.setType("image/png") --设置类型
this.startActivity(intent)

-- 拨打电话
import "android.net.Uri"
import "android.content.Intent"
uri = Uri.parse("tel:110")
intent = Intent(Intent.ACTION_CALL, uri)
intent.setAction("android.intent.action.VIEW")
activity.startActivity(intent)



--Source：氚-Tritium 2957148920
--OpenSource：Apache License Version

-- 免费帖求个一键三连不过分吧₍˄·͈༝·͈˄*₎◞ ̑̑

-- 里面包含了最基础的工具
-- 例如说toast/snack提示，获取状态栏高度，从文件转换成drawable等
-- 根据方法名见名之意，
-- 由于太古老了，你们可以自行查看

local _M={
  mContext = activity
}

function _M.setContext(context)
  _M.mContext = context or activity
end

function _M.getStatusBarHeight()
  local resources = activity.getResources()
  local height = resources.getDimensionPixelSize(resources.getIdentifier("status_bar_height","dimen","android"))
  return height or 0
end

function _M.dp2px(dpValue)
  local scale = activity.getResources().getDisplayMetrics().scaledDensity
  return dpValue * scale + 0.5
end

function _M.maxLength(id, length)
  local length = tointeger(length)
  if(type(length)=="number")then
    id.setFilters({luajava.bindClass "android.text.InputFilter$LengthFilter"(math.abs(length))})
   else
    _M.toast("length is not a number")
  end
end

--获取文件Drawable方法
function _M.getFileDrawable(filePath)
  local FileInputStream = luajava.bindClass "java.io.FileInputStream"
  local BitmapFactory = luajava.bindClass "android.graphics.BitmapFactory"
  local BitmapDrawable = luajava.bindClass "android.graphics.drawable.BitmapDrawable"
  local fis = FileInputStream(activity.getLuaDir(filePath))
  local bitmap = BitmapFactory.decodeStream(fis)
  fis.close()
  return BitmapDrawable(activity.getResources(), bitmap)
end

function _M.getIconDrawable(filePath, viewSize)
  local PorterDuffColorFilter = luajava.bindClass "android.graphics.PorterDuffColorFilter"
  local PorterDuff = luajava.bindClass "android.graphics.PorterDuff"
  local BitmapDrawable = luajava.bindClass "android.graphics.drawable.BitmapDrawable"
  local Bitmap = luajava.bindClass "android.graphics.Bitmap"
  local Matrix = luajava.bindClass "android.graphics.Matrix"
  local TypedValue = luajava.bindClass "android.util.TypedValue"
  local bitmap = LuaBitmap.getLocalBitmap(filePath)
  local scale = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, viewSize or 24, activity.getResources().getDisplayMetrics()) / bitmap.getWidth()
  local matrix = Matrix() matrix.postScale(scale, scale)
  local drawable = BitmapDrawable(activity.Resources, Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getWidth(), matrix, true))
  return drawable
end

--文件分享函数
function _M.shareFile(filePath)
  --导入所需类
  local File = luajava.bindClass "java.io.File"
  local Uri = luajava.bindClass "android.net.Uri"
  local Intent = luajava.bindClass "android.content.Intent"
  local MimeTypeMap = luajava.bindClass "android.webkit.MimeTypeMap"
  local FileProvider = luajava.bindClass "androidx.core.content.FileProvider"
  --获取文件名
  local fileName = File(filePath).getName()
  --获取mime类型
  local mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileName:match("%.(.+)"))
  --构建文件uri
  local authorities = activity.getPackageName()
  local fileUri = FileProvider.getUriForFile(activity, authorities, File(filePath))
  --构建intent
  local shareIntent = Intent(Intent.ACTION_VIEW)
  shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
  --判断一下 mimeType 是否有获取到
  if mimeType then
    --设置intent数据
    shareIntent.setDataAndType(fileUri, mimeType)
   else
    _M.Toast("无法找到打开此文件类型的程序")
    --结束函数进程
    return
  end
  --激活 intent 开始分享
  activity.startActivity(shareIntent)
end

function _M.clearTable(tab)
  for k, _ in pairs(tab) do
    tab[k] = nil
  end
end

function _M.getGradientDrawable(color,topLeft,topRight,bottomLeft,bottomRight)
  return luajava.bindClass "android.graphics.drawable.GradientDrawable"()
  .setShape(0)
  .setColor(color)
  .setCornerRadii({
    _M.dp2px(topLeft),_M.dp2px(topLeft),
    _M.dp2px(topRight),_M.dp2px(topRight),
    _M.dp2px(bottomRight),_M.dp2px(bottomRight),
    _M.dp2px(bottomLeft),_M.dp2px(bottomLeft)
  })
end

function _M.modifyItemOffsets(outRect, view, parent, state, spanCount)
  -- 获取RecyclerView的LayoutManager，用于判断当前布局类型
  local layoutManager = parent.getLayoutManager()

  -- 获取当前item的位置
  local position = parent.getChildAdapterPosition(view)
  -- 获取当前item的布局参数
  local layoutParams = view.getLayoutParams()
  -- 获取当前item在瀑布流布局中的跨度索引
  local spanIndex = layoutParams.getSpanIndex()

  local function dp2px(dpValue)
    local scale = activity.getResources().getDisplayMetrics().scaledDensity
    return dpValue * scale + 0.5
  end

  -- 设置每个方向的间距
  local spacing = dp2px(5) -- 定义总间距
  local halfSpacing = spacing / 2 -- 计算间距的一半，用于更灵活的间距设置
  -- 根据列的索引设置左右间距
  if spanIndex == 0 then
    -- 如果视图在第一列
    outRect.left = 0 -- 左边有完整的间距
    outRect.right = halfSpacing -- 右边有一半的间距
   elseif spanIndex == (spanCount -1) then
    -- 如果视图在最后一列
    outRect.left = halfSpacing -- 左边有一半的间距
    outRect.right = 0 -- 右边有完整的间距
   else
    -- 如果视图在中间列
    outRect.left = halfSpacing -- 左边有一半的间距
    outRect.right = halfSpacing -- 右边有一半的间距
  end

  -- 设置item的上下间距
  outRect.top = halfSpacing
  outRect.bottom = spacing
end

function _M.getApplicationName()
   return activity.getPackageManager().getApplicationLabel(activity.getPackageManager().getApplicationInfo(activity.getPackageName(), 0)) 
end

--弹出Toast消息提示
function _M.Toast(...)
  --判断数据类型
  if type(...) == "table" then
    --dump解析一下
    toastText = dump(...)
   else
    --初始化表
    local contentTable = table.pack(...)
    --获取表长度
    local max = table.maxn(contentTable)
    --初始化第一项内容
    toastText = tostring(contentTable[1])
    --循环遍历表数据
    for i = 2, max do
      --读取数据表内容
      local temp = contentTable[i]
      --判断该项数据类型
      if type(temp) == "table" then
        --dump 解析一下
        local temp = dump(temp)
        --拼接到原有字符串后部
        toastText = toastText.."\n"..temp
       else
        --拼接到原有字符串后部
        toastText = toastText.."\n".. tostring(temp)
      end
    end
  end
  --输出内容
  local Toast = luajava.bindClass "android.widget.Toast"
  Toast.makeText(activity,tostring(toastText),Toast.LENGTH_SHORT).show()
end

--弹出Snackbar消息提示
function _M.Snack(content,condition)
  local Snackbar = luajava.bindClass "com.google.android.material.snackbar.Snackbar"
  local anchor=activity.findViewById(android.R.id.content)
  if condition ==true then
    Snackbar.make(anchor, tostring(content), Snackbar.LENGTH_LONG).show()
   else
    Snackbar.make(anchor, tostring(content), Snackbar.LENGTH_SHORT).show()
  end
end

--弹出Toast消息提示
function _M.toast(...)
  --判断数据类型
  if type(...) == "table" then
    --dump解析一下
    toastText = dump(...)
   else
    --初始化表
    local contentTable = table.pack(...)
    --获取表长度
    local max = table.maxn(contentTable)
    --初始化第一项内容
    toastText = tostring(contentTable[1])
    --循环遍历表数据
    for i = 2, max do
      --读取数据表内容
      local temp = contentTable[i]
      --判断该项数据类型
      if type(temp) == "table" then
        --dump 解析一下
        local temp = dump(temp)
        --拼接到原有字符串后部
        toastText = toastText.."\n"..temp
       else
        --拼接到原有字符串后部
        toastText = toastText.."\n".. tostring(temp)
      end
    end
  end
  --输出内容
  local Toast = luajava.bindClass "android.widget.Toast"
  Toast.makeText(activity,tostring(toastText),Toast.LENGTH_SHORT).show()
end

--弹出Snackbar消息提示
function _M.snack(content,condition)
  local Snackbar = luajava.bindClass "com.google.android.material.snackbar.Snackbar"
  local anchor = activity.findViewById(android.R.id.content)
  if condition ==true then
    Snackbar.make(anchor, tostring(content), Snackbar.LENGTH_LONG).show()
   else
    Snackbar.make(anchor, tostring(content), Snackbar.LENGTH_SHORT).show()
  end
end

return _M



--Source：Aqora
--Modify：氚-Tritium 2957148920
--OpenSource：Apache License Version

-- 免费帖求个一键三连不过分吧₍˄·͈༝·͈˄*₎◞ ̑̑

local _M = { }
local Menu = luajava.bindClass "android.view.Menu"
--local MenuItem = luajava.bindClass "android.view.MenuItem"
local NONE = Menu.NONE
local SHOW_AS_ACTION_FLAGS = {
  never = 0, -- MenuItem.SHOW_AS_ACTION_NEVER
  ifRoom = 1, -- MenuItem.SHOW_AS_ACTION_IF_ROOM
  always = 2, -- MenuItem.SHOW_AS_ACTION_ALWAYS
  withText = 4, -- MenuItem.SHOW_AS_ACTION_WITH_TEXT
  collapseActionView = 8, -- MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW
}

-- @param menu Menu 或 SubMenu
-- @param itemDef 包含菜单项定义的表。
local processMenuItem = function(menu, itemDef)
  -- 判断这是否是一个子菜单
  local isSubMenu = type(itemDef [ 1 ]) == "table"
  local method = isSubMenu and "addSubMenu" or "add"

  -- 调用 menu 对象的 add 或 addSubMenu 方法创建菜单
  local item = menu[method](itemDef.groupId or NONE, itemDef.itemId or NONE, itemDef.order or NONE, itemDef.title)

  -- 处理 showAsAction 属性
  local showAsAction = itemDef.showAsAction
  if showAsAction and not isSubMenu then
    local toConstant = function(flagsStr, flagsTbl)
      if type(flagsStr) == "number" then
        return flagsStr
      end

      local ret = 0
      for flag in  utf8.gmatch(flagsStr, "[^|]+") do
        local value = flagsTbl[flag]
        if not value then
          error(string.format("Unknown showAsAction flag: %s", flag))
        end
        ret = ret | value
      end
      return ret
    end

    item.setShowAsActionFlags(toConstant(showAsAction, SHOW_AS_ACTION_FLAGS))
  end

  itemDef.groupId, itemDef.itemId, itemDef.order, itemDef.title, itemDef.showAsAction = nil, nil, nil, nil, nil

  for k, v in pairs(itemDef) do
    if type(k) ~= "number" then
      item[k] = v
    end
  end

  if isSubMenu then
    _M.inflate(item, itemDef [ 1 ])
  end
end

-- @param menu  Menu 对象
-- @param items 描述菜单结构的定义表
_M.inflate = function(menu, items)
  assert(type(items) == "table", "The second parameter must be a table.")
  for _, itemDef in ipairs(items) do
    -- 如果数组中的元素不是 table，那么直接跳过就
    if type(itemDef) ~= "table" then
      continue
    end
    -- 处理每个菜单项
    processMenuItem(menu, itemDef)
  end
end

-- return _M

-- 演示demo，和popMenu一样
local MenuInflater = _M
local Toast = luajava.bindClass "android.widget.Toast"

function onCreateOptionsMenu(menu)
  MenuInflater.inflate(menu, {
    {
      title = "Menu",
      showAsAction = "always|withText",
    },
    {
      title = "Edit",
      showAsAction = "always|withText",
      icon = activity.getResources().getDrawable(luajava.bindClass("com.google.android.material.R").drawable.material_ic_edit_black_24dp),
      onMenuItemClick = function(item)
        Toast.makeText(activity, item.getTitle().." Clicked!", Toast.LENGTH_SHORT).show()
      end

    },
    {
      title = "OverflowSubMenu",
      {
        {
          title = "MenuItem1",
          enabled = false,
          onMenuItemClick = function(item)
            Toast.makeText(activity, item.getTitle().." Clicked!", Toast.LENGTH_SHORT).show()
          end
        },
        {
          title = "MenuItem2",
          onMenuItemClick = function(item)
            Toast.makeText(activity, item.getTitle().." Clicked!", Toast.LENGTH_SHORT).show()
          end
        },
        {
          title = "MenuItem3",
          onMenuItemClick = function(item)
            Toast.makeText(activity, item.getTitle().." Clicked!", Toast.LENGTH_SHORT).show()
          end
        },
      },
    },
    {
      title = "OverflowMenu1",
      checked = true,
      checkable = true,
      onMenuItemClick = function(item)
        item.setChecked(not item.checked)
        Toast.makeText(activity, item.getTitle().." Checked "..tostring(item.checked), Toast.LENGTH_SHORT).show()
      end
    },
    {
      title = "OverflowMenu2",
      onMenuItemClick = function(item)
        Toast.makeText(activity, item.getTitle().." Clicked!", Toast.LENGTH_SHORT).show()
      end
    }
  })
end




import "com.google.android.material.card.MaterialCardView"
layout={
  LinearLayout,
  orientation='vertical',
  layout_width='fill',
  layout_height='fill',
  background='#F5F5F5',
  padding='16dp',
  {
    MaterialCardView,
    id="startCard",
    layout_marginTop='10%h',--顶距
    layout_width="match_parent",
    layout_height="wrap",
    Radius="12dp",
    cardElevation="8dp",
    strokeWidth="1dp",
    strokeColor="#E0E0E0",
    {
      Button,
      id="startBtn",
      layout_width="match_parent",
      layout_height="56dp",
      text="开始测试",
      textSize="20sp",
      background="#4CAF50",
      textColor="#FFFFFF",
      letterSpacing="0.1",
      padding="16dp"
    }
  },
  {
    TextView,
    id="gameStatusText",
    layout_width="match_parent",
    layout_height="wrap",
    text="准备开始游戏",
    textSize="18sp",
    gravity="center",
    padding="16dp",
    textColor="#757575"
  },
  -- 1秒内未点击的进度条（红色）
  {
    ProgressBar,
    id="timeoutBar",
    style="?android:attr/progressBarStyleHorizontal",
    layout_width="match_parent",
    layout_height="8dp",
    max="100",
    progress="100",
    visibility="gone",
    --progressDrawable=activity.Resources.getDrawable(android.R.drawable.progress_horizontal).setColorFilter(PorterDuffColorFilter(0xFFF44336,PorterDuff.Mode.SRC_IN))
  },
  {
    MaterialCardView,
    id="clickCard",
    layout_width="match_parent",
    layout_height="180dp",
    Radius="12dp",
    cardElevation="8dp",
    strokeWidth="1dp",
    strokeColor="#E0E0E0",
    visibility="gone",
    layout_marginTop="16dp",
    {
      FrameLayout,
      layout_width="match_parent",
      layout_height="match_parent",
      {
        MaterialCardView,
        id="clickArea",
        layout_width="120dp",
        layout_height="120dp",
        layout_gravity="center",
        Radius="60dp",
        cardElevation="4dp",
        strokeWidth="0dp",
        background="#2196F3",
        onClick="onClickArea",
        {
          TextView,
          id="clickText",
          layout_width="match_parent",
          layout_height="match_parent",
          text="点击!",
          textSize="24sp",
          gravity="center",
          textColor="#FFFFFF"
        }
      }
    }
  },
  -- 游戏总时间进度条（绿色）
  {
    LinearLayout,
    orientation='vertical',
    layout_width="match_parent",
    layout_height="wrap",
    layout_marginTop="16dp",
    {
      ProgressBar,
      id="gameTimeBar",
      style="?android:attr/progressBarStyleHorizontal",
      layout_width="match_parent",
      layout_height="8dp",
      max="100",
      progress="100",
      visibility="gone",
     --5 progressDrawable=activity.Resources.getDrawable(android.R.drawable.progress_horizontal).setColorFilter(PorterDuffColorFilter(0xFF4CAF50,PorterDuff.Mode.SRC_IN))
    },
    {
      TextView,
      id="timeLeftText",
      layout_width="match_parent",
      layout_height="wrap",
      text="剩余时间: 10.0秒",
      textSize="14sp",
      gravity="right",
      padding="4dp",
      textColor="#757575",
      visibility="gone"
    }
  },
  {
    LinearLayout,
    orientation='horizontal',
    layout_width="match_parent",
    layout_height="wrap",
    layout_marginTop="16dp",
    gravity="center",
    {
      TextView,
      id="clickCountText",
      layout_width="wrap_content",
      layout_height="wrap_content",
      text="点击次数: 0",
      textSize="18sp",
      padding="8dp",
      textColor="#212121"
    },
    {
      TextView,
      id="cpsText",
      layout_width="wrap_content",
      layout_height="wrap_content",
      text="速度: 0 CPS",
      textSize="18sp",
      padding="8dp",
      textColor="#212121"
    }
  },
  {
    LinearLayout,
    orientation='horizontal',
    layout_width="match_parent",
    layout_height="wrap",
    layout_marginTop="8dp",
    gravity="center",
    {
      TextView,
      id="fastestClickText",
      layout_width="wrap_content",
      layout_height="wrap_content",
      text="最快: 0ms",
      textSize="14sp",
      padding="8dp",
      textColor="#757575"
    },
    {
      TextView,
      id="highestCPSText",
      layout_width="wrap_content",
      layout_height="wrap_content",
      text="最高: 0 CPS",
      textSize="14sp",
      padding="8dp",
      textColor="#757575"
    }
  },
  {
    TextView,
    id="resultText",
    layout_width="match_parent",
    layout_height="wrap",
    text="",
    textSize="20sp",
    gravity="center",
    padding="24dp",
    textColor="#4CAF50",
    visibility="gone"
  }
};

activity.setContentView(loadlayout(layout))

-- 游戏状态变量
local isGameRunning = false
local isClickPhase = false
local clickCount = 0
local gameStartTime = 0
local timer
local timeoutTimer  -- 1秒超时计时器
local gameTimer     -- 游戏总时间计时器
local timeoutProgress = 100  -- 1秒超时进度
local gameTimeProgress = 100 -- 游戏总时间进度
local totalGameTime = 10     -- 游戏总时间(秒)
local fastestClick = 0       -- 最快点击间隔(秒)
local highestCPS = 0         -- 最高点击速度
local lastClickTime = 0      -- 上次点击时间

-- 点击区域事件
function onClickArea(v)
  if not isClickPhase then return end
  
  local currentTime = os.clock()
  
  -- 更新点击次数
  clickCount = clickCount + 1
  clickCountText.text = "点击次数: "..clickCount
  
  -- 计算当前CPS（添加保护措施）
  local elapsedTime = currentTime - gameStartTime
  local cps = 0
  if elapsedTime > 0 then
    cps = clickCount / elapsedTime
  end
  
  -- 格式化显示（确保数字有效）
  if cps ~= cps then  -- 检查NaN
    cps = 0
  end
  
  cpsText.text = string.format("速度: %.1f CPS", math.min(cps, 999))  -- 限制最大显示值
  
  -- 更新最快点击和最高CPS
  if clickCount > 1 then
    local clickInterval = currentTime - lastClickTime
    if fastestClick == 0 or clickInterval < fastestClick then
      fastestClick = clickInterval
      fastestClickText.text = string.format("最快: %dms", math.floor(fastestClick * 1000))
    end
    
    if cps > highestCPS then
      highestCPS = cps
      highestCPSText.text = string.format("最高: %.1f CPS", highestCPS)
    end
  end
  
  lastClickTime = currentTime
  
  -- 重置1秒超时进度条
  timeoutProgress = 100
  timeoutBar.progress = timeoutProgress
  
  -- 点击动画效果
  v.animate().scaleX(0.9).scaleY(0.9).setDuration(100).start()
  v.animate().scaleX(1).scaleY(1).setDuration(100).setStartDelay(100).start()
end

-- 开始游戏
function startGame()
  if isGameRunning then return end
  
  -- 重置游戏状态
  isGameRunning = true
  isClickPhase = false
  clickCount = 0
  timeoutProgress = 100
  gameTimeProgress = 100
  fastestClick = 0
  highestCPS = 0
  
  -- 更新UI
  startBtn.enabled = false
  startBtn.text = "准备中..."
  gameStatusText.text = "准备开始"
  gameStatusText.textColor = 0xFF757575
  clickCard.visibility = 0
  resultText.visibility = 8
  clickCountText.text = "点击次数: 0"
  cpsText.text = "速度: 0 CPS"
  fastestClickText.text = "最快: 0ms"
  highestCPSText.text = "最高: 0 CPS"
  
  -- 1秒倒计时
  local count = 1
  gameStatusText.text = "倒计时: "..count.." 秒"
  
  timer = Ticker()
  timer.Period = 1000
  timer.onTick = function()
    count = count - 1
    if count > 0 then
      gameStatusText.text = "倒计时: "..count.." 秒"
    else
      timer.stop()
      startClickPhase()
    end
  end
  timer.start()
end

-- 开始点击阶段
function startClickPhase()
  isClickPhase = true
  gameStartTime = os.clock()
  lastClickTime = gameStartTime
  
  -- 显示游戏元素
  clickCard.visibility = 0
  timeoutBar.visibility = 0
  gameTimeBar.visibility = 0
  timeLeftText.visibility = 0
  
  -- 重置进度条
  timeoutProgress = 100
  gameTimeProgress = 100
  timeoutBar.progress = timeoutProgress
  gameTimeBar.progress = gameTimeProgress
  
  -- 更新状态文本
  gameStatusText.text = "游戏进行中 - 快速点击!"
  gameStatusText.textColor = 0xFF2196F3
  
  -- 1秒超时计时器 (每0.01ms更新一次)
  timeoutTimer = Ticker()
  timeoutTimer.Period = 0.01
  timeoutTimer.onTick = function()
    timeoutProgress = timeoutProgress - 0.1
    timeoutBar.progress = timeoutProgress
    
    if timeoutProgress <= 0 then
      endGame("超时! 1秒内没有点击")
    end
  end
  timeoutTimer.start()
  
  -- 游戏总时间计时器 (每1ms更新一次)
  local remainingTime = totalGameTime/100
  timeLeftText.text = string.format("剩余时间: %.1f秒", remainingTime)
  
  gameTimer = Ticker()
  gameTimer.Period = 100
  gameTimer.onTick = function()
    -- 计算剩余时间百分比
    remainingTime = totalGameTime - (os.clock() - gameStartTime)
    gameTimeProgress = (remainingTime / totalGameTime) * 100
    gameTimeBar.progress = gameTimeProgress
    
    timeLeftText.text = string.format("剩余时间: %.1f秒", remainingTime > 0 and remainingTime or 0)
    
    if remainingTime <= 0 then
      endGame("时间到!")
    end
  end
  gameTimer.start()
end

-- 结束游戏
function endGame(reason)
  isGameRunning = false
  isClickPhase = false
  
  -- 停止所有计时器
  if timer then timer.stop() end
  if timeoutTimer then timeoutTimer.stop() end
  if gameTimer then gameTimer.stop() end
  
  -- 计算最终CPS
  local elapsedTime = os.clock() - gameStartTime
  local cps = 0
  if elapsedTime > 0 then
    cps = clickCount / elapsedTime
  end
  
  -- 格式化显示（确保数字有效）
  if cps ~= cps then  -- 检查NaN
    cps = 0
  end
  
  -- 更新UI
  clickCard.visibility = 8
  timeoutBar.visibility = 8
  gameTimeBar.visibility = 8
  timeLeftText.visibility = 8
  startBtn.enabled = true
  startBtn.text = "再来一次"
  
  gameStatusText.text = reason or "游戏结束"
  gameStatusText.textColor = 0xFFF44336
  
  resultText.text = string.format("最终成绩: %d 次\n平均速度: %.1f CPS", clickCount, cps)
  resultText.visibility = 0
  
  -- 添加动画效果
  clickArea.animate().scaleX(1.1).scaleY(1.1).setDuration(200).start()
  clickArea.animate().scaleX(1).scaleY(1).setDuration(200).setStartDelay(200).start()
end

-- 开始按钮点击事件
startBtn.onClick=function()
  startGame()
end

-- 初始化UI动画
clickArea.animate()
.scaleX(1.05).scaleY(1.05)
.setDuration(500)
.start()



--这不是最新版本

--请前往
--https://gitee.com/slimeabc/AndLua-Tetris


require "import"
import "android.app.*"
import "android.os.*"
import "android.widget.*"
import "android.view.*"
import "android.util.DisplayMetrics"
import "androidx.appcompat.widget.LinearLayoutCompat"
import "com.google.android.material.snackbar.Snackbar"
import "android.content.DialogInterface"
import "android.graphics.*"
import "android.graphics.drawable.GradientDrawable"
import "com.google.android.material.button.MaterialButton"
import "android.content.res.ColorStateList"

local metrics = DisplayMetrics()
activity.getWindowManager().getDefaultDisplay().getMetrics(metrics)
local screenW  = metrics.widthPixels
local screenH = metrics.heightPixels

local BOARD_W, BOARD_H = 10, 20
local BTN_AREA_H = 260
local CELL_W = math.floor(screenW / BOARD_W)
local CELL_H = math.floor((screenH * 0.8 - BTN_AREA_H) / BOARD_H)
local CELL   = math.min(CELL_W, CELL_H)

local SHAPES = {
  { {0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0}, {0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0} },
  { {0,1,0,0,0,1,0,0,0,1,1,0,0,0,0,0}, {0,0,0,0,1,1,1,0,1,0,0,0,0,0,0,0}, {1,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0}, {0,0,1,0,1,1,1,0,0,0,0,0,0,0,0,0} },
  { {0,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0}, {0,1,0,0,0,1,1,0,0,1,0,0,0,0,0,0}, {0,0,0,0,1,1,1,0,0,1,0,0,0,0,0,0}, {0,1,0,0,1,1,0,0,0,1,0,0,0,0,0,0} },
  { {1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0} },
  { {0,0,0,0,1,0,0,0,1,1,1,0,0,0,0,0}, {0,0,0,0,1,1,0,0,1,0,0,0,1,0,0,0}, {0,0,0,0,1,1,1,0,0,0,1,0,0,0,0,0}, {0,0,0,0,0,1,0,0,0,1,0,0,1,1,0,0} }
}
local COLORS = { 0xFFFF0000, 0xFF00FF00, 0xFF0000FF, 0xFFFFFF00, 0xFF00FFFF }

local board = {}
for y = 1, BOARD_H do
  board[y] = {}
  for x = 1, BOARD_W do board[y][x] = 0 end
end

local cur = { x=0, y=0, rot=1, id=1 }
local score = 0
local handler = Handler()
local runnable
local gameActive = true
local paused = false
local parentView = activity.findViewById(android.R.id.content)

local function getDelay() return tonumber(activity.getSharedData("fallDelay") or 1000) end

local function valid(x, y, rot, id)
  local shape = SHAPES[id][rot]
  for i = 1, 16 do
    if shape[i] == 1 then
      local px = x + (i-1)%4
      local py = y + math.floor((i-1)/4)
      if px < 1 or px > BOARD_W or py > BOARD_H or (py >= 1 and board[py][px] ~= 0) then
        return false
      end
    end
  end
  return true
end

local function place(x, y, rot, id, val)
  local shape = SHAPES[id][rot]
  for i = 1, 16 do
    if shape[i] == 1 then
      local px = x + (i-1)%4
      local py = y + math.floor((i-1)/4)
      if py >= 1 and py <= BOARD_H and px >= 1 and px <= BOARD_W then
        board[py][px] = val
      end
    end
  end
end

local function clearLines()
  local lines = 0
  local y = BOARD_H
  while y >= 1 do
    local full = true
    for x = 1, BOARD_W do
      if board[y][x] == 0 then
        full = false
        break
      end
    end
    if full then
      table.remove(board, y)
      table.insert(board, 1, {})
      for x = 1, BOARD_W do
        board[1][x] = 0
      end
      lines = lines + 1
    else
      y = y - 1
    end
  end

  score = score + lines * 100
  scoreLabel.setText("Score: " .. score)
end


local function newPiece()
  cur.id  = math.random(1, #SHAPES)
  cur.rot = 1
  cur.x   = math.floor(BOARD_W/2) - 1
  cur.y   = 0
  return valid(cur.x, cur.y, cur.rot, cur.id)
end

local function draw()
  for y = 1, BOARD_H do
    for x = 1, BOARD_W do
      cells[y][x].setBackgroundColor(0xFFEEEEEE)
    end
  end
  for y = 1, BOARD_H do
    for x = 1, BOARD_W do
      if board[y][x] ~= 0 then
        cells[y][x].setBackgroundColor(COLORS[board[y][x]])
      end
    end
  end
  local shape = SHAPES[cur.id][cur.rot]
  for i = 1, 16 do
    if shape[i] == 1 then
      local px = cur.x + (i-1)%4
      local py = cur.y + math.floor((i-1)/4)
      if py >= 1 and py <= BOARD_H and px >= 1 and px <= BOARD_W then
        cells[py][px].setBackgroundColor(COLORS[cur.id])
      end
    end
  end
end

local function resetGame()
  for y = 1, BOARD_H do
    board[y] = {}
    for x = 1, BOARD_W do board[y][x] = 0 end
  end
  score = 0
  scoreLabel.setText("Score: 0")
  gameActive = true
  paused = false
  pauseBtn.setText("| | 暂停")
  pauseOverlay.setAlpha(0)
  if newPiece() then
    draw()
    handler.postDelayed(runnable, getDelay())
  end
end

local function showRestartDialog()
  local wasPaused = paused
  paused = true
  pauseBtn.setText("▶ 继续")
  pauseOverlay.setAlpha(0.5)
  handler.removeCallbacks(runnable)
  AlertDialog.Builder(activity)
  .setTitle("确认重新开始")
  .setMessage("是否要重新开始游戏？")
  .setCancelable(false)
  .setPositiveButton("确定", DialogInterface.OnClickListener{
    onClick = function(dialog, which)
      resetGame()
    end
  })
  .setNegativeButton("取消", DialogInterface.OnClickListener{
    onClick = function(dialog, which)
      if not wasPaused then
        paused = false
        pauseBtn.setText("| | 暂停")
        pauseOverlay.setAlpha(0)
        handler.postDelayed(runnable, getDelay())
      end
      dialog.dismiss()
    end
  })
  .show()
end

runnable = Runnable{
  run = function()
    if paused then return end
    if valid(cur.x, cur.y+1, cur.rot, cur.id) then
      cur.y = cur.y + 1
    else
      place(cur.x, cur.y, cur.rot, cur.id, cur.id)
      clearLines()
      if not newPiece() then
        handler.removeCallbacks(runnable)
        gameActive = false
        Snackbar.make(parentView, "Game Over! Score: "..score, Snackbar.LENGTH_LONG)
        .setAction("重新开始", View.OnClickListener{ onClick = function() resetGame() end })
        .show()
        return
      end
    end
    draw()
    handler.postDelayed(runnable, getDelay())
  end
}

local function onKey(key)
  if not gameActive or paused then return end
  if key == "left" and valid(cur.x-1, cur.y, cur.rot, cur.id) then
    cur.x = cur.x - 1
  elseif key == "right" and valid(cur.x+1, cur.y, cur.rot, cur.id) then
    cur.x = cur.x + 1
  elseif key == "rotate" then
    local newRot = cur.rot % (#SHAPES[cur.id]) + 1
    if valid(cur.x, cur.y, newRot, cur.id) then cur.rot = newRot end
  elseif key == "down" then
    while valid(cur.x, cur.y+1, cur.rot, cur.id) do cur.y = cur.y + 1 end
    place(cur.x, cur.y, cur.rot, cur.id, cur.id)
    clearLines()
    if not newPiece() then return end
  end
  draw()
end

local function makeRoundRect(color, radius)
  local gd = GradientDrawable()
  gd.setColor(color)
  gd.setCornerRadius(radius)
  return gd
end

local scroll = ScrollView(activity)
local main = LinearLayout(activity)
main.setOrientation(LinearLayout.VERTICAL)
main.setGravity(Gravity.CENTER_HORIZONTAL)
main.setPadding(0, 0, 0, 20)

local topCard = LinearLayout(activity)
topCard.setOrientation(LinearLayout.HORIZONTAL)
topCard.setGravity(Gravity.CENTER_VERTICAL)
topCard.setPadding(30, 20, 30, 20)
--topCard.setBackground(makeRoundRect(0xFF424242, 18))
local topLp = LinearLayout.LayoutParams(-1, -2)
topLp.setMargins(30, 30, 30, 20)
topCard.setLayoutParams(topLp)

scoreLabel = TextView(activity)
scoreLabel.setText("Score: 0")
scoreLabel.setTextSize(22)
scoreLabel.setTextColor(0xFFFFFFFF)
topCard.addView(scoreLabel)

local space = Space(activity)
local spaceLp = LinearLayout.LayoutParams(0, 1, 1)
space.setLayoutParams(spaceLp)
topCard.addView(space)

pauseBtn = MaterialButton(activity)
pauseBtn.setText("| | 暂停")
--pauseBtn.setIconTint(ColorStateList.valueOf(0xFFFFFFFF))
--pauseBtn.setTextColor(0xFFFFFFFF)
--pauseBtn.setBackgroundTintList(ColorStateList.valueOf(0xFF3F51B5))
--pauseBtn.setCornerRadius(12)
pauseBtn.setOnClickListener(View.OnClickListener{
  onClick = function()
    paused = not paused
    pauseBtn.setText(paused and "▶ 继续" or "| | 暂停")
    if paused then
      pauseOverlay.setAlpha(0.5)
      handler.removeCallbacks(runnable)
    else
      pauseOverlay.setAlpha(0)
      handler.postDelayed(runnable, getDelay())
    end
  end
})
topCard.addView(pauseBtn)
main.addView(topCard)

local gridWrapper = FrameLayout(activity)
local gridLp = LinearLayout.LayoutParams(-2, -2)
gridLp.gravity = Gravity.CENTER
gridWrapper.setLayoutParams(gridLp)

local grid = LinearLayout(activity)
grid.setOrientation(LinearLayout.VERTICAL)
cells = {}
for y = 1, BOARD_H do
  local row = LinearLayout(activity)
  row.setOrientation(LinearLayout.HORIZONTAL)
  cells[y] = {}
  for x = 1, BOARD_W do
    local cell = TextView(activity)
    local p = LinearLayout.LayoutParams(CELL, CELL)
    p.setMargins(1, 1, 1, 1)
    cell.setLayoutParams(p)
    cell.setBackground(makeRoundRect(0xFFEEEEEE, 4))
    row.addView(cell)
    cells[y][x] = cell
  end
  grid.addView(row)
end
gridWrapper.addView(grid)

pauseOverlay = View(activity)
pauseOverlay.setBackgroundColor(0xAA000000)
pauseOverlay.setAlpha(0)
gridWrapper.addView(pauseOverlay)
main.addView(gridWrapper)

local btnRow = LinearLayout(activity)
btnRow.setOrientation(LinearLayout.HORIZONTAL)
btnRow.setPadding(30, 20, 30, 0)

local function makeBtn(text, key)
  local b = MaterialButton(activity)
  b.setText(text)
  --b.setTextColor(0xFFFFFFFF)
  --b.setBackgroundTintList(ColorStateList.valueOf(0xFF3F51B5))
  --b.setCornerRadius(12)
  local p = LinearLayout.LayoutParams(0, -2, 1)
  p.setMargins(6, 0, 6, 0)
  b.setLayoutParams(p)
  b.setOnClickListener{ onClick = function() onKey(key) end }
  btnRow.addView(b)
end

makeBtn("←", "left")
makeBtn("→", "right")
makeBtn("↓", "down")
makeBtn("旋转", "rotate")
main.addView(btnRow)

restartBtn = MaterialButton(activity)
restartBtn.setText("重新开始")
--restartBtn.setTextColor(0xFFFFFFFF)
--restartBtn.setBackgroundTintList(ColorStateList.valueOf(0xFFE91E63))
--restartBtn.setCornerRadius(12)
local restartLp = LinearLayout.LayoutParams(-1, -2)
restartLp.setMargins(30, 110, 30, 30)
restartBtn.setLayoutParams(restartLp)
restartBtn.setOnClickListener{ onClick = function() showRestartDialog() end }
main.addView(restartBtn)

settingsBtn = MaterialButton(activity)
settingsBtn.setText("设置")
--settingsBtn.setTextColor(0xFFFFFFFF)
--settingsBtn.setBackgroundTintList(ColorStateList.valueOf(0xFF607D8B))
--settingsBtn.setCornerRadius(12)
local settingsLp = LinearLayout.LayoutParams(-1, -2)
settingsLp.setMargins(30, 0, 30, 30)
settingsBtn.setLayoutParams(settingsLp)
settingsBtn.setOnClickListener(View.OnClickListener{
  onClick = function()
    paused = true
    pauseBtn.setText("▶ 继续")
    pauseOverlay.setAlpha(0.5)
    handler.removeCallbacks(runnable)
    activity.newActivity("settings.lua")
  end
})
main.addView(settingsBtn)

scroll.addView(main)
activity.setTitle("AndLua Tetris")
activity.setContentView(scroll)

local application = activity.getApplication()
application.registerActivityLifecycleCallbacks(luajava.createProxy(
  "android.app.Application$ActivityLifecycleCallbacks", {
    onActivityPaused = function(activity)
      if not paused then
        paused = true
        pauseBtn.setText("▶ 继续")
        pauseOverlay.setAlpha(0.5)
        handler.removeCallbacks(runnable)
      end
    end,
    onActivityResumed = function(activity) end,
    onActivityCreated = function() end,
    onActivityStarted = function() end,
    onActivityStopped = function() end,
    onActivitySaveInstanceState = function() end,
    onActivityDestroyed = function() end,
}))

math.randomseed(os.time())
if newPiece() then
  draw()
  handler.postDelayed(runnable, getDelay())
end



--不要发布无意义垃圾/病毒/不文明用语/违反当地法律法规内容
--搬运帖注明来源且不可设置为付费
--本帖子为AI指导

--以下为main.lua文件内容
require "import"
import "android.app.*"
import "android.os.*"
import "android.widget.*"
import "android.view.*"
import "androidx.appcompat.widget.LinearLayoutCompat"
import "com.google.android.material.slider.RangeSlider"
import "com.google.android.material.color.DynamicColors" -- 必须
-- 不用管14~21，这是加载布局用的
import "lemon.material.switches.MaterialSwitch"
import "lemon.material.textfield.MaterialTextField"
import "com.google.android.material.slider.RangeSlider"
import "lemon.material.floatingactionbutton.FloatingActionButton"
import "com.google.android.material.floatingactionbutton.FloatingActionButton"
import "androidx.appcompat.widget.LinearLayoutCompat"
import "com.google.android.material.button.MaterialButton"
import "com.google.android.material.materialswitch.MaterialSwitch"

-- ✅ 全局开启莫奈：必须放在setTheme之前，全局优先级最高
DynamicColors.applyToActivityIfAvailable(activity)

-- 基础MD3主题框架
activity.setTheme(R.style.Theme_Material3_Blue)
activity.setTitle("莫奈取色")
activity.setContentView(loadlayout("layout"))


--之后就可以在layout.aly写md3控件了，根据前面14~21如下(例子)
{
  LinearLayoutCompat;
  layout_height = "fill";
  gravity = "center";
  orientation = "vertical";
  layout_width = "fill";
  {
    RangeSlider;
    id = "slider";
  };
  {
    FloatingActionButton;
  };
  {
    MaterialButton;
  };
  {
    MaterialSwitch;
  };
  {
    MaterialTextField;
    layout_width = "300dp";
    hint = "请输入文本";
  };
};




--目前还有一些小问题,最主要的问题应该是
--activity.setTheme(R.style.Theme_Material3_Blue)
--主题这个东西-大家在使用的时候--自己去寻找哪些主题可以适配
--各种主题带来的效果都不一样
--希望能多发点评论，提提意见
--修改内容的地方在右上角，三个点

require "import"
import "android.app.*"
import "android.os.*"
import "android.widget.*"
import "android.view.*"
import "androidx.appcompat.widget.LinearLayoutCompat"

import "android.graphics.drawable.GradientDrawable"
import "com.flask.colorpicker.*"
import "com.flask.colorpicker.builder.*"
import "android.content.Context"
import "com.google.android.material.color.DynamicColors"
import "com.google.android.material.color.DynamicColorsOptions"
import "android.app.AlertDialog$Builder"
import "java.lang.System"
import "android.os.Build"

import "com.google.android.material.shape.ShapeAppearanceModel"
import "com.google.android.material.shape.RoundedCornerTreatment"
import "com.google.android.material.shape.RelativeCornerSize"

import "androidx.appcompat.widget.LinearLayoutCompat"
import "com.google.android.material.slider.Slider"
import "lemon.material.switches.MaterialSwitch"
import "com.google.android.material.materialswitch.MaterialSwitch"
import "lemon.material.switches.MaterialSwitchBar"
import "com.google.android.material.button.MaterialButton"
import "com.google.android.material.switchmaterial.SwitchMaterial"
import "lemon.material.button.MaterialHeroButton"

local COLOR_MAP = {
  {name="经典紫", color=0xFF9C27B0},
  {name="海洋蓝", color=0xFF2196F3},
  {name="自然绿", color=0xFF4CAF50},
  {name="优雅紫", color=0xFF673AB7},
  {name="活力橙", color=0xFFFF9800},
  {name="浪漫粉", color=0xFFE91E63},
  {name="清新青", color=0xFF00BCD4},
  {name="深邃靛", color=0xFF3F51B5}
}

local DEF_ALPHA = true
local DEF_LIGHT = true
local DEF_BORDER = true
local DEF_EDIT = true
local DEF_PREVIEW = true
local DEF_DENSITY = 10
local DEF_WHEEL = ColorPickerView.WHEEL_TYPE.FLOWER

local Context = luajava.bindClass "android.content.Context"
local DynamicColors = luajava.bindClass "com.google.android.material.color.DynamicColors"
local DynamicColorsOptions = luajava.bindClass "com.google.android.material.color.DynamicColorsOptions"
local AlertDialogBuilder = luajava.bindClass "android.app.AlertDialog$Builder"
local System = luajava.bindClass "java.lang.System"
local MaterialAlertDialogBuilder = luajava.bindClass "com.google.android.material.dialog.MaterialAlertDialogBuilder"

local sp = activity.getSharedPreferences("dynamic_color", Context.MODE_PRIVATE)
local editor = sp.edit()
local DEFAULT_COLOR = 0xFF3988FF
local seedColor = sp.getInt("seed_color", DEFAULT_COLOR)

local options = DynamicColorsOptions.Builder()
.setThemeOverlay(luajava.bindClass "com.google.android.material.R$style".Theme_Material3_DayNight_NoActionBar)
.setContentBasedSource(seedColor)
.build()
DynamicColors.applyToActivityIfAvailable(activity, options)

function changeColor()
  local checkedItem = 0
  for i, v in ipairs(COLOR_MAP) do
    if v.color == seedColor then
      checkedItem = i - 1
      break
    end
  end
  local items = {}
  for _, v in ipairs(COLOR_MAP) do
    table.insert(items, v.name)
  end
  local selected = checkedItem
  MaterialAlertDialogBuilder(activity)
  .setTitle("选择主题颜色")
  .setSingleChoiceItems(items, checkedItem, function(_, which)
    selected = which
  end)
  .setPositiveButton("确认", function()
    local newColor = COLOR_MAP[selected + 1].color
    editor.putInt("seed_color", newColor)
    editor.apply()
    MaterialAlertDialogBuilder(activity)
    .setTitle("主题已更换")
    .setMessage("需要重启 App 才能应用新主题，是否立即重启？")
    .setCancelable(false)
    .setPositiveButton("重启", function()
      if Build.VERSION.SDK_INT >= 31 then
        activity.recreate()
       else
        local i = activity.getIntent()
        activity.finish()
        activity.startActivity(i)
        activity.finishAffinity()
        System.exit(0)
      end
    end)
    .setNegativeButton("稍后", nil)
    .show()
  end)
  .setNegativeButton("取消", nil)
  .show()
end

function changeColorAdvanced()
  local currentSeed = sp.getInt("seed_color", DEFAULT_COLOR)
  local builder = ColorPickerDialogBuilder
  .with(activity)
  .setTitle("选择主题种子色")
  .initialColor(currentSeed)
  .wheelType(DEF_WHEEL)
  .density(DEF_DENSITY)
  .showBorder(DEF_BORDER)

  if DEF_ALPHA and DEF_LIGHT then
   elseif DEF_ALPHA then
    builder.alphaSliderOnly()
   elseif DEF_LIGHT then
    builder.lightnessSliderOnly()
   else
    builder.noSliders()
  end

  builder
  .showColorEdit(DEF_EDIT)
  .showColorPreview(DEF_PREVIEW)
  .setPositiveButton("确定", luajava.createProxy(
  "com.flask.colorpicker.builder.ColorPickerClickListener",
  { onClick = function(_, selectedColor, _)
      editor.putInt("seed_color", selectedColor)
      editor.apply()
      MaterialAlertDialogBuilder(activity)
      .setTitle("主题已更换")
      .setMessage("需要重启才能应用新主题，是否立即重启？")
      .setCancelable(false)
      .setPositiveButton("重启", function()
        if Build.VERSION.SDK_INT >= 31 then
          activity.recreate()
         else
          local i = activity.getIntent()
          activity.finish()
          activity.startActivity(i)
          activity.finishAffinity()
          System.exit(0)
        end
      end)
      .setNegativeButton("稍后", nil)
      .show()
    end }))
  .setNegativeButton("取消", nil)
  .build()
  .show()
end

import "com.google.android.material.color.MaterialColors"
local colorPrimary = MaterialColors.getColorOrNull(activity, androidx.attr.colorPrimary)
local colorSecondary = MaterialColors.getColorOrNull(activity, material.attr.colorSecondary)
local colorTertiary = MaterialColors.getColorOrNull(activity, material.attr.colorTertiary)

布局={
  LinearLayoutCompat;
  layout_width = "fill";
  orientation = "vertical";
  gravity = "center";
  layout_height = "fill";
  {
    MaterialButton;
  };
  {
    SwitchMaterial;
  };
  {
    MaterialSwitch;
  };
  {
    MaterialSwitchBar;
  };
  {
    Slider;
  };
  {
    MaterialHeroButton;
  };
};

activity
.setTheme(R.style.Theme_Material3_Blue)
.setTitle("MD3主题色切换")
.setContentView(loadlayout(布局))

function onCreateOptionsMenu(menu)
  menu.add("切换主题颜色")
  menu.add("色盘主题颜色")
  return true
end

function onOptionsItemSelected(item)
  local title = tostring(item.getTitle())
  if title == "导入脚本项目" then
    import "android.content.Intent"
    import "android.net.Uri"
    local intent = Intent(Intent.ACTION_GET_CONTENT)
    intent.setType("*/*")
    intent.addCategory(Intent.CATEGORY_OPENABLE)
    activity.startActivityForResult(Intent.createChooser(intent, "选择导入Lgg文件项目"), 1000)

  elseif title == "修改软件设置" then
    print("修改软件设置")
    elseif title == "切换主题颜色" then
    changeColor()
    elseif title == "色盘主题颜色" then
    changeColorAdvanced()
  elseif title == "加入官方群" then
    print("加入官方群")
  elseif item.getItemId() == android.R.id.home then
    if drawer_layout.isDrawerOpen(3) then
      drawer_layout.closeDrawer(3)
     else
      drawer_layout.openDrawer(3)
    end
  end
  return true
end




--[[
  教程：在 alua 中使用 Material Design 3 创建可折叠顶部应用栏

  本教程演示了如何创建一个常见且现代的 UI 模式：可折叠的顶部应用栏。
  当用户向上滚动内容时，顶部的这个大型应用栏会平滑地折叠成一个标准尺寸的工具栏。

  这个效果是通过 CoordinatorLayout（协调者布局）和 Google Material Design 库中的几个关键组件共同实现的。

  请通过实现“边到边（edge-to-edge）”显示来提供更具沉浸感的用户体验。
  而不是通过手动设置AppBarLayout的Padding，这是不必要的，且是会破坏显示效果的。

  请使用attr获取值来设置MaterialToolbar与CollapsingToolbarLayout的高度，而不是硬编码。

  以下代码参考至 Material Components for Android 官方的 TopAppBar Collapsing Medium 实践
  开源协议：https://github.com/material-components/material-components-android/blob/master/LICENSE
]]

-- 步骤 1: 导入所有必需的类
-- 我们首先导入构建布局和管理其行为所需的所有 Android 和 Material Design 组件。
import "androidx.activity.EdgeToEdge" -- 一个帮助我们轻松实现“边到边”显示的辅助类。
import "android.os.Build" -- 提供有关当前设备 Android 版本的信息，用于条件判断。
import "androidx.coordinatorlayout.widget.CoordinatorLayout" -- 一个功能强大的布局，用于协调其子视图之间的行为和动画。它是我们实现折叠效果的基础。
import "com.google.android.material.appbar.AppBarLayout" -- 一个专为工具栏设计的容器，可以响应滚动事件。
import "com.google.android.material.appbar.MaterialToolbar" -- 应用栏中实际的工具栏，用于显示标题和操作按钮。
import "com.google.android.material.appbar.CollapsingToolbarLayout" -- MaterialToolbar 的一个包装器，它实现了标题的缩放和工具栏的折叠动画效果。
import "com.google.android.material.textview.MaterialTextView" -- 用于显示文本的 UI 组件。
import "androidx.core.widget.NestedScrollView" -- 一个支持嵌套滚动的可滚动视图，能与 CoordinatorLayout 和 AppBarLayout 协同工作。
import "android.util.TypedValue" -- 一个工具类，用于从主题资源中检索复杂的数据，例如尺寸值。


-- 步骤 2: 设置 Activity 的主题和标题
this -- 在 alua 中，'this' 关键字通常指向当前的 Activity（活动）上下文。
.setTheme(material.style.Theme_Material3_DynamicColors_DayNight_NoActionBar) -- 我们设置一个现代的 Material 3 主题。让我们解析一下这个主题名称：
-- - Theme_Material3: 指定我们正在使用最新的 Material Design 3 设计规范。
-- - DynamicColors: 在 Android 12 及更高版本上，这允许应用的主题色根据用户的壁纸颜色动态变化，提供个性化体验。
-- - DayNight: 主题会根据系统的设置自动在日间（浅色）和夜间（深色）模式之间切换。
-- - NoActionBar: 我们告诉系统，这个 Activity 将由我们自己提供工具栏（在这里是 MaterialToolbar），所以系统不需要提供一个默认的 ActionBar。
.setTitle("TopAppBar Collapsing Medium") -- 这个标题会显示在系统的“最近应用”概览等界面中。


-- 步骤 3: 定义一个辅助函数，用于从主题中获取尺寸
-- 这个函数对于创建健壮且适应性强的 UI 至关重要。
-- 它的作用是直接从当前应用的主题属性中，获取一个标准尺寸值（例如工具栏的高度）。
function getAttrPixelSize(attr)
  -- 创建一个新的 TypedValue 对象。这个对象将作为一个容器，来存放我们从主题中获取到的原始属性值。
  local tv = TypedValue()
  -- 尝试解析属性。`this.getTheme().resolveAttribute()` 会在当前激活的主题中查找我们传入的属性（例如 androidx.attr.actionBarSize）。
  -- 如果找到了这个属性，它会把属性的值填充到 'tv' 对象中，并返回 true。
  if this.getTheme().resolveAttribute(attr, tv, true) then
    -- 如果属性被成功找到，我们就将它的原始数据转换为对应当前设备屏幕密度的精确像素尺寸。
    -- 这个方法会处理所有复杂的计算，包括屏幕密度（mdpi, xhdpi 等），确保 UI 元素在任何屏幕上都具有正确的物理尺寸。
    return TypedValue.complexToDimensionPixelSize(tv.data, this.getResources().getDisplayMetrics())
  end
  -- 如果由于某种原因在主题中找不到该属性，则返回 nil。
  return nil
end

--[[
  ## 教程核心：为什么不应该硬编码布局中的尺寸值（例如 height="144dp"）？ ##

  在 Android 开发中，使用像 `getAttrPixelSize` 这样的函数来动态获取尺寸，而不是直接写死一个具体的数值，是一项至关重要的最佳实践。主要原因有以下三点：

  1.  **保证设计规范的一致性**:
      Material Design 对其组件（如应用栏、按钮等）的尺寸有明确的规范，例如，顶部应用栏分为小、中、大三种标准高度。这些标准值已经被预先定义在 Material 主题的属性中（如 `actionBarSize`、`collapsingToolbarLayoutMediumSize`）。通过引用这些主题属性，你可以确保你的应用界面遵循了官方的设计规范，看起来专业且和其他遵循规范的应用体验一致。如果你硬编码一个值，比如 `56dp`，它可能在手机上看起来是对的，但在平板上或者在未来的设计规范更新后就可能变得不协调。

  2.  **极大地提高了可维护性和主题化能力**:
      想象一下，你的应用有 20 个页面，每个页面的工具栏高度都硬编码为 `56dp`。有一天，产品经理要求将所有工具栏的高度都改大一点。你就必须手动去修改这 20 个文件，非常繁琐且容易出错。
      相反，如果你所有页面的工具栏高度都引用了主题属性 `?attr/actionBarSize`，那么你只需要在中心的主题文件（styles.xml）中修改这一个属性的值，整个应用的所有工具栏高度就会自动更新。这使得修改和维护应用的整体风格变得异常简单高效。

  3.  **增强了对不同设备和配置的适配能力**:
      虽然 `dp`（密度无关像素）已经解决了不同屏幕密度的问题，但 Android 的设备形态远比这复杂。例如，平板电脑在横屏时，可能会期望一个比手机上更高的工具栏以获得更好的视觉平衡。通过主题和资源限定符，我们可以为平板（sw600dp）、横屏（land）等不同配置提供不同的主题属性值。当你的布局引用主题属性时，系统会自动根据当前设备的配置选择最合适的尺寸值。而硬编码的值是死板的，它无法响应这些变化，导致你的应用在不同设备上的显示效果可能不佳。

  **总结**: 引用主题属性是一种“面向未来”的开发方式，它让你的应用更灵活、更易于维护，并且能更好地适应多样化的 Android 生态。
]]


-- 步骤 4: 使用 Lua 表来声明式地定义布局层级结构
-- 这种方式使得布局的结构一目了然，非常易于阅读和理解。
local layout = {
  CoordinatorLayout; -- 根布局。它的核心作用是管理和协调其直接子视图之间的交互。
  layout_width = "fill"; -- 填充整个屏幕的宽度。
  layout_height = "fill"; -- 填充整个屏幕的高度。
  {
    AppBarLayout; -- 这个容器专门用来放置那些需要响应滚动事件的工具栏组件。
    layout_width = "match_parent"; -- 宽度匹配其父布局（即 CoordinatorLayout）。
    layout_height = "wrap_content"; -- 高度由其内部的子视图决定。
    fitsSystemWindows = "true"; -- 这个属性告诉布局要为系统窗口（如顶部的状态栏）留出内边距，从而防止我们的工具栏内容绘制到状态栏的下面。
    {
      CollapsingToolbarLayout;
      layout_width = "match_parent";
      -- 在这里，我们调用了之前定义的辅助函数，从 Material 3 主题中获取一个“中等大小”可折叠应用栏的标准高度。告别硬编码！
      layout_height = getAttrPixelSize(material.attr.collapsingToolbarLayoutMediumSize);
      -- `layout_scrollFlags` 定义了此视图的滚动行为，是实现折叠效果的关键：
      -- 'scroll': 表示这个视图会跟随内容的滚动而滚动。
      -- 'exitUntilCollapsed': 表示视图会向上滚动并离开屏幕，直到它完全折叠成最小高度为止，此时它会固定在顶部不再滚动。
      -- 'snap': 表示如果用户停止滚动时，视图正处于部分展开/折叠的中间状态，它会自动以动画的形式吸附到“完全展开”或“完全折叠”的状态，避免悬在尴尬的中间位置。
      layout_scrollFlags = "scroll|exitUntilCollapsed|snap";
      {
        MaterialToolbar;
        layout_width = "match_parent";
        -- 我们从主题中获取一个标准操作栏的高度。这将是我们的工具栏在完全折叠后的最终高度。
        layout_height = getAttrPixelSize(androidx.attr.actionBarSize);
        title = "Medium Title";
        -- `layout_collapseMode` 是另一个关键属性。它告诉 CollapsingToolbarLayout，当布局折叠时，这个视图（Toolbar）应该被“钉”在顶部保持可见。
        layout_collapseMode="pin";
      };
    };
  };
  {
    NestedScrollView; -- 这是我们主要的、可滚动的内容区域。
    layout_width = "match_parent";
    layout_height = "match_parent";
    -- 强制填满 NestedScrollView 的高度
    fillViewport = "true";
    -- `layout_behavior` 是连接 AppBarLayout 和 NestedScrollView 的桥梁。
    -- 这个特殊的字符串值告诉 CoordinatorLayout：“当这个 NestedScrollView 滚动时，请根据它的滚动来更新 AppBarLayout 的位置和状态。”
    -- 这正是折叠效果产生的直接原因。这个值是 Material 库预定义的一个行为（Behavior）。
    layout_behavior = "appbar_scrolling_view_behavior";
    {
      MaterialTextView;
      padding = "16dp";
      -- 我们在这里放入大量文本，以确保 NestedScrollView 能够滚动，这样我们才能观察到应用栏的折叠效果。
      text = [[		Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam in scelerisque sem. Mauris volutpat, dolor id interdum ullamcorper, risus dolor egestas lectus, sit amet mattis purus dui nec risus. Maecenas non sodales nisi, vel dictum dolor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Suspendisse blandit eleifend diam, vel rutrum tellus vulputate quis. Aliquam eget libero aliquet, imperdiet nisl a, ornare ex. Sed rhoncus est ut libero porta lobortis. Fusce in dictum tellus.

		Suspendisse interdum ornare ante. Aliquam nec cursus lorem. Morbi id magna felis. Vivamus egestas, est a condimentum egestas, turpis nisl iaculis ipsum, in dictum tellus dolor sed neque. Morbi tellus erat, dapibus ut sem a, iaculis tincidunt dui. Interdum et malesuada fames ac ante ipsum primis in faucibus. Curabitur et eros porttitor, ultricies urna vitae, molestie nibh. Phasellus at commodo eros, non aliquet metus. Sed maximus nisl nec dolor bibendum, vel congue leo egestas.

		Sed interdum tortor nibh, in sagittis risus mollis quis. Curabitur mi odio, condimentum sit amet auctor at, mollis non turpis. Nullam pretium libero vestibulum, finibus orci vel, molestie quam. Fusce blandit tincidunt nulla, quis sollicitudin libero facilisis et. Integer interdum nunc ligula, et fermentum metus hendrerit id. Vestibulum lectus felis, dictum at lacinia sit amet, tristique id quam. Cras eu consequat dui. Suspendisse sodales nunc ligula, in lobortis sem porta sed. Integer id ultrices magna, in luctus elit. Sed a pellentesque est.

		Aenean nunc velit, lacinia sed dolor sed, ultrices viverra nulla. Etiam a venenatis nibh. Morbi laoreet, tortor sed facilisis varius, nibh orci rhoncus nulla, id elementum leo dui non lorem. Nam mollis ipsum quis auctor varius. Quisque elementum eu libero sed commodo. In eros nisl, imperdiet vel imperdiet et, scelerisque a mauris. Pellentesque varius ex nunc, quis imperdiet eros placerat ac. Duis finibus orci et est auctor tincidunt. Sed non viverra ipsum. Nunc quis augue egestas, cursus lorem at, molestie sem. Morbi a consectetur ipsum, a placerat diam. Etiam vulputate dignissim convallis. Integer faucibus mauris sit amet finibus convallis.

		Phasellus in aliquet mi. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In volutpat arcu ut felis sagittis, in finibus massa gravida. Pellentesque id tellus orci. Integer dictum, lorem sed efficitur ullamcorper, libero justo consectetur ipsum, in mollis nisl ex sed nisl. Donec maximus ullamcorper sodales. Praesent bibendum rhoncus tellus nec feugiat. In a ornare nulla. Donec rhoncus libero vel nunc consequat, quis tincidunt nisl eleifend. Cras bibendum enim a justo luctus vestibulum. Fusce dictum libero quis erat maximus, vitae volutpat diam dignissim.]],
    }
  };
};

-- 步骤 5: 构建并应用布局
-- `loadlayout` 函数会将我们上面用 Lua 表定义的布局结构，“解析并实例化”成一个真实的 Android View（视图）层级。
-- 然后，我们把这个新创建的视图传递给 `setContentView` 方法，使其显示在屏幕上。
this.setContentView(loadlayout(layout))

-- 步骤 6: 启用“边到边”显示，以获得更现代化的外观
-- 这个功能允许我们的应用内容（特别是根布局 CoordinatorLayout）绘制到系统栏（顶部的状态栏和底部的导航栏）的后面，实现全屏沉浸式效果。
EdgeToEdge.enable(this)

-- 在 Android 11 (API 30) 及更高版本上，为了保证导航栏按钮的可见性，系统可能会在导航栏区域强制添加一个半透明的背景。
-- 在“边到边”的设计中，这个额外的背景通常是我们不想要的，因为它会和我们的应用内容产生视觉冲突。
if Build.VERSION.SDK_INT >= 30 then
  -- 通过将这个属性设置为 false，我们告诉系统不要强制添加这个对比度背景，从而让我们的应用内容可以清晰地显示在导航栏区域。
  this.window.setNavigationBarContrastEnforced(false)
end



--[[
  Copyright 2018 The Android Open Source Project

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      https://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
]]
--[[
部分代码来自 https://github.com/material-components/material-components-android，遵循 Apache License 2.0。
]]

-- ⚠️ 注意！！！！
-- 由于 LuaAppX Pro 的开发者写的💩山发力了
-- 所以你可能需要将 LuaAppX Pro 更新至 4.3.0 或更高版本才能运行！



-- 颜色工具模块，示例中用于设置状态栏/分隔线等颜色
local colors = require "Colors"

-- 访问系统版本号等常量（例如 Build.VERSION.SDK_INT）
import "android.os.Build"

-- 使用 AndroidX 的 GridLayout：
-- 选择 AndroidX 而不是系统原生 android.widget.GridLayout，
-- 因为 AndroidX 版本 API 更完整（如更好的权重/Spec 支持），
-- 并在 Material 组件项目中更常见。
import "androidx.gridlayout.widget.GridLayout"

-- 常用基础视图
import "android.widget.TextView"
import "android.widget.ImageView"
import "android.widget.LinearLayout"
import "android.widget.ScrollView"

-- 开启沉浸式边到边（EdgeToEdge），让内容延伸到系统栏区域
import "androidx.activity.EdgeToEdge"

-- Material 组件：新版 MaterialSwitch 与旧版 SwitchMaterial
import "com.google.android.material.materialswitch.MaterialSwitch"
import "com.google.android.material.switchmaterial.SwitchMaterial"
import "com.google.android.material.textview.MaterialTextView"
import "com.google.android.material.divider.MaterialDivider"

-- 设置主题为 Material3 动态配色日夜间主题，并设置标题
this
.setTheme(material.style.Theme_Material3_DynamicColors_DayNight)
.setTitle("Material Switch")

-- 根布局说明：
-- 1) 最外层使用 ScrollView：内容较多时可滚动显示，避免小屏幕溢出。
-- 2) ScrollView 内包裹一个垂直方向 LinearLayout，便于分区组织内容。
local layout = {
  -- 最外层：可滚动容器
  ScrollView,
  -- ScrollView 自身宽高占满屏幕
  layout_width = "fill",
  layout_height = "fill",
  -- 让内容考虑系统状态栏/导航栏的内边距（配合 EdgeToEdge 使用）
  fitsSystemWindows = "true",

  {
    -- 主内容容器：垂直线性布局，承载各个模块
    LinearLayout,
    id = "mainViewGroup",
    layout_width = "fill", -- 宽度填满父容器
    orientation = "vertical",
    padding = "8dp", -- 统一内边距，提升观感与可触达性

    ----------------------------------------------------------------
    -- 区块一：使用 GridLayout 展示两列的“标题 + 开关”行
    -- 说明：
    -- - 这里使用两列排列：左侧放说明文本，右侧放开关控件。
    -- - 部分标题（text1/2/3/4）需要横跨两列，使用 columnSpan=2 实现。
    ----------------------------------------------------------------
    {
      GridLayout,
      -- GridLayout 尺寸设为 wrap，可根据内容自然伸展；
      -- 放在 ScrollView 内，因此不必强求填满。
      layout_width = "wrap",
      layout_height = "wrap",
      id = "cridlay",
      columnCount = 2, -- 两列网格
      useDefaultMargins = true, -- 启用默认间距，使元素间距更均衡

      -- 说明性标题（后续通过 setColumnSpan 设为跨两列）
      {
        TextView,
        id = "text1",
        -- 文本建议使用 wrap，避免 MATCH_PARENT 与跨列时产生不期望的测量冲突
        layout_width = "wrap",
        layout_height = "wrap",
        text = "开关元素会响应 colorSecondary 的变化",
        textSize = "12sp",
      },

      -- 可用开关（选中）：与左侧说明文字同一行右侧位置
      {
        MaterialSwitch,
        -- 放在网格的“单元格”中，wrap 更利于网格自身控制尺寸
        layout_width = "wrap",
        layout_height = "wrap",
        checked = true,
        enabled = true,
        text = "启用",
        -- 为右侧开关留出少许尾部间距，避免贴边（仅示例效果）
        layout_marginRight = "14dp",
      },

      -- 可用开关（未选中）：位于下一行右侧；左侧将配对应说明或留空
      {
        MaterialSwitch,
        layout_width = "wrap",
        layout_height = "wrap",
        checked = false,
        enabled = true,
        text = "启用",
      },

      -- “禁用状态”小节标题，后续跨两列
      {
        TextView,
        id = "text2",
        layout_width = "wrap",
        layout_height = "wrap",
        text = "禁用的开关元素无法交互，并且会显示为淡化状态。",
        textSize = "12sp",
      },

      -- 不可用且选中
      {
        MaterialSwitch,
        layout_width = "wrap",
        layout_height = "wrap",
        checked = true,
        enabled = false,
        text = "禁用",
      },

      -- 不可用且未选中
      {
        MaterialSwitch,
        layout_width = "wrap",
        layout_height = "wrap",
        checked = false,
        enabled = false,
        text = "禁用",
      },

      -- “带图标的开关”小节标题，后续跨两列
      {
        TextView,
        id = "text3",
        layout_width = "wrap",
        layout_height = "wrap",
        text = "带图标的开关元素，默认大小为 16dp，也可以设置为 24dp",
        textSize = "12sp",
      },

      -- 带图标的开关（默认图标尺寸）
      {
        MaterialSwitch,
        layout_width = "wrap",
        layout_height = "wrap",
        checked = true,
        enabled = true,
        text = "启用",
        -- 使用示例图标资源（需 Material 依赖）
        thumbIconResource = material.drawable.ic_mtrl_chip_checked_black,
      },

      -- 带图标的开关（指定更大图标尺寸）
      {
        MaterialSwitch,
        layout_width = "wrap",
        layout_height = "wrap",
        checked = true,
        enabled = true,
        text = "启用",
        thumbIconResource = material.drawable.ic_mtrl_chip_checked_black,
        thumbIconSize = "24dp",
      },
    },

    ----------------------------------------------------------------
    -- 分隔线：将上方 GridLayout 与下方“可控开关区”分隔
    ----------------------------------------------------------------
    {
      MaterialDivider,
      layout_width = "fill",
      layout_height = "4dp",
      layout_marginTop = "8dp",
      layout_marginBottom = "8dp",
      -- 使用主题中的次要色，示例中取自 colors 模块
      dividerColor = colors.colorSecondary,
    },

    ----------------------------------------------------------------
    -- 控制开关：用于统一启用/禁用下方 toggled_views 区域内的多个开关
    -- 当此开关开启：下方所有 MaterialSwitch setEnabled(true)
    -- 当此开关关闭：下方所有 MaterialSwitch setEnabled(false)
    ----------------------------------------------------------------
    {
      MaterialSwitch,
      id = "switch_toggle",
      layout_width = "fill",
      -- 放置在独立一行，便于理解“这是总控制开关”
      checked = false,
      enabled = true,
      text = "打开此开关以控制下方开关的启用状态",
      textSize = "12sp",
    },

    ----------------------------------------------------------------
    -- 被“控制开关”所管理的子开关区域（toggled_views）
    -- 说明：
    -- - 这里使用垂直 LinearLayout 存放多个 MaterialSwitch。
    -- - 初始均为 enabled=false，由上方总开关统一控制。
    ----------------------------------------------------------------
    {
      LinearLayout,
      id = "toggled_views",
      layout_width = "fill",
      layout_height = "wrap",
      -- 侧向缩进，让层级关系更清晰
      layout_marginLeft = "8dp",
      orientation = "vertical",

      {
        MaterialSwitch,
        layout_width = "fill",
        layout_height = "wrap",
        checked = true,
        enabled = false,
        text = "由开关控制",
      },

      {
        MaterialSwitch,
        layout_width = "fill",
        layout_height = "wrap",
        checked = false,
        enabled = false,
        text = "由开关控制",
      },
    },

    ----------------------------------------------------------------
    -- 再次分隔线：分隔“可控开关区”和“已弃用的 SwitchMaterial 演示区”
    ----------------------------------------------------------------
    {
      MaterialDivider,
      layout_width = "fill",
      layout_height = "4dp",
      layout_marginTop = "8dp",
      layout_marginBottom= "8dp",
      dividerColor = colors.colorSecondary,
    },

    ----------------------------------------------------------------
    -- 区块二：已弃用的 SwitchMaterial 演示（不建议新项目使用）
    -- 说明：
    -- - 仅用于对比旧新组件差异。
    -- - 布局同样采用两列 GridLayout。
    ----------------------------------------------------------------
    {
      GridLayout,
      columnCount = 2,
      useDefaultMargins= true,

      {
        TextView,
        id = "text4",
        text = "已弃用的开关设计（SwitchMaterial）",
        textSize = "12sp",
        -- 该标题同样会在后续设置为跨两列
      },

      {
        SwitchMaterial,
        checked = true,
        enabled = true,
        text = "启用",
        layout_marginEnd = "14dp",
      },

      {
        SwitchMaterial,
        checked = false,
        enabled = true,
        text = "启用",
      },

      {
        SwitchMaterial,
        checked = true,
        enabled = false,
        text = "禁用",
      },

      {
        SwitchMaterial,
        checked = false,
        enabled = false,
        text = "禁用",
      },
    },
  },
}

-- 将上述布局表构建为实际视图并作为 Activity 内容视图
this.setContentView(loadlayout(layout))

-- 启用边到边显示；当系统版本支持时，关闭导航栏对比度强制，避免色块不协调
EdgeToEdge.enable(this)
if Build.VERSION.SDK_INT >= 30 then
  this.window.setNavigationBarContrastEnforced(false)
end
-- 设置状态栏颜色，示例中选用容器表面色
this.window.setStatusBarColor(colors.colorSurfaceContainer)
-- 取消顶栏阴影
this.supportActionBar.setElevation(0)

----------------------------------------------------------------
-- 工具函数：设置“某个子视图”在 GridLayout 中的跨列数（等价于 XML 的
-- android:layout_columnSpan="N"）
--
-- 设计要点：
-- 1) 使用 GridLayout.spec(GridLayout.UNDEFINED, span) 让 GridLayout
--    自行决定起始列，仅指定“跨多少列”，与 XML 行为保持一致。
-- 2) 若需要拉伸填满跨列区域的宽度，可将子视图的 layout_width 设为 "fill"
--    或在需要时配合权重（AndroidX 版本支持 float 权重的重载）。
----------------------------------------------------------------
function setColumnSpan(view, span)
  local params = view.getLayoutParams()
  -- 仅当该子视图确实处于 GridLayout 下时才设置（防御式编程）
  if params ~= nil and params.getClass().getName() == "androidx.gridlayout.widget.GridLayout$LayoutParams" then
    -- 保持行 spec 不变（若为空则设为默认），只修改列的跨列数
    if params.rowSpec == nil then
      params.rowSpec = GridLayout.spec(GridLayout.UNDEFINED, 1)
    end
    -- 指定“跨 span 列”，起始列不固定（与 XML 行为一致）
    params.columnSpec = GridLayout.spec(GridLayout.UNDEFINED, span, 1.0)
    view.setLayoutParams(params)
  end
end

-- 将四个小节标题设为跨两列，使其占据一整行宽度，更符合分组语义
setColumnSpan(text1, 2)
setColumnSpan(text2, 2)
setColumnSpan(text3, 2)
setColumnSpan(text4, 2)

----------------------------------------------------------------
-- 逻辑：收集 toggled_views 容器中的所有 MaterialSwitch，
-- 由上方的“总控制开关 switch_toggle”统一启用/禁用它们。
----------------------------------------------------------------
local toggledSwitches = {}
do
local count = toggled_views.getChildCount()
for i = 0, count - 1 do
  local v = toggled_views.getChildAt(i)
  -- 通过类名判断是否为 MaterialSwitch（也可用 instanceof 判断）
  if v.getClass().getName() == "com.google.android.material.materialswitch.MaterialSwitch" then
    table.insert(toggledSwitches, v)
  end
end
end

-- 给“总控制开关”注册监听器：
-- 当其勾选状态变化时，遍历子开关并统一设置 enabled = isChecked
switch_toggle.setOnCheckedChangeListener{
  onCheckedChanged = function(buttonView, isChecked)
    for _, ms in ipairs(toggledSwitches) do
      ms.setEnabled(isChecked)
    end
  end
}



-- 引入控件
import "com.google.android.material.textfield.TextInputLayout"
import "com.google.android.material.textfield.TextInputEditText"
import "android.widget.Toast"
import "android.widget.Button"
import "android.graphics.Color"
import "android.graphics.drawable.ColorDrawable"
import "android.text.TextWatcher"
import "android.text.Editable"
import "androidx.appcompat.widget.LinearLayoutCompat"
import "android.content.res.ColorStateList"

-- 根布局
activity.setContentView(loadlayout({
  LinearLayoutCompat,
  orientation = "vertical",
  layout_width = "fill",
  layout_height = "fill",
  padding = "24dp",

  -- TextInputLayout + TextInputEditText 组合
  {
    TextInputLayout,
    id = "textInputLayout",
    layout_width = "fill",
    layout_marginBottom = "16dp",
    hint = "请输入内容", -- 提示文字
    endIconMode = 2, -- 右侧清除图标
    startIconDrawable = R.drawable.ic_android,
    startIconTintList = ColorStateList.valueOf(Color.BLUE),
    boxBackgroundMode = TextInputLayout.BOX_BACKGROUND_OUTLINE,
    {
      TextInputEditText,
      id = "editText",
      layout_width = "fill",
      layout_height = "wrap",
      style = material.style.Widget_Material3_TextInputEditText_OutlinedBox,
      theme = material.style.Widget_Material3_TextInputEditText_OutlinedBox,
    }
  },

  -- 控制区（下面所有按钮保持不变，只是引用 id 改成 editText / textInputLayout）
  {
    LinearLayoutCompat,
    orientation = "vertical",
    layout_width = "fill",
    layout_height = "wrap",
    gravity = "center",

    -- 第一行按钮
    {
      LinearLayoutCompat,
      orientation = "horizontal",
      layout_width = "fill",
      layout_height = "wrap",
      gravity = "center",
      {
        Button,
        text = "设置错误",
        onClick = function()
          -- TextInputLayout 负责显示错误
          textInputLayout.setError("这是一条错误提示")
          textInputLayout.setErrorEnabled(true)
        end
      },
      {
        Button,
        text = "清除错误",
        onClick = function()
          textInputLayout.setError(nil)
          textInputLayout.setErrorEnabled(false)
        end
      }
    },

    -- 第二行按钮
    {
      LinearLayoutCompat,
      orientation = "horizontal",
      layout_width = "fill",
      layout_height = "wrap",
      gravity = "center",
      {
        Button,
        text = "设置辅助文字",
        onClick = function()
          textInputLayout.setHelperText("这是辅助/提示文字")
        end
      },
      {
        Button,
        text = "清除辅助文字",
        onClick = function()
          textInputLayout.setHelperText(nil)
        end
      }
    },

    -- 第三行按钮
    {
      LinearLayoutCompat,
      orientation = "horizontal",
      layout_width = "fill",
      layout_height = "wrap",
      gravity = "center",
      {
        Button,
        text = "设置文字",
        onClick = function()
          editText.setText("Hello TextInputEditText!")
        end
      },
      {
        Button,
        text = "获取文字",
        onClick = function()
          local txt = editText.getText()
          Toast.makeText(activity, "当前文字：" .. tostring(txt), 0).show()
        end
      }
    },

    -- 第四行按钮
    {
      LinearLayoutCompat,
      orientation = "horizontal",
      layout_width = "fill",
      layout_height = "wrap",
      gravity = "center",
      {
        Button,
        text = "设置单行",
        onClick = function()
          editText.setSingleLine(true)
        end
      },
      {
        Button,
        text = "设置多行",
        onClick = function()
          editText.setSingleLine(false)
        end
      }
    },

    -- 第五行按钮
    {
      LinearLayoutCompat,
      orientation = "horizontal",
      layout_width = "fill",
      layout_height = "wrap",
      gravity = "center",
      {
        Button,
        text = "切换图标",
        onClick = function()
          local mode = (textInputLayout.getEndIconMode() + 1) % 3
          textInputLayout.setEndIconMode(mode)
        end
      },
      {
        Button,
        text = "切换图标颜色",
        onClick = function()
          local colors = {Color.RED, Color.GREEN, Color.BLUE}
          local idx = (math.random(1, 3))
          textInputLayout.setStartIconTintList(ColorStateList.valueOf(colors[idx]))
        end
      }
    },

    -- 第六行按钮
    {
      LinearLayoutCompat,
      orientation = "horizontal",
      layout_width = "fill",
      layout_height = "wrap",
      gravity = "center",
      {
        Button,
        text = "设置文字大小",
        onClick = function()
          editText.setTextSize(24) -- 24sp
        end
      },
      {
        Button,
        text = "设置圆角",
        onClick = function()
          textInputLayout.setBoxCornerRadii(360, 360, 360, 360)
        end
      }
    }
  }
}))

-- 文字监听器示例（监听 TextInputEditText）
editText.addTextChangedListener(luajava.createProxy("android.text.TextWatcher", {
  beforeTextChanged = function(s, start, count, after) end,
  onTextChanged = function(s, start, before, count) end,
  afterTextChanged = function(editable)
    if editable and editable.length() < 4 then
      textInputLayout.setError("至少输入 4 个字符")
     else
      textInputLayout.setErrorEnabled(false)
    end
  end
}))



----------------------------------------------------------------------
-- 1. 导入依赖的 Android 与 Material 组件
-- 引入这些类后，就能在布局和代码中使用它们。
----------------------------------------------------------------------

local colors = require "Colors"
import "android.view.MenuItem" -- 菜单项类，用于 BottomAppBar 的菜单
import "android.os.Build" -- 系统版本判断
import "android.widget.LinearLayout" -- 普通的线性布局
import "androidx.activity.EdgeToEdge" -- Edge-to-Edge 布局工具，让内容沉浸到状态栏和导航栏
import "androidx.core.widget.NestedScrollView" -- 支持嵌套滑动的滚动容器
import "androidx.coordinatorlayout.widget.CoordinatorLayout" -- 协调布局，支持 AppBar/FAB 协调滚动
import "com.google.android.material.appbar.AppBarLayout" -- 顶部 AppBar（虽然这里没用到，示例性引入）
import "com.google.android.material.floatingactionbutton.FloatingActionButton" -- 悬浮按钮 FAB
import "com.google.android.material.bottomappbar.BottomAppBar"-- 底部工具栏 BottomAppBar
import "com.google.android.material.materialswitch.MaterialSwitch" -- Material 风格开关
import "com.google.android.material.snackbar.Snackbar" -- 底部提示 Snackbar
import "com.google.android.material.textview.MaterialTextView" -- 材料风格的文字视图

----------------------------------------------------------------------
-- 2. 设置 Activity 样式与布局
-- 这里通过链式调用配置主题、标题，然后用一个 Lua table 定义整个页面布局。
-- 这个 table 会被 setContentView 转换成 Android View 树。
----------------------------------------------------------------------

activity
.setTheme(material.style.Theme_Material3_DynamicColors_DayNight)
-- ↑ 设置为 Material3 动态配色主题，支持深色模式/动态取色。
.setTitle("BottomAppBar")
-- ↑ 设置标题栏标题
.setContentView({

  --------------------------------------------------------------------
  -- 根布局：CoordinatorLayout
  -- CoordinatorLayout 是 Material Design 推荐的根容器，
  -- 能协调子控件的滚动行为（例如 FAB 随着 BottomAppBar 联动）。
  --------------------------------------------------------------------
  CoordinatorLayout,
  layout_width = "fill",
  layout_height = "fill",
  id = "coordinator", -- 这里的 id 很重要，Snackbar 需要依附它来显示
  fitsSystemWindows = "true", -- 保证内容不会被状态栏遮挡

  --------------------------------------------------------------------
  -- 可滚动区域：NestedScrollView
  -- 带有“appbar_scrolling_view_behavior”，可以与 BottomAppBar 协调滑动。
  --------------------------------------------------------------------
  {
    NestedScrollView,
    layout_behavior = "appbar_scrolling_view_behavior",
    layout_width = "fill",
    layout_height = "fill",

    ----------------------------------------------------------------
    -- 内部内容：一个 LinearLayout 垂直布局，里面放开关和文字。
    ----------------------------------------------------------------
    {
      LinearLayout,
      layout_width = "fill",
      layout_height = "wrap_content",
      orientation = "vertical",
      gravity = "center",
      padding = "8dp",

      --------------------------------------------------------------
      -- Switch 1：控制 FAB 显示/隐藏
      --------------------------------------------------------------
      {
        MaterialSwitch,
        id = "swtIsShowFab",
        text = "显示FAB",
        checked = true, -- 默认选中，表示 FAB 默认显示
      },

      --------------------------------------------------------------
      -- Switch 2：控制 BottomAppBar 是否在滚动时自动隐藏
      --------------------------------------------------------------
      {
        MaterialSwitch,
        id = "swtIsHideBottomBar",
        text = "滑动时隐藏BottomAppBar",
      },

      --------------------------------------------------------------
      -- 示例文本：这里放了一大段 Material Design 介绍文字。
      -- 用来填充内容，方便测试滚动效果。
      --------------------------------------------------------------
      {
        MaterialTextView,
        text = [[Material Design is a design system built and supported by Google designers and developers. Material.io includes in-depth UX guidance and UI component implementations for Android, Flutter, and the Web.

The latest version, Material 3, enables personal, adaptive, and expressive experiences – from dynamic color and enhanced accessibility, to foundations for large screen layouts and design tokens. M3 Expressive takes this a step further by adding more flexible components, vibrant styles, and fully integrated motion.

Material Design is a design system built and supported by Google designers and developers. Material.io includes in-depth UX guidance and UI component implementations for Android, Flutter, and the Web.

The latest version, Material 3, enables personal, adaptive, and expressive experiences – from dynamic color and enhanced accessibility, to foundations for large screen layouts and design tokens. M3 Expressive takes this a step further by adding more flexible components, vibrant styles, and fully integrated motion.

Material Design is a design system built and supported by Google designers and developers. Material.io includes in-depth UX guidance and UI component implementations for Android, Flutter, and the Web.

The latest version, Material 3, enables personal, adaptive, and expressive experiences – from dynamic color and enhanced accessibility, to foundations for large screen layouts and design tokens. M3 Expressive takes this a step further by adding more flexible components, vibrant styles, and fully integrated motion.

Material Design is a design system built and supported by Google designers and developers. Material.io includes in-depth UX guidance and UI component implementations for Android, Flutter, and the Web.

The latest version, Material 3, enables personal, adaptive, and expressive experiences – from dynamic color and enhanced accessibility, to foundations for large screen layouts and design tokens. M3 Expressive takes this a step further by adding more flexible components, vibrant styles, and fully integrated motion.

Material Design is a design system built and supported by Google designers and developers. Material.io includes in-depth UX guidance and UI component implementations for Android, Flutter, and the Web.

The latest version, Material 3, enables personal, adaptive, and expressive experiences – from dynamic color and enhanced accessibility, to foundations for large screen layouts and design tokens. M3 Expressive takes this a step further by adding more flexible components, vibrant styles, and fully integrated motion.]],
      },
    },
  },

  --------------------------------------------------------------------
  -- 底部工具栏：BottomAppBar
  -- Material Design 组件，用来放菜单和作为 FAB 的挂载点。
  --------------------------------------------------------------------
  {
    BottomAppBar,
    id = "bottomBar",
    layout_width = "fill",
    layout_gravity = "bottom|center", -- 吸附在底部
    fitsSystemWindows = "true",
  },

  --------------------------------------------------------------------
  -- 悬浮按钮：FloatingActionButton
  -- 通过 layout_anchor 属性指定挂在 BottomAppBar 上。
  --------------------------------------------------------------------
  {
    FloatingActionButton,
    id = "fab",
    layout_anchor = "bottomBar", -- 指定锚点
    imageResource = androidx.drawable.abc_ic_search_api_material, -- 设置图标
    --src = "images/ic_email_outline.png", -- src 也可以换成本地图片
  },
})

----------------------------------------------------------------------
-- 3. Edge-to-Edge 设置
-- EdgeToEdge.enable 会让内容沉浸到状态栏/导航栏区域。
-- Android 11+ (SDK 30) 时禁用导航栏的强制对比度，否则可能出现白边。
----------------------------------------------------------------------
EdgeToEdge.enable(this)
if Build.VERSION.SDK_INT >= 30 then
  this.window.setNavigationBarContrastEnforced(false)
end
-- 设置状态栏颜色，示例中选用容器表面色
this.window.setStatusBarColor(colors.colorSurfaceContainer)
-- 取消顶栏阴影
this.supportActionBar.setElevation(0)

----------------------------------------------------------------------
-- 4. 去掉 FAB 默认的阴影动画
-- Material FAB 默认有按压/悬停的阴影效果，
-- 这里手动清除，保持平面化风格。
----------------------------------------------------------------------
fab.setStateListAnimator(nil)
fab.setTranslationZ(0)
fab.setCompatPressedTranslationZ(0)
fab.setCompatHoveredFocusedTranslationZ(0)

----------------------------------------------------------------------
-- 5. 定义 BottomAppBar 的菜单
-- 这里使用 Lua 表来定义多个菜单项，
-- 然后再用循环把它们逐个添加到 BottomAppBar.menu 中。
----------------------------------------------------------------------
local menu = {
  {
    title = "搜索",
    icon = androidx.drawable.abc_ic_search_api_material,
    showAsAction = MenuItem.SHOW_AS_ACTION_NEVER, -- 永不放在工具栏上
    onMenuItemClick = function() snack("测试1") end
  },
  {
    title = "清空",
    icon = androidx.drawable.abc_ic_clear_material,
    showAsAction = MenuItem.SHOW_AS_ACTION_IF_ROOM, -- 有空间时显示
    onMenuItemClick = function() snack("测试2") end
  },
  {
    title = "语音",
    icon = androidx.drawable.abc_ic_voice_search_api_material,
    showAsAction = MenuItem.SHOW_AS_ACTION_ALWAYS, -- 总是显示
    onMenuItemClick = function() snack("测试3") end
  },
}

-- 把上面定义的菜单逐个加到 BottomAppBar
for k, v in pairs(menu) do
  bottomBar.menu
  .add(0, k - 1, k - 1, v.title) -- 添加菜单项
  .setIcon(v.icon) -- 设置图标
  .setShowAsAction(v.showAsAction) -- 设置显示策略
  .setOnMenuItemClickListener(v.onMenuItemClick) -- 点击事件
end

----------------------------------------------------------------------
-- 6. 监听 Switch 开关变化
----------------------------------------------------------------------

-- 控制 FAB 显示/隐藏
swtIsShowFab.setOnCheckedChangeListener{
  onCheckedChanged = function(buttonView, isChecked)
    if isChecked then
      fab.show()
     else
      fab.hide()
    end
  end
}

-- 控制 BottomAppBar 是否滚动隐藏
swtIsHideBottomBar.setOnCheckedChangeListener{
  onCheckedChanged = function(buttonView, isChecked)
    bottomBar.setHideOnScroll(isChecked)
  end
}

----------------------------------------------------------------------
-- 7. FAB 点击事件
-- 点击 FAB 时，显示一个 Snackbar 提示。
----------------------------------------------------------------------
function fab.onClick()
  snack("FAB点击")
end

----------------------------------------------------------------------
-- 8. SnackBar 辅助函数
-- Snackbar 是 Material 提示控件，会显示在底部。
-- 这里让它依附 BottomAppBar，避免被覆盖。
----------------------------------------------------------------------
function snack(text)
  Snackbar.make(coordinator, text, Snackbar.LENGTH_SHORT)
  .setAnchorView(bottomBar)
  .show()
end



-- 1. 导入Android原生控件类（Material Design及AndroidX组件）
-- 用于在Lua代码中创建和操作对应UI元素，所有导入类均为官方标准控件
import "com.google.android.material.tabs.TabItem" -- Material Design标签项控件
import "android.widget.LinearLayout" -- 线性布局容器（水平/垂直排列子视图）
import "com.google.android.material.tabs.TabLayout" -- Material Design标签栏容器
import "com.google.android.material.search.SearchView" -- Material Design搜索输入视图
import "com.google.android.material.appbar.MaterialToolbar" -- Material Design工具栏（替代传统ActionBar）
import "com.google.android.material.search.SearchBar" -- Material Design搜索栏（含搜索框和交互逻辑）
import "android.widget.ProgressBar" -- 进度指示器（当前代码未使用，预留扩展）
import "androidx.recyclerview.widget.RecyclerView" -- AndroidX可复用列表控件（高效展示大量数据）
import "com.google.android.material.appbar.AppBarLayout" -- Material Design应用栏容器（控制工具栏滚动行为）
import "androidx.coordinatorlayout.widget.CoordinatorLayout" -- 协调布局（处理子视图间依赖滚动，如工具栏+列表）
import "android.widget.ScrollView" -- 滚动视图（当前代码未使用，预留扩展）
import "android.widget.FrameLayout" -- 帧布局（单层视图容器，当前代码未使用，预留扩展）
import "com.google.android.material.card.MaterialCardView" -- Material Design卡片控件（带阴影、圆角的容器）
import "android.widget.TextView" -- 文本显示控件（用于展示文字内容）
import "androidx.recyclerview.widget.LinearLayoutManager" -- RecyclerView的线性布局管理器（控制列表垂直/水平排列）
import "androidx.appcompat.widget.LinearLayoutCompat" -- 兼容版线性布局（适配低版本系统，功能与LinearLayout类似）

-- 2. 导入自定义Lua工具类
local LuaRecyclerAdapter = require "LuaRecyclerAdapter" -- 自定义RecyclerView适配器（封装列表数据绑定逻辑）
local Colors = require "Colors" -- 自定义颜色工具类（存储预设颜色值，如背景色）

-- 3. 初始化当前Activity（Android页面核心组件）
-- 设置主题、标题并加载UI布局
activity
.setTheme(R.style.Theme_Material3_Green_NoActionBar)
.setContentView(loadlayout(
-- 根布局：CoordinatorLayout（协调布局）
-- 核心作用：管理子视图间的依赖关系（如列表滚动时控制AppBarLayout的显示/隐藏）
{
  CoordinatorLayout,
  layout_width = "match_parent", -- 宽度：占满整个父容器（屏幕宽度）
  layout_height = "match_parent", -- 高度：占满整个父容器（屏幕高度）

  -- 子视图1：RecyclerView（可复用列表）- 核心内容区域
  {
    RecyclerView,
    id = "recycler_view", -- 唯一标识，用于后续在代码中通过ID获取该控件
    layout_width = "match_parent", -- 宽度占满父容器
    layout_height = "match_parent", -- 高度占满父容器
    paddingTop = "8dp", -- 顶部内边距：8dp（控制列表项与顶部控件的间距）
    paddingBottom = "8dp", -- 底部内边距：8dp（控制列表项与底部的间距）
    clipToPadding = false, -- 关键属性：滚动时内容可超出内边距区域（避免顶部/底部列表项被内边距遮挡）
    -- 布局行为：绑定AppBarLayout滚动逻辑（列表滚动时，AppBarLayout会同步响应，如折叠/展开）
    layout_behavior = "appbar_scrolling_view_behavior",
  },

  -- 子视图2：AppBarLayout（应用栏容器）- 管理顶部搜索栏和标签栏
  -- 作用：统一控制顶部控件的滚动行为（如随列表滚动隐藏/显示）
  {
    AppBarLayout,
    id = "app_bar_layout", -- 唯一标识，用于后续控制应用栏行为
    layout_width = "match_parent", -- 宽度占满父容器
    layout_height = "wrap_content", -- 高度自适应子视图（搜索栏+标签栏的总高度）
    fitsSystemWindows = true, -- 关键属性：让控件适配系统窗口（如延伸到状态栏区域，配合Edge-to-Edge）
    liftOnScroll = false, -- 滚动时是否提升应用栏层级（false：滚动时不额外抬高阴影层级，保持视觉稳定）

    -- 子视图2.1：SearchBar（搜索栏）- 顶部搜索输入控件
    {
      SearchBar,
      id = "open_search_bar", -- 唯一标识，用于后续绑定搜索交互（如点击展开SearchView）
      layout_width = "match_parent", -- 宽度占满AppBarLayout
      layout_height = "wrap_content", -- 高度自适应搜索栏内容（默认高度）
      hint = "Enter text to search", -- 提示文本：搜索框未输入时显示的引导文字
    },

    -- 子视图2.2：TabLayout（标签栏）- 分类切换控件
    {
      TabLayout,
      -- 滚动标志：值为2对应"SCROLL_FLAG_SCROLL"（官方常量）
      -- 作用：当RecyclerView滚动时，该标签栏会跟随AppBarLayout一起滚动（可被隐藏）
      layout_scrollFlags = 2,
      layout_width = "match_parent", -- 宽度占满AppBarLayout
      layout_height = "wrap_content", -- 高度自适应标签内容（默认高度）
      id = "tabs", -- 唯一标识，用于后续在代码中添加标签项（如之前代码中的Explore/Flights/Trips）
    },
  },

  -- 子视图3：FrameLayout（帧布局）- 上下文工具栏容器
  -- 作用：承载"上下文工具栏"（如列表项选中时显示的批量操作栏），默认隐藏
  {
    FrameLayout,
    id = "contextual_toolbar_container", -- 唯一标识，用于控制容器显示/隐藏
    layout_width = "match_parent", -- 宽度占满父容器
    layout_height = "wrap_content", -- 高度自适应子视图（工具栏高度）
    elevation = "5dp", -- 阴影高度：5dp（让工具栏浮于其他视图之上，增强层次感）
    visibility = 8, -- 可见性：值为8对应"View.GONE"（官方常量），默认完全隐藏（不占屏幕空间）

    -- 子视图3.1：MaterialToolbar（上下文工具栏）- 批量操作栏
    {
      MaterialToolbar,
      id = "contextual_toolbar", -- 唯一标识，用于后续添加批量操作按钮（如删除、选中全部）
      layout_width = "match_parent", -- 宽度占满帧布局
      layout_height = "wrap_content", -- 高度自适应工具栏内容（默认高度）
    },
  },

  -- 子视图4：SearchView（搜索视图）- 搜索展开后的全屏搜索容器
  -- 作用：SearchBar点击后展开的详细搜索界面，包含搜索输入和建议列表
  {
    SearchView,
    id = "open_search_view", -- 唯一标识，用于控制搜索视图的展开/收起
    layout_width = "match_parent", -- 宽度占满父容器
    layout_height = "match_parent", -- 高度占满父容器（全屏显示）
    hint = "Enter text to search", -- 提示文本：与SearchBar一致，保持体验统一
    -- 锚点绑定：将SearchView与SearchBar关联（展开时以SearchBar为基准定位，避免布局错乱）
    layout_anchor = "open_search_bar",

    -- 子视图4.1：ScrollView（滚动视图）- 承载搜索建议列表
    -- 作用：当搜索建议过多时，允许垂直滚动查看所有建议
    {
      ScrollView,
      layout_width = "match_parent", -- 宽度占满SearchView
      layout_height = "match_parent", -- 高度占满SearchView

      -- 子视图4.1.1：LinearLayout（线性布局）- 搜索建议容器
      -- 作用：垂直排列所有搜索建议项（如文本、图标等）
      {
        LinearLayout,
        id = "open_search_view_suggestion_container", -- 唯一标识，用于动态添加搜索建议项
        layout_width = "match_parent", -- 宽度占满ScrollView
        layout_height = "wrap_content", -- 高度自适应建议项总高度
        orientation = "vertical", -- 子视图排列方向：垂直（从上到下）
      },
    },
  },
})) -- 加载UI布局文件（"layout"为布局文件标识，对应XML或Lua定义的布局结构）

-- 4. 配置Edge-to-Edge（沉浸式边缘显示）
-- 让UI延伸到状态栏和导航栏区域，提升视觉沉浸感
local EdgeToEdge = luajava.bindClass"androidx.activity.EdgeToEdge" -- 绑定AndroidX的EdgeToEdge工具类
local Build = luajava.bindClass "android.os.Build" -- 绑定系统版本控制类（用于判断Android SDK版本）
EdgeToEdge.enable(this) -- 启用当前Activity的Edge-to-Edge模式（this指代当前Activity实例）

-- 5. 适配导航栏对比度（仅Android 11及以上版本生效）
-- 当SDK版本≥30（Android 11）时，关闭导航栏对比度强制功能，避免影响沉浸式显示效果
if Build.VERSION.SDK_INT >= 30 then
  activity.getWindow().setNavigationBarContrastEnforced(false)
end

-- 6. 设置状态栏颜色
-- 将状态栏颜色设为自定义背景色，实现状态栏与页面背景视觉统一
activity.getWindow().setStatusBarColor(Colors.colorBackground)

-- 7. 给TabLayout（标签栏）添加标签项
-- 循环创建3个标签（Explore/Flights/Trips），并添加到标签栏中
-- tabs为布局文件中定义的TabLayout控件实例（通过ID自动关联）
for k, v in pairs({
    { title = "Explore" }, -- 第一个标签：标题为"Explore"
    { title = "Flights" }, -- 第二个标签：标题为"Flights"
    { title = "Trips" } -- 第三个标签：标题为"Trips"
  }) do
  -- 1. 调用tabs.newTab()创建新标签实例；2. 调用setText(v.title)设置标签文本；3. 调用tabs.addTab()将标签添加到标签栏
  tabs.addTab(tabs.newTab().setText(v.title))
end

-- 8. 初始化RecyclerView的数据源
-- 创建空表data，后续用于存储列表要展示的数据（共50条示例数据）
local data = {}

-- 循环生成50条示例数据，每条数据含key字段（用于标识唯一数据，当前未实际使用，预留扩展）
for i = 1, 50 do
  table.insert(data, { key = i }) -- 将每条数据插入data表末尾
end

-- 9. 定义RecyclerView列表项的UI结构（Lua布局语法）
-- 描述单个列表项的视图层级和属性，后续会被适配器复用渲染
local item = {
  -- 最外层容器：兼容版线性布局（LinearLayoutCompat）
  LinearLayoutCompat;
  layout_width = "fill"; -- 宽度占满父容器（RecyclerView的宽度）
  gravity = "center"; -- 子视图在水平方向居中对齐
  {
    -- 卡片容器：MaterialCardView（带阴影、圆角，支持点击/选中状态）
    MaterialCardView;
    layout_width = "match_parent", -- 宽度占满父容器（LinearLayoutCompat）
    layout_height = "wrap_content", -- 高度自适应子视图内容
    checkable = true, -- 支持选中状态（点击后可标记为选中）
    clickable = true, -- 支持点击交互（可设置点击事件）
    focusable = true, -- 支持获取焦点（适配键盘导航等场景）
    -- 内边距：上下4dp，左右16dp（控制卡片与父容器的间距）
    layout_marginTop = "4dp",
    layout_marginBottom = "4dp",
    layout_marginLeft = "16dp",
    layout_marginRight = "16dp",
    {
      -- 卡片内部容器：垂直方向线性布局（LinearLayout）
      LinearLayout,
      layout_width = "wrap_content", -- 宽度自适应子视图（文本内容）
      layout_height = "wrap_content", -- 高度自适应子视图
      -- 内边距：上下8dp/64dp，左右12dp（控制文本与卡片边缘的间距）
      paddingTop = "8dp",
      paddingBottom = "64dp",
      paddingLeft = "12dp",
      paddingRight = "12dp",
      orientation = "vertical", -- 子视图垂直排列（标题在上，副标题在下）
      {
        -- 列表项标题文本：TextView（显示地址主内容）
        TextView,
        id = "cat_searchbar_recycler_title", -- 文本视图唯一ID（用于后续查找和修改文本）
        layout_width = "wrap_content",
        layout_height = "wrap_content",
        layout_marginBottom = "4dp", -- 与下方副标题的间距（4dp）
        text = "481 Van Brunt Street", -- 默认文本（后续会被数据源动态替换）
      },
      {
        -- 列表项副标题文本：TextView（显示地址区域）
        TextView,
        id = "cat_searchbar_recycler_subtitle", -- 唯一ID（用于动态修改文本）
        layout_width = "wrap_content",
        layout_height = "wrap_content",
        text = "Brooklyn, NY", -- 默认文本（后续会被动态替换）
      },
    }
  }
}

-- 10. 创建RecyclerView的适配器（连接数据与UI）
-- LuaRecyclerAdapter为自定义适配器，参数依次为：数据源、列表项UI结构、回调函数
local adapter = LuaRecyclerAdapter(data, item,
{
  -- 数据绑定回调：当列表项被复用渲染时触发（核心逻辑）
  -- holder：列表项视图持有者（存储当前项的视图引用）
  -- position：当前列表项的位置（从0开始，与Java中RecyclerView位置一致）
  onBindViewHolder = function(viewHolder, pos, views, currentData)
    -- 【注意】当前代码缺少"动态绑定数据到UI"的逻辑，需补充以下代码才能正常显示动态内容：
    -- 示例：通过ID查找标题文本视图，并设置动态文本
    -- local titleTv = views.cat_searchbar_recycler_title
    -- titleTv.setText("动态地址标题." .. pos)
    -- 示例：设置副标题文本
    -- local subtitleTv = views.cat_searchbar_recycler_subtitle
    -- subtitleTv.setText("动态区域." .. pos)
  end,
})

-- 11. 配置RecyclerView并设置适配器
-- recycler_view为布局文件中定义的RecyclerView实例（通过ID自动关联）
recycler_view
.setAdapter(adapter) -- 设置适配器，将数据与UI关联
-- 设置布局管理器：线性布局管理器（垂直排列列表项，关联当前Activity上下文）
.setLayoutManager(LinearLayoutManager(activity))



-- 引入控件
import "lemon.material.textfield.MaterialTextField"
import "android.widget.Toast"
import "android.widget.Button"
import "android.graphics.Color"
import "android.graphics.drawable.ColorDrawable"
import "android.text.TextWatcher"
import "android.text.Editable"
import "androidx.appcompat.widget.LinearLayoutCompat"

-- 根布局
activity.setContentView(loadlayout({
  LinearLayoutCompat,
  orientation = "vertical",
  layout_width = "fill",
  layout_height = "fill",
  padding = "24dp",
  -- 演示用的 MaterialTextField
  {
    MaterialTextField,
    id = "textField",
    layout_width = "fill",
    layout_marginBottom = "16dp",
    hint = "请输入内容",
    BoxCornerRadii = 16, -- 圆角
    EndIconMode = 2, -- 右侧清除图标（0=无，1=密码可见，2=清除）
    StartIconDrawable = R.drawable.ic_android, -- 左侧图标
    StartIconTintList = Color.BLUE
  },

  -- 控制区
  {
    LinearLayoutCompat,
    orientation = "vertical",
    layout_width = "fill",
    layout_height = "wrap",
    gravity = "center",

    -- 一行按钮
    {
      LinearLayoutCompat,
      orientation = "horizontal",
      layout_width = "fill",
      layout_height = "wrap",
      gravity = "center",
      {
        Button,
        text = "设置错误",
        onClick = function()
          textField.setError("这是一条错误提示")
          textField.setErrorEnabled(true)
        end
      },
      {
        Button,
        text = "清除错误",
        onClick = function()
          textField.setError(nil)
          textField.setErrorEnabled(false)
        end
      }
    },

    -- 第二行按钮
    {
      LinearLayoutCompat,
      orientation = "horizontal",
      layout_width = "fill",
      layout_height = "wrap",
      gravity = "center",
      {
        Button,
        text = "设置辅助文字",
        onClick = function()
          textField.setHelperText("这是辅助/提示文字")
        end
      },
      {
        Button,
        text = "清除辅助文字",
        onClick = function()
          textField.setHelperText(nil)
        end
      }
    },

    -- 第三行按钮
    {
      LinearLayoutCompat,
      orientation = "horizontal",
      layout_width = "fill",
      layout_height = "wrap",
      gravity = "center",
      {
        Button,
        text = "设置文字",
        onClick = function()
          textField.setText("Hello MaterialTextField!")
        end
      },
      {
        Button,
        text = "获取文字",
        onClick = function()
          local txt = textField.getText()
          Toast.makeText(activity, "当前文字：" .. txt, 0).show()
        end
      }
    },

    -- 第四行按钮
    {
      LinearLayoutCompat,
      orientation = "horizontal",
      layout_width = "fill",
      layout_height = "wrap",
      gravity = "center",
      {
        Button,
        text = "设置单行",
        onClick = function()
          textField.setSingleLine(true)
        end
      },
      {
        Button,
        text = "设置多行",
        onClick = function()
          textField.setSingleLine(false)
        end
      }
    },

    -- 第五行按钮
    {
      LinearLayoutCompat,
      orientation = "horizontal",
      layout_width = "fill",
      layout_height = "wrap",
      gravity = "center",
      {
        Button,
        text = "切换图标",
        onClick = function()
          -- 切换 EndIconMode：0-无 1-密码可见 2-清除
          local mode = (textField.getLayout().getEndIconMode() + 1) % 3
          textField.setEndIconMode(mode)
        end
      },
      {
        Button,
        text = "切换图标颜色",
        onClick = function()
          local colors = {Color.RED, Color.GREEN, Color.BLUE}
          local idx = (math.random(1, 3))
          textField.setStartIconTintList(colors[idx])
        end
      }
    },

    -- 第六行按钮
    {
      LinearLayoutCompat,
      orientation = "horizontal",
      layout_width = "fill",
      layout_height = "wrap",
      gravity = "center",
      {
        Button,
        text = "设置文字大小",
        onClick = function()
          textField.setTextSize(18) -- 18sp
        end
      },
      {
        Button,
        text = "设置圆角",
        onClick = function()
          textField.setBoxCornerRadii(360)
        end
      }
    }
  }
}))

-- 文字监听器示例
textField.addTextChangedListener(luajava.createProxy("android.text.TextWatcher", {
  beforeTextChanged = function(s, start, count, after) end,
  onTextChanged = function(s, start, before, count) end,
  afterTextChanged = function(editable)
    -- 实时校验：长度小于 4 时提示错误
    if editable and editable.length() < 4 then
      textField.setError("至少输入 4 个字符")
     else
      textField.setError(nil)
    end
  end
}))



--Source：氚-Tritium 2957148920
--OpenSource：Apache License Version

-- 免费帖求个一键三连不过分吧₍˄·͈༝·͈˄*₎◞ ̑̑

local Slider = luajava.bindClass "com.google.android.material.slider.Slider"
local ScrollView = luajava.bindClass "android.widget.ScrollView"
local MaterialDivider = luajava.bindClass "com.google.android.material.divider.MaterialDivider"
local MaterialTextView = luajava.bindClass "com.google.android.material.textview.MaterialTextView"
local LinearLayoutCompat = luajava.bindClass "androidx.appcompat.widget.LinearLayoutCompat"
local ColorStateList = luajava.bindClass "android.content.res.ColorStateList"

activity
.setTitle("Slider拖动条控件")
.setContentView(loadlayout({
  ScrollView;--纵向滑动控件
  layout_width='fill';--布局宽度
  layout_height='fill';--布局高度
  verticalScrollBarEnabled=false;--隐藏纵向滑条
  overScrollMode=View.OVER_SCROLL_NEVER,--隐藏圆弧阴影

  {
    LinearLayoutCompat;--线性控件
    orientation='vertical';--布局方向
    layout_width='fill';--布局宽度
    layout_height='fill';--布局高度
    padding='8dp';--控件内边距
    {
      LinearLayoutCompat;--线性控件
      layout_width='fill';--布局宽度
      layout_height='wrap';--布局高度
      {
        MaterialTextView;--文本控件
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        text='默认样式  ';--显示文字
      };
      {
        MaterialTextView;--文本控件
        layout_width='fill';--控件宽度
        layout_height='wrap';--控件高度
        text='拖动改变值';--显示文字
        id="tv",
      };
    };

    {
      Slider,--滑动输入控件
      layout_width="fill";
      layout_marginTop='8dp';--布局顶距
      layout_marginBottom='8dp';--布局底距
      --HaloTintList=ColorStateList.valueOf(0xaa44354280),--点击球周围波纹颜色
      --TickActiveTintList=ColorStateList.valueOf(0xffFEFBFF),--滑到的刻度颜色
      --TickInactiveTintList=ColorStateList.valueOf(0xff354280),--未滑到的刻度颜色
      --TrackInactiveTintList=ColorStateList.valueOf(0x33354280),--未滑到的轨道颜色
      --ThumbTintList=ColorStateList.valueOf(0xff354280),--球的颜色
      --TrackActiveTintList=ColorStateList.valueOf(0xdd354280),--滑过的轨道颜色
      --trackHeight=100,--高度最大值
      --StepSize=1,--设置刻度间隔
      ValueFrom=1,--最小值
      ValueTo=60,--最大值
      value=10;--当前值
      id='slider';--控件ID
    },

    {
      MaterialDivider,--横向分割线
      layout_marginTop='8dp';--布局顶距
      layout_marginBottom='8dp';--布局底距
    },
    {
      MaterialTextView;--文本控件
      layout_width='fill';--控件宽度
      layout_height='wrap';--控件高度
      text='更高的拖动条\ntips：你并不能设置他的高度改变拖动条的高度';--显示文字
    };
    {
      Slider,--滑动输入控件
      layout_width="fill";
      layout_marginTop='8dp';--布局顶距
      layout_marginBottom='8dp';--布局底距
      trackHeight=100,--最大值
      ValueFrom=1,--最小值
      ValueTo=60,--最大值
      value=10;--当前值
    },

    {
      MaterialDivider,--横向分割线
      layout_marginTop='8dp';--布局顶距
      layout_marginBottom='8dp';--布局底距
    },
    {
      MaterialTextView;--文本控件
      layout_width='fill';--控件宽度
      layout_height='wrap';--控件高度
      text='带刻度的拖动条\ntips:请注意，你使用刻度时，当前值最大值和最小值必须是0或刻度值的整数倍，否则将导致Java层崩溃';--显示文字
    };
    {
      Slider,--滑动输入控件
      layout_width="fill";
      layout_marginTop='8dp';--布局顶距
      StepSize=.5,--设置刻度间隔
      ValueFrom=0,--最小值
      ValueTo=60,--最大值
      value=10;--当前值
    },
    {
      Slider,--滑动输入控件
      layout_width="fill";
      layout_marginTop='4dp';--布局顶距
      layout_marginBottom='4dp';--布局底距
      StepSize=5,--设置刻度间隔
      ValueFrom=0,--最小值
      ValueTo=100,--最大值
      value=80;--当前值
    },
    {
      Slider,--滑动输入控件
      layout_width="fill";
      layout_marginBottom='8dp';--布局底距
      StepSize=10,--设置刻度间隔
      ValueFrom=0,--最小值
      ValueTo=200,--最大值
      value=120;--当前值
    },

    {
      MaterialDivider,--横向分割线
      layout_marginTop='8dp';--布局顶距
      layout_marginBottom='8dp';--布局底距
    },
    {
      MaterialTextView;--文本控件
      layout_width='fill';--控件宽度
      layout_height='wrap';--控件高度
      text='不允许通过拖动修改值';--显示文字
    };
    {
      Slider,--滑动输入控件
      layout_width="fill";
      layout_marginTop='8dp';--布局顶距
      layout_marginBottom='8dp';--布局底距
      enabled=false,
      ValueFrom=1,--最小值
      ValueTo=60,--最大值
      value=30;--当前值
    },

    {
      MaterialDivider,--横向分割线
      layout_marginTop='8dp';--布局顶距
      layout_marginBottom='8dp';--布局底距
    },
    {
      MaterialTextView;--文本控件
      layout_width='fill';--控件宽度
      layout_height='wrap';--控件高度
      text='隐藏拖动提示';--显示文字
    };
    {
      Slider,--滑动输入控件
      layout_width="fill";
      layout_marginTop='8dp';--布局顶距
      layout_marginBottom='8dp';--布局底距
      labelBehavior=2,
      ValueFrom=1,--最小值
      ValueTo=60,--最大值
      value=10;--当前值
    },

    {
      MaterialDivider,--横向分割线
      layout_marginTop='8dp';--布局顶距
      layout_marginBottom='8dp';--布局底距
    },
    {
      MaterialTextView;--文本控件
      layout_width='fill';--控件宽度
      layout_height='wrap';--控件高度
      text='修改滑条的圆弧半径大小';--显示文字
    };
    {
      Slider,--滑动输入控件
      layout_width="fill";
      layout_marginTop='8dp';--布局顶距
      layout_marginBottom='8dp';--布局底距
      thumbRadius=60,--滑轨
      ValueFrom=1,--最小值
      ValueTo=60,--最大值
      value=10;--当前值
    },

    {
      MaterialDivider,--横向分割线
      layout_marginTop='8dp';--布局顶距
      layout_marginBottom='8dp';--布局底距
    },
    {
      MaterialTextView;--文本控件
      layout_width='fill';--控件宽度
      layout_height='wrap';--控件高度
      text='你还可以修改它的各种颜色';--显示文字
    };
    {
      Slider,--滑动输入控件
      layout_width="fill";
      layout_marginTop='8dp';--布局顶距
      layout_marginBottom='8dp';--布局底距
      HaloTintList=ColorStateList.valueOf(0x44354280),--点击球周围波纹颜色
      TickActiveTintList=ColorStateList.valueOf(0xffFEFBFF),--滑到的刻度颜色
      TickInactiveTintList=ColorStateList.valueOf(0xff354280),--未滑到的刻度颜色
      TrackInactiveTintList=ColorStateList.valueOf(0x33354280),--未滑到的轨道颜色
      ThumbTintList=ColorStateList.valueOf(0xff354280),--球的颜色
      TrackActiveTintList=ColorStateList.valueOf(0xdd354280),--滑过的轨道颜色
      trackHeight=100,--高度最大值
      ValueFrom=1,--最小值
      ValueTo=60,--最大值
      value=10;--当前值
    },

    {
      MaterialDivider,--横向分割线
      layout_marginTop='8dp';--布局顶距
      layout_marginBottom='8dp';--布局底距
    },
    {
      MaterialTextView;--文本控件
      layout_width='fill';--控件宽度
      layout_height='wrap';--控件高度
      text='下面是一些拖动条';--显示文字
    };
    {
      LinearLayoutCompat;--线性控件
      orientation='vertical';--布局方向
      layout_width='fill';--布局宽度
      layout_height='fill';--布局高度
      id='container';--控件ID
    };
  }
}))

slider.addOnSliderTouchListener(Slider.OnSliderTouchListener{
  onStartTrackingTouch = function(view)
    --view.getValue()
    -- 当用户开始拖动时，这个方法会被调用

  end,
  onStopTrackingTouch = function(view)
    -- 当用户停止拖动时，这个方法会被调用

  end
})

slider.addOnChangeListener(Slider.OnChangeListener{
  onValueChange = function(view, value, fromUser)
    -- 数值发生改变时，这个方法会被调用
    tv.setText(tostring(value))
  end
})

for i = 1,200 do
  container.addView(loadlayout({
    Slider,--滑动输入控件
    trackHeight=100,
    Value=(30 * math.sin(.15 * i)) + 40,
    ValueTo=100,
  }))
end




--Source：氚-Tritium 2957148920
--OpenSource：Apache License Version

-- 免费帖求个一键三连不过分吧₍˄·͈༝·͈˄*₎◞ ̑̑

local LinearLayoutCompat = luajava.bindClass "androidx.appcompat.widget.LinearLayoutCompat"
local ChipGroup = luajava.bindClass "com.google.android.material.chip.ChipGroup"
local Chip = luajava.bindClass "com.google.android.material.chip.Chip"
local ColorStateList = luajava.bindClass "android.content.res.ColorStateList"
local MaterialDivider = luajava.bindClass "com.google.android.material.divider.MaterialDivider"
local MaterialTextView = luajava.bindClass "com.google.android.material.textview.MaterialTextView"


local circularProgressDrawable = luajava.newInstance("androidx.swiperefreshlayout.widget.CircularProgressDrawable", activity)
circularProgressDrawable.setStyle(luajava.bindClass("androidx.swiperefreshlayout.widget.CircularProgressDrawable").DEFAULT)
circularProgressDrawable.setColorFilter(0xff888888, luajava.bindClass("android.graphics.PorterDuff").Mode.SRC_ATOP)
circularProgressDrawable.start()

activity
.setTheme(R.style.Theme_Material3_Blue)
.setTitle("Chip纸片标签控件")
.setContentView(loadlayout({
  LinearLayoutCompat;--线性控件
  orientation='vertical';--布局方向
  layout_width='fill';--布局宽度
  layout_height='fill';--布局高度
  padding='8dp';--控件内边距

  {
    MaterialTextView;--文本控件
    layout_width='fill';--控件宽度
    layout_height='wrap';--控件高度
    text='一些Chip实例\ntips：纵向距离为负值时可以强行减小间距';--显示文字
  };


  {
    ChipGroup;--标签容器控件
    layout_width='fill';--控件宽度
    layout_height='wrap';--控件高度
    chipSpacingVertical='-6dp';--控件内边距

    {
      Chip;--标签控件
      layout_width='wrap';--控件宽度
      layout_height='wrap';--控件高度
      text='Chip';--显示文字
      gravity='center';--重力
    };
    {
      Chip;--标签控件
      layout_width='wrap';--控件宽度
      layout_height='wrap';--控件高度
      text='Checkable Chip';--显示文字
      gravity='center';--重力
      Checkable=true,
    };
    {
      Chip;--标签控件
      layout_width='wrap';--控件宽度
      layout_height='wrap';--控件高度
      text='Icon Chip';--显示文字
      gravity='center';--重力
      Checkable=true,
      ChipIcon=activity.getResources().getDrawable(luajava.bindClass("com.google.android.material.R").drawable.material_ic_edit_black_24dp),
    };
    {
      Chip;--标签控件
      layout_width='wrap';--控件宽度
      layout_height='wrap';--控件高度
      text='Stroke Chip';--显示文字
      textColor="#2196f3",
      gravity='center';--重力
      ChipStrokeWidth="1dp",
      ChipStrokeColor=ColorStateList.valueOf(0xFF2196f3),
      ChipCornerRadius="32dp",
    };
    {
      Chip;--标签控件
      layout_width='wrap';--控件宽度
      layout_height='wrap';--控件高度
      text='Close Chip';--显示文字
      gravity='center';--重力
      CloseIconEnabled=true,
    };
    {
      Chip;--标签控件
      layout_width='wrap';--控件宽度
      layout_height='wrap';--控件高度
      text='Disabled Chip';--显示文字
      gravity='center';--重力
      enabled=false,
    };
    {
      Chip;--标签控件
      layout_width='wrap';--控件宽度
      layout_height='wrap';--控件高度
      text='Loading Chip';--显示文字
      gravity='center';--重力
      enabled=false,
      ChipIcon=circularProgressDrawable,
    };
  };

  {
    MaterialDivider,--横向分割线
    layout_marginTop='8dp';--布局顶距
    layout_marginBottom='8dp';--布局底距
  },

  {
    MaterialTextView;--文本控件
    layout_width='fill';--控件宽度
    layout_height='wrap';--控件高度
    text='单行Chip并且只允许单选';--显示文字
  };

  {
    HorizontalScrollView;--横向滑动控件
    layout_width='fill';--布局宽度
    layout_height='wrap';--布局高度
    horizontalScrollBarEnabled=false;--隐藏横向滑条
    overScrollMode=View.OVER_SCROLL_NEVER,--隐藏圆弧阴影

    {
      ChipGroup;--标签容器控件
      layout_width='fill';--控件宽度
      layout_height='wrap';--控件高度
      singleLine=true;
      SingleSelection=true,

      {
        Chip;--标签控件
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        text='Checkable Chip';--显示文字
        gravity='center';--重力
        Checkable=true,
      };
      {
        Chip;--标签控件
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        text='Checkable Chip';--显示文字
        gravity='center';--重力
        Checkable=true,
      };
      {
        Chip;--标签控件
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        text='Checkable Chip';--显示文字
        gravity='center';--重力
        Checkable=true,
      };
      {
        Chip;--标签控件
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        text='Checkable Chip';--显示文字
        gravity='center';--重力
        Checkable=true,
      };
      {
        Chip;--标签控件
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        text='Checkable Chip';--显示文字
        gravity='center';--重力
        Checkable=true,
      };
    }
  };

  {
    MaterialDivider,--横向分割线
    layout_marginTop='8dp';--布局顶距
    layout_marginBottom='8dp';--布局底距
  },

  {
    MaterialTextView;--文本控件
    layout_width='fill';--控件宽度
    layout_height='wrap';--控件高度
    text='单行Chip并且允许多选';--显示文字
  };

  {
    HorizontalScrollView;--横向滑动控件
    layout_width='fill';--布局宽度
    layout_height='wrap';--布局高度
    horizontalScrollBarEnabled=false;--隐藏横向滑条
    overScrollMode=View.OVER_SCROLL_NEVER,--隐藏圆弧阴影

    {
      ChipGroup;--标签容器控件
      layout_width='fill';--控件宽度
      layout_height='wrap';--控件高度
      singleLine=true;
      SingleSelection=false,

      {
        Chip;--标签控件
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        text='Checkable Chip';--显示文字
        gravity='center';--重力
        Checkable=true,
      };
      {
        Chip;--标签控件
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        text='Checkable Chip';--显示文字
        gravity='center';--重力
        Checkable=true,
      };
      {
        Chip;--标签控件
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        text='Checkable Chip';--显示文字
        gravity='center';--重力
        Checkable=true,
      };
      {
        Chip;--标签控件
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        text='Checkable Chip';--显示文字
        gravity='center';--重力
        Checkable=true,
      };
      {
        Chip;--标签控件
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        text='Checkable Chip';--显示文字
        gravity='center';--重力
        Checkable=true,
      };
    }
  };

  {
    MaterialDivider,--横向分割线
    layout_marginTop='8dp';--布局顶距
    layout_marginBottom='8dp';--布局底距
  },

  {
    MaterialTextView;--文本控件
    layout_width='fill';--控件宽度
    layout_height='wrap';--控件高度
    text='一些其他的Chip实例\ntips：使用setChipText或者setText均可设置Chip内容';--显示文字
  };

  {
    ChipGroup;--标签容器控件
    layout_width='fill';--控件宽度
    layout_height='wrap';--控件高度
    chipSpacingVertical='-4dp';--控件内边距

    {
      Chip;--标签控件
      layout_width='wrap';--控件宽度
      layout_height='wrap';--控件高度
      text='Disabled Chip';--显示文字
      gravity='center';--重力
      enabled=false,
    };
    {
      Chip;--标签控件
      layout_width='wrap';--控件宽度
      layout_height='wrap';--控件高度
      text='Loading Chip';--显示文字
      gravity='center';--重力
      enabled=false,
      ChipIcon=circularProgressDrawable,
    };
    {
      Chip;--标签控件
      layout_width='wrap';--控件宽度
      layout_height='wrap';--控件高度
      ChipText='setChipText Chip';--显示文字
      gravity='center';--重力
    };

    {
      Chip;--标签控件
      layout_width='wrap';--控件宽度
      layout_height='wrap';--控件高度
      text='setText Chip';--显示文字
      gravity='center';--重力
    };

    {
      Chip;--标签控件
      layout_width='wrap';--控件宽度
      layout_height='wrap';--控件高度
      text='Padding Chip';--显示文字
      gravity='center';--重力

      --内边距什么的
      textStartPadding= 0,
      textEndPadding= 0,
      iconStartPadding= 0,
      iconEndPadding= 0,
      chipIconSize= 0,
      chipStartPadding= 0,
      chipEndPadding= 0,
    };

    {
      Chip;--标签控件
      layout_width='wrap';--控件宽度
      layout_height='wrap';--控件高度
      text='Padding Chip';--显示文字
      gravity='center';--重力

      --内边距什么的
      textStartPadding= 10,
      textEndPadding= 90,
      iconStartPadding= 30,
      iconEndPadding= 0,
      chipIconSize= 30,
      chipStartPadding= 80,
      chipEndPadding= 0,
    };
    
    {
      Chip;--标签控件
      layout_width='wrap';--控件宽度
      layout_height='wrap';--控件高度
      text='A Long A Long A Long A Long A Long A Long A Long Text Chip';--显示文字
      gravity='center';
      ellipsize='middle';
    };
    {
      Chip;--标签控件
      layout_width='wrap';--控件宽度
      layout_height='wrap';--控件高度
      text='Checked Chip';--显示文字
      gravity='center';--重力
      Checkable=true,
      Checked=true,
    };
  };
}))




--Source：氚-Tritium 2957148920
--OpenSource：Apache License Version

-- 免费帖求个一键三连不过分吧₍˄·͈༝·͈˄*₎◞ ̑̑

local LinearLayoutCompat = luajava.bindClass "androidx.appcompat.widget.LinearLayoutCompat"
local MaterialButton = luajava.bindClass "com.google.android.material.button.MaterialButton"

function print(content)
  local Toast = luajava.bindClass "android.widget.Toast"
  Toast.makeText(activity,tostring(content),Toast.LENGTH_SHORT).show()
end

activity
.setTitle("BottomSheetDialog")
.setContentView(loadlayout({
  LinearLayoutCompat;--线性控件
  orientation='vertical';--布局方向
  layout_width='fill';--布局宽度
  layout_height='fill';--布局高度
  gravity='center';--控件内容的重力方向

  {
    MaterialButton;--纽扣控件
    layout_width='wrap';--控件宽度
    layout_height='wrap';--控件高度
    text='Button';--显示文字
    id='btn';--设置控件ID
    gravity='center';--重力
  };
}))


local function addContent(list)
  local Build = luajava.bindClass"android.os.Build"
  local WindowManager = luajava.bindClass"android.view.WindowManager"
  local ExpandableListView = luajava.bindClass"android.widget.ExpandableListView"
  local ArrayExpandableListAdapter = luajava.bindClass"android.widget.ArrayExpandableListAdapter"
  local LinearLayoutCompat = luajava.bindClass"androidx.appcompat.widget.LinearLayoutCompat"
  local MaterialTextView = luajava.bindClass"com.google.android.material.textview.MaterialTextView"
  local function NavigationBarHeight()
    if Build.VERSION.SDK_INT >= 19 then
      return activity.getResources().getDimensionPixelSize(activity.getResources().getIdentifier("navigation_bar_height", "dimen", "android"))
    end
    return 0
  end
  local dialog_view = (loadlayout({
    LinearLayoutCompat,
    layout_width = "fill",
    layout_height = .8 * activity.getHeight(),
    orientation = "vertical",
    {
      ExpandableListView;--折叠列表适配器
      layout_width = "fill";--布局宽度
      layout_height = "fill";--布局高度
      id = "Expandable";
      dividerHeight = 1;--分割线宽度0为无隔断线
      NestedScrollingEnabled = true,
      verticalScrollBarEnabled = false;--隐藏滑条
      paddingBottom = NavigationBarHeight()
    }
  }))

  local BottomSheetDialog = luajava.bindClass"com.google.android.material.bottomsheet.BottomSheetDialog"
  local BottomSheetBehavior = luajava.bindClass"com.google.android.material.bottomsheet.BottomSheetBehavior"

  --创建BottomSheetDialog对象
  local mBottomSheetDialog = BottomSheetDialog(activity);
  mBottomSheetDialog.setContentView(dialog_view);

  --获得父窗体,并设置为透明
  dialog_view.getParent().setBackgroundResource(android.R.color.transparent);

  --创建BottomSheetBehavior对,设置Dialog默认弹出高度
  BottomSheetBehavior.from(dialog_view.getParent()).setPeekHeight(.8 * activity.getHeight());

  --解决弹出Dialog后状态栏会变黑
  if Build.VERSION.SDK_INT >= 21 then
    mBottomSheetDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
  end

  local adp = ArrayExpandableListAdapter(activity)
  Expandable.setAdapter(adp)

  for i = 1, #list do
    adp.add(list[i].title, list[i].data.subtitle)
  end

  Expandable.onChildClick = function (l, view, pid, cid)
    print(list[pid + 1].data.content[cid + 1])
    mBottomSheetDialog.dismiss()
  end

  mBottomSheetDialog.show();
end

btn.setOnClickListener(View.OnClickListener{
  onClick = function(view)
    local DataLuaTable = require ("cjson").decode([==[[{"data":{"content":["layout=--全屏框架\n{\n  LinearLayout;--线性控件\n  orientation='vertical';--布局方向\n  layout_width='fill';--布局宽度\n  layout_height='fill';--布局高度\n  background='#ffeeeeee';--布局背景\n\n}\nactivity.setContentView(loadlayout(layout))","layout=--弹窗框架\n{\n  LinearLayout;--线性控件\n  orientation='vertical';--布局方向\n  layout_width='fill';--布局宽度\n  layout_height='fill';--布局高度\n  background='#ffeeeeee';--布局背景\n  {\n    TextView;--文本控件\n    layout_width='fill';--控件宽度\n    layout_height='200dp';--控件高度\n    text='本文内容';--显示文字\n    textSize='16sp';--文字大小\n    textColor='#333333';--文字颜色\n    gravity='center';--重力\n  };\n};\n\ntc=AlertDialog.Builder(this).show()\n--tc.setCancelable(false)--禁用返回键\ntc.getWindow().setContentView(loadlayout(layout));\nimport\"android.graphics.drawable.ColorDrawable\"\ntc.getWindow().setBackgroundDrawable(ColorDrawable(0x00000000));--背景透明\ntc.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);--支持输入法\n--tc.dismiss()--关闭\n--tc.show()--显示"],"subtitle":["全屏框架","弹窗框架"]},"title":"常见框架"},{"data":{"content":["{\n  LinearLayoutCompat;--线性控件\n  orientation='vertical';--布局方向\n  layout_width='fill';--布局宽度\n  layout_height='fill';--布局高度\n  --background='#ffffff';--布局背景\n\n};","{\n  DrawerLayout;--抽屉布局\n  orientation='vertical';--布局方向\n  layout_width='fill';--布局宽度\n  layout_height='fill';--布局高度\n  --background='#ffffff';--布局背景\n\n};","{\n  RelativeLayout;--相对布局\n  layout_width='fill';--布局宽度\n  layout_height='fill';--布局高度\n  --background='#ffffff';--布局背景\n\n};","{\n  AbsoluteLayout;--绝对布局\n  layout_width='fill';--布局宽度\n  layout_height='fill';--布局高度\n  --background='#ffffff';--布局背景\n\n};","{\n  FrameLayout;--帧布局\n  layout_width='fill';--布局宽度\n  layout_height='fill';--布局高度\n  --background='#ffffff';--布局背景\n\n};","{\n  TextInputLayout;--输入框布局\n  layout_width='fill';--布局宽度\n  layout_height='fill';--布局高度\n  hint='提示文字';\n  --background='#ffffff';--布局背景\n\n};","{\n  RippleLayout;--水波纹布局\n  layout_width='fill';--布局宽度\n  layout_height='fill';--布局高度\n  RippleColor='#ffffff';--水波纹颜色\n  RippleLineColor='#ffeeeeee';--水波纹线条颜色\n  Circle=true;--长按圆圈\n\n};","{\n  CoordinatorLayout;--协调布局\n  layout_width='fill';--布局宽度\n  layout_height='fill';--布局高度\n  \n\n};","{\n  AppBarLayout;-- 应用栏布局布局\n  layout_width='fill';--布局宽度\n  layout_height='fill';--布局高度\n  layout_scrollFlags=\"scroll|exitUtilCollapsed|snap\",\n  id='appbarLayout';\n  -- scroll 标志头部与主体一起滚动\n  -- enterAlways 标志头部与主体一起滚动，头部滚到位后，主体继续向上或者向下滚动\n  -- exitUntilCollapsed 保证页面上至少能看到最小化的工具栏，不会完全看不到工具栏。上滚动：先收缩头部，然后头部固定不动，主体继续向上滚动。下滚动：主体先滚动，头部不动。主体全部拉出去，头部向下展开。\n  -- enterAlwaysCollapsed 与enterAlways标志位一起使用。该标志位有折叠操作，单独的enterAlways没有折叠操作。\n  -- snap 用户松开手指，系统自行判断。接下来是全部向上滚到顶或者全部向下展开。\n \n  \n};","{\n  CollapsingToolbarLayout;--可折叠应用栏布局\n  layout_width='fill';--布局宽度\n  layout_height='fill';--布局高度\n  layout_scrollFlags=\"scroll|exitUntilCollapsed|snap\",\n  id='toolbarLayout';\n  -- scroll 标志头部与主体一起滚动\n  -- enterAlways 标志头部与主体一起滚动，头部滚到位后，主体继续向上或者向下滚动\n  -- exitUntilCollapsed 保证页面上至少能看到最小化的工具栏，不会完全看不到工具栏。上滚动：先收缩头部，然后头部固定不动，主体继续向上滚动。下滚动：主体先滚动，头部不动。主体全部拉出去，头部向下展开。\n  -- enterAlwaysCollapsed 与enterAlways标志位一起使用。该标志位有折叠操作，单独的enterAlways没有折叠操作。\n  -- snap 用户松开手指，系统自行判断。接下来是全部向上滚到顶或者全部向下展开。\n  title=\"标题内容文本\";\n\n\n};","{\n  SlidingLayout;--滑动菜单布局\n  layout_width='fill';--布局宽度\n  layout_height='fill';--布局高度\n  background='#ffffff';--布局背景\n\n};","{\n  TableLayout;--表格布局\n  layout_width='fill';--布局宽度\n  layout_height='fill';--布局高度\n  background='#ffffff';--布局背景\n  --CollapseColumns='';--隐藏指定的列\n  --ShrinkColumns='';--收缩指定的列以适合屏幕,不会挤出屏幕\n  --StretchColumns='';--尽量把指定的列填充空白部分\n  --layout_column='';--控件放在指定的列\n  --layout_span='';--该控件所跨越的列数\n\n};","{\n  GridLayout;--网格布局\n  orientation='vertical';--布局方向\n  layout_width='fill';--布局宽度\n  layout_height='fill';--布局高度\n  background='#ffffff';--布局背景\n  RowCount='1';--行数\n  ColumnCount='1';--列数\n\n};","{\n  PageLayout;--滑动窗体布局\n  layout_width='fill';--布局宽度\n  layout_height='fill';--布局高度\n  background='#ffffff';--布局背景\n\n};","{\n  PullingLayout;--拉动刷新\n  layout_width='fill';--宽度\n  layout_height='fill';--高度\n  id=\"pull\";\n  backgroundColor=\"#ffffffff\",\n  --PullDownEnabled=true;--下拉刷新\n  PullUpEnabled=true;--上拉加载\n\n};","{\n  SwipeRefreshLayout,--拉动刷新\n  layout_width='fill';--宽度\n  layout_height='fill';--高度\n  id=\"swipe\";\n        \n};"],"subtitle":["LinearLayoutCompat - 线性布局","DrawerLayout - 抽屉布局","RelativeLayout - 相对布局","AbsoluteLayout - 绝对布局","FrameLayout - 帧布局","TextInputLayout - 输入框布局","RippleLayout - 水波纹布局","CoordinatorLayout - 协调布局","AppBarLayout - 应用栏布局","CollapsingToolbarLayout - 折叠应用栏布局","SlidingLayout - 滑动菜单布局","TableLayout - 表格布局","GridLayout - 网格布局","PageLayout - 滑动窗体布局","PullingLayout - 拉动刷新","SwipeRefreshLayout - 拉动刷新"]},"title":"常见布局"},{"data":{"content":["{\n  MaterialToolbar,--工具栏控件\n  id=\"toolbar\",\n  layout_width='fill';--布局宽度\n  layout_height='56dp';--布局高度\n  navigationIcon=luajava.bindClass \"com.google.android.material.R\".drawable.ic_arrow_back_black_24,\n  title=\"标题\",\n  --subtitle=\"副标题\",\n  --layout_collapseMode=\"pin\",\n  -- pin 固定模式：ToolBar不动，不受CollapsingBarLayout折叠影响\n  -- parallax 视差模式：随着CollapsingBarLayout的折叠展开，Toolbar也收缩展开。设置了这个还须要设置折叠系数collapseParallaxMultiplier。为0时就是后面的app:layout_collapseMode 为none的模式。\n  -- none 默认值：CollapsingBarLayout折叠多少距离，ToolBar也移动多少距离。\n  \n  },","{\n  NestedScrollView, --嵌套可滚动控件\n  layout_width=\"fill\",\n  layout_height=\"fill\",\n  --fillViewport=\"true\",\n  \n};","{\n  ScrollView;--纵向滑动控件\n  layout_width='fill';--布局宽度\n  layout_height='fill';--布局高度\n  verticalScrollBarEnabled=false;--隐藏纵向滑条\n  --overScrollMode=View.OVER_SCROLL_NEVER,--隐藏圆弧阴影\n\n};","{\n  HorizontalScrollView;--横向滑动控件\n  layout_width='fill';--布局宽度\n  layout_height='fill';--布局高度\n  horizontalScrollBarEnabled=false;--隐藏横向滑条\n  --overScrollMode=View.OVER_SCROLL_NEVER,--隐藏圆弧阴影\n\n};","{\n  MaterialTextView;--文本控件\n  layout_width='fill';--控件宽度\n  layout_height='fill';--控件高度\n  text='本文内容';--显示文字\n  textSize='16sp';--文字大小\n  textColor='#333333';--文字颜色\n  --id='Text';--设置控件ID\n  --singleLine=true;--设置单行输入\n  --ellipsize='end';--多余文字用省略号显示\n  --start 开始 middle 中间 end 结尾\n  --Typeface=Typeface.DEFAULT;--字体\n  --textIsSelectable=true;--文本可复制\n  --style=\"?android:attr\/buttonBarButtonStyle\";--点击特效\n  gravity='center';--重力\n};","{\n  MaterialCardView;--卡片控件\n  layout_width='fill';--卡片宽度\n  layout_height='fill';--卡片高度\n  cardBackgroundColor='#ffffff';--卡片颜色\n  layout_margin='0dp';--卡片边距\n  cardElevation='0dp';--卡片阴影\n  strokeWidth=\"0dp\", --边框宽度\n  strokeColor=\"#ffffff\", --边框颜色\n  clickable=true;--点击效果\n  radius='5dp';--卡片圆角\n\n};","{\n  AppCompatImageView;--图片控件\n  layout_width='fill';--图片宽度\n  layout_height='fill';--图片高度\n  src='';--图片路径\n  --id='Image';--设置控件ID\n  --ColorFilter='';--图片着色\n  --ColorFilter=Color.BLUE;--设置图片着色\n  scaleType='fitXY';--图片拉伸\n  layout_gravity='center';--重力\n};","{\n  ImageView;--图片控件\n  layout_width='fill';--图片宽度\n  layout_height='fill';--图片高度\n  src='';--图片路径\n  --id='Image';--设置控件ID\n  --ColorFilter='';--图片着色\n  --ColorFilter=Color.BLUE;--设置图片着色\n  scaleType='fitXY';--图片拉伸\n  layout_gravity='center';--重力\n};","{\n  MaterialButton;--纽扣控件\n  layout_width='fill';--控件宽度\n  layout_height='fill';--控件高度\n  text='纽扣名称';--显示文字\n  textSize='16sp';--文字大小\n  textColor='#333333';--文字颜色\n  --id='Button';--设置控件ID\n  allCaps=false,--关闭按钮英文自动变大写\n  backgroundColor='#FFE5E5E5';--背景颜色\n  gravity='center';--重力\n};","{\n  MaterialSwitch;--开关控件\n  layout_width='wrap';--控件宽度\n  layout_height='wrap';--控件高度\n  id='Switch';--设置控件ID\n  text='本文内容';--显示文字\n  textSize='16sp';--文字大小\n  textColor='#333333';--文字颜色\n  gravity='center';--重力\n  --SwitchMinWidth='0dp';--开关最小宽度\n  --SwitchPadding='0dp';--开关与文字的间距\n  --showText=true;--开关上是否显示文字\n  --textOff=\"开\",--设置开关checked的文字\n  --textOn=\"关\",--设置开关关闭时的文字  \n  --checked=true;--代码中设置复选框初始化状态\n  --enabled=false ;--设置复选框为灰色,默认不可点击\n  --clickable=false;--设置复选框为彩色，默认不可点击\n};","{\n  MaterialCheckBox;--复选框控件\n  layout_width='fill';--控件宽度\n  layout_height='fill';--控件高度\n  text='纽扣名称';--显示文字\n  textSize='16sp';--文字大小\n  textColor='#333333';--文字颜色\n  --id='CheckBox';--设置控件ID\n  gravity='center';--重力\n};","{\n  Space;--空隙控件\n  layout_width='fill';--控件宽度\n  layout_height='wrap';--控件高度\n};","{\n  RadioGroup;--单选框容器控件\n  layout_width='fill';--控件宽度\n  layout_height='wrap';--控件高度\n  \n  \n};","{\n  ChipGroup;--标签容器控件\n  layout_width='fill';--控件宽度\n  layout_height='wrap';--控件高度\n  \n  \n};","{\n  MaterialRadioButton;--单选框控件\n  layout_width='fill';--控件宽度\n  layout_height='fill';--控件高度\n  text='纽扣名称';--显示文字\n  textSize='16sp';--文字大小\n  textColor='#333333';--文字颜色\n  --id='RadioButton';--设置控件ID\n  gravity='center';--重力\n};","{Chip;--标签控件\n  layout_width='wrap';--控件宽度\n  layout_height='wrap';--控件高度\n  text='标签名称';--显示文字\n  textSize='16sp';--文字大小\n  textColor='#333333';--文字颜色\n  --id='chip';--设置控件ID\n  gravity='center';--重力\n};","{\n  TextInputEditText;--编辑框控件\n  layout_width='fill';--控件宽度\n  layout_height='wrap';--控件高度\n  id='Edit';--设置控件ID\n  Hint=' 请输入';--编辑框内容为空时提示文字\n  hintTextColor='#FF747474';--提示文字颜色\n  textSize='16sp';--本文大小\n  textColor='#333333';--本文颜色\n  gravity='left';--重力\n  --background='#00ffffff';--底条透明\n  --Error='请输入';--气泡提示\n  --ellipsize='end';--多余文字用省略号显示\n  --start 开始 middle 中间 end 结尾\n  --singleLine=true;--设置单行输入，禁止换行\n  --imeOptions='actionSearch';--设置回车键搜索,必须开启单行输入才能生效\n  --actionGo(前往) actionDone(完成) actionNext(下一项) actionSearch(搜索) actionSend(发送)\n  --minLines=2;--默认占用的行数\n  --MaxLines=5;--设置最大输入行数\n  --MaxEms=5;--设置每行最大宽度为五个字符的宽度\n  --maxLength=\"10\",--限制最多输入文字个数\n  --password=true;--开启密码类型，自动隐藏为*号\n  --InputType='number';--设置只可输入数字\n  --cursorVisible=false;--隐藏光标\n  --focusable=false;--禁止获得焦点,会无法编辑\n  --focusableInTouchMode=false,--禁止弹出输入法\n};","{\n  EditText;--编辑框控件\n  layout_width='fill';--控件宽度\n  layout_height='wrap';--控件高度\n  id='Edit';--设置控件ID\n  Hint=' 请输入';--编辑框内容为空时提示文字\n  hintTextColor='#FF747474';--提示文字颜色\n  textSize='16sp';--本文大小\n  textColor='#333333';--本文颜色\n  gravity='left';--重力\n  --background='#00ffffff';--底条透明\n  --Error='请输入';--气泡提示\n  --ellipsize='end';--多余文字用省略号显示\n  --start 开始 middle 中间 end 结尾\n  --singleLine=true;--设置单行输入，禁止换行\n  --imeOptions='actionSearch';--设置回车键搜索,必须开启单行输入才能生效\n  --actionGo(前往) actionDone(完成) actionNext(下一项) actionSearch(搜索) actionSend(发送)\n  --minLines=2;--默认占用的行数\n  --MaxLines=5;--设置最大输入行数\n  --MaxEms=5;--设置每行最大宽度为五个字符的宽度\n  --maxLength=\"10\",--限制最多输入文字个数\n  --password=true;--开启密码类型，自动隐藏为*号\n  --InputType='number';--设置只可输入数字\n  --cursorVisible=false;--隐藏光标\n  --focusable=false;--禁止获得焦点,会无法编辑\n  --focusableInTouchMode=false,--禁止弹出输入法\n};","{\n    ViewPager,--翻页控件\n    id=\"viewpage\",\n    layout_width='fill';--控件宽度\n    layout_height='fill';--控件高度\n    pages={\n      \n    },\n},","{\n  SearchView;--搜索控件\n  layout_width='fill';--控件宽度\n  layout_height='fill';--控件高度\n  id='Search';\n  queryHint='请输入关键字';--提示\n  --conifiedByDefault=false;--搜索图标内外\n  maxWidth='fill';--最大宽度\n  --inputType='number';--设置仅数字输入\n};","{\n  LuaWebView;--浏览器控件\n  layout_width='fill';--浏览器宽度\n  layout_height='fill';--浏览器高度\n  id='webView';--控件ID\n};","{\n  MaterialDivider,--横向分割线\n  --layout_height='2px';--控件高度\n  layout_width='fill';--控件宽度\n    },","{\n  MaterialDivider,--纵向分割线\n  layout_height='fill';--控件高度\n  --layout_width='2px';--控件宽度\n},","{\n  ProgressBar,--进度条控件\n  layout_width=\"fill\";--布局宽度\n  layout_height=\"fill\";--布局高度\n  --Max='';--设置最大进度\n  --Progress='';--设置当前进度\n  --SecondaryProgress='';--设置第二进度\n  --indeterminate=false;--设置是否为不明确进度进度条,true为明确\n  --indeterminate=false;--模糊模式,true为开启\n  --style='?android:attr\/progressBarStyleLarge';--超大号圆形风格\n  --style='?android:attr\/progressBarStyleSmall';--小号风格\n  --style='?android:attr\/progressBarStyleSmallTitle';--标题型风格\n  --style='?android:attr\/progressBarStyleHorizontal';--长形进度条\n};","{\n  LinearProgressIndicator,--线性进度指示器\n  layout_width=\"wrap\";--布局宽度\n  layout_height=\"wrap\";--布局高度\n  trackCornerRadius=\"2dp\",\n  id=\"mProgress\",\n  --Max='';--设置最大进度\n  --Progress='';--设置当前进度\n  --SecondaryProgress='';--设置第二进度\n  --indeterminate=false;--设置是否为不明确进度进度条,true为明确\n};","{\n  CircularProgressIndicator,--循环渐进指示器\n  Indeterminate=true,\n  trackCornerRadius=\"2dp\",\n  id=\"mProgress\",\n};","{\n  NavigationView,--导航栏控件\n  id=\"navigationbar\",--导航栏ID\n  layout_width=\"wrap\",--导航栏宽度\n  layout_height=\"fill\",--导航栏高度    \n},","{\n  BottomNavigationView,--底部导航栏控件\n  id=\"bottombar\",--导航栏ID\n  layout_width=\"fill\",--导航栏宽度\n  layout_height=\"wrap\",--导航栏高度    \n},","{\n  Slider,--滑动输入控件\n  layout_width=\"fill\";\n  --HaloTintList=ColorStateList.valueOf(0xaa44354280),--点击球周围波纹颜色\n  --TickActiveTintList=ColorStateList.valueOf(0xffFEFBFF),--滑到的刻度颜色\n  --TickInactiveTintList=ColorStateList.valueOf(0xff354280),--未滑到的刻度颜色\n  --TrackInactiveTintList=ColorStateList.valueOf(0x33354280),--未滑到的轨道颜色\n  --ThumbTintList=ColorStateList.valueOf(0xff354280),--球的颜色\n  --TrackActiveTintList=ColorStateList.valueOf(0xdd354280),--滑过的轨道颜色\n  trackHeight=100,--最大值\n  StepSize=1,--设置刻度间隔\n  ValueFrom=1,--最小值\n  ValueTo=60,--最大值\n  value=10;--当前值\n},","{\n  SeekBar;--拖动条控件\n  layout_width='fill';--拖动条宽度\n  layout_height='fill';--拖动条高度\n  Max='';--设置最大进度\n  Progress='';--设置当前进度\n  SecondaryProgress='';--设置第二进度\n  --indeterminate=false;--设置是否为不明确进度进度条,true为明确\n  --indeterminate=false;--模糊模式,true为开启\n};","{\n  AutoCompleteTextView;--自动补全文本框\n  layout_width='fill';--文本宽度\n  layout_height='wrap';--文本高度\n};","{\n  LuaEditor;--Lua代码编辑器\n  layout_width=\"fill\";\n  layout_height='fill';\n  layout_weight=\"1\";\n  id=\"editor\";\n},"],"subtitle":["MaterialToolbar - 工具栏控件","NestedScrollView - 嵌套可滚动控件","ScrollView - 纵向滑动控件","HorizontalScrollView - 横向滑动控件","MaterialTextView - 文本控件","MaterialCardView - 卡片控件","AppCompatImageView - 图片控件","ImageView - 图片控件","MaterialButton - 纽扣控件","MaterialSwitch - 开关控件","MaterialCheckBox - 复选框控件","Space - 空隙控件","RadioGroup - 单选控件容器","ChipGroup - 标签控件容器","MaterialRadioButton - 单选框控件","Chip - 标签控件","TextInputEditText - 编辑框控件","EditText - 编辑框控件","ViewPager - 翻页控件","SearchView - 搜索控件","LuaWebView - 浏览器","横向分割线←→","纵向分割线↑↓","ProgressBar - 进度条控件","LinearProgressIndicator - 线性进度指示器","CircularProgressIndicator - 圆形进度指示器","NavigationView - 导航栏控件","BottomNavigationView - 底部导航栏控件","Slider - 滑动输入控件","SeekBar - 拖动条控件","AutoCompleteTextView - 自动补全文本框","LuaEditor - Lua代码编辑器"]},"title":"常见控件"},{"data":{"content":["layout_width='fill';--布局宽度","layout_height='fill';--布局高度","orientation='vertical';--布局方向","layout_weight='1';--权重值","layout_gravity='center';--控件内容的重力方向\n--左:left 右:right 中:center 顶:top 底:bottom","gravity='center';--控件内容的重力方向\n--左:left 右:right 中:center 顶:top 底:bottom","singleLine=true;--设置单行输入","strokeWidth='0dp', --边框宽度","strokeColor='#000000', --边框颜色","background='';--布局背景","backgroundColor='#ffffff';--背景颜色","cardBackgroundColor='#ffffff';--卡片颜色","id='控件ID';--控件ID","layout_margin='dp';--控件外边距","layout_marginTop='dp';--布局顶距","layout_marginBottom='dp';--布局底距","layout_marginLeft='dp';--布局左距","layout_marginRight='dp';--布局右距","padding='dp';--控件内边距","paddingTop='dp';--布局内顶距","paddingBottom='dp';--布局内底距","paddingLeft='dp';--布局内左距","paddingRight='dp';--布局内右距","alpha=0.6;--控件透明度"],"subtitle":["layout_width - 布局宽度","layout_height - 布局高度","orientation - 布局方向","layout_weight - 权重值","layout_gravity - 对齐方式","gravity - 重力","singleLine - 设置单行输入","strokeWidth - 边框宽度","strokeColor - 边框颜色","background - 布局背景","backgroundColor - 背景颜色","cardBackgroundColor - 卡片背景色","id - 控件ID","layout_margin - 控件外边距","layout_marginTop - 布局顶距↑","layout_marginBottom - 布局底距↓","layout_marginLeft - 布局左距←","layout_marginRight - 布局右距→","padding - 控件内边距","paddingTop - 布局内顶距↑","paddingBottom - 布局内底距↓","paddingLeft - 布局内左距←","paddingRight - 布局内右距→","alpha - 控件透明度"]},"title":"常见属性"},{"data":{"content":["id.setOnClickListener(View.OnClickListener{\n  onClick=function(view)\n\n    return true\n  end\n})","function onLongClick(id, callback)\n  if Build.VERSION.SDK_INT <34 then\n    id.setOnLongClickListener(View.OnLongClickListener{\n      onLongClick=function(v)\n        callback(v)\n        return true\n      end\n    })\n   else\n    id.setOnLongClickListener(View.OnLongClickListener{\n      onLongClick=function(v)\n        callback(v)\n        return true\n      end,\n      onLongClickUseDefaultHapticFeedback=function()\n        return true\n      end\n    })\n  end\nend\n\nonLongClick(id, function(view)--控件长按事件\n\n\nend)","id.onTouch=function(view)--控件触摸事件\n\nend;","list.onItemClick=function(parent,view,position,index)--列表点击事件\n\nend;","list.onItemLongClick=function(parent,view,position,index)--列表点击事件\n\nend;","id.setVisibility(View.GONE)--隐藏控件","id.setVisibility(View.VISIBLE)--显示控件","id.setVisibility(View.INVISIBLE)--控件不可视","--编辑框控件监听事件\nEdit.addTextChangedListener({\n  onTextChanged=function(s)\n    print(tostring(Edit.Text))\n  end\n})","--输入框回车键监听\nid.setOnKeyListener({\n  onKey=function(v,keyCode,event)\n    import \"java.awt.event.*\"\n    if (KeyEvent.KEYCODE_ENTER == keyCode and KeyEvent.ACTION_DOWN == event.getAction()) then      \n      \n    end\n  end\n})","--设置导航键点击监听\ntoolbar.setNavigationOnClickListener(View.OnClickListener{\n  onClick=function(view)\n\n  end\n})","--设置菜单列表点击监听\ntoolbar.setOnMenuItemClickListener(MaterialToolbar.OnMenuItemClickListener{\n  onMenuItemClick=function(item)\n\n  end\n})","-- 拖动条触摸监听\nslider.addOnSliderTouchListener({--触摸事件\n  onStartTrackingTouch=function(view)\n\n  end,\n  onStopTrackingTouch=function(view)\n\n  end\n})","-- SwipeRefreshLayout刷新监听\nswipe.setOnRefreshListener(SwipeRefreshLayout.OnRefreshListener{\n  onRefresh=function()\n\n  end\n})","--浏览器控件LuaWebView常用API\nid.loadUrl(\"http:\/\/www.androlua.cn\")--加载网页\nid.loadUrl(\"file:\/\/\/storage\/sdcard0\/index.html\")--加载本地文件\nid.getTitle()--获取网页标题\nid.getUrl()--获取当前Url\nid.requestFocusFromTouch()--设置支持获取手势焦点\nid.getSettings().setJavaScriptEnabled(true)--设置支持JS\nid.setPluginsEnabled(true)--支持插件\nid.setUseWideViewPort(false)--调整图片自适应\nid.getSettings().setSupportZoom(true)--支持缩放\nid.getSettings().setLayoutAlgorithm(LayoutAlgorithm.SINGLE_COLUMN)--支持重新布局\nid.supportMultipleWindows()--设置多窗口\nid.stopLoading()--停止加载网页\n\n--状态监听\nid.setWebViewClient{\n  shouldOverrideUrlLoading=function(view,url)\n    --Url即将跳转\n  end,\n  onPageStarted=function(view,url,favicon)\n    --网页加载\n  end,\n  onPageFinished=function(view,url)\n    --网页加载完成\n  end\n}"],"subtitle":["onClick - 控件点击事件","onLongClick - 控件长按事件","onTouch - 控件触摸事件","onItemClick - 列表点击事件","onItemLongClick - 列表长按事件","View.GONE - 隐藏控件","View.VISIBLE - 显示控件","View.INVISIBLE - 控件不可视","onTextChanged - 输入框监听","onKeyListener - 输入框回车键监听","navigationOnClick - 导航键点击事件","onMenuItemClick - 工具栏菜单点击事件","onSliderTouch - 拖动条监听","onRefresh - 下拉刷新监听","LuaWebView - 常见事件"]},"title":"监听事件"},{"data":{"content":["{\n  RecyclerView;--回收适配器控件\n  layout_width='fill';--宽度\n  layout_height='fill';--高度\n  VerticalFadingEdgeEnabled=false;\n  id=\"recycler\";\n};","{\n  ListView;--列表适配器\n  layout_width='fill';--宽度\n  layout_height='fill';--高度\n  id=\"list\";\n  DividerHeight=1;--分割线高度0为无隔断线\n  --verticalScrollBarEnabled=false;--隐藏滑条\n};","{\n  HorizontalListView;--横向列表适配器\n  layout_width='fill';--宽度\n  layout_height='fill';--高度\n  id=\"list\";\n  DividerWidth=1;--分割线宽度0为无隔断线\n  --horizontalScrollBarEnabled=false;--隐藏滑条\n};","{\n  GridView;--网格适配器\n  layout_width='fill';--宽度\n  layout_height='fill';--高度\n  id=\"grid\";\n  numColumns=3;--列数\n  --ColumnWidth='';--每一列的宽度\n  --StretchMode='';--缩放\n  --HorizontalSpacing='';--两列直接的边距\n  --VerticalSpacing='';--两行之间的间距\n  --overScrollMode=\"\";--去除滑动圆弧形\n  --verticalScrollBarEnabled=false;--隐藏滑条\n};","{\n  Spinner;--下拉列表适配器\n  layout_width='fill';--宽度\n  layout_height='fill';--高度\n  id=\"spin\";\n};","{\n  ExpandableListView;--折叠列表适配器\n  layout_width=\"fill\";--布局宽度\n  layout_height=\"fill\";--布局高度\n  id=\"expan\";\n  dividerHeight=1;--分割线宽度0为无隔断线\n  verticalScrollBarEnabled=false;--隐藏滑条\n};"],"subtitle":["RecyclerView -- 回收适配器控件","ListView - 列表适配器","HorizontalListView - 横向列表适配器","GridView - 网格适配器","Spinner - 下拉列表适配器","ExpandableListView - 折叠列表适配器"]},"title":"适配器控件"},{"data":{"content":["--普通对话框\nlocal dialog = MaterialAlertDialogBuilder(this)\n.setTitle(\"标题\")\n.setMessage(\"消息\")\n--.setCancelable(false)--禁用返回键\n.setPositiveButton(\"积极\",{\n  onClick=function(a)\n    print(\"点击了积极按钮\")\n  end\n})\n.setNeutralButton(\"中立\",nil)\n.setNegativeButton(\"否认\",nil)\n.show()","--列表对话框\nitems={}\nfor i=1,5 do\n  table.insert(items,\"项目\"..tostring(i))\nend\nlocal dialog = MaterialAlertDialogBuilder(this)\n.setTitle(\"列表对话框\")\n.setItems(items,{\n  onClick=function(l,v)\n    print(items[v+1])\n  end\n})\n.show()","--单选对话框\nlocal item = {}\nfor i=1,5 do\n  table.insert(item,\"单选项目\"..tostring(i))\nend\nlocal dialog = MaterialAlertDialogBuilder(this)\n.setTitle(\"列表对话框\")\n.setSingleChoiceItems(item,-1,{\n  onClick=function(v,pos)\n    print(item[pos+1])\n  end\n})\n.show();","--多选对话框\nlocal items={}\nfor i=1,5 do\n  table.insert(items,\"多选项目\"..tostring(i))\nend\nlocal dialog = MaterialAlertDialogBuilder(this)\n.setTitle(\"多选框\")\n.setMultiChoiceItems(items, nil,{\n  onClick=function(v,pos)\n    print(items[pos+1])\n  end\n})\n.show();","--输入对话框\nlocal dialog = MaterialAlertDialogBuilder(this)\n.setTitle(\"标题\")\n.setView(loadlayout({\n  LinearLayout;\n  orientation=\"vertical\";\n  Focusable=true,\n  FocusableInTouchMode=true,\n  {\n    TextView;\n    id=\"Prompt\",\n    textSize=\"15sp\",\n    layout_marginTop=\"10dp\";\n    layout_marginLeft=\"3dp\",\n    layout_width=\"80%w\";\n    layout_gravity=\"center\",\n    text=\"输入:\";\n  };\n  {\n    EditText;\n    hint=\"输入\";\n    layout_marginTop=\"5dp\";\n    layout_width=\"80%w\";\n    layout_gravity=\"center\",\n    id=\"edit\";\n  };\n}))\n.setPositiveButton(\"确定\",{\n  onClick=function(a)\n    print(edit.Text)\n  end\n})\n.setNegativeButton(\"取消\",nil)\n.show()\nimport \"android.view.View$OnFocusChangeListener\"\nedit.setOnFocusChangeListener(OnFocusChangeListener{\n  onFocusChange=function(v,hasFocus)\n    if hasFocus then\n      Prompt.setTextColor(0xFD009688)\n    end\n  end\n})","--PopupMenu(弹出式菜单)\nlocal pop = PopupMenu(activity,popmenu_position)\nlocal menu = pop.Menu\nlocal menu1 = menu.addSubMenu(\"二级菜单\")\nmenu1.add('项目1').onMenuItemClick=function(a)\n  --事件\nend\nmenu1.add('项目1').onMenuItemClick=function(a)\n  --事件\nend\nmenu.add('项目1').onMenuItemClick=function(a)\n  --事件\nend\nmenu.add('项目2').onMenuItemClick=function(a)\n  --事件\nend\npop.show()--显示"],"subtitle":["普通对话框","列表对话框","单选对话框","多选对话框","输入对话框","弹出式菜单"]},"title":"对话框"},{"data":{"content":["--url 请求网址  cookie 身份识别信息  charset 内容编码  header 请求头\nHttp.get(url,cookie,charset,header,function(code,content,cookie,heafer)\n  --code 响应代码  content 网页源码内容  cookie 用户身份识别信息  header 响应头\n  if(code == 200 and content)then\n\n   else\n\n  end\nend)","--url 请求网址  data 发送的post数据  cookie 身份识别信息  charset 内容编码  header 请求头\nHttp.post(url,data,cookie,charset,header,function(code,content,cookie,heafer)\n  --code 响应代码  content 网页源码内容  cookie 用户身份识别信息  header 响应头\n  if(code == 200 and content)then\n\n   else\n\n  end\nend)","--url 请求网址  path 文件保存路径 cookie 身份识别信息  header 请求头\nHttp.download(url,path,cookie,header,function(code,heafer)\n  --code 响应代码  header 响应头\n  if(code == 200 and content)then\n\n   else\n\n  end\nend)","import \"java.net.URLEncoder\"--URL编码\nstr = URLEncoder.encode(内容)","import \"java.net.URLDecoder\"--URL解码\nstr = URLDecoder.decode(内容)","import \"http\"--url 请求网址  cookie 网页要求的cookie  ua 浏览器识别  header http请求头\nbody,cookie,code,headers=http.get(url,cookie,ua,header)\n--body 网页源码内容  cookie 用户身份识别信息  code 响应代码  headers 服务器返回的头信息","import \"http\"--url 请求网址  data post的字符串或字符串数据组表  cookie 网页要求的cookie  ua 浏览器识别  header http请求头\nbody,cookie,code,headers=http.post(url,data,cookie,ua,header)\n--body 网页源码内容  cookie 用户身份识别信息  code 响应代码  headers 服务器返回的头信息","import \"http\"--url 请求网址  cookie 网页要求的cookie  ua 浏览器识别  ref 来源页网址  header http请求头\ncode,headers=http.download(url,cookie,ua,ref,header)\n--code 响应代码  headers 服务器返回的头信息","import \"http\"--url 请求网址  datas upload的字符串数据组表  files upload的文件名数据表  cookie 网页要求的cookie  ua 浏览器识别  header http请求头\nbody,cookie,code,headers=http.upload(url,datas,files,cookie,ua,header)\n--body 网页源码内容  cookie 用户身份识别信息  code 响应代码  headers 服务器返回的头信息"],"subtitle":["异步Http.get","异步Http.post","异步Http.download","URL编码","URL解码","同步Http.get","同步Http.post","同步Http.download","同步Http.upload"]},"title":"网络模块"},{"data":{"content":["layout=--常规框架\n{\n  LinearLayoutCompat;--线性控件\n  orientation='vertical';--布局方向\n  layout_width='fill';--布局宽度\n  layout_height='fill';--布局高度\n  background='#ffeeeeee';--布局背景\n  {\n    ListView;--列表适配器\n    layout_width='fill';--宽度\n    layout_height='fill';--高度\n    id=\"list\";\n    dividerHeight=\"1\";--分割线高度\n    verticalScrollBarEnabled=false;--隐藏滑条\n  };\n}\n\nactivity.setContentView(loadlayout(layout))\n\n\ndata={\n  {name=\"列表内容1\"},\n  {name=\"列表内容2\"},\n  {name=\"列表内容3\"}\n};\nitem={\n  MaterialTextView;--文本控件\n  layout_width='fill';--控件宽度\n  layout_height='45dp';--控件高度\n  text='本文内容';--显示文字\n  textSize='16sp';--文字大小\n  textColor='#333333';--文字颜色\n  --style=\"?android:attr\/buttonBarButtonStyle\";--点击特效\n  gravity='center';--重力\n  id='name';--设置控件ID\n  --左:left 右:right 中:center 顶:top 底:bottom\n};\nlocal adp = LuaAdapter(activity, data, item)--设置适配器\nlist.setAdapter(adp)\n\n--adp.clear()--清空适配器\n\n--动态添加内容到适配器\nadp.add{name=\"列表内容4\"}--将一组内容添加到适配器\nadp.add{name=\"列表内容5\"}--将一组内容添加到适配器\nadp.add{name=\"列表内容6\"}--将一组内容添加到适配器\n\nlist.onItemClick=function(l,v,p,i)--列表适配器点击事件\n  print(\"点击  \"..v.Tag.name.text)\nend\n\nlist.onItemLongClick=function(l,v,p,i)--列表适配器长按事件\n  print(\"长按  \"..v.Tag.name.text)\n  return true\nend"],"subtitle":["列表适配器"]},"title":"其他开源列表"}]]==])

    addContent(DataLuaTable)
  end
})



--Source：氚-Tritium 2957148920
--OpenSource：Apache License Version

-- 免费帖求个一键三连不过分吧₍˄·͈༝·͈˄*₎◞ ̑̑

require "import"
import "androidx.appcompat.widget.ContentFrameLayout"
import "androidx.fragment.app.FragmentContainerView"
import "androidx.appcompat.widget.LinearLayoutCompat"
import "com.google.android.material.bottomnavigation.BottomNavigationView"
import "com.google.android.material.textview.MaterialTextView"

local LinearLayoutCompat = luajava.bindClass "androidx.appcompat.widget.LinearLayoutCompat"
local MaterialButtonGroup = luajava.bindClass "com.google.android.material.button.MaterialButtonGroup"
local MaterialSplitButton = luajava.bindClass "com.google.android.material.button.MaterialSplitButton"
local MaterialButtonToggleGroup = luajava.bindClass "com.google.android.material.button.MaterialButtonToggleGroup"
local MaterialButton = luajava.bindClass "com.google.android.material.button.MaterialButton"
local ColorStateList = luajava.bindClass "android.content.res.ColorStateList"
local MaterialDivider = luajava.bindClass "com.google.android.material.divider.MaterialDivider"
local MaterialTextView = luajava.bindClass "com.google.android.material.textview.MaterialTextView"

activity.setTitle("MaterialButton纽扣控件")
activity.setContentView(loadlayout({
  ScrollView;--纵向滑动控件
  layout_width='fill';--布局宽度
  layout_height='fill';--布局高度
  verticalScrollBarEnabled=false;--隐藏纵向滑条
  overScrollMode=View.OVER_SCROLL_NEVER,--隐藏圆弧阴影

  {
    LinearLayoutCompat;--线性控件
    orientation='vertical';--布局方向
    layout_width='fill';--布局宽度
    layout_height='fill';--布局高度
    padding="8dp",
    id="root",
    {
      MaterialTextView;--文本控件
      layout_width='fill';--控件宽度
      layout_height='wrap';--控件高度
      text='MaterialSplitButton和普通的buttonGroup一样, 样式也和下面的基本一致';--显示文字
    };
    {
      MaterialSplitButton;
      layout_width='fill';
      layout_height='wrap';
      gravity='center';--重力

      {
        MaterialButton;--纽扣控件
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        text='Button';--显示文字
        gravity='center';--重力
      };
      {
        MaterialButton;--纽扣控件
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        text='Button';--显示文字
        gravity='center';--重力
      };
      {
        MaterialButton;--纽扣控件
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        text='Button';--显示文字
        gravity='center';--重力
      }
    };
    {
      LinearLayoutCompat;--线性控件
      layout_width='fill';--布局宽度
      layout_height='wrap';--布局高度

      {
        MaterialSplitButton;
        layout_width='wrap';
        layout_height='wrap';
        gravity='center';--重力

        {
          MaterialButton;--纽扣控件
          layout_width='wrap';--控件宽度
          layout_height='wrap';--控件高度
          text='Button';--显示文字
          gravity='center';--重力
          --style = luajava.bindClass("com.google.android.material.R").attr.materialButtonOutlinedStyle ,
        };
        {
          MaterialButton;--纽扣控件
          layout_width='wrap';--控件宽度
          layout_height='wrap';--控件高度
          text='Button';--显示文字
          gravity='center';--重力
          style = luajava.bindClass("com.google.android.material.R").attr.materialButtonOutlinedStyle ,
        }
      };
      {
        Space,
        layout_width='8dp';--控件宽度
        layout_height='wrap';--控件高度
      },
      {
        MaterialSplitButton;
        layout_width='wrap';
        layout_height='wrap';
        gravity='center';--重力

        {
          MaterialButton;--纽扣控件
          layout_width='wrap';--控件宽度
          layout_height='wrap';--控件高度
          text='Button';--显示文字
          gravity='center';--重力
          style = luajava.bindClass("com.google.android.material.R").attr.materialButtonOutlinedStyle ,
        };
        {
          MaterialButton;--纽扣控件
          layout_width='wrap';--控件宽度
          layout_height='wrap';--控件高度
          --text='Button';--显示文字
          gravity='center';--重力
          icon=activity.getResources().getDrawable(luajava.bindClass("com.google.android.material.R").drawable.material_ic_edit_black_24dp),
          style=luajava.bindClass("com.google.android.material.R").attr.materialIconButtonFilledTonalStyle
        };
      }
    };
    {
      MaterialDivider,--横向分割线
      layout_marginTop='8dp';--布局顶距
      layout_marginBottom='8dp';--布局底距
    },
    {
      MaterialTextView;--文本控件
      layout_width='fill';--控件宽度
      layout_height='wrap';--控件高度
      text='MaterialButtonToggleGroup和普通的buttonGroup一样，也可以参考chip，我这边就不过多赘述';--显示文字
    };
    {
      MaterialButtonToggleGroup,
      layout_width="fill",
      layout_height="wrap",
      singleSelection=true,
      gravity='center';--重力

      {
        MaterialButton,
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        text='Button';--显示文字
        gravity='center';--重力
      },
      {
        MaterialButton,
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        text='Button';--显示文字
        gravity='center';--重力
      },
      {
        MaterialButton,
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        text='Button';--显示文字
        gravity='center';--重力
      },
    },
    {
      MaterialButtonToggleGroup,
      layout_width="fill",
      layout_height="wrap",
      singleSelection=true,
      gravity='center';--重力
      id="group",

      {
        MaterialButton,
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        text='Button';--显示文字
        gravity='center';--重力
        style = luajava.bindClass("com.google.android.material.R").attr.materialButtonOutlinedStyle ,
      },
      {
        MaterialButton,
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        text='Button';--显示文字
        gravity='center';--重力
        style = luajava.bindClass("com.google.android.material.R").attr.materialButtonOutlinedStyle ,
      },
      {
        MaterialButton,
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        text='Button';--显示文字
        gravity='center';--重力
        style = luajava.bindClass("com.google.android.material.R").attr.materialButtonOutlinedStyle ,
      },
    },
    {
      MaterialDivider,--横向分割线
      layout_marginTop='8dp';--布局顶距
      layout_marginBottom='8dp';--布局底距
    },
    {
      MaterialTextView;--文本控件
      layout_width='fill';--控件宽度
      layout_height='wrap';--控件高度
      text='使用MaterialButtonGroup可以产生联动效果';--显示文字
    };
    {
      MaterialButtonGroup;
      layout_width='fill';
      layout_height='wrap';
      gravity='center';--重力

      {
        MaterialButton;--纽扣控件
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        text='Button';--显示文字
        gravity='center';--重力
      };

      {
        MaterialButton;--纽扣控件
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        text='Button';--显示文字
        gravity='center';--重力
      };
      {
        MaterialButton;--纽扣控件
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        text='Button';--显示文字
        gravity='center';--重力
      }
    };

    {
      MaterialDivider,--横向分割线
      layout_marginTop='8dp';--布局顶距
      layout_marginBottom='8dp';--布局底距
    },
    {
      MaterialTextView;--文本控件
      layout_width='fill';--控件宽度
      layout_height='wrap';--控件高度
      text='你可以通过style设置他的样式';--显示文字
    };

    {
      MaterialButtonGroup;
      layout_width='fill';
      layout_height='wrap';
      gravity='center';--重力

      {
        MaterialButton;--纽扣控件
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        text='Button';--显示文字
        gravity='center';--重力
        style=luajava.bindClass("com.google.android.material.R").attr.materialIconButtonStyle
      };

      {
        MaterialButton;--纽扣控件
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        text='Button';--显示文字
        gravity='center';--重力
        onClick=lambda(v)v.setText(utf8.len(v.text)==0 and "Button" or "");
        style=luajava.bindClass("com.google.android.material.R").attr.materialIconButtonOutlinedStyle
      };

      {
        MaterialButton;--纽扣控件
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        text='Button';--显示文字
        gravity='center';--重力
        style=luajava.bindClass("com.google.android.material.R").attr.materialButtonElevatedStyle
      }
    };
    {
      MaterialButtonGroup;
      layout_width='fill';
      layout_height='wrap';
      gravity='center';--重力

      {
        MaterialButton;--纽扣控件
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        text='Button';--显示文字
        gravity='center';--重力
        --icon=activity.getResources().getDrawable(luajava.bindClass("com.google.android.material.R").drawable.material_ic_edit_black_24dp),
        style=luajava.bindClass("com.google.android.material.R").attr.materialIconButtonFilledTonalStyle
      };
      {
        MaterialButton;--纽扣控件
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        --text='Button';--显示文字
        gravity='center';--重力
        icon=activity.getResources().getDrawable(luajava.bindClass("com.google.android.material.R").drawable.material_ic_edit_black_24dp),
        style=luajava.bindClass("com.google.android.material.R").attr.materialIconButtonStyle
      };

      {
        MaterialButton;--纽扣控件
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        --text='Button';--显示文字
        gravity='center';--重力
        icon=activity.getResources().getDrawable(luajava.bindClass("com.google.android.material.R").drawable.material_ic_edit_black_24dp),
        style=luajava.bindClass("com.google.android.material.R").attr.materialIconButtonFilledStyle
      };
      {
        MaterialButton;--纽扣控件
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        --text='Button';--显示文字
        gravity='center';--重力
        icon=activity.getResources().getDrawable(luajava.bindClass("com.google.android.material.R").drawable.material_ic_edit_black_24dp),
        style=luajava.bindClass("com.google.android.material.R").attr.materialIconButtonOutlinedStyle
      };
      {
        MaterialButton;--纽扣控件
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        --text='Button';--显示文字
        gravity='center';--重力
        icon=activity.getResources().getDrawable(luajava.bindClass("com.google.android.material.R").drawable.material_ic_edit_black_24dp),
        style=luajava.bindClass("com.google.android.material.R").attr.materialIconButtonFilledTonalStyle
      }
    };

    {
      MaterialDivider,--横向分割线
      layout_marginTop='8dp';--布局顶距
      layout_marginBottom='8dp';--布局底距
    },
    {
      MaterialTextView;--文本控件
      layout_width='fill';--控件宽度
      layout_height='wrap';--控件高度
      text='通过setter方法一样可以达到效果';--显示文字
    };

    {
      MaterialButtonGroup;
      layout_width='fill';
      layout_height='wrap';
      gravity='center';--重力

      {
        MaterialButton;--纽扣控件
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        text='Button';--显示文字
        textColor="#000000",
        gravity='center';--重力
        BackgroundTintList=ColorStateList.valueOf(0xffffffff),
        StrokeWidth="1dp";
        StrokeColor=ColorStateList.valueOf(0xff000000),
      };

      {
        MaterialButton;--纽扣控件
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        text='Button';--显示文字
        textColor="#000000",
        gravity='center';--重力
        rippleColor=ColorStateList.valueOf(0xffff0000),
        BackgroundTintList=ColorStateList.valueOf(0x00),
      };
      {
        MaterialButton;--纽扣控件
        layout_width='wrap';--控件宽度
        layout_height='wrap';--控件高度
        text='Button';--显示文字
        gravity='center';--重力
        icon=activity.getResources().getDrawable(luajava.bindClass("com.google.android.material.R").drawable.material_ic_edit_black_24dp),
        id="btn",
      }
    }
  };
}))
--[[
btn.setOnClickListener(View.OnClickListener{
  onClick = function(view)
    print(view.text)
  end
})
--]]
group.addOnButtonCheckedListener(MaterialButtonToggleGroup.OnButtonCheckedListener{
  onButtonChecked = function(group, checkedId, isChecked)
    print(group, checkedId, isChecked)
  end
})
--[[
for i = 1, group.getChirdCount() do
  group.getChirdCount(i-1).setOnClickListener(View.OnClickListener{
    onClick = function(view)
      for j = 1, group.getChirdCount() do
        group.getChirdCount(j-1).setEnabled(false)
      end
      view.setEnabled(true)
    end
  })
end
--]]




--[[
  Material Design Carousel Demo for Android

  Author: Aqora
  QQ: 2241056127

  Description:
  使用Material Design 3的Carousel组件实现图片轮播效果
  包含圆角卡片、图片加载、动态文字颜色适配等功能
]]

-- 绑定Java类
local bindClass = luajava.bindClass
local MDC_R = bindClass "com.google.android.material.R"
local RecyclerView = bindClass "androidx.recyclerview.widget.RecyclerView"
local LinearLayoutCompat = bindClass "androidx.appcompat.widget.LinearLayoutCompat"
local MaterialTextView = bindClass "com.google.android.material.textview.MaterialTextView"
local AppCompatImageView = bindClass "androidx.appcompat.widget.AppCompatImageView"
local MaskableFrameLayout = bindClass "com.google.android.material.carousel.MaskableFrameLayout"
local LuaCustRecyclerAdapter = bindClass "github.znzsofficial.adapter.LuaCustRecyclerAdapter"
local LuaCustRecyclerHolder = bindClass "github.znzsofficial.adapter.LuaCustRecyclerHolder"
local CarouselLayoutManager = bindClass "com.google.android.material.carousel.CarouselLayoutManager"
local CarouselSnapHelper = bindClass "com.google.android.material.carousel.CarouselSnapHelper"
local ShapeAppearanceModel = bindClass "com.google.android.material.shape.ShapeAppearanceModel"
local HeroCarouselStrategy = bindClass "com.google.android.material.carousel.HeroCarouselStrategy"
local Glide = bindClass "com.bumptech.glide.Glide"
local Palette = bindClass "androidx.palette.graphics.Palette"
local ViewUtils = bindClass "com.google.android.material.internal.ViewUtils"
local MarginLayoutParams = bindClass "android.view.ViewGroup$MarginLayoutParams"

-- 获取Glide实例
local glide = Glide.with(activity)
local binding = {} -- 视图绑定表

-- 获取主题形状样式
local styleAttrs = int{MDC_R.attr.shapeAppearanceCornerExtraLarge}
local typedArray = activity.obtainStyledAttributes(styleAttrs)
local shapeAppearanceResId = typedArray.getResourceId(0, 0)
typedArray.recycle() -- 及时回收资源

-- 主Activity布局
activity {
  title = "Carousel Demo", -- 标题
  contentView = loadlayout({ -- 加载布局
    LinearLayoutCompat,
    orientation = "vertical",
    layout_width = "match",
    layout_height = "match",
    gravity = "center",
    {
      RecyclerView,
      id = "carousel_recycler_view",
      layout_width = "match_parent",
      layout_height = "196dp", -- 固定高度适合轮播展示
      layout_margin = "16dp", -- 外边距
    },
  }, binding)
}

-- 初始化轮播布局管理器
local carouselLayoutManager = CarouselLayoutManager(HeroCarouselStrategy())
carouselLayoutManager.setCarouselAlignment(carouselLayoutManager.ALIGNMENT_START) -- 左对齐
binding.carousel_recycler_view.setLayoutManager(carouselLayoutManager)

-- 图片数据源（QQ头像ID）
local list = {
  "1447017701", "1362883587", "2664233790", "2241056127",
  "3080756895", "2957148920", "839963468", "3373587110",
  "3299699002", "1811066180", "2059263736", "3562519024",
}

-- 创建RecyclerView适配器
local adapter_recycler_view = LuaCustRecyclerAdapter(this, {
  getItemCount = function()
    return #list -- 返回列表项总数
  end,

  onCreateViewHolder = function(viewGroup, pos)
    local views = {} -- 存储视图引用

    -- 加载单项布局
    local holder = LuaCustRecyclerHolder(loadlayout({
      MaskableFrameLayout,
      id = "carousel_item_container",
      layout_width = "150dp", -- 固定宽度保证一致性
      layout_height = "match_parent",
      shapeAppearanceModel = ShapeAppearanceModel.builder(activity, shapeAppearanceResId, 0).build(), -- 应用圆角样式
      {
        AppCompatImageView,
        id = "carousel_image_view",
        layout_width = "match_parent",
        layout_height = "match_parent",
        scaleType = "centerCrop", -- 居中裁剪
      },
      {
        MaterialTextView,
        layout_gravity = "start|bottom", -- 左下角定位
        layout_width = "match",
        layout_height = "wrap",
        layout_margin = "15dp",
        id = "description",
        textSize = "21sp",
        textStyle = "bold",
      },
      }, views)) {
      tag = views -- 将视图引用存入holder
    }

    -- 设置项间距
    local params = MarginLayoutParams(views.carousel_item_container.layoutParams)
    params.setMarginStart(ViewUtils.dpToPx(activity, 4))
    params.setMarginEnd(ViewUtils.dpToPx(activity, 4))
    views.carousel_item_container.setLayoutParams(params)

    -- 设置点击效果
    local attrs = int{android.R.attr.selectableItemBackground}
    local ta = activity.obtainStyledAttributes(attrs)
    views.carousel_item_container.setForeground(activity.getDrawable(ta.getResourceId(0, 0)))
    ta.recycle() -- 回收资源

    return holder
  end,

  onBindViewHolder = function(holder, pos)
    local views = holder.tag -- 获取视图引用
    local qqId = list[pos + 1] -- 获取当前项数据（Lua数组索引从1开始）

    -- 使用Glide加载网络图片
    glide.load(string.format("http://q2.qlogo.cn/headimg_dl?dst_uin=%s&spec=640", qqId))
    .listener {
      onLoadFailed = function(_, _, _, _)
        print("图片加载失败，QQ号:", qqId)
      end,
      onResourceReady = function(resource, _, _, _, _)
        -- 图片加载成功后提取主色调
        local palette = Palette.from(resource.getBitmap()).generate()
        local swatch = palette.getMutedSwatch()
        if swatch then
          views.description.setTextColor(swatch.getRgb()) -- 设置文字颜色
        end
      end
    }
    .into(views.carousel_image_view)

    -- 设置描述文本
    views.description.setText("Carousel Demo")

    -- 点击项时平滑滚动到该位置
    views.carousel_item_container.onClick = function()
      binding.carousel_recycler_view.smoothScrollToPosition(pos)
    end
  end
})

-- 设置适配器
binding.carousel_recycler_view.setAdapter(adapter_recycler_view)

-- 添加轮播吸附效果
local snapHelper = CarouselSnapHelper()
snapHelper.attachToRecyclerView(binding.carousel_recycler_view)