[
  {
    "path": ".github/scripts/check_openlist.sh",
    "content": "#!/bin/bash\n\nGIT_REPO=\"https://github.com/OpenListTeam/OpenList.git\"\n\nfunction compare_versions() {\n  local v1=\"${1#v}\"\n  local v2=\"${2#v}\"\n  local max_len=0\n\n  IFS='.' read -r -a v1_parts <<< \"$v1\"\n  IFS='.' read -r -a v2_parts <<< \"$v2\"\n\n  if [ \"${#v1_parts[@]}\" -gt \"${#v2_parts[@]}\" ]; then\n    max_len=\"${#v1_parts[@]}\"\n  else\n    max_len=\"${#v2_parts[@]}\"\n  fi\n\n  for ((i=0; i<max_len; i++)); do\n    local p1=\"${v1_parts[i]:-0}\"\n    local p2=\"${v2_parts[i]:-0}\"\n\n    p1=$(echo \"$p1\" | grep -oE '^[0-9]+' || echo 0)\n    p2=$(echo \"$p2\" | grep -oE '^[0-9]+' || echo 0)\n\n    if ((10#$p1 > 10#$p2)); then\n      echo 1\n      return\n    fi\n\n    if ((10#$p1 < 10#$p2)); then\n      echo -1\n      return\n    fi\n  done\n\n  echo 0\n}\n\nfunction get_latest_version() {\n    echo $(git -c 'versionsort.suffix=-' ls-remote --exit-code --refs --sort='version:refname' --tags $GIT_REPO | tail --lines=1 | cut --delimiter='/' --fields=3)\n}\n\nLATEST_VER=\"\"\nfor index in $(seq 5)\ndo\n    echo \"Try to get latest version, index=$index\"\n    LATEST_VER=$(get_latest_version)\n    if [ -z \"$LATEST_VER\" ]; then\n      if [ \"$index\" -ge 5 ]; then\n        echo \"Failed to get latest version, exit\"\n        exit 1\n      fi\n      echo \"Failed to get latest version, sleep 15s and retry\"\n      sleep 15\n    else\n      break\n    fi\n\ndone\n\necho \"Latest OpenList version $LATEST_VER\"\n\necho \"openlist_version=$LATEST_VER\" >> \"$GITHUB_ENV\"\n# VERSION_FILE=\"$GITHUB_WORKSPACE/openlist_version.txt\"\n\nVER=$(cat \"$VERSION_FILE\")\n\nif [ -z \"$VER\" ]; then\n  VER=\"v3.25.1\"\n  echo \"No version file, use default version ${VER}\"\nfi\n\necho \"Current OpenList version: $VER\"\n\nCOMPARE_RESULT=$(compare_versions \"$VER\" \"$LATEST_VER\")\n\nif [ \"$COMPARE_RESULT\" -ge 0 ]; then\n    echo \"Current >= Latest\"\n    echo \"openlist_update=0\" >> \"$GITHUB_ENV\"\nelse\n    echo \"Current < Latest\"\n    echo \"openlist_update=1\" >> \"$GITHUB_ENV\"\nfi\n"
  },
  {
    "path": ".github/scripts/lzy_web.py",
    "content": "import requests, os, datetime, sys\n\n# Cookie 中 phpdisk_info 的值\ncookie_phpdisk_info = os.environ.get('phpdisk_info')\n# Cookie 中 ylogin 的值\ncookie_ylogin = os.environ.get('ylogin')\n\n# 请求头\nheaders = {\n    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Safari/537.36 Edg/89.0.774.45',\n    'Accept-Language': 'zh-CN,zh;q=0.9',\n    'Referer': 'https://pc.woozooo.com/account.php?action=login'\n}\n\n# 小饼干\ncookie = {\n    'ylogin': cookie_ylogin,\n    'phpdisk_info': cookie_phpdisk_info\n}\n\n\n# 日志打印\ndef log(msg):\n    utc_time = datetime.datetime.utcnow()\n    china_time = utc_time + datetime.timedelta(hours=8)\n    print(f\"[{china_time.strftime('%Y.%m.%d %H:%M:%S')}] {msg}\")\n\n\n# 检查是否已登录\ndef login_by_cookie():\n    url_account = \"https://pc.woozooo.com/account.php\"\n    if cookie['phpdisk_info'] is None:\n        log('ERROR: 请指定 Cookie 中 phpdisk_info 的值！')\n        return False\n    if cookie['ylogin'] is None:\n        log('ERROR: 请指定 Cookie 中 ylogin 的值！')\n        return False\n    res = requests.get(url_account, headers=headers, cookies=cookie, verify=True)\n    if '网盘用户登录' in res.text:\n        log('ERROR: 登录失败,请更新Cookie')\n        return False\n    else:\n        log('登录成功')\n        return True\n\n\n# 上传文件\ndef upload_file(file_dir, folder_id):\n    file_name = os.path.basename(file_dir)\n    url_upload = \"https://up.woozooo.com/fileup.php\"\n    headers['Referer'] = f'https://up.woozooo.com/mydisk.php?item=files&action=index&u={cookie_ylogin}'\n    post_data = {\n        \"task\": \"1\",\n        \"folder_id\": folder_id,\n        \"id\": \"WU_FILE_0\",\n        \"name\": file_name,\n    }\n    files = {'upload_file': (file_name, open(file_dir, \"rb\"), 'application/octet-stream')}\n    res = requests.post(url_upload, data=post_data, files=files, headers=headers, cookies=cookie, timeout=120).json()\n    log(f\"{file_dir} -> {res['info']}\")\n    return res['zt'] == 1\n\n\n# 上传文件夹内的文件\ndef upload_folder(folder_dir, folder_id):\n    file_list = sorted(os.listdir(folder_dir), reverse=True)\n    for file in file_list:\n        path = os.path.join(folder_dir, file)\n        if os.path.isfile(path):\n            upload_file(path, folder_id)\n        else:\n            upload_folder(path, folder_id)\n\n\n# 上传\ndef upload(dir, folder_id):\n    if dir is None:\n        log('ERROR: 请指定上传的文件路径')\n        return\n    if folder_id is None:\n        log('ERROR: 请指定蓝奏云的文件夹id')\n        return\n    if os.path.isfile(dir):\n        upload_file(dir, str(folder_id))\n    else:\n        upload_folder(dir, str(folder_id))\n\n\nif __name__ == '__main__':\n    argv = sys.argv[1:]\n    if len(argv) != 2:\n        log('ERROR: 参数错误,请以这种格式重新尝试\\npython lzy_web.py 需上传的路径 蓝奏云文件夹id')\n    # 需上传的路径\n    upload_path = argv[0]\n    # 蓝奏云文件夹id\n    lzy_folder_id = argv[1]\n    if login_by_cookie():\n        upload(upload_path, lzy_folder_id)"
  },
  {
    "path": ".github/scripts/update_pubspec_version.sh",
    "content": "#!/bin/bash\n\nVERSION_FILE=\"$GITHUB_WORKSPACE/openlist_version\"\nPUBSPEC_FILE=\"$GITHUB_WORKSPACE/pubspec.yaml\"\n\nif [ ! -f \"$VERSION_FILE\" ]; then\n    echo \"Error: openlist_version file not found\"\n    exit 1\nfi\n\nif [ ! -f \"$PUBSPEC_FILE\" ]; then\n    echo \"Error: pubspec.yaml file not found\"\n    exit 1\nfi\n\nOPENLIST_VERSION=$(cat \"$VERSION_FILE\")\n\nBASE_VERSION=${OPENLIST_VERSION#v}\n\necho \"Updating pubspec.yaml version to: $BASE_VERSION\"\n\nsed -i \"s/^version: [0-9]\\+\\.[0-9]\\+\\.[0-9]\\++[0-9]\\+.*/version: ${BASE_VERSION}+1/\" \"$PUBSPEC_FILE\"\n\necho \"pubspec.yaml version updated successfully\""
  },
  {
    "path": ".github/workflows/build.yaml",
    "content": "name: Build\n\non:\n  push:\n    branches:\n      - \"main\"\n    paths-ignore:\n      - \"*.md\"\n      - \"*.sh\"\n      - \"release.yaml\"\n  #      - \"sync_frp.yaml\"\n  pull_request:\n    branches: [\"main\"]\n\n  workflow_dispatch:\n\njobs:\n  version:\n    name: Get OpenList Version Information\n    runs-on: ubuntu-latest\n    outputs:\n      version_name: ${{ steps.generate.outputs.version_name }}\n      openlist_version: ${{ steps.openlist_version.outputs.openlist_version }}\n      openlist_git_commit: ${{ steps.openlist_version.outputs.openlist_git_commit }}\n      openlist_web_version: ${{ steps.openlist_version.outputs.openlist_web_version }}\n      openlist_built_at: ${{ steps.openlist_version.outputs.openlist_built_at }}\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          fetch-depth: 0\n\n      - name: Generate Version Name\n        id: generate\n        run: |\n          # Generate unified version with timestamp for entire workflow\n          BASE_VERSION=$(cat openlist_version)\n          TIMESTAMP=$(date +%y%m%d%H)\n          VERSION_NAME=\"${BASE_VERSION}.${TIMESTAMP}\"\n          echo \"version_name=${VERSION_NAME}\" >> $GITHUB_OUTPUT\n\n      - name: Download OpenList Source Code\n        run: |\n          cd $GITHUB_WORKSPACE/openlist-lib/scripts\n          chmod +x *.sh\n          ./init_openlist.sh\n\n      - name: Extract OpenList Version Info\n        id: openlist_version\n        run: |\n          # After init_openlist.sh, OpenList backend source is in openlist-lib/ with .git directory removed\n          # We need to get version info from the latest remote tag that was cloned\n          cd $GITHUB_WORKSPACE/openlist-lib/scripts\n          \n          # Re-fetch the tag information from remote (since .git was removed by init_openlist.sh)\n          GIT_REPO=\"https://github.com/OpenListTeam/OpenList.git\"\n          OPENLIST_VERSION=$(git -c 'versionsort.suffix=-' ls-remote --exit-code --refs --sort='version:refname' --tags $GIT_REPO | tail -n 1 | cut -d'/' -f3)\n          \n          # Get the commit hash for this tag from remote\n          OPENLIST_GIT_COMMIT=$(git ls-remote $GIT_REPO \"refs/tags/${OPENLIST_VERSION}\" | cut -f1 | cut -c1-7)\n          \n          # Get frontend version from OpenList-Frontend latest release\n          OPENLIST_WEB_VERSION=$(curl -fsSL --max-time 10 \"https://api.github.com/repos/OpenListTeam/OpenList-Frontend/releases/latest\" | grep \"tag_name\" | head -n 1 | awk -F \":\" '{print $2}' | sed 's/\"//g;s/,//g;s/ //g' || echo \"rolling\")\n          \n          # Build timestamp\n          OPENLIST_BUILT_AT=$(date +'%F %T %z')\n          \n          echo \"openlist_version=${OPENLIST_VERSION}\" >> $GITHUB_OUTPUT\n          echo \"openlist_git_commit=${OPENLIST_GIT_COMMIT}\" >> $GITHUB_OUTPUT\n          echo \"openlist_web_version=${OPENLIST_WEB_VERSION}\" >> $GITHUB_OUTPUT\n          echo \"openlist_built_at=${OPENLIST_BUILT_AT}\" >> $GITHUB_OUTPUT\n\n  android:\n    name: Build OpenList Android APK (Dev)\n    needs: [version]\n    runs-on: ubuntu-latest\n    env:\n      release_output: \"${{ github.workspace }}/build/app/outputs/apk/release\"\n      debug_output: \"${{ github.workspace }}/build/app/outputs/flutter-apk\"\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          fetch-depth: 0\n\n      - name: Download OpenList Android AAR from latest build\n        uses: dawidd6/action-download-artifact@v6\n        with:\n          workflow: sync_openlist.yaml\n          name: openlist-android-aar\n          path: ${{ github.workspace }}/android/app/libs/\n          check_artifacts: true\n          search_artifacts: true\n\n      - uses: actions/setup-java@v4\n        with:\n          distribution: temurin\n          java-version: 17\n\n      - name: Setup Gradle\n        uses: gradle/gradle-build-action@v3\n\n      - name: Init Signature (Release only)\n        if: github.event_name != 'pull_request'\n        run: |\n          touch local.properties\n          cd android\n          echo ALIAS_NAME='${{ secrets.ALIAS_NAME }}' >> local.properties\n          echo ALIAS_PASSWORD='${{ secrets.ALIAS_PASSWORD }}' >> local.properties\n          echo KEY_PASSWORD='${{ secrets.KEY_PASSWORD }}' >> local.properties\n          echo KEY_PATH='./key.jks' >> local.properties\n          # 从Secrets读取无换行符Base64解码, 然后保存到到app/key.jks\n          echo ${{ secrets.KEY_STORE }} | base64 --decode > $GITHUB_WORKSPACE/android/app/key.jks\n\n      - uses: subosito/flutter-action@v2\n        with:\n          flutter-version: '3.32.7'\n\n      - name: Generate iOS Build Number\n        run: |\n          IOS_BUILD_NUMBER=$(date +%y%m%d%H)\n          echo \"ios_build_number=$IOS_BUILD_NUMBER\" >> $GITHUB_ENV\n      \n      - name: Build Release APK\n        if: github.event_name != 'pull_request'\n        env:\n          BUILD_VERSION_NAME: ${{ needs.version.outputs.version_name }}\n        run: flutter build apk --split-per-abi --release --target-platform android-arm,android-arm64,android-x64\n      \n      - name: Build Debug APK\n        if: github.event_name == 'pull_request'\n        env:\n          BUILD_VERSION_NAME: ${{ needs.version.outputs.version_name }}\n        run: flutter build apk --split-per-abi --debug --target-platform android-arm,android-arm64,android-x64\n\n      - name: Upload missing_rules.txt\n        if: failure()\n        uses: actions/upload-artifact@v4\n        with:\n          name: \"missing_rules\"\n          path: \"${{ github.workspace }}/build/app/outputs/mapping/release/missing_rules.txt\"\n\n      - name: Set Build Paths and Version\n        run: |\n          if [ \"${{ github.event_name }}\" == \"pull_request\" ]; then\n            OUTPUT_DIR=\"${{ env.debug_output }}\"\n            BUILD_TYPE=\"debug\"\n            echo \"ver_name=${{ needs.version.outputs.version_name }}-debug\" >> $GITHUB_ENV\n          else\n            OUTPUT_DIR=\"${{ env.release_output }}\"\n            BUILD_TYPE=\"release\"\n            echo \"ver_name=${{ needs.version.outputs.version_name }}\" >> $GITHUB_ENV\n          fi\n          \n          echo \"output_path=$OUTPUT_DIR\" >> $GITHUB_ENV\n          echo \"build_type=$BUILD_TYPE\" >> $GITHUB_ENV\n\n      - name: Upload App To Artifact arm64-v8a\n        if: success () || failure ()\n        uses: actions/upload-artifact@v4\n        with:\n          name: \"OpenList-Mobile-${{ env.ver_name }}_arm64-v8a\"\n          path: |\n            ${{ env.output_path }}/*arm64-v8a*.apk\n            ${{ env.output_path }}/*v8a.apk\n\n      - name: Upload App To Artifact arm-v7a\n        if: success () || failure ()\n        uses: actions/upload-artifact@v4\n        with:\n          name: \"OpenList-Mobile-${{ env.ver_name }}_arm-v7a\"\n          path: |\n            ${{ env.output_path }}/*armeabi-v7a*.apk\n            ${{ env.output_path }}/*v7a.apk\n\n      - name: Upload App To Artifact x86_64\n        if: success () || failure ()\n        uses: actions/upload-artifact@v4\n        with:\n          name: \"OpenList-Mobile-${{ env.ver_name }}_x86_64\"\n          path: |\n            ${{ env.output_path }}/*x86_64*.apk\n            ${{ env.output_path }}/*x64*.apk\n\n  ios:\n    name: Build OpenList iOS App (Dev)\n    needs: [version]\n    runs-on: macos-latest\n    env:\n      output: \"${{ github.workspace }}/build/ios/ipa\"\n      testflight_ready: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && secrets.TESTFLIGHT_UPLOAD_ENABLED == 'true' && secrets.APP_STORE_CONNECT_KEY_ID != '' && secrets.APP_STORE_CONNECT_ISSUER_ID != '' && secrets.APP_STORE_CONNECT_KEY_P8 != '' && secrets.IOS_CERT_PFX != '' && secrets.IOS_CERT_PASSWORD != '' && secrets.IOS_PROVISION_PROFILE != '' }}\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          fetch-depth: 0\n\n      - name: Select Xcode 26.3.0\n        run: |\n          XCODE_APP=\"/Applications/Xcode_26.3.0.app\"\n          if [ ! -d \"$XCODE_APP\" ]; then\n            echo \"Xcode_26.3.0 not found at $XCODE_APP\"\n            echo \"Installed Xcode apps:\"\n            ls -1 /Applications | grep '^Xcode' || true\n            exit 1\n          fi\n          sudo xcode-select -s \"$XCODE_APP/Contents/Developer\"\n          xcodebuild -version\n\n      - name: Download OpenList iOS Framework from latest build\n        uses: dawidd6/action-download-artifact@v6\n        with:\n          workflow: sync_openlist.yaml\n          name: openlist-ios-xcframework\n          path: ${{ github.workspace }}/ios/Frameworks/\n          check_artifacts: true\n          search_artifacts: true\n\n      - uses: subosito/flutter-action@v2\n        with:\n          flutter-version: '3.32.7'\n\n      - name: Generate iOS Build Number\n        run: |\n          IOS_BUILD_NUMBER=$(date +%y%m%d%H)\n          echo \"ios_build_number=$IOS_BUILD_NUMBER\" >> $GITHUB_ENV\n\n      - name: Install iOS Signing Assets\n        if: ${{ env.testflight_ready == 'true' }}\n        env:\n          IOS_CERT_PFX: ${{ secrets.IOS_CERT_PFX }}\n          IOS_CERT_PASSWORD: ${{ secrets.IOS_CERT_PASSWORD }}\n          IOS_PROVISION_PROFILE: ${{ secrets.IOS_PROVISION_PROFILE }}\n        run: |\n          KEYCHAIN_PATH=\"${RUNNER_TEMP}/openlist.keychain-db\"\n          KEYCHAIN_PASSWORD=\"temp_keychain_password\"\n\n          security create-keychain -p \"$KEYCHAIN_PASSWORD\" \"$KEYCHAIN_PATH\"\n          security set-keychain-settings -lut 21600 \"$KEYCHAIN_PATH\"\n          security unlock-keychain -p \"$KEYCHAIN_PASSWORD\" \"$KEYCHAIN_PATH\"\n          security list-keychains -d user -s \"$KEYCHAIN_PATH\" login.keychain-db\n          security default-keychain -s \"$KEYCHAIN_PATH\"\n\n          CERT_PATH=\"${RUNNER_TEMP}/ios_signing.pfx\"\n          CERT_PEM_PATH=\"${RUNNER_TEMP}/ios_signing.pem\"\n          CERT_P12_PATH=\"${RUNNER_TEMP}/ios_signing_mac.p12\"\n          echo \"$IOS_CERT_PFX\" | base64 --decode > \"$CERT_PATH\"\n\n          if openssl pkcs12 -in \"$CERT_PATH\" -out \"$CERT_PEM_PATH\" -nodes -legacy -passin pass:\"$IOS_CERT_PASSWORD\"; then\n            openssl pkcs12 -export -in \"$CERT_PEM_PATH\" -out \"$CERT_P12_PATH\" -passout pass:\"$IOS_CERT_PASSWORD\" -legacy\n          else\n            openssl pkcs12 -in \"$CERT_PATH\" -out \"$CERT_PEM_PATH\" -nodes -passin pass:\"$IOS_CERT_PASSWORD\"\n            openssl pkcs12 -export -in \"$CERT_PEM_PATH\" -out \"$CERT_P12_PATH\" -passout pass:\"$IOS_CERT_PASSWORD\"\n          fi\n\n          security import \"$CERT_P12_PATH\" -k \"$KEYCHAIN_PATH\" -P \"$IOS_CERT_PASSWORD\" -A\n          security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k \"$KEYCHAIN_PASSWORD\" \"$KEYCHAIN_PATH\"\n\n          security find-identity -v -p codesigning \"$KEYCHAIN_PATH\"\n          SIGNING_IDENTITY=$(security find-identity -v -p codesigning \"$KEYCHAIN_PATH\" | head -n 1 | sed -E 's/.*\"(.*)\"/\\1/')\n          echo \"signing_identity=$SIGNING_IDENTITY\" >> $GITHUB_ENV\n          echo \"keychain_path=$KEYCHAIN_PATH\" >> $GITHUB_ENV\n\n          PROFILE_PATH=\"${RUNNER_TEMP}/openlist.mobileprovision\"\n          echo \"$IOS_PROVISION_PROFILE\" | base64 --decode > \"$PROFILE_PATH\"\n\n          PROFILE_PLIST=\"${RUNNER_TEMP}/profile.plist\"\n          security cms -D -i \"$PROFILE_PATH\" > \"$PROFILE_PLIST\"\n\n          PROFILE_UUID=$(/usr/libexec/PlistBuddy -c \"Print UUID\" \"$PROFILE_PLIST\")\n          PROFILE_NAME=$(/usr/libexec/PlistBuddy -c \"Print Name\" \"$PROFILE_PLIST\")\n          TEAM_ID=$(/usr/libexec/PlistBuddy -c \"Print TeamIdentifier:0\" \"$PROFILE_PLIST\")\n\n          mkdir -p \"$HOME/Library/MobileDevice/Provisioning Profiles\"\n          cp \"$PROFILE_PATH\" \"$HOME/Library/MobileDevice/Provisioning Profiles/${PROFILE_UUID}.mobileprovision\"\n\n          echo \"profile_name=$PROFILE_NAME\" >> $GITHUB_ENV\n          echo \"team_id=$TEAM_ID\" >> $GITHUB_ENV\n\n      - name: Build iOS App (No CodeSign)\n        if: ${{ env.testflight_ready != 'true' }}\n        env:\n          BUILD_VERSION_NAME: ${{ needs.version.outputs.version_name }}\n        run: |\n          echo \"Building iOS app with CocoaPods...\"\n          flutter build ios --release --no-codesign --build-number \"$ios_build_number\"\n\n      - name: Build iOS App (Signed for TestFlight)\n        if: ${{ env.testflight_ready == 'true' }}\n        env:\n          BUILD_VERSION_NAME: ${{ needs.version.outputs.version_name }}\n        run: |\n          echo \"Building signed iOS IPA for TestFlight...\"\n          BUNDLE_ID=\"${{ secrets.IOS_BUNDLE_ID }}\"\n          if [ -z \"$BUNDLE_ID\" ]; then\n            BUNDLE_ID=\"org.oplist.app\"\n          fi\n\n          flutter build ios --release --no-codesign --build-number \"$ios_build_number\"\n\n          EXPORT_OPTIONS_PATH=\"${RUNNER_TEMP}/exportOptions.plist\"\n          /usr/libexec/PlistBuddy -c \"Clear dict\" \"$EXPORT_OPTIONS_PATH\" || true\n          /usr/libexec/PlistBuddy -c \"Add :method string app-store\" \"$EXPORT_OPTIONS_PATH\"\n          /usr/libexec/PlistBuddy -c \"Add :signingStyle string manual\" \"$EXPORT_OPTIONS_PATH\"\n          /usr/libexec/PlistBuddy -c \"Add :teamID string $team_id\" \"$EXPORT_OPTIONS_PATH\"\n          /usr/libexec/PlistBuddy -c \"Add :signingCertificate string $signing_identity\" \"$EXPORT_OPTIONS_PATH\"\n          /usr/libexec/PlistBuddy -c \"Add :provisioningProfiles dict\" \"$EXPORT_OPTIONS_PATH\"\n          /usr/libexec/PlistBuddy -c \"Add :provisioningProfiles:$BUNDLE_ID string $profile_name\" \"$EXPORT_OPTIONS_PATH\"\n\n          ARCHIVE_PATH=\"${RUNNER_TEMP}/OpenList.xcarchive\"\n          xcodebuild \\\n            -workspace ios/Runner.xcworkspace \\\n            -scheme Runner \\\n            -configuration Release \\\n            -sdk iphoneos \\\n            -archivePath \"$ARCHIVE_PATH\" \\\n            archive \\\n            CODE_SIGNING_ALLOWED=NO \\\n            CODE_SIGNING_REQUIRED=NO\n\n          xcodebuild \\\n            -exportArchive \\\n            -archivePath \"$ARCHIVE_PATH\" \\\n            -exportOptionsPlist \"$EXPORT_OPTIONS_PATH\" \\\n            -exportPath \"${{ env.output }}\" \\\n            OTHER_CODE_SIGN_FLAGS=\"--keychain $keychain_path\"\n\n      - name: Create IPA\n        if: ${{ env.testflight_ready != 'true' }}\n        run: |\n          mkdir -p ${{ env.output }}\n\n          APP_PATH=\"\"\n          for candidate in \\\n            \"build/ios/iphoneos/Runner.app\" \\\n            \"build/ios/Release-iphoneos/Runner.app\" \\\n            \"build/ios/Release-unknown/Runner.app\" \\\n            \"build/ios/archive/Runner.xcarchive/Products/Applications/Runner.app\"\n          do\n            if [ -d \"$candidate\" ]; then\n              APP_PATH=\"$candidate\"\n              break\n            fi\n          done\n\n          if [ -z \"$APP_PATH\" ]; then\n            echo \"Runner.app not found in expected locations.\"\n            echo \"Available build/ios directories for diagnostics:\"\n            find build/ios -maxdepth 5 -type d || true\n            exit 1\n          fi\n\n          TMP_DIR=$(mktemp -d)\n          mkdir -p \"$TMP_DIR/Payload\"\n          cp -R \"$APP_PATH\" \"$TMP_DIR/Payload/\"\n          (\n            cd \"$TMP_DIR\"\n            zip -qry \"${{ env.output }}/OpenList-Mobile.ipa\" Payload/\n          )\n\n      - name: Normalize IPA Name\n        if: ${{ env.testflight_ready == 'true' }}\n        run: |\n          mkdir -p ${{ env.output }}\n          IPA_PATH=$(ls ${{ env.output }}/*.ipa | head -n 1)\n          cp \"$IPA_PATH\" \"${{ env.output }}/OpenList-Mobile.ipa\"\n\n      - name: Install Fastlane\n        if: ${{ env.testflight_ready == 'true' }}\n        run: sudo gem install fastlane -N\n\n      - name: Prepare App Store Connect API Key\n        if: ${{ env.testflight_ready == 'true' }}\n        env:\n          APP_STORE_CONNECT_KEY_ID: ${{ secrets.APP_STORE_CONNECT_KEY_ID }}\n          APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}\n        run: |\n          mkdir -p ${{ runner.temp }}/asc_key\n          echo \"${{ secrets.APP_STORE_CONNECT_KEY_P8 }}\" | base64 --decode > ${{ runner.temp }}/asc_key/AuthKey.p8\n          python3 - <<'PY'\n          import json\n          import os\n\n          key_path = os.path.join(os.environ['RUNNER_TEMP'], 'asc_key', 'AuthKey.p8')\n          with open(key_path, 'r', encoding='utf-8') as f:\n            key_content = f.read().strip()\n\n          data = {\n            'key_id': os.environ.get('APP_STORE_CONNECT_KEY_ID', ''),\n            'issuer_id': os.environ.get('APP_STORE_CONNECT_ISSUER_ID', ''),\n            'key': key_content,\n          }\n\n          out_path = os.path.join(os.environ['RUNNER_TEMP'], 'asc_key', 'api_key.json')\n          with open(out_path, 'w', encoding='utf-8') as f:\n            json.dump(data, f)\n          PY\n\n      - name: Upload to TestFlight\n        if: ${{ env.testflight_ready == 'true' }}\n        run: |\n          fastlane pilot upload \\\n            --api_key_path \"${{ runner.temp }}/asc_key/api_key.json\" \\\n            --ipa \"${{ env.output }}/OpenList-Mobile.ipa\" \\\n            --skip_waiting_for_build_processing true\n\n      - name: Set iOS Version\n        run: |\n          if [ \"${{ github.event_name }}\" == \"pull_request\" ]; then\n            echo \"ver_name=${{ needs.version.outputs.version_name }}-debug\" >> $GITHUB_ENV\n          else\n            echo \"ver_name=${{ needs.version.outputs.version_name }}\" >> $GITHUB_ENV\n          fi\n\n      - name: Upload iOS App To Artifact\n        if: success() || failure()\n        uses: actions/upload-artifact@v4\n        with:\n          name: \"OpenList-Mobile-iOS-${{ env.ver_name }}\"\n          path: \"${{ env.output }}/OpenList-Mobile.ipa\"\n"
  },
  {
    "path": ".github/workflows/build_openlist.yaml",
    "content": "name: Build OpenList Libraries\n\non:\n  workflow_dispatch:\n  workflow_call:\n\npermissions:\n  contents: read\n\njobs:\n  version:\n    name: Get OpenList Version Information\n    runs-on: ubuntu-latest\n    outputs:\n      openlist_version: ${{ steps.openlist_version.outputs.openlist_version }}\n      openlist_git_commit: ${{ steps.openlist_version.outputs.openlist_git_commit }}\n      openlist_web_version: ${{ steps.openlist_version.outputs.openlist_web_version }}\n      openlist_built_at: ${{ steps.openlist_version.outputs.openlist_built_at }}\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          fetch-depth: 0\n\n      - name: Download OpenList Source Code\n        run: |\n          cd $GITHUB_WORKSPACE/openlist-lib/scripts\n          chmod +x *.sh\n          ./init_openlist.sh\n\n      - name: Extract OpenList Version Info\n        id: openlist_version\n        run: |\n          cd $GITHUB_WORKSPACE/openlist-lib/scripts\n          \n          GIT_REPO=\"https://github.com/OpenListTeam/OpenList.git\"\n          OPENLIST_VERSION=$(git -c 'versionsort.suffix=-' ls-remote --exit-code --refs --sort='version:refname' --tags $GIT_REPO | tail -n 1 | cut -d'/' -f3)\n          OPENLIST_GIT_COMMIT=$(git ls-remote $GIT_REPO \"refs/tags/${OPENLIST_VERSION}\" | cut -f1 | cut -c1-7)\n          OPENLIST_WEB_VERSION=$(curl -fsSL --max-time 10 \"https://api.github.com/repos/OpenListTeam/OpenList-Frontend/releases/latest\" | grep \"tag_name\" | head -n 1 | awk -F \":\" '{print $2}' | sed 's/\"//g;s/,//g;s/ //g' || echo \"rolling\")\n          OPENLIST_BUILT_AT=$(date +'%F %T %z')\n          \n          echo \"openlist_version=${OPENLIST_VERSION}\" >> $GITHUB_OUTPUT\n          echo \"openlist_git_commit=${OPENLIST_GIT_COMMIT}\" >> $GITHUB_OUTPUT\n          echo \"openlist_web_version=${OPENLIST_WEB_VERSION}\" >> $GITHUB_OUTPUT\n          echo \"openlist_built_at=${OPENLIST_BUILT_AT}\" >> $GITHUB_OUTPUT\n\n  build_android:\n    name: Build OpenList Android AAR\n    needs: [version]\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          fetch-depth: 0\n\n      - name: Download OpenList Source Code\n        run: |\n          cd $GITHUB_WORKSPACE/openlist-lib/scripts\n          chmod +x *.sh\n          ./init_openlist.sh\n          ./init_web.sh\n\n      - uses: actions/setup-go@v5\n        with:\n          go-version: 1.25.0\n\n      - uses: actions/setup-java@v4\n        with:\n          distribution: temurin\n          java-version: 17\n\n      - uses: nttld/setup-ndk@v1\n        id: setup-ndk\n        with:\n          ndk-version: r25c\n\n      - name: Setup Gradle\n        uses: gradle/gradle-build-action@v3\n\n      - name: Build OpenList Android AAR\n        run: |\n          cd $GITHUB_WORKSPACE/openlist-lib/scripts\n          chmod +x *.sh\n          ./init_gomobile.sh\n          ./gobind.sh\n        env:\n          ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}\n          OPENLIST_VERSION: ${{ needs.version.outputs.openlist_version }}\n          OPENLIST_WEB_VERSION: ${{ needs.version.outputs.openlist_web_version }}\n          OPENLIST_BUILT_AT: ${{ needs.version.outputs.openlist_built_at }}\n          OPENLIST_GIT_COMMIT: ${{ needs.version.outputs.openlist_git_commit }}\n          OPENLIST_GIT_AUTHOR: The OpenList Projects Contributors <noreply@openlist.team>\n\n      - name: Upload OpenList Android AAR\n        uses: actions/upload-artifact@v4\n        with:\n          name: \"openlist-android-aar\"\n          path: \"${{ github.workspace }}/android/app/libs/*.aar\"\n          retention-days: 50\n\n  build_ios:\n    name: Build OpenList iOS Framework\n    needs: [version]\n    runs-on: macos-latest\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          fetch-depth: 0\n\n      - uses: actions/setup-go@v5\n        with:\n          go-version: 1.25.0\n\n      - name: Download OpenList Source Code\n        run: |\n          cd $GITHUB_WORKSPACE/openlist-lib/scripts\n          chmod +x *.sh\n          ./init_openlist.sh\n          ./init_web_ios.sh\n\n      - name: Build OpenList iOS Framework\n        run: |\n          cd $GITHUB_WORKSPACE/openlist-lib/scripts\n          chmod +x *.sh\n          ./init_gomobile.sh\n          ./gobind_ios.sh\n        env:\n          OPENLIST_VERSION: ${{ needs.version.outputs.openlist_version }}\n          OPENLIST_WEB_VERSION: ${{ needs.version.outputs.openlist_web_version }}\n          OPENLIST_BUILT_AT: ${{ needs.version.outputs.openlist_built_at }}\n          OPENLIST_GIT_COMMIT: ${{ needs.version.outputs.openlist_git_commit }}\n          OPENLIST_GIT_AUTHOR: The OpenList Projects Contributors <noreply@openlist.team>\n\n      - name: Upload OpenList iOS Framework\n        uses: actions/upload-artifact@v4\n        with:\n          name: \"openlist-ios-xcframework\"\n          path: \"${{ github.workspace }}/ios/Frameworks/*.xcframework\"\n          retention-days: 50\n"
  },
  {
    "path": ".github/workflows/release.yaml",
    "content": "name: Release\n\non:\n  workflow_dispatch:\n\npermissions:\n  contents: write\n\njobs:\n  version:\n    name: Get OpenList Version Information\n    runs-on: ubuntu-latest\n    outputs:\n      version_name: ${{ steps.generate.outputs.version_name }}\n      openlist_version: ${{ steps.openlist_version.outputs.openlist_version }}\n      openlist_git_commit: ${{ steps.openlist_version.outputs.openlist_git_commit }}\n      openlist_web_version: ${{ steps.openlist_version.outputs.openlist_web_version }}\n      openlist_built_at: ${{ steps.openlist_version.outputs.openlist_built_at }}\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          fetch-depth: 0\n\n      - name: Generate Version Name\n        id: generate\n        run: |\n          # Generate unified version with timestamp for entire workflow\n          BASE_VERSION=$(cat openlist_version)\n          TIMESTAMP=$(date +%y%m%d%H)\n          VERSION_NAME=\"${BASE_VERSION}.${TIMESTAMP}\"\n          echo \"version_name=${VERSION_NAME}\" >> $GITHUB_OUTPUT\n\n      - name: Download OpenList Source Code\n        run: |\n          cd $GITHUB_WORKSPACE/openlist-lib/scripts\n          chmod +x *.sh\n          ./init_openlist.sh\n\n      - name: Extract OpenList Version Info\n        id: openlist_version\n        run: |\n          # After init_openlist.sh, OpenList backend source is in openlist-lib/ with .git directory removed\n          # We need to get version info from the latest remote tag that was cloned\n          cd $GITHUB_WORKSPACE/openlist-lib/scripts\n          \n          # Re-fetch the tag information from remote (since .git was removed by init_openlist.sh)\n          GIT_REPO=\"https://github.com/OpenListTeam/OpenList.git\"\n          OPENLIST_VERSION=$(git -c 'versionsort.suffix=-' ls-remote --exit-code --refs --sort='version:refname' --tags $GIT_REPO | tail -n 1 | cut -d'/' -f3)\n          \n          # Get the commit hash for this tag from remote\n          OPENLIST_GIT_COMMIT=$(git ls-remote $GIT_REPO \"refs/tags/${OPENLIST_VERSION}\" | cut -f1 | cut -c1-7)\n          \n          # Get frontend version from OpenList-Frontend latest release\n          OPENLIST_WEB_VERSION=$(curl -fsSL --max-time 10 \"https://api.github.com/repos/OpenListTeam/OpenList-Frontend/releases/latest\" | grep \"tag_name\" | head -n 1 | awk -F \":\" '{print $2}' | sed 's/\"//g;s/,//g;s/ //g' || echo \"rolling\")\n          \n          # Build timestamp\n          OPENLIST_BUILT_AT=$(date +'%F %T %z')\n          \n          echo \"openlist_version=${OPENLIST_VERSION}\" >> $GITHUB_OUTPUT\n          echo \"openlist_git_commit=${OPENLIST_GIT_COMMIT}\" >> $GITHUB_OUTPUT\n          echo \"openlist_web_version=${OPENLIST_WEB_VERSION}\" >> $GITHUB_OUTPUT\n          echo \"openlist_built_at=${OPENLIST_BUILT_AT}\" >> $GITHUB_OUTPUT\n\n  android:\n    name: Build OpenList Android APK\n    needs: [version]\n    runs-on: ubuntu-latest\n    env:\n      output: \"${{ github.workspace }}/build/app/outputs/apk/release\"\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          fetch-depth: 0\n\n      - name: Download OpenList Android AAR from latest build\n        uses: dawidd6/action-download-artifact@v6\n        with:\n          workflow: sync_openlist.yaml\n          name: openlist-android-aar\n          path: ${{ github.workspace }}/android/app/libs/\n          check_artifacts: true\n          search_artifacts: true\n\n      - uses: actions/setup-java@v4\n        with:\n          distribution: temurin\n          java-version: 17\n\n      - name: Setup Gradle\n        uses: gradle/gradle-build-action@v3\n\n      - name: Init Signature\n        run: |\n          touch local.properties\n          cd android\n          echo ALIAS_NAME='${{ secrets.ALIAS_NAME }}' >> local.properties\n          echo ALIAS_PASSWORD='${{ secrets.ALIAS_PASSWORD }}' >> local.properties\n          echo KEY_PASSWORD='${{ secrets.KEY_PASSWORD }}' >> local.properties\n          echo KEY_PATH='./key.jks' >> local.properties\n          # 从Secrets读取无换行符Base64解码, 然后保存到到app/key.jks\n          echo ${{ secrets.KEY_STORE }} | base64 --decode > $GITHUB_WORKSPACE/android/app/key.jks\n\n      - uses: subosito/flutter-action@v2\n        with:\n          flutter-version: '3.32.7'\n          \n      - name: Build APK with Version\n        env:\n          BUILD_VERSION_NAME: ${{ needs.version.outputs.version_name }}\n        run: flutter build apk --split-per-abi --release\n\n      - name: Upload missing_rules.txt\n        if: failure()\n        uses: actions/upload-artifact@v4\n        with:\n          name: \"missing_rules\"\n          path: \"${{ github.workspace }}/build/app/outputs/mapping/release/missing_rules.txt\"\n\n      - name: Upload Android APKs for Release\n        uses: actions/upload-artifact@v4\n        with:\n          name: \"android-release-files\"\n          path: \"${{ env.output }}/*.apk\"\n\n  # ios:\n  #   name: Build OpenList iOS App\n  #   needs: [version]\n  #   runs-on: macos-latest\n  #   env:\n  #     output: \"${{ github.workspace }}/build/ios/ipa\"\n  #   steps:\n  #      - uses: actions/checkout@v3\n  #        with:\n  #          fetch-depth: 0\n  #\n  #      - name: Download OpenList iOS Framework from latest build\n  #        uses: dawidd6/action-download-artifact@v6\n  #        with:\n  #          workflow: sync_openlist.yaml\n  #          name: openlist-ios-xcframework\n  #          path: ${{ github.workspace }}/ios/Frameworks/\n  #          check_artifacts: true\n  #          search_artifacts: true\n\n  #     - uses: subosito/flutter-action@v2\n  #       with:\n  #         flutter-version: '3.32.7'\n\n  #     - name: Build iOS App\n  #       env:\n  #         BUILD_VERSION_NAME: ${{ needs.version.outputs.version_name }}\n  #       run: |\n  #         flutter build ios --release --no-codesign\n\n  #     - name: Create IPA\n  #       run: |\n  #         mkdir -p ${{ env.output }}\n  #         cd build/ios/iphoneos\n  #         mkdir Payload\n  #         cp -r Runner.app Payload/\n  #         zip -r ${{ env.output }}/OpenList-Mobile.ipa Payload/\n\n  #     - name: Upload iOS IPA for Release\n  #       uses: actions/upload-artifact@v4\n  #       with:\n  #         name: \"ios-release-files\"\n  #         path: \"${{ env.output }}/OpenList-Mobile.ipa\"\n\n  release:\n    name: Create GitHub Release\n    needs: [version, android] # Add ios when iOS build is enabled: [version, android, ios]\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          fetch-depth: 0\n\n      - name: Setup Node\n        uses: actions/setup-node@v4\n        with:\n          node-version: \"22\"\n\n      - name: Prepare changelog generation\n        id: prepare_changelog\n        run: |\n          git tag -d rolling 2>/dev/null || true\n          PRE_RELEASE_TAGS=$(git tag -l | grep -E \"(-|\\+)\" || true)\n          if [ -n \"$PRE_RELEASE_TAGS\" ]; then\n            echo \"$PRE_RELEASE_TAGS\" | xargs -r git tag -d\n          fi\n\n      - name: Generate changelog\n        id: generate_changelog\n        run: |\n          npx changelogithub --output ${{ github.workspace }}/GENERATED_CHANGELOG.txt || echo \"\" > ${{ github.workspace }}/GENERATED_CHANGELOG.txt\n\n      - name: Create final changelog\n        id: create_changelog\n        run: |\n          echo \"[Auto Sync OpenList] ${{ needs.version.outputs.openlist_version }}\" > ${{ github.workspace }}/FINAL_CHANGELOG.txt\n          echo \"\" >> ${{ github.workspace }}/FINAL_CHANGELOG.txt\n          echo \"**OpenList Backend:** ${{ needs.version.outputs.openlist_version }} (${{ needs.version.outputs.openlist_git_commit }})\" >> ${{ github.workspace }}/FINAL_CHANGELOG.txt\n          echo \"**OpenList Frontend:** ${{ needs.version.outputs.openlist_web_version }}\" >> ${{ github.workspace }}/FINAL_CHANGELOG.txt\n          echo \"**Built at:** ${{ needs.version.outputs.openlist_built_at }}\" >> ${{ github.workspace }}/FINAL_CHANGELOG.txt\n          echo \"\" >> ${{ github.workspace }}/FINAL_CHANGELOG.txt\n          echo \"---\" >> ${{ github.workspace }}/FINAL_CHANGELOG.txt\n          echo \"\" >> ${{ github.workspace }}/FINAL_CHANGELOG.txt\n\n          if [ -s \"${{ github.workspace }}/GENERATED_CHANGELOG.txt\" ]; then\n            cat ${{ github.workspace }}/GENERATED_CHANGELOG.txt >> ${{ github.workspace }}/FINAL_CHANGELOG.txt\n          else\n            echo \"No changes in this release.\" >> ${{ github.workspace }}/FINAL_CHANGELOG.txt\n          fi\n\n      - name: Download Android artifacts\n        uses: actions/download-artifact@v4\n        with:\n          name: android-release-files\n          path: release_files/\n\n      # - name: Download iOS artifacts\n      #   uses: actions/download-artifact@v4\n      #   with:\n      #     name: ios-release-files\n      #     path: release_files/\n\n      - uses: softprops/action-gh-release@v1\n        with:\n          name: ${{ needs.version.outputs.version_name }}\n          tag_name: ${{ needs.version.outputs.version_name }}\n          body_path: ${{ github.workspace }}/FINAL_CHANGELOG.txt\n          draft: false\n          prerelease: false\n          files: release_files/*\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/sync_openlist.yaml",
    "content": "name: Check Updates\n\non:\n  schedule:\n    - cron: \"0 5,17 * * *\" # 每日5点和17点执行\n  workflow_dispatch:\n    inputs:\n      force_build:\n        description: 'Force build OpenList libraries'\n        required: false\n        type: boolean\n        default: false\n  push:\n    branches:\n      - \"master\"\n    paths:\n      - \"sync_openlist.yaml\"  \n\npermissions:\n  contents: write\n  actions: write\n\njobs:\n  check_and_update:\n    name: Check for version updates\n    runs-on: ubuntu-latest\n    outputs:\n      openlist_update: ${{ steps.set_output.outputs.openlist_update }}\n      openlist_version: ${{ steps.set_output.outputs.openlist_version }}\n    env:\n      VERSION_FILE: ${{ github.workspace }}/openlist_version\n    steps:\n      - uses: actions/checkout@v3\n      \n      - name: Check OpenList Version\n        run: |\n          cd $GITHUB_WORKSPACE/.github/scripts\n          chmod +x ./*.sh\n\n          touch ${{ env.VERSION_FILE }}\n          ./check_openlist.sh\n\n      - name: Set Output Variables\n        id: set_output\n        run: |\n          echo \"openlist_version=${{ env.openlist_version }}\" >> $GITHUB_OUTPUT\n          echo \"openlist_update=${{ env.openlist_update }}\" >> $GITHUB_OUTPUT\n\n      - name: Import GPG key\n        if: env.openlist_update == '1'\n        uses: crazy-max/ghaction-import-gpg@v6\n        with:\n          gpg_private_key: ${{ secrets.BOT_GPG_PRIVATE_KEY }}\n          passphrase: ${{ secrets.BOT_GPG_PASSPHRASE }}\n          git_user_signingkey: true\n          git_commit_gpgsign: true\n          git_tag_gpgsign: true\n\n      - name: Update Version Files\n        if: env.openlist_update == '1'\n        run: |\n          echo -e \"${{ env.openlist_version }}\" > ${{ env.VERSION_FILE }}\n          \n          chmod +x $GITHUB_WORKSPACE/.github/scripts/update_pubspec_version.sh\n          $GITHUB_WORKSPACE/.github/scripts/update_pubspec_version.sh\n\n          git config user.name \"${{ secrets.BOT_USERNAME }}\"\n          git config user.email \"${{ secrets.BOT_USEREMAIL }}\"\n          git add .\n          git commit -m \"[bot] Update openlist to ${{ env.openlist_version }}\"\n          git push\n\n  build_openlist_libraries:\n    name: Build OpenList Libraries\n    needs: [check_and_update]\n    if: needs.check_and_update.outputs.openlist_update == '1' || inputs.force_build == true\n    uses: ./.github/workflows/build_openlist.yaml\n\n  trigger_release:\n    name: Trigger Release Workflow\n    needs: [check_and_update, build_openlist_libraries]\n    if: needs.check_and_update.outputs.openlist_update == '1' && ( success() || failure() )\n    runs-on: ubuntu-latest\n    steps:\n      - name: Trigger Release Workflow\n        run: |\n          gh workflow run release.yaml -R ${{ github.repository }}\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}  \n"
  },
  {
    "path": ".gitignore",
    "content": "# Miscellaneous\n*.class\n*.log\n*.pyc\n*.swp\n.DS_Store\n.atom/\n.buildlog/\n.history\n.svn/\nmigrate_working_dir/\ndocs/\n.github/prompts/\nCHANGELOG.md\n\n# IntelliJ related\n*.iml\n*.ipr\n*.iws\n.idea/\n\n# The .vscode folder contains launch configuration and tasks you configure in\n# VS Code which you may wish to be included in version control, so this line\n# is commented out by default.\n#.vscode/\n\n# Flutter/Dart/Pub related\n**/doc/api/\n**/ios/Flutter/.last_build_id\n.dart_tool/\n.flutter-plugins\n.flutter-plugins-dependencies\n.pub-cache/\n.pub/\n/build/\n\n# Symbolication related\napp.*.symbols\n\n# Obfuscation related\napp.*.map.json\n\n# Android Studio will place build artifacts here\n/android/app/debug\n/android/app/profile\n/android/app/release\n\nlocal.properties\n\n*.aar\n*.exe\n*.tgz\n*.jar\n*.zip\n*.so\n\nandroid/app/.cxx\nandroid/app/build"
  },
  {
    "path": ".metadata",
    "content": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrades etc.\n#\n# This file should be version controlled and should not be manually edited.\n\nversion:\n  revision: \"fcf2c11572af6f390246c056bc905eca609533a0\"\n  channel: \"stable\"\n\nproject_type: app\n\n# Tracks metadata for the flutter migrate command\nmigration:\n  platforms:\n    - platform: root\n      create_revision: fcf2c11572af6f390246c056bc905eca609533a0\n      base_revision: fcf2c11572af6f390246c056bc905eca609533a0\n    - platform: ios\n      create_revision: fcf2c11572af6f390246c056bc905eca609533a0\n      base_revision: fcf2c11572af6f390246c056bc905eca609533a0\n\n  # User provided section\n\n  # List of Local paths (relative to this file) that should be\n  # ignored by the migrate tool.\n  #\n  # Files that are not part of the templates will be ignored by default.\n  unmanaged_files:\n    - 'lib/main.dart'\n    - 'ios/Runner.xcodeproj/project.pbxproj'\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU AFFERO GENERAL PUBLIC LICENSE\n                       Version 3, 19 November 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU Affero General Public License is a free, copyleft license for\nsoftware and other kinds of works, specifically designed to ensure\ncooperation with the community in the case of network server software.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nour General Public Licenses are intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  Developers that use our General Public Licenses protect your rights\nwith two steps: (1) assert copyright on the software, and (2) offer\nyou this License which gives you legal permission to copy, distribute\nand/or modify the software.\n\n  A secondary benefit of defending all users' freedom is that\nimprovements made in alternate versions of the program, if they\nreceive widespread use, become available for other developers to\nincorporate.  Many developers of free software are heartened and\nencouraged by the resulting cooperation.  However, in the case of\nsoftware used on network servers, this result may fail to come about.\nThe GNU General Public License permits making a modified version and\nletting the public access it on a server without ever releasing its\nsource code to the public.\n\n  The GNU Affero General Public License is designed specifically to\nensure that, in such cases, the modified source code becomes available\nto the community.  It requires the operator of a network server to\nprovide the source code of the modified version running there to the\nusers of that server.  Therefore, public use of a modified version, on\na publicly accessible server, gives the public access to the source\ncode of the modified version.\n\n  An older license, called the Affero General Public License and\npublished by Affero, was designed to accomplish similar goals.  This is\na different license, not a version of the Affero GPL, but Affero has\nreleased a new version of the Affero GPL which permits relicensing under\nthis license.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU Affero General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Remote Network Interaction; Use with the GNU General Public License.\n\n  Notwithstanding any other provision of this License, if you modify the\nProgram, your modified version must prominently offer all users\ninteracting with it remotely through a computer network (if your version\nsupports such interaction) an opportunity to receive the Corresponding\nSource of your version by providing access to the Corresponding Source\nfrom a network server at no charge, through some standard or customary\nmeans of facilitating copying of software.  This Corresponding Source\nshall include the Corresponding Source for any work covered by version 3\nof the GNU General Public License that is incorporated pursuant to the\nfollowing paragraph.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the work with which it is combined will remain governed by version\n3 of the GNU General Public License.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU Affero General Public License from time to time.  Such new versions\nwill be similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU Affero General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU Affero General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU Affero General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published\n    by the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If your software can interact with users remotely through a computer\nnetwork, you should also make sure that it provides a way for users to\nget its source.  For example, if your program is a web application, its\ninterface could display a \"Source\" link that leads users to an archive\nof the code.  There are many ways you could offer source, and different\nsolutions will be better for different programs; see section 13 for the\nspecific requirements.\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU AGPL, see\n<https://www.gnu.org/licenses/>.\n"
  },
  {
    "path": "README.md",
    "content": "\n\n<div align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/OpenListTeam/Logo/main/logo.svg\" height=\"100px\" alt=\"OpenList Logo\">\n  <h1>OpenList-Mobile</h1>\n</div>\n\n\n<div align=\"center\">\n\n[![Release](https://github.com/OpenListTeam/OpenList-Mobile/actions/workflows/release.yaml/badge.svg)](https://github.com/OpenListTeam/OpenList-Mobile/actions/workflows/release.yaml)\n[![Build](https://github.com/OpenListTeam/OpenList-Mobile/actions/workflows/build.yaml/badge.svg)](https://github.com/OpenListTeam/OpenList-Mobile/actions/workflows/build.yaml)\n[![Sync OpenList](https://github.com/OpenListTeam/OpenList-Mobile/actions/workflows/sync_openlist.yaml/badge.svg)](https://github.com/OpenListTeam/OpenList-Mobile/actions/workflows/sync_openlist.yaml)\n[![License](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](LICENSE)\n[![Flutter](https://img.shields.io/badge/Flutter-3.32.7-blue.svg)](https://flutter.dev/)\n[![OpenList](https://img.shields.io/github/v/release/OpenListTeam/OpenList?label=OpenList)](https://github.com/OpenListTeam/OpenList)\n\n</div>\n\n\n<div align=\"center\">\n  <a href=\"README.md\">🇨🇳 中文</a> | <a href=\"README_EN.md\">🇺🇸 English</a>\n</div>\n\n\n**OpenList-Mobile** 是一个基于 [OpenList](https://github.com/OpenListTeam/OpenList) 的移动端文件服务器应用，使用 Flutter 框架开发。支持局域网文件共享、远程访问和在线管理。\n\n### 下载安装\n\n#### 稳定版本\n- [📱 发布版](https://github.com/OpenListTeam/OpenList-Mobile/releases/latest) - 推荐使用\n\n#### 开发版本\n- [🔧 构建版](https://github.com/OpenListTeam/OpenList-Mobile/actions/workflows/build.yaml) - 最新功能\n\n> **自动更新**：[GitHub Actions](https://github.com/OpenListTeam/OpenList-Mobile/actions/workflows/sync_openlist.yaml) 每日早晚五点自动检查最新的 [OpenList](https://github.com/OpenListTeam/OpenList/releases) 版本并构建发布，确保始终使用最新版本。\n\n\n### 支持平台\n- Android\n- iOS (实验性，仍在开发)\n"
  },
  {
    "path": "README_EN.md",
    "content": "<div align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/OpenListTeam/Logo/main/logo.svg\" height=\"100px\" alt=\"OpenList Logo\">\n  <h1>OpenList-Mobile</h1>\n</div>\n\n\n<div align=\"center\">\n\n[![Release](https://github.com/OpenListTeam/OpenList-Mobile/actions/workflows/release.yaml/badge.svg)](https://github.com/OpenListTeam/OpenList-Mobile/actions/workflows/release.yaml)\n[![Build](https://github.com/OpenListTeam/OpenList-Mobile/actions/workflows/build.yaml/badge.svg)](https://github.com/OpenListTeam/OpenList-Mobile/actions/workflows/build.yaml)\n[![Sync OpenList](https://github.com/OpenListTeam/OpenList-Mobile/actions/workflows/sync_openlist.yaml/badge.svg)](https://github.com/OpenListTeam/OpenList-Mobile/actions/workflows/sync_openlist.yaml)\n[![License](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](LICENSE)\n[![Flutter](https://img.shields.io/badge/Flutter-3.32.7-blue.svg)](https://flutter.dev/)\n[![OpenList](https://img.shields.io/github/v/release/OpenListTeam/OpenList?label=OpenList)](https://github.com/OpenListTeam/OpenList)\n\n</div>\n\n<div align=\"center\">\n  <a href=\"README.md\">🇨🇳 中文</a> | <a href=\"README_EN.md\">🇺🇸 English</a>\n</div>\n\n\n**OpenList-Mobile** is a mobile file server application based on [OpenList](https://github.com/OpenListTeam/OpenList), built with Flutter framework. Turn your phone into a powerful file server with LAN file sharing, remote access, and online management capabilities.\n\n### Download & Installation\n\n#### Stable Release\n- [📱 Latest Release](https://github.com/OpenListTeam/OpenList-Mobile/releases/latest) - Recommended\n\n#### Development Build\n- [🔧 Development Build](https://github.com/OpenListTeam/OpenList-Mobile/actions/workflows/build.yaml) - Latest Features\n\n> **Auto-Update**: [GitHub Actions](https://github.com/OpenListTeam/OpenList-Mobile/actions/workflows/sync_openlist.yaml) automatically checks for the latest [OpenList](https://github.com/OpenListTeam/OpenList/releases) version twice daily (5 AM & 5 PM) and builds releases, ensuring always have access to the latest version.\n\n### Supported Platforms\n- Android\n- iOS (Experimental, still in development)"
  },
  {
    "path": "analysis_options.yaml",
    "content": "# This file configures the analyzer, which statically analyzes Dart code to\n# check for errors, warnings, and lints.\n#\n# The issues identified by the analyzer are surfaced in the UI of Dart-enabled\n# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be\n# invoked from the command line by running `flutter analyze`.\n\n# The following line activates a set of recommended lints for Flutter apps,\n# packages, and plugins designed to encourage good coding practices.\ninclude: package:flutter_lints/flutter.yaml\n\nlinter:\n  # The lint rules applied to this project can be customized in the\n  # section below to disable rules from the `package:flutter_lints/flutter.yaml`\n  # included above or to enable additional rules. A list of all available lints\n  # and their documentation is published at https://dart.dev/lints.\n  #\n  # Instead of disabling a lint rule for the entire project in the\n  # section below, it can also be suppressed for a single line of code\n  # or a specific dart file by using the `// ignore: name_of_lint` and\n  # `// ignore_for_file: name_of_lint` syntax on the line or in the file\n  # producing the lint.\n  rules:\n    # avoid_print: false  # Uncomment to disable the `avoid_print` rule\n    # prefer_single_quotes: true  # Uncomment to enable the `prefer_single_quotes` rule\n\n# Additional information about this file can be found at\n# https://dart.dev/guides/language/analysis-options\n"
  },
  {
    "path": "android/.gitignore",
    "content": "gradle-wrapper.jar\n/.gradle\n/captures/\n/gradlew\n/gradlew.bat\n/local.properties\nGeneratedPluginRegistrant.java\n\n# Remember to never publicly share your keystore.\n# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app\nkey.properties\n**/*.keystore\n**/*.jks\n\n*.aar\n*.exe\n*.tgz\n*.jar\n*.zip\n*.so\n\n"
  },
  {
    "path": "android/app/build.gradle",
    "content": "plugins {\n    id \"com.android.application\"\n    id \"kotlin-android\"\n    id \"kotlin-parcelize\"\n    id \"kotlin-kapt\"\n    id \"dev.flutter.flutter-gradle-plugin\"\n    id \"kotlinx-serialization\"\n}\n\ndef pro = new Properties()\ndef localPropertiesFile = rootProject.file('local.properties')\nif (localPropertiesFile.exists()) {\n    localPropertiesFile.withReader('UTF-8') { reader ->\n        pro.load(reader)\n    }\n}\n\nstatic def releaseTime() {\n    return new Date().format(\"yyMMddHH\", TimeZone.getTimeZone(\"GMT+8\"))\n}\n\ndef openlistVersionFile = rootProject.file(\"../openlist_version\")\ndef openlistVersion = openlistVersionFile.exists() ? openlistVersionFile.readLines()[0] : \"1.0.0\"\n\n// Check if version is provided via environment variable (from CI/CD)\ndef providedVersion = System.getenv('BUILD_VERSION_NAME')\ndef baseVersion = openlistVersion.startsWith('v') ? openlistVersion.substring(1) : openlistVersion\n\n// Use provided version if available, otherwise generate with timestamp\ndef version = providedVersion ?: (baseVersion + \".\" + releaseTime())\ndef gitCommits = Integer.parseInt('git rev-list HEAD --count'.execute().text.trim())\n\nandroid {\n    namespace \"com.openlist.mobile\"\n    compileSdk = 35\n    ndkVersion = \"27.0.12077973\"\n\n    signingConfigs {\n        release {\n            if (pro[\"KEY_PATH\"] != null && pro[\"KEY_PASSWORD\"] != null && \n                pro[\"ALIAS_NAME\"] != null && pro[\"ALIAS_PASSWORD\"] != null) {\n                storeFile file(pro[\"KEY_PATH\"])\n                storePassword pro[\"KEY_PASSWORD\"]\n                keyAlias pro[\"ALIAS_NAME\"]\n                keyPassword pro[\"ALIAS_PASSWORD\"]\n            }\n        }\n    }\n\n    defaultConfig {\n        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).\n        applicationId \"com.openlist.mobile\"\n        // You can update the following values to match your application needs.\n        // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.\n        minSdk 21\n        targetSdk flutter.targetSdkVersion\n        versionCode gitCommits\n        versionName version\n\n        \n        buildConfigField(\"String\", \"OPENLIST_VERSION\", \"\\\"${openlistVersion}\\\"\")\n    }\n\n\n    buildFeatures {\n        buildConfig true\n    }\n\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_17\n        targetCompatibility JavaVersion.VERSION_17\n        coreLibraryDesugaringEnabled true\n    }\n\n\n    kotlinOptions {\n        jvmTarget = '17'\n    }\n\n    sourceSets {\n        main {\n            jniLibs.srcDirs = ['libs']\n            java.srcDirs = ['src/main/java', 'src/main/kotlin']\n        }\n    }\n\n    splits {\n        abi {\n            enable true  // 改为始终启用,而不是根据任务名判断\n            reset()\n            include 'armeabi-v7a', 'arm64-v8a', 'x86_64'\n            universalApk false  // 改为false避免生成通用APK\n        }\n    }\n\n//    kotlin {\n//        jvmToolchain  = 17\n//    }\n\n    buildTypes {\n        release {\n            // TODO: Add your own signing config for the release build.\n            // Signing with the debug keys for now, so `flutter run --release` works.\n            if (pro[\"KEY_PATH\"] != null && pro[\"KEY_PASSWORD\"] != null && \n                pro[\"ALIAS_NAME\"] != null && pro[\"ALIAS_PASSWORD\"] != null) {\n                signingConfig signingConfigs.release\n            }\n        }\n\n        debug {\n            ndk {\n                //noinspection ChromeOsAbiSupport\n//                abiFilters \"arm64-v8a\"\n//                abiFilters \"x86\"\n            }\n        }\n    }\n}\n\nandroidComponents {\n    onVariants(selector().all()) { variant ->\n        variant.outputs.forEach { output ->\n            def versionName = output.versionName.orNull ?: \"1.0.0\"\n            def abi = \"\"\n            // 简化ABI过滤器检查逻辑\n            if (output.filters != null && !output.filters.isEmpty()) {\n                for (filter in output.filters) {\n                    if (filter.filterType.name() == \"ABI\") {\n                        abi = \"_${filter.identifier}\"\n                        break\n                    }\n                }\n            }\n            output.outputFileName.set(\"OpenList-Mobile-${versionName}${abi}.apk\")\n        }\n    }\n}\n\n\nflutter {\n    source '../..'\n}\n////获取flutter的sdk路径\n//def flutterRoot = localProperties.getProperty('flutter.sdk')\n//if (flutterRoot == null) {\n//    throw new Exception(\"Flutter SDK not found. Define location with flutter.sdk in the local.properties file.\")\n//}\ndependencies {\n    implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs')\n\n//    compileOnly files(\"$flutterRoot/bin/cache/artifacts/engine/android-arm/flutter.jar\")\n    implementation project(\":utils\")\n    //noinspection GradleDependency\n    implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'\n\n    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'\n\n    implementation 'com.louiscad.splitties:splitties-systemservices:3.0.0'\n\n    implementation 'com.github.cioccarellia:ksprefs:2.4.0'\n\n    implementation(\"org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0\")\n\n    // Core library desugaring for flutter_local_notifications\n    coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'\n\n    // WorkManager for background tasks\n    implementation 'androidx.work:work-runtime-ktx:2.9.0'\n\n    // Room\n//    implementation(\"androidx.room:room-runtime:$room_version\")\n//    implementation(\"androidx.room:room-ktx:$room_version\")\n//    ksp(\"androidx.room:room-compiler:$room_version\")\n//    androidTestImplementation(\"androidx.room:room-testing:$room_version\")\n\n}"
  },
  {
    "path": "android/app/src/debug/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <!-- The INTERNET permission is required for development. Specifically,\n         the Flutter tool needs it to communicate with the running application\n         to allow setting breakpoints, to provide hot reload, etc.\n    -->\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n</manifest>\n"
  },
  {
    "path": "android/app/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\">\n\n    <!-- 前台服务权限 -->\n    <uses-permission android:name=\"android.permission.FOREGROUND_SERVICE\" />\n    <uses-permission android:name=\"android.permission.FOREGROUND_SERVICE_DATA_SYNC\" />\n    \n    <!-- 开机启动权限 -->\n    <uses-permission android:name=\"android.permission.RECEIVE_BOOT_COMPLETED\" />\n    \n    <!-- 查询包权限 -->\n    <uses-permission\n        android:name=\"android.permission.QUERY_ALL_PACKAGES\"\n        tools:ignore=\"QueryAllPackagesPermission\" />\n\n    <!-- 通知权限 -->\n    <uses-permission android:name=\"android.permission.POST_NOTIFICATIONS\" />\n    \n    <!-- 网络权限 -->\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    \n    <!-- 存储权限 -->\n    <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\" />\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />\n    <uses-permission\n        android:name=\"android.permission.READ_INTERNAL_STORAGE\"\n        tools:ignore=\"SystemPermissionTypo\" />\n    <uses-permission\n        android:name=\"android.permission.MANAGE_EXTERNAL_STORAGE\"\n        tools:ignore=\"ScopedStorage\" />\n    \n    <!-- 电池优化和唤醒锁权限 -->\n    <uses-permission android:name=\"android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS\" />\n    <uses-permission android:name=\"android.permission.WAKE_LOCK\" />\n    \n    <!-- 安装权限 -->\n    <uses-permission android:name=\"android.permission.REQUEST_INSTALL_PACKAGES\" />\n\n    <application\n        android:name=\"com.openlist.mobile.App\"\n        android:icon=\"@mipmap/ic_launcher\"\n\n        android:label=\"OpenList\"\n        android:networkSecurityConfig=\"@xml/network_security_config\"\n        android:requestLegacyExternalStorage=\"true\"\n        android:roundIcon=\"@mipmap/ic_launcher\"\n        android:supportsRtl=\"true\"\n\n\n        android:usesCleartextTraffic=\"true\"\n        tools:ignore=\"UnusedAttribute\">\n        <activity\n            android:name=\".SwitchServerActivity\"\n            android:exported=\"true\"\n            android:launchMode=\"singleTask\"\n            android:taskAffinity=\"openlist.switch\"\n            android:theme=\"@android:style/Theme.NoDisplay\" />\n        <activity\n            android:name=\".MainActivity\"\n            android:configChanges=\"orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode\"\n            android:exported=\"true\"\n            android:hardwareAccelerated=\"true\"\n            android:launchMode=\"singleTop\"\n            android:theme=\"@style/LaunchTheme\"\n            android:windowSoftInputMode=\"adjustResize\">\n            <!-- Specifies an Android theme to apply to this Activity as soon as\n                 the Android process has started. This theme is visible to the user\n                 while the Flutter UI initializes. After that, this theme continues\n                 to determine the Window background behind the Flutter UI. -->\n            <meta-data\n                android:name=\"io.flutter.embedding.android.NormalTheme\"\n                android:resource=\"@style/NormalTheme\" />\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n        <!-- Don't delete the meta-data below.\n             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->\n        <meta-data\n            android:name=\"flutterEmbedding\"\n            android:value=\"2\" />\n\n        <!-- OpenList background service -->\n        <service\n            android:name=\".OpenListService\"\n            android:exported=\"false\"\n            android:enabled=\"true\"\n            android:foregroundServiceType=\"dataSync\" />\n\n        <!-- Boot receiver -->\n        <receiver\n            android:name=\".BootReceiver\"\n            android:exported=\"true\"\n            android:enabled=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.BOOT_COMPLETED\" />\n                <action android:name=\"android.intent.action.MY_PACKAGE_REPLACED\" />\n            </intent-filter>\n        </receiver>\n\n        <!-- OpenList Tile -->\n        <service\n            android:name=\".OpenListTileService\"\n            android:exported=\"true\"\n            android:icon=\"@mipmap/ic_launcher\"\n            android:label=\"OpenList\"\n            android:permission=\"android.permission.BIND_QUICK_SETTINGS_TILE\">\n            <intent-filter>\n                <action android:name=\"android.service.quicksettings.action.QS_TILE\" />\n            </intent-filter>\n        </service>\n\n    </application>\n</manifest>"
  },
  {
    "path": "android/app/src/main/java/com/openlist/pigeon/GeneratedApi.java",
    "content": "// Autogenerated from Pigeon (v16.0.0), do not edit directly.\n// See also: https://pub.dev/packages/pigeon\n\npackage com.openlist.pigeon;\n\nimport android.util.Log;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport io.flutter.plugin.common.BasicMessageChannel;\nimport io.flutter.plugin.common.BinaryMessenger;\nimport io.flutter.plugin.common.MessageCodec;\nimport io.flutter.plugin.common.StandardMessageCodec;\nimport java.io.ByteArrayOutputStream;\nimport java.nio.ByteBuffer;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/** Generated class from Pigeon. */\n@SuppressWarnings({\"unused\", \"unchecked\", \"CodeBlock2Expr\", \"RedundantSuppression\", \"serial\"})\npublic class GeneratedApi {\n\n  /** Error class for passing custom error details to Flutter via a thrown PlatformException. */\n  public static class FlutterError extends RuntimeException {\n\n    /** The error code. */\n    public final String code;\n\n    /** The error details. Must be a datatype supported by the api codec. */\n    public final Object details;\n\n    public FlutterError(@NonNull String code, @Nullable String message, @Nullable Object details) \n    {\n      super(message);\n      this.code = code;\n      this.details = details;\n    }\n  }\n\n  @NonNull\n  protected static ArrayList<Object> wrapError(@NonNull Throwable exception) {\n    ArrayList<Object> errorList = new ArrayList<Object>(3);\n    if (exception instanceof FlutterError) {\n      FlutterError error = (FlutterError) exception;\n      errorList.add(error.code);\n      errorList.add(error.getMessage());\n      errorList.add(error.details);\n    } else {\n      errorList.add(exception.toString());\n      errorList.add(exception.getClass().getSimpleName());\n      errorList.add(\n        \"Cause: \" + exception.getCause() + \", Stacktrace: \" + Log.getStackTraceString(exception));\n    }\n    return errorList;\n  }\n\n  @NonNull\n  protected static FlutterError createConnectionError(@NonNull String channelName) {\n    return new FlutterError(\"channel-error\",  \"Unable to establish connection on channel: \" + channelName + \".\", \"\");\n  }\n\n  /** Asynchronous error handling return type for non-nullable API method returns. */\n  public interface Result<T> {\n    /** Success case callback method for handling returns. */\n    void success(@NonNull T result);\n\n    /** Failure case callback method for handling errors. */\n    void error(@NonNull Throwable error);\n  }\n  /** Asynchronous error handling return type for nullable API method returns. */\n  public interface NullableResult<T> {\n    /** Success case callback method for handling returns. */\n    void success(@Nullable T result);\n\n    /** Failure case callback method for handling errors. */\n    void error(@NonNull Throwable error);\n  }\n  /** Asynchronous error handling return type for void API method returns. */\n  public interface VoidResult {\n    /** Success case callback method for handling returns. */\n    void success();\n\n    /** Failure case callback method for handling errors. */\n    void error(@NonNull Throwable error);\n  }\n  /** Generated interface from Pigeon that represents a handler of messages from Flutter. */\n  public interface AppConfig {\n\n    @NonNull \n    Boolean isWakeLockEnabled();\n\n    void setWakeLockEnabled(@NonNull Boolean enabled);\n\n    @NonNull \n    Boolean isStartAtBootEnabled();\n\n    void setStartAtBootEnabled(@NonNull Boolean enabled);\n\n    @NonNull \n    Boolean isAutoCheckUpdateEnabled();\n\n    void setAutoCheckUpdateEnabled(@NonNull Boolean enabled);\n\n    @NonNull \n    Boolean isAutoOpenWebPageEnabled();\n\n    void setAutoOpenWebPageEnabled(@NonNull Boolean enabled);\n\n    @NonNull \n    String getDataDir();\n\n    void setDataDir(@NonNull String dir);\n\n    @NonNull \n    Boolean isSilentJumpAppEnabled();\n\n    void setSilentJumpAppEnabled(@NonNull Boolean enabled);\n\n    /** The codec used by AppConfig. */\n    static @NonNull MessageCodec<Object> getCodec() {\n      return new StandardMessageCodec();\n    }\n    /**Sets up an instance of `AppConfig` to handle messages through the `binaryMessenger`. */\n    static void setUp(@NonNull BinaryMessenger binaryMessenger, @Nullable AppConfig api) {\n      {\n        BasicMessageChannel<Object> channel =\n            new BasicMessageChannel<>(\n                binaryMessenger, \"dev.flutter.pigeon.openlist_mobile.AppConfig.isWakeLockEnabled\", getCodec());\n        if (api != null) {\n          channel.setMessageHandler(\n              (message, reply) -> {\n                ArrayList<Object> wrapped = new ArrayList<Object>();\n                try {\n                  Boolean output = api.isWakeLockEnabled();\n                  wrapped.add(0, output);\n                }\n catch (Throwable exception) {\n                  ArrayList<Object> wrappedError = wrapError(exception);\n                  wrapped = wrappedError;\n                }\n                reply.reply(wrapped);\n              });\n        } else {\n          channel.setMessageHandler(null);\n        }\n      }\n      {\n        BasicMessageChannel<Object> channel =\n            new BasicMessageChannel<>(\n                binaryMessenger, \"dev.flutter.pigeon.openlist_mobile.AppConfig.setWakeLockEnabled\", getCodec());\n        if (api != null) {\n          channel.setMessageHandler(\n              (message, reply) -> {\n                ArrayList<Object> wrapped = new ArrayList<Object>();\n                ArrayList<Object> args = (ArrayList<Object>) message;\n                Boolean enabledArg = (Boolean) args.get(0);\n                try {\n                  api.setWakeLockEnabled(enabledArg);\n                  wrapped.add(0, null);\n                }\n catch (Throwable exception) {\n                  ArrayList<Object> wrappedError = wrapError(exception);\n                  wrapped = wrappedError;\n                }\n                reply.reply(wrapped);\n              });\n        } else {\n          channel.setMessageHandler(null);\n        }\n      }\n      {\n        BasicMessageChannel<Object> channel =\n            new BasicMessageChannel<>(\n                binaryMessenger, \"dev.flutter.pigeon.openlist_mobile.AppConfig.isStartAtBootEnabled\", getCodec());\n        if (api != null) {\n          channel.setMessageHandler(\n              (message, reply) -> {\n                ArrayList<Object> wrapped = new ArrayList<Object>();\n                try {\n                  Boolean output = api.isStartAtBootEnabled();\n                  wrapped.add(0, output);\n                }\n catch (Throwable exception) {\n                  ArrayList<Object> wrappedError = wrapError(exception);\n                  wrapped = wrappedError;\n                }\n                reply.reply(wrapped);\n              });\n        } else {\n          channel.setMessageHandler(null);\n        }\n      }\n      {\n        BasicMessageChannel<Object> channel =\n            new BasicMessageChannel<>(\n                binaryMessenger, \"dev.flutter.pigeon.openlist_mobile.AppConfig.setStartAtBootEnabled\", getCodec());\n        if (api != null) {\n          channel.setMessageHandler(\n              (message, reply) -> {\n                ArrayList<Object> wrapped = new ArrayList<Object>();\n                ArrayList<Object> args = (ArrayList<Object>) message;\n                Boolean enabledArg = (Boolean) args.get(0);\n                try {\n                  api.setStartAtBootEnabled(enabledArg);\n                  wrapped.add(0, null);\n                }\n catch (Throwable exception) {\n                  ArrayList<Object> wrappedError = wrapError(exception);\n                  wrapped = wrappedError;\n                }\n                reply.reply(wrapped);\n              });\n        } else {\n          channel.setMessageHandler(null);\n        }\n      }\n      {\n        BasicMessageChannel<Object> channel =\n            new BasicMessageChannel<>(\n                binaryMessenger, \"dev.flutter.pigeon.openlist_mobile.AppConfig.isAutoCheckUpdateEnabled\", getCodec());\n        if (api != null) {\n          channel.setMessageHandler(\n              (message, reply) -> {\n                ArrayList<Object> wrapped = new ArrayList<Object>();\n                try {\n                  Boolean output = api.isAutoCheckUpdateEnabled();\n                  wrapped.add(0, output);\n                }\n catch (Throwable exception) {\n                  ArrayList<Object> wrappedError = wrapError(exception);\n                  wrapped = wrappedError;\n                }\n                reply.reply(wrapped);\n              });\n        } else {\n          channel.setMessageHandler(null);\n        }\n      }\n      {\n        BasicMessageChannel<Object> channel =\n            new BasicMessageChannel<>(\n                binaryMessenger, \"dev.flutter.pigeon.openlist_mobile.AppConfig.setAutoCheckUpdateEnabled\", getCodec());\n        if (api != null) {\n          channel.setMessageHandler(\n              (message, reply) -> {\n                ArrayList<Object> wrapped = new ArrayList<Object>();\n                ArrayList<Object> args = (ArrayList<Object>) message;\n                Boolean enabledArg = (Boolean) args.get(0);\n                try {\n                  api.setAutoCheckUpdateEnabled(enabledArg);\n                  wrapped.add(0, null);\n                }\n catch (Throwable exception) {\n                  ArrayList<Object> wrappedError = wrapError(exception);\n                  wrapped = wrappedError;\n                }\n                reply.reply(wrapped);\n              });\n        } else {\n          channel.setMessageHandler(null);\n        }\n      }\n      {\n        BasicMessageChannel<Object> channel =\n            new BasicMessageChannel<>(\n                binaryMessenger, \"dev.flutter.pigeon.openlist_mobile.AppConfig.isAutoOpenWebPageEnabled\", getCodec());\n        if (api != null) {\n          channel.setMessageHandler(\n              (message, reply) -> {\n                ArrayList<Object> wrapped = new ArrayList<Object>();\n                try {\n                  Boolean output = api.isAutoOpenWebPageEnabled();\n                  wrapped.add(0, output);\n                }\n catch (Throwable exception) {\n                  ArrayList<Object> wrappedError = wrapError(exception);\n                  wrapped = wrappedError;\n                }\n                reply.reply(wrapped);\n              });\n        } else {\n          channel.setMessageHandler(null);\n        }\n      }\n      {\n        BasicMessageChannel<Object> channel =\n            new BasicMessageChannel<>(\n                binaryMessenger, \"dev.flutter.pigeon.openlist_mobile.AppConfig.setAutoOpenWebPageEnabled\", getCodec());\n        if (api != null) {\n          channel.setMessageHandler(\n              (message, reply) -> {\n                ArrayList<Object> wrapped = new ArrayList<Object>();\n                ArrayList<Object> args = (ArrayList<Object>) message;\n                Boolean enabledArg = (Boolean) args.get(0);\n                try {\n                  api.setAutoOpenWebPageEnabled(enabledArg);\n                  wrapped.add(0, null);\n                }\n catch (Throwable exception) {\n                  ArrayList<Object> wrappedError = wrapError(exception);\n                  wrapped = wrappedError;\n                }\n                reply.reply(wrapped);\n              });\n        } else {\n          channel.setMessageHandler(null);\n        }\n      }\n      {\n        BasicMessageChannel<Object> channel =\n            new BasicMessageChannel<>(\n                binaryMessenger, \"dev.flutter.pigeon.openlist_mobile.AppConfig.getDataDir\", getCodec());\n        if (api != null) {\n          channel.setMessageHandler(\n              (message, reply) -> {\n                ArrayList<Object> wrapped = new ArrayList<Object>();\n                try {\n                  String output = api.getDataDir();\n                  wrapped.add(0, output);\n                }\n catch (Throwable exception) {\n                  ArrayList<Object> wrappedError = wrapError(exception);\n                  wrapped = wrappedError;\n                }\n                reply.reply(wrapped);\n              });\n        } else {\n          channel.setMessageHandler(null);\n        }\n      }\n      {\n        BasicMessageChannel<Object> channel =\n            new BasicMessageChannel<>(\n                binaryMessenger, \"dev.flutter.pigeon.openlist_mobile.AppConfig.setDataDir\", getCodec());\n        if (api != null) {\n          channel.setMessageHandler(\n              (message, reply) -> {\n                ArrayList<Object> wrapped = new ArrayList<Object>();\n                ArrayList<Object> args = (ArrayList<Object>) message;\n                String dirArg = (String) args.get(0);\n                try {\n                  api.setDataDir(dirArg);\n                  wrapped.add(0, null);\n                }\n catch (Throwable exception) {\n                  ArrayList<Object> wrappedError = wrapError(exception);\n                  wrapped = wrappedError;\n                }\n                reply.reply(wrapped);\n              });\n        } else {\n          channel.setMessageHandler(null);\n        }\n      }\n      {\n        BasicMessageChannel<Object> channel =\n            new BasicMessageChannel<>(\n                binaryMessenger, \"dev.flutter.pigeon.openlist_mobile.AppConfig.isSilentJumpAppEnabled\", getCodec());\n        if (api != null) {\n          channel.setMessageHandler(\n              (message, reply) -> {\n                ArrayList<Object> wrapped = new ArrayList<Object>();\n                try {\n                  Boolean output = api.isSilentJumpAppEnabled();\n                  wrapped.add(0, output);\n                }\n catch (Throwable exception) {\n                  ArrayList<Object> wrappedError = wrapError(exception);\n                  wrapped = wrappedError;\n                }\n                reply.reply(wrapped);\n              });\n        } else {\n          channel.setMessageHandler(null);\n        }\n      }\n      {\n        BasicMessageChannel<Object> channel =\n            new BasicMessageChannel<>(\n                binaryMessenger, \"dev.flutter.pigeon.openlist_mobile.AppConfig.setSilentJumpAppEnabled\", getCodec());\n        if (api != null) {\n          channel.setMessageHandler(\n              (message, reply) -> {\n                ArrayList<Object> wrapped = new ArrayList<Object>();\n                ArrayList<Object> args = (ArrayList<Object>) message;\n                Boolean enabledArg = (Boolean) args.get(0);\n                try {\n                  api.setSilentJumpAppEnabled(enabledArg);\n                  wrapped.add(0, null);\n                }\n catch (Throwable exception) {\n                  ArrayList<Object> wrappedError = wrapError(exception);\n                  wrapped = wrappedError;\n                }\n                reply.reply(wrapped);\n              });\n        } else {\n          channel.setMessageHandler(null);\n        }\n      }\n    }\n  }\n  /** Generated interface from Pigeon that represents a handler of messages from Flutter. */\n  public interface NativeCommon {\n\n    @NonNull \n    Boolean startActivityFromUri(@NonNull String intentUri);\n\n    @NonNull \n    Long getDeviceSdkInt();\n\n    @NonNull \n    String getDeviceCPUABI();\n\n    @NonNull \n    String getVersionName();\n\n    @NonNull \n    Long getVersionCode();\n\n    void toast(@NonNull String msg);\n\n    void longToast(@NonNull String msg);\n\n    /** The codec used by NativeCommon. */\n    static @NonNull MessageCodec<Object> getCodec() {\n      return new StandardMessageCodec();\n    }\n    /**Sets up an instance of `NativeCommon` to handle messages through the `binaryMessenger`. */\n    static void setUp(@NonNull BinaryMessenger binaryMessenger, @Nullable NativeCommon api) {\n      {\n        BasicMessageChannel<Object> channel =\n            new BasicMessageChannel<>(\n                binaryMessenger, \"dev.flutter.pigeon.openlist_mobile.NativeCommon.startActivityFromUri\", getCodec());\n        if (api != null) {\n          channel.setMessageHandler(\n              (message, reply) -> {\n                ArrayList<Object> wrapped = new ArrayList<Object>();\n                ArrayList<Object> args = (ArrayList<Object>) message;\n                String intentUriArg = (String) args.get(0);\n                try {\n                  Boolean output = api.startActivityFromUri(intentUriArg);\n                  wrapped.add(0, output);\n                }\n catch (Throwable exception) {\n                  ArrayList<Object> wrappedError = wrapError(exception);\n                  wrapped = wrappedError;\n                }\n                reply.reply(wrapped);\n              });\n        } else {\n          channel.setMessageHandler(null);\n        }\n      }\n      {\n        BasicMessageChannel<Object> channel =\n            new BasicMessageChannel<>(\n                binaryMessenger, \"dev.flutter.pigeon.openlist_mobile.NativeCommon.getDeviceSdkInt\", getCodec());\n        if (api != null) {\n          channel.setMessageHandler(\n              (message, reply) -> {\n                ArrayList<Object> wrapped = new ArrayList<Object>();\n                try {\n                  Long output = api.getDeviceSdkInt();\n                  wrapped.add(0, output);\n                }\n catch (Throwable exception) {\n                  ArrayList<Object> wrappedError = wrapError(exception);\n                  wrapped = wrappedError;\n                }\n                reply.reply(wrapped);\n              });\n        } else {\n          channel.setMessageHandler(null);\n        }\n      }\n      {\n        BasicMessageChannel<Object> channel =\n            new BasicMessageChannel<>(\n                binaryMessenger, \"dev.flutter.pigeon.openlist_mobile.NativeCommon.getDeviceCPUABI\", getCodec());\n        if (api != null) {\n          channel.setMessageHandler(\n              (message, reply) -> {\n                ArrayList<Object> wrapped = new ArrayList<Object>();\n                try {\n                  String output = api.getDeviceCPUABI();\n                  wrapped.add(0, output);\n                }\n catch (Throwable exception) {\n                  ArrayList<Object> wrappedError = wrapError(exception);\n                  wrapped = wrappedError;\n                }\n                reply.reply(wrapped);\n              });\n        } else {\n          channel.setMessageHandler(null);\n        }\n      }\n      {\n        BasicMessageChannel<Object> channel =\n            new BasicMessageChannel<>(\n                binaryMessenger, \"dev.flutter.pigeon.openlist_mobile.NativeCommon.getVersionName\", getCodec());\n        if (api != null) {\n          channel.setMessageHandler(\n              (message, reply) -> {\n                ArrayList<Object> wrapped = new ArrayList<Object>();\n                try {\n                  String output = api.getVersionName();\n                  wrapped.add(0, output);\n                }\n catch (Throwable exception) {\n                  ArrayList<Object> wrappedError = wrapError(exception);\n                  wrapped = wrappedError;\n                }\n                reply.reply(wrapped);\n              });\n        } else {\n          channel.setMessageHandler(null);\n        }\n      }\n      {\n        BasicMessageChannel<Object> channel =\n            new BasicMessageChannel<>(\n                binaryMessenger, \"dev.flutter.pigeon.openlist_mobile.NativeCommon.getVersionCode\", getCodec());\n        if (api != null) {\n          channel.setMessageHandler(\n              (message, reply) -> {\n                ArrayList<Object> wrapped = new ArrayList<Object>();\n                try {\n                  Long output = api.getVersionCode();\n                  wrapped.add(0, output);\n                }\n catch (Throwable exception) {\n                  ArrayList<Object> wrappedError = wrapError(exception);\n                  wrapped = wrappedError;\n                }\n                reply.reply(wrapped);\n              });\n        } else {\n          channel.setMessageHandler(null);\n        }\n      }\n      {\n        BasicMessageChannel<Object> channel =\n            new BasicMessageChannel<>(\n                binaryMessenger, \"dev.flutter.pigeon.openlist_mobile.NativeCommon.toast\", getCodec());\n        if (api != null) {\n          channel.setMessageHandler(\n              (message, reply) -> {\n                ArrayList<Object> wrapped = new ArrayList<Object>();\n                ArrayList<Object> args = (ArrayList<Object>) message;\n                String msgArg = (String) args.get(0);\n                try {\n                  api.toast(msgArg);\n                  wrapped.add(0, null);\n                }\n catch (Throwable exception) {\n                  ArrayList<Object> wrappedError = wrapError(exception);\n                  wrapped = wrappedError;\n                }\n                reply.reply(wrapped);\n              });\n        } else {\n          channel.setMessageHandler(null);\n        }\n      }\n      {\n        BasicMessageChannel<Object> channel =\n            new BasicMessageChannel<>(\n                binaryMessenger, \"dev.flutter.pigeon.openlist_mobile.NativeCommon.longToast\", getCodec());\n        if (api != null) {\n          channel.setMessageHandler(\n              (message, reply) -> {\n                ArrayList<Object> wrapped = new ArrayList<Object>();\n                ArrayList<Object> args = (ArrayList<Object>) message;\n                String msgArg = (String) args.get(0);\n                try {\n                  api.longToast(msgArg);\n                  wrapped.add(0, null);\n                }\n catch (Throwable exception) {\n                  ArrayList<Object> wrappedError = wrapError(exception);\n                  wrapped = wrappedError;\n                }\n                reply.reply(wrapped);\n              });\n        } else {\n          channel.setMessageHandler(null);\n        }\n      }\n    }\n  }\n  /** Generated interface from Pigeon that represents a handler of messages from Flutter. */\n  public interface Android {\n\n    void addShortcut();\n\n    void startService();\n\n    void setAdminPwd(@NonNull String pwd);\n\n    @NonNull \n    Long getOpenListHttpPort();\n\n    @NonNull \n    Boolean isRunning();\n\n    @NonNull \n    String getOpenListVersion();\n\n    /** The codec used by Android. */\n    static @NonNull MessageCodec<Object> getCodec() {\n      return new StandardMessageCodec();\n    }\n    /**Sets up an instance of `Android` to handle messages through the `binaryMessenger`. */\n    static void setUp(@NonNull BinaryMessenger binaryMessenger, @Nullable Android api) {\n      {\n        BasicMessageChannel<Object> channel =\n            new BasicMessageChannel<>(\n                binaryMessenger, \"dev.flutter.pigeon.openlist_mobile.Android.addShortcut\", getCodec());\n        if (api != null) {\n          channel.setMessageHandler(\n              (message, reply) -> {\n                ArrayList<Object> wrapped = new ArrayList<Object>();\n                try {\n                  api.addShortcut();\n                  wrapped.add(0, null);\n                }\n catch (Throwable exception) {\n                  ArrayList<Object> wrappedError = wrapError(exception);\n                  wrapped = wrappedError;\n                }\n                reply.reply(wrapped);\n              });\n        } else {\n          channel.setMessageHandler(null);\n        }\n      }\n      {\n        BasicMessageChannel<Object> channel =\n            new BasicMessageChannel<>(\n                binaryMessenger, \"dev.flutter.pigeon.openlist_mobile.Android.startService\", getCodec());\n        if (api != null) {\n          channel.setMessageHandler(\n              (message, reply) -> {\n                ArrayList<Object> wrapped = new ArrayList<Object>();\n                try {\n                  api.startService();\n                  wrapped.add(0, null);\n                }\n catch (Throwable exception) {\n                  ArrayList<Object> wrappedError = wrapError(exception);\n                  wrapped = wrappedError;\n                }\n                reply.reply(wrapped);\n              });\n        } else {\n          channel.setMessageHandler(null);\n        }\n      }\n      {\n        BasicMessageChannel<Object> channel =\n            new BasicMessageChannel<>(\n                binaryMessenger, \"dev.flutter.pigeon.openlist_mobile.Android.setAdminPwd\", getCodec());\n        if (api != null) {\n          channel.setMessageHandler(\n              (message, reply) -> {\n                ArrayList<Object> wrapped = new ArrayList<Object>();\n                ArrayList<Object> args = (ArrayList<Object>) message;\n                String pwdArg = (String) args.get(0);\n                try {\n                  api.setAdminPwd(pwdArg);\n                  wrapped.add(0, null);\n                }\n catch (Throwable exception) {\n                  ArrayList<Object> wrappedError = wrapError(exception);\n                  wrapped = wrappedError;\n                }\n                reply.reply(wrapped);\n              });\n        } else {\n          channel.setMessageHandler(null);\n        }\n      }\n      {\n        BasicMessageChannel<Object> channel =\n            new BasicMessageChannel<>(\n                binaryMessenger, \"dev.flutter.pigeon.openlist_mobile.Android.getOpenListHttpPort\", getCodec());\n        if (api != null) {\n          channel.setMessageHandler(\n              (message, reply) -> {\n                ArrayList<Object> wrapped = new ArrayList<Object>();\n                try {\n                  Long output = api.getOpenListHttpPort();\n                  wrapped.add(0, output);\n                }\n catch (Throwable exception) {\n                  ArrayList<Object> wrappedError = wrapError(exception);\n                  wrapped = wrappedError;\n                }\n                reply.reply(wrapped);\n              });\n        } else {\n          channel.setMessageHandler(null);\n        }\n      }\n      {\n        BasicMessageChannel<Object> channel =\n            new BasicMessageChannel<>(\n                binaryMessenger, \"dev.flutter.pigeon.openlist_mobile.Android.isRunning\", getCodec());\n        if (api != null) {\n          channel.setMessageHandler(\n              (message, reply) -> {\n                ArrayList<Object> wrapped = new ArrayList<Object>();\n                try {\n                  Boolean output = api.isRunning();\n                  wrapped.add(0, output);\n                }\n catch (Throwable exception) {\n                  ArrayList<Object> wrappedError = wrapError(exception);\n                  wrapped = wrappedError;\n                }\n                reply.reply(wrapped);\n              });\n        } else {\n          channel.setMessageHandler(null);\n        }\n      }\n      {\n        BasicMessageChannel<Object> channel =\n            new BasicMessageChannel<>(\n                binaryMessenger, \"dev.flutter.pigeon.openlist_mobile.Android.getOpenListVersion\", getCodec());\n        if (api != null) {\n          channel.setMessageHandler(\n              (message, reply) -> {\n                ArrayList<Object> wrapped = new ArrayList<Object>();\n                try {\n                  String output = api.getOpenListVersion();\n                  wrapped.add(0, output);\n                }\n catch (Throwable exception) {\n                  ArrayList<Object> wrappedError = wrapError(exception);\n                  wrapped = wrappedError;\n                }\n                reply.reply(wrapped);\n              });\n        } else {\n          channel.setMessageHandler(null);\n        }\n      }\n    }\n  }\n  /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */\n  public static class Event {\n    private final @NonNull BinaryMessenger binaryMessenger;\n\n    public Event(@NonNull BinaryMessenger argBinaryMessenger) {\n      this.binaryMessenger = argBinaryMessenger;\n    }\n\n    /** Public interface for sending reply. */ \n    /** The codec used by Event. */\n    static @NonNull MessageCodec<Object> getCodec() {\n      return new StandardMessageCodec();\n    }\n    public void onServiceStatusChanged(@NonNull Boolean isRunningArg, @NonNull VoidResult result) {\n      final String channelName = \"dev.flutter.pigeon.openlist_mobile.Event.onServiceStatusChanged\";\n      BasicMessageChannel<Object> channel =\n          new BasicMessageChannel<>(\n              binaryMessenger, channelName, getCodec());\n      channel.send(\n          new ArrayList<Object>(Collections.singletonList(isRunningArg)),\n          channelReply -> {\n            if (channelReply instanceof List) {\n              List<Object> listReply = (List<Object>) channelReply;\n              if (listReply.size() > 1) {\n                result.error(new FlutterError((String) listReply.get(0), (String) listReply.get(1), (String) listReply.get(2)));\n              } else {\n                result.success();\n              }\n            }  else {\n              result.error(createConnectionError(channelName));\n            } \n          });\n    }\n    public void onServerLog(@NonNull Long levelArg, @NonNull String timeArg, @NonNull String logArg, @NonNull VoidResult result) {\n      final String channelName = \"dev.flutter.pigeon.openlist_mobile.Event.onServerLog\";\n      BasicMessageChannel<Object> channel =\n          new BasicMessageChannel<>(\n              binaryMessenger, channelName, getCodec());\n      channel.send(\n          new ArrayList<Object>(Arrays.asList(levelArg, timeArg, logArg)),\n          channelReply -> {\n            if (channelReply instanceof List) {\n              List<Object> listReply = (List<Object>) channelReply;\n              if (listReply.size() > 1) {\n                result.error(new FlutterError((String) listReply.get(0), (String) listReply.get(1), (String) listReply.get(2)));\n              } else {\n                result.success();\n              }\n            }  else {\n              result.error(createConnectionError(channelName));\n            } \n          });\n    }\n  }\n}\n"
  },
  {
    "path": "android/app/src/main/kotlin/com/openlist/mobile/App.kt",
    "content": "package com.openlist.mobile\n\nimport android.app.Application\nimport android.util.Log\nimport com.openlist.mobile.model.openlist.OpenList\nimport com.openlist.mobile.utils.ToastUtils.longToast\nimport io.flutter.app.FlutterApplication\n\nval app by lazy { App.app }\n\nclass App : FlutterApplication() {\n    companion object {\n        private const val TAG = \"App\"\n        lateinit var app: Application\n    }\n\n\n    override fun onCreate() {\n        super.onCreate()\n\n        app = this\n        \n        // Early initialization of OpenList to prepare for boot startup\n        try {\n            Log.d(TAG, \"Performing early OpenList initialization\")\n            OpenList.init()\n            Log.d(TAG, \"OpenList early initialization completed\")\n        } catch (e: Exception) {\n            Log.e(TAG, \"Failed to initialize OpenList early\", e)\n        }\n        \n        // Set global exception handler to catch uncaught exceptions\n        Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->\n            Log.e(TAG, \"Uncaught exception in thread ${thread.name}\", throwable)\n            \n            // Log detailed info for JNI related errors\n            if (throwable.message?.contains(\"JNI\") == true || \n                throwable.message?.contains(\"native\") == true ||\n                throwable is UnsatisfiedLinkError) {\n                Log.e(TAG, \"Native/JNI related crash detected\")\n            }\n            \n            // Call default exception handler\n            val defaultHandler = Thread.getDefaultUncaughtExceptionHandler()\n            defaultHandler?.uncaughtException(thread, throwable)\n        }\n    }\n}"
  },
  {
    "path": "android/app/src/main/kotlin/com/openlist/mobile/BootReceiver.kt",
    "content": "package com.openlist.mobile\n\nimport android.content.BroadcastReceiver\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Build\nimport android.util.Log\nimport com.openlist.mobile.config.AppConfig\n\n/**\n * Boot receiver - handles device boot and package update events\n */\nclass BootReceiver : BroadcastReceiver() {\n    companion object {\n        private const val TAG = \"BootReceiver\"\n    }\n\n    override fun onReceive(context: Context?, intent: Intent?) {\n        if (context == null || intent?.action == null) return\n        \n        Log.d(TAG, \"Received broadcast: ${intent.action}\")\n\n        when (intent.action) {\n            Intent.ACTION_BOOT_COMPLETED -> {\n                handleBootCompleted(context)\n            }\n            Intent.ACTION_MY_PACKAGE_REPLACED -> {\n                handlePackageReplaced(context)\n            }\n        }\n    }\n\n    private fun handleBootCompleted(context: Context) {\n        if (!AppConfig.isStartAtBootEnabled) {\n            Log.d(TAG, \"Auto-start disabled, skipping\")\n            return\n        }\n\n        // Clear manual stop flag on boot\n        AppConfig.isManuallyStoppedByUser = false\n        \n        Log.d(TAG, \"Starting OpenList service\")\n        startService(context)\n    }\n\n    private fun handlePackageReplaced(context: Context) {\n        if (!AppConfig.isStartAtBootEnabled) {\n            Log.d(TAG, \"Auto-start disabled, skipping package update restart\")\n            return\n        }\n        \n        Log.d(TAG, \"Starting OpenList service after package update\")\n        startService(context)\n    }\n\n    private fun startService(context: Context) {\n        try {\n            val serviceIntent = Intent(context, OpenListService::class.java)\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n                context.startForegroundService(serviceIntent)\n            } else {\n                context.startService(serviceIntent)\n            }\n            Log.d(TAG, \"Service start command sent\")\n        } catch (e: Exception) {\n            Log.e(TAG, \"Failed to start service\", e)\n        }\n    }\n}\n"
  },
  {
    "path": "android/app/src/main/kotlin/com/openlist/mobile/MainActivity.kt",
    "content": "package com.openlist.mobile\n\nimport android.content.BroadcastReceiver\nimport android.content.Context\nimport android.content.Intent\nimport android.content.IntentFilter\nimport android.os.Bundle\nimport android.util.Log\nimport androidx.localbroadcastmanager.content.LocalBroadcastManager\nimport com.openlist.mobile.bridge.AndroidBridge\nimport com.openlist.mobile.bridge.AppConfigBridge\nimport com.openlist.mobile.bridge.CommonBridge\nimport com.openlist.mobile.bridge.ServiceBridge\nimport com.openlist.mobile.model.ShortCuts\nimport com.openlist.mobile.model.openlist.Logger\nimport com.openlist.pigeon.GeneratedApi\nimport com.openlist.pigeon.GeneratedApi.VoidResult\nimport io.flutter.embedding.android.FlutterActivity\nimport io.flutter.plugin.common.MethodChannel\nimport io.flutter.plugins.GeneratedPluginRegistrant\nimport kotlinx.coroutines.DelicateCoroutinesApi\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.GlobalScope\nimport kotlinx.coroutines.launch\n\nclass MainActivity : FlutterActivity() {\n    companion object {\n        private const val TAG = \"MainActivity\"\n        \n        // 静态引用，供其他组件访问\n        @Volatile\n        var serviceBridge: ServiceBridge? = null\n            private set\n    }\n\n    private val receiver by lazy { MyReceiver() }\n    private var mEvent: GeneratedApi.Event? = null\n\n    @OptIn(DelicateCoroutinesApi::class)\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        ShortCuts.buildShortCuts(this)\n        LocalBroadcastManager.getInstance(this)\n            .registerReceiver(receiver, IntentFilter(OpenListService.ACTION_STATUS_CHANGED))\n\n        GeneratedPluginRegistrant.registerWith(this.flutterEngine!!)\n\n        val binaryMessage = flutterEngine!!.dartExecutor.binaryMessenger\n        GeneratedApi.AppConfig.setUp(binaryMessage, AppConfigBridge)\n        GeneratedApi.Android.setUp(binaryMessage, AndroidBridge(this))\n        GeneratedApi.NativeCommon.setUp(binaryMessage, CommonBridge(this))\n        mEvent = GeneratedApi.Event(binaryMessage)\n\n        // 设置服务桥接\n        val serviceChannel = MethodChannel(binaryMessage, \"com.openlist.mobile/service\")\n        serviceBridge = ServiceBridge(this, serviceChannel)\n\n        Logger.addListener(object : Logger.Listener {\n            override fun onLog(level: Int, time: String, msg: String) {\n                GlobalScope.launch(Dispatchers.Main) {\n                    mEvent?.onServerLog(level.toLong(), time, msg, object : VoidResult {\n                        override fun success() {\n\n                        }\n\n                        override fun error(error: Throwable) {\n                        }\n\n                    })\n                }\n            }\n\n        })\n    }\n\n    override fun onPause() {\n        super.onPause()\n        // Trigger database sync when app goes to background\n        triggerDatabaseSync(\"onPause\")\n    }\n\n    override fun onStop() {\n        super.onStop()\n        // Trigger database sync when app is stopped\n        triggerDatabaseSync(\"onStop\")\n    }\n\n    override fun onTrimMemory(level: Int) {\n        super.onTrimMemory(level)\n        // Trigger database sync on memory pressure\n        when (level) {\n            TRIM_MEMORY_UI_HIDDEN,\n            TRIM_MEMORY_BACKGROUND,\n            TRIM_MEMORY_MODERATE,\n            TRIM_MEMORY_COMPLETE -> {\n                triggerDatabaseSync(\"onTrimMemory:$level\")\n            }\n        }\n    }\n\n    override fun onDestroy() {\n        super.onDestroy()\n        // Trigger database sync before activity is destroyed\n        triggerDatabaseSync(\"onDestroy\")\n        \n        LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver)\n    }\n\n    /**\n     * Trigger database synchronization through the service\n     */\n    private fun triggerDatabaseSync(reason: String) {\n        try {\n            val serviceInstance = OpenListService.serviceInstance\n            if (serviceInstance != null && OpenListService.isRunning) {\n                Log.d(TAG, \"Triggering database sync due to: $reason\")\n                serviceInstance.forceImmediateDbSync()\n            } else {\n                Log.d(TAG, \"Service not running, skipping database sync for: $reason\")\n            }\n        } catch (e: Exception) {\n            Log.e(TAG, \"Failed to trigger database sync for $reason\", e)\n        }\n    }\n\n\n    inner class MyReceiver : BroadcastReceiver() {\n        override fun onReceive(context: Context, intent: Intent) {\n            when (intent.action) {\n                OpenListService.ACTION_STATUS_CHANGED -> {\n                    Log.d(TAG, \"onReceive: ACTION_STATUS_CHANGED\")\n\n                    mEvent?.onServiceStatusChanged(OpenListService.isRunning, object : VoidResult {\n                        override fun success() {}\n                        override fun error(error: Throwable) {\n                        }\n                    })\n                }\n\n\n\n            }\n\n        }\n    }\n\n}\n"
  },
  {
    "path": "android/app/src/main/kotlin/com/openlist/mobile/OpenListService.kt",
    "content": "package com.openlist.mobile\n\nimport openlistlib.Openlistlib\nimport android.annotation.SuppressLint\nimport android.app.Notification\nimport android.app.NotificationChannel\nimport android.app.NotificationManager\nimport android.app.PendingIntent\nimport android.app.Service\nimport android.content.BroadcastReceiver\nimport android.content.Context\nimport android.content.Intent\nimport android.content.IntentFilter\nimport android.os.Build\nimport android.os.IBinder\nimport android.os.PowerManager\nimport android.util.Log\nimport androidx.localbroadcastmanager.content.LocalBroadcastManager\nimport com.openlist.mobile.config.AppConfig\nimport com.openlist.mobile.model.openlist.OpenList\nimport com.openlist.mobile.utils.AndroidUtils.registerReceiverCompat\nimport com.openlist.mobile.utils.ClipboardUtils\nimport com.openlist.mobile.utils.ToastUtils.toast\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.Job\nimport kotlinx.coroutines.SupervisorJob\nimport kotlinx.coroutines.delay\nimport kotlinx.coroutines.isActive\nimport kotlinx.coroutines.launch\nimport splitties.systemservices.powerManager\n\n/**\n * OpenList后台服务 - 提供OpenList核心功能并实现保活机制\n */\nclass OpenListService : Service(), OpenList.Listener {\n    companion object {\n        const val TAG = \"OpenListService\"\n        const val ACTION_SHUTDOWN =\n            \"com.openlist.openlistandroid.service.OpenListService.ACTION_SHUTDOWN\"\n\n        const val ACTION_COPY_ADDRESS =\n            \"com.openlist.openlistandroid.service.OpenListService.ACTION_COPY_ADDRESS\"\n\n        const val ACTION_STATUS_CHANGED =\n            \"com.openlist.openlistandroid.service.OpenListService.ACTION_STATUS_CHANGED\"\n\n        const val NOTIFICATION_CHAN_ID = \"openlist_server\"\n        const val FOREGROUND_ID = 5224\n\n        @Volatile\n        var isRunning: Boolean = false\n            private set\n\n        @Volatile\n        var serviceInstance: OpenListService? = null\n            private set\n    }\n\n    private val mScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)\n    private val mNotificationReceiver = NotificationActionReceiver()\n    private val mReceiver = MyReceiver()\n    private var mWakeLock: PowerManager.WakeLock? = null\n    private var mLocalAddress: String = \"\"\n    private var mDbSyncJob: Job? = null\n\n    // Database sync interval in milliseconds (5 minutes)\n    private val DB_SYNC_INTERVAL = 5 * 60 * 1000L\n\n    override fun onBind(p0: Intent?): IBinder? = null\n\n    @Suppress(\"DEPRECATION\")\n    private fun notifyStatusChanged() {\n        Log.d(TAG, \"notifyStatusChanged: isRunning=$isRunning\")\n        \n        LocalBroadcastManager.getInstance(this)\n            .sendBroadcast(Intent(ACTION_STATUS_CHANGED))\n\n        // Notify ServiceBridge of status change\n        try {\n            MainActivity.serviceBridge?.notifyServiceStatusChanged(isRunning)\n        } catch (e: Exception) {\n            Log.e(TAG, \"Failed to notify ServiceBridge\", e)\n        }\n\n        if (!isRunning) {\n            // Stop foreground service and remove notification\n            stopForeground(true)\n            cancelNotification()\n            stopSelf()\n        } else {\n            // Update notification with current status\n            Log.d(TAG, \"Updating notification after status change\")\n            updateNotification()\n        }\n    }\n\n    @SuppressLint(\"WakelockTimeout\")\n    override fun onCreate() {\n        super.onCreate()\n        Log.d(TAG, \"OpenListService created\")\n\n        serviceInstance = this\n\n        // Android 8.0+ must start foreground notification immediately\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n            initOrUpdateNotification()\n        }\n\n        // Register broadcast receivers\n        try {\n            LocalBroadcastManager.getInstance(this)\n                .registerReceiver(mReceiver, IntentFilter(ACTION_STATUS_CHANGED))\n            registerReceiverCompat(mNotificationReceiver, ACTION_SHUTDOWN, ACTION_COPY_ADDRESS)\n        } catch (e: Exception) {\n            Log.e(TAG, \"Failed to register receivers\", e)\n        }\n\n        // Add OpenList listener\n        OpenList.addListener(this)\n\n        // Acquire wake lock if enabled\n        if (AppConfig.isWakeLockEnabled) {\n            try {\n                mWakeLock = powerManager.newWakeLock(\n                    PowerManager.PARTIAL_WAKE_LOCK,\n                    \"openlist::service\"\n                )\n                mWakeLock?.acquire()\n            } catch (e: Exception) {\n                Log.e(TAG, \"Failed to acquire wake lock\", e)\n            }\n        }\n\n        Log.d(TAG, \"Service onCreate completed\")\n    }\n\n    @Suppress(\"DEPRECATION\")\n    override fun onDestroy() {\n        super.onDestroy()\n        Log.d(TAG, \"OpenListService destroyed\")\n\n        serviceInstance = null\n\n        // 取消所有协程作业\n        mScope.coroutineContext[Job]?.cancel()\n\n        // 释放唤醒锁\n        try {\n            mWakeLock?.release()\n            mWakeLock = null\n            Log.d(TAG, \"Wake lock released\")\n        } catch (e: Exception) {\n            Log.e(TAG, \"Failed to release wake lock\", e)\n        }\n\n        // 停止前台服务并取消通知\n        stopForeground(true)\n        cancelNotification()\n\n        // 注销广播接收器\n        try {\n            LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver)\n            unregisterReceiver(mNotificationReceiver)\n            Log.d(TAG, \"Broadcast receivers unregistered\")\n        } catch (e: Exception) {\n            Log.e(TAG, \"Failed to unregister receivers\", e)\n        }\n\n        // 移除OpenList监听器\n        OpenList.removeListener(this)\n        \n        // Stop database sync task\n        stopDatabaseSyncTask()\n    }\n\n    override fun onShutdown(type: String) {\n        Log.d(TAG, \"OpenList shutdown: $type\")\n        if (!OpenList.isRunning()) {\n            isRunning = false\n            // Stop database sync task when service shuts down\n            stopDatabaseSyncTask()\n            notifyStatusChanged()\n        }\n    }\n\n    /**\n     * Start periodic database synchronization task\n     */\n    private fun startDatabaseSyncTask() {\n        stopDatabaseSyncTask() // Stop any existing task first\n        \n        mDbSyncJob = mScope.launch(Dispatchers.IO) {\n            while (isActive && isRunning) {\n                try {\n                    delay(DB_SYNC_INTERVAL)\n                    if (isRunning && OpenList.isRunning()) {\n                        Log.d(TAG, \"Performing periodic database sync\")\n                        OpenList.forceDatabaseSync()\n                    }\n                } catch (e: Exception) {\n                    Log.e(TAG, \"Error during periodic database sync\", e)\n                }\n            }\n        }\n        Log.d(TAG, \"Database sync task started\")\n    }\n\n    /**\n     * Stop database synchronization task\n     */\n    private fun stopDatabaseSyncTask() {\n        mDbSyncJob?.cancel()\n        mDbSyncJob = null\n        Log.d(TAG, \"Database sync task stopped\")\n    }\n\n    /**\n     * Force immediate database synchronization\n     */\n    fun forceImmediateDbSync() {\n        mScope.launch(Dispatchers.IO) {\n            try {\n                if (isRunning && OpenList.isRunning()) {\n                    Log.d(TAG, \"Performing immediate database sync\")\n                    OpenList.forceDatabaseSync()\n                }\n            } catch (e: Exception) {\n                Log.e(TAG, \"Error during immediate database sync\", e)\n            }\n        }\n    }\n\n    /**\n     * Public method: Stop OpenList service manually\n     */\n    fun stopOpenListService() {\n        if (isRunning) {\n            Log.d(TAG, \"User manually stopping service\")\n            // Set flag to indicate user manually stopped the service\n            AppConfig.isManuallyStoppedByUser = true\n            startOrShutdown()\n        }\n    }\n\n    /**\n     * Start or shutdown OpenList service\n     */\n    private fun startOrShutdown() {\n        if (isRunning) {\n            Log.d(TAG, \"Shutting down OpenList\")\n            mScope.launch(Dispatchers.IO) {\n                try {\n                    if (OpenList.isRunning()) {\n                        Log.d(TAG, \"Forcing database sync before shutdown\")\n                        OpenList.forceDatabaseSync()\n                    }\n                    \n                    OpenList.shutdown()\n                    isRunning = false\n                    launch(Dispatchers.Main) {\n                        notifyStatusChanged()\n                    }\n                } catch (e: Exception) {\n                    Log.e(TAG, \"Shutdown error\", e)\n                    launch(Dispatchers.Main) {\n                        toast(\"关闭失败: ${e.message}\")\n                    }\n                }\n            }\n        } else {\n            Log.d(TAG, \"Starting OpenList from user action\")\n            AppConfig.isManuallyStoppedByUser = false\n            toast(getString(R.string.starting))\n            startOpenListBackend()\n        }\n    }\n\n    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {\n        Log.d(TAG, \"onStartCommand called\")\n\n        // Check manual stop flag\n        if (AppConfig.isManuallyStoppedByUser) {\n            Log.d(TAG, \"Service was manually stopped by user, not starting\")\n            stopSelf()\n            return START_NOT_STICKY\n        }\n\n        // Start OpenList if not running\n        if (!isRunning) {\n            Log.d(TAG, \"Starting OpenList backend\")\n            startOpenListBackend()\n        }\n\n        return START_STICKY\n    }\n\n    /**\n     * Start OpenList backend service\n     */\n    private fun startOpenListBackend() {\n        if (isRunning) {\n            Log.d(TAG, \"OpenList already running\")\n            return\n        }\n        \n        Log.d(TAG, \"Initializing and starting OpenList\")\n        isRunning = true\n        \n        mScope.launch(Dispatchers.IO) {\n            try {\n                // Initialize OpenList\n                OpenList.init()\n                delay(100)\n                \n                // Start OpenList\n                OpenList.startup()\n                \n                // Clear cached address to force refresh\n                mLocalAddress = \"\"\n                \n                // Update UI on success\n                launch(Dispatchers.Main) {\n                    notifyStatusChanged()\n                    startDatabaseSyncTask()\n                }\n                \n                Log.d(TAG, \"OpenList started successfully\")\n            } catch (e: Exception) {\n                Log.e(TAG, \"Failed to start OpenList\", e)\n                isRunning = false\n                launch(Dispatchers.Main) {\n                    toast(\"启动失败: ${e.message}\")\n                    notifyStatusChanged()\n                }\n            }\n        }\n    }\n\n    inner class MyReceiver : BroadcastReceiver() {\n        override fun onReceive(context: Context?, intent: Intent) {\n            when (intent.action) {\n                ACTION_STATUS_CHANGED -> {\n                    Log.d(TAG, \"Status changed broadcast received\")\n                }\n            }\n        }\n    }\n\n    /**\n     * Get local address safely\n     */\n    private fun localAddress(): String {\n        return try {\n            if (mLocalAddress.isEmpty()) {\n                Log.d(TAG, \"Fetching local address...\")\n                mLocalAddress = Openlistlib.getOutboundIPString()\n                Log.d(TAG, \"Local address: $mLocalAddress\")\n            }\n            mLocalAddress\n        } catch (e: Exception) {\n            Log.e(TAG, \"Failed to get local address\", e)\n            \"Initializing...\"\n        }\n    }\n\n    /**\n     * Initialize or update notification\n     */\n    @Suppress(\"DEPRECATION\")\n    private fun initOrUpdateNotification() {\n        try {\n            Log.d(TAG, \"Creating/updating notification with address: ${localAddress()}\")\n            \n            val pendingIntentFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {\n                PendingIntent.FLAG_IMMUTABLE\n            } else {\n                0\n            }\n\n            val pendingIntent = PendingIntent.getActivity(\n                this, 0, Intent(this, MainActivity::class.java), pendingIntentFlags\n            )\n\n            val shutdownAction = PendingIntent.getBroadcast(\n                this, 0, Intent(ACTION_SHUTDOWN), pendingIntentFlags\n            )\n\n            val copyAddressPendingIntent = PendingIntent.getBroadcast(\n                this, 0, Intent(ACTION_COPY_ADDRESS), pendingIntentFlags\n            )\n\n            val builder = Notification.Builder(applicationContext)\n\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n                val chan = NotificationChannel(\n                    NOTIFICATION_CHAN_ID,\n                    getString(R.string.openlist_server),\n                    NotificationManager.IMPORTANCE_LOW\n                )\n                chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE\n                chan.setShowBadge(false)\n                \n                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {\n                    chan.setBlockable(false)\n                }\n                \n                val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager\n                notificationManager.createNotificationChannel(chan)\n                \n                builder.setChannelId(NOTIFICATION_CHAN_ID)\n            }\n\n            val smallIconRes = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n                R.drawable.server\n            } else {\n                R.mipmap.ic_launcher_round\n            }\n\n            val notification = builder\n                .setContentTitle(getString(R.string.openlist_server_running))\n                .setContentText(\"地址: ${localAddress()}\")\n                .setSmallIcon(smallIconRes)\n                .setContentIntent(pendingIntent)\n                .addAction(0, getString(R.string.shutdown), shutdownAction)\n                .addAction(0, getString(R.string.copy_address), copyAddressPendingIntent)\n                .setOngoing(true)\n                .setAutoCancel(false)\n                .build()\n\n            notification.flags = notification.flags or \n                Notification.FLAG_NO_CLEAR or\n                Notification.FLAG_ONGOING_EVENT\n\n            startForeground(FOREGROUND_ID, notification)\n        } catch (e: Exception) {\n            Log.e(TAG, \"Failed to create notification\", e)\n            // Minimal fallback\n            try {\n                val minimal = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n                    Notification.Builder(applicationContext, NOTIFICATION_CHAN_ID)\n                } else {\n                    Notification.Builder(applicationContext)\n                }.setContentTitle(\"OpenList\")\n                    .setContentText(\"Starting...\")\n                    .setSmallIcon(R.mipmap.ic_launcher_round)\n                    .build()\n                startForeground(FOREGROUND_ID, minimal)\n            } catch (fallbackError: Exception) {\n                Log.e(TAG, \"Failed to create minimal notification\", fallbackError)\n            }\n        }\n    }\n\n    /**\n     * 更新通知\n     */\n    private fun updateNotification() {\n        initOrUpdateNotification()\n    }\n\n    /**\n     * 取消通知\n     */\n    private fun cancelNotification() {\n        try {\n            val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager\n            notificationManager.cancel(FOREGROUND_ID)\n            Log.d(TAG, \"Notification cancelled\")\n        } catch (e: Exception) {\n            Log.e(TAG, \"Failed to cancel notification\", e)\n        }\n    }\n\n    inner class NotificationActionReceiver : BroadcastReceiver() {\n        override fun onReceive(ctx: Context?, intent: Intent?) {\n            when (intent?.action) {\n                ACTION_SHUTDOWN -> {\n                    Log.d(TAG, \"Shutdown action received from notification\")\n                    startOrShutdown()\n                }\n\n                ACTION_COPY_ADDRESS -> {\n                    Log.d(TAG, \"Copy address action received from notification\")\n                    ClipboardUtils.copyText(\"OpenList\", localAddress())\n                    toast(R.string.address_copied)\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "android/app/src/main/kotlin/com/openlist/mobile/OpenListTileService.kt",
    "content": "package com.openlist.mobile\n\nimport android.content.BroadcastReceiver\nimport android.content.Context\nimport android.content.Intent\nimport android.content.IntentFilter\nimport android.graphics.drawable.Icon\nimport android.os.Build\nimport android.service.quicksettings.Tile\nimport android.service.quicksettings.TileService\nimport android.util.Log\nimport androidx.annotation.RequiresApi\nimport androidx.localbroadcastmanager.content.LocalBroadcastManager\nimport com.openlist.mobile.config.AppConfig\n\n\n@RequiresApi(Build.VERSION_CODES.N)\nclass OpenListTileService : TileService() {\n    companion object {\n        private const val TAG = \"OpenListTileService\"\n        private const val CLICK_DEBOUNCE_TIME = 2000L // 2秒防重复点击\n    }\n\n    private var lastClickTime = 0L\n\n    private val statusReceiver = object : BroadcastReceiver() {\n        override fun onReceive(context: Context, intent: Intent) {\n            when (intent.action) {\n                OpenListService.ACTION_STATUS_CHANGED -> {\n                    Log.d(TAG, \"Service status changed, updating tile\")\n                    // 添加小延迟确保状态稳定\n                    qsTile?.let {\n                        android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({\n                            updateTileState()\n                        }, 100)\n                    }\n                }\n            }\n        }\n    }\n\n    override fun onStartListening() {\n        super.onStartListening()\n        Log.d(TAG, \"Tile started listening\")\n        LocalBroadcastManager.getInstance(this)\n            .registerReceiver(statusReceiver, IntentFilter(OpenListService.ACTION_STATUS_CHANGED))\n\n        updateTileState()\n    }\n\n    override fun onStopListening() {\n        super.onStopListening()\n        Log.d(TAG, \"Tile stopped listening\")\n        try {\n            LocalBroadcastManager.getInstance(this).unregisterReceiver(statusReceiver)\n        } catch (e: Exception) {\n            Log.w(TAG, \"Failed to unregister receiver\", e)\n        }\n    }\n\n    override fun onClick() {\n        super.onClick()\n        \n        // 防重复点击\n        val currentTime = System.currentTimeMillis()\n        if (currentTime - lastClickTime < CLICK_DEBOUNCE_TIME) {\n            Log.d(TAG, \"Click ignored due to debounce\")\n            return\n        }\n        lastClickTime = currentTime\n        \n        val isRunning = OpenListService.isRunning\n        Log.d(TAG, \"Tile clicked, service running: $isRunning\")\n        \n        // 设置瓦片为过渡状态，显示操作进行中\n        setTileTransitionState(!isRunning)\n        \n        if (isRunning) {\n            stopOpenListService()\n        } else {\n            startOpenListService()\n        }\n        // 移除立即状态更新，依赖广播接收器异步更新\n        // updateTileState() - 现在由广播接收器处理\n    }\n\n    private fun startOpenListService() {\n        try {\n            AppConfig.isManuallyStoppedByUser = false\n            val intent = Intent(this, OpenListService::class.java)\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n                startForegroundService(intent)\n            } else {\n                startService(intent)\n            }\n\n            Log.d(TAG, \"Service start command sent from tile\")\n        } catch (e: Exception) {\n            Log.e(TAG, \"Failed to start service from tile\", e)\n        }\n    }\n\n    private fun stopOpenListService() {\n        try {\n            AppConfig.isManuallyStoppedByUser = true\n            val serviceInstance = OpenListService.serviceInstance\n            if (serviceInstance != null && OpenListService.isRunning) {\n                serviceInstance.stopOpenListService()\n            }\n            // 移除else分支的冗余stopService调用\n            Log.d(TAG, \"Service stop command sent from tile\")\n        } catch (e: Exception) {\n            Log.e(TAG, \"Failed to stop service from tile\", e)\n            // 出错时恢复瓦片状态\n            updateTileState()\n        }\n    }\n\n    /**\n     * 设置瓦片过渡状态，显示操作正在进行中\n     */\n    private fun setTileTransitionState(targetActiveState: Boolean) {\n        val tile = qsTile ?: return\n        \n        // 设置过渡状态\n        tile.state = if (targetActiveState) Tile.STATE_UNAVAILABLE else Tile.STATE_UNAVAILABLE\n        tile.label = if (targetActiveState) \"启动中...\" else \"停止中...\"\n        tile.contentDescription = if (targetActiveState) \"OpenList Starting\" else \"OpenList Stopping\"\n        \n        try {\n            val icon = Icon.createWithResource(this, R.mipmap.ic_launcher)\n            tile.icon = icon\n        } catch (e: Exception) {\n            Log.w(TAG, \"Failed to set tile icon during transition\", e)\n        }\n        \n        tile.updateTile()\n        Log.d(TAG, \"Tile set to transition state: ${if (targetActiveState) \"starting\" else \"stopping\"}\")\n    }\n\n    private fun updateTileState() {\n        val tile = qsTile ?: return\n        val isRunning = OpenListService.isRunning\n        Log.d(TAG, \"Updating tile state, service running: $isRunning\")\n\n        if (isRunning) {\n            tile.state = Tile.STATE_ACTIVE\n            tile.label = \"OpenList\"\n            tile.contentDescription = \"OpenList Running\"\n        } else {\n            tile.state = Tile.STATE_INACTIVE\n            tile.label = \"OpenList\"\n            tile.contentDescription = \"OpenList Stopped\"\n        }\n        try {\n            val icon = Icon.createWithResource(this, R.mipmap.ic_launcher)\n            tile.icon = icon\n        } catch (e: Exception) {\n            Log.w(TAG, \"Failed to set tile icon\", e)\n        }\n\n        tile.updateTile()\n    }\n}"
  },
  {
    "path": "android/app/src/main/kotlin/com/openlist/mobile/SwitchServerActivity.kt",
    "content": "package com.openlist.mobile\n\nimport android.app.Activity\nimport android.content.Intent\nimport android.os.Bundle\nimport android.util.Log\nimport com.openlist.mobile.config.AppConfig\nimport com.openlist.mobile.utils.ToastUtils.toast\n\nclass SwitchServerActivity : Activity() {\n    companion object {\n        private const val TAG = \"SwitchServerActivity\"\n    }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        if (OpenListService.isRunning) {\n            Log.d(TAG, \"Service is running, stopping it\")\n            // 设置手动停止标志\n            AppConfig.isManuallyStoppedByUser = true\n            startService(Intent(this, OpenListService::class.java).apply {\n                action = OpenListService.ACTION_SHUTDOWN\n            })\n        } else {\n            // 检查是否被手动停止\n            if (AppConfig.isManuallyStoppedByUser) {\n                Log.d(TAG, \"Service was manually stopped, clearing flag and starting\")\n                // 清除手动停止标志\n                AppConfig.isManuallyStoppedByUser = false\n            }\n            Log.d(TAG, \"Starting service\")\n            startService(Intent(this, OpenListService::class.java))\n        }\n\n        finish()\n    }\n}"
  },
  {
    "path": "android/app/src/main/kotlin/com/openlist/mobile/bridge/AndroidBridge.kt",
    "content": "package com.openlist.mobile.bridge\n\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Build\nimport android.util.Log\nimport com.openlist.mobile.OpenListService\nimport com.openlist.mobile.BuildConfig\nimport com.openlist.mobile.R\nimport com.openlist.mobile.SwitchServerActivity\nimport com.openlist.mobile.config.AppConfig\nimport com.openlist.mobile.model.openlist.OpenList\nimport com.openlist.mobile.utils.MyTools\nimport com.openlist.mobile.utils.ToastUtils.longToast\nimport com.openlist.mobile.utils.ToastUtils.toast\nimport com.openlist.pigeon.GeneratedApi\n\nclass AndroidBridge(private val context: Context) : GeneratedApi.Android {\n    companion object {\n        private const val TAG = \"AndroidBridge\"\n    }\n\n    override fun addShortcut() {\n        MyTools.addShortcut(\n            context,\n            context.getString(R.string.app_switch),\n            \"openlist_mobile_switch\",\n            R.drawable.openlist_switch,\n            Intent(context, SwitchServerActivity::class.java)\n        )\n    }\n\n    override fun startService() {\n        // 清除手动停止标志，表示用户手动启动了服务\n        AppConfig.isManuallyStoppedByUser = false\n        Log.d(TAG, \"Starting service via AndroidBridge, manual stop flag cleared\")\n        context.startService(Intent(context, OpenListService::class.java))\n    }\n\n    override fun setAdminPwd(pwd: String) {\n        OpenList.setAdminPassword(pwd)\n    }\n\n    override fun getOpenListHttpPort(): Long {\n        return OpenList.getHttpPort().toLong()\n    }\n\n    override fun isRunning() = OpenListService.isRunning\n\n\n    override fun getOpenListVersion() = BuildConfig.OPENLIST_VERSION\n}"
  },
  {
    "path": "android/app/src/main/kotlin/com/openlist/mobile/bridge/AppConfigBridge.kt",
    "content": "package com.openlist.mobile.bridge\n\nimport com.openlist.mobile.config.AppConfig\nimport com.openlist.pigeon.GeneratedApi\n\n\nobject AppConfigBridge : GeneratedApi.AppConfig {\n    override fun isWakeLockEnabled() = AppConfig.isWakeLockEnabled\n\n    override fun isStartAtBootEnabled() = AppConfig.isStartAtBootEnabled\n\n    override fun isAutoCheckUpdateEnabled() = AppConfig.isAutoCheckUpdateEnabled\n    override fun isAutoOpenWebPageEnabled() = AppConfig.isAutoOpenWebPageEnabled\n    override fun getDataDir() = AppConfig.dataDir\n\n    override fun setDataDir(dir: String) {\n        AppConfig.dataDir = dir\n    }\n\n    override fun isSilentJumpAppEnabled(): Boolean = AppConfig.isSilentJumpAppEnabled\n\n    override fun setSilentJumpAppEnabled(enabled: Boolean) {\n        AppConfig.isSilentJumpAppEnabled = enabled\n    }\n\n    override fun setAutoOpenWebPageEnabled(enabled: Boolean) {\n        AppConfig.isAutoOpenWebPageEnabled = enabled\n    }\n\n    override fun setAutoCheckUpdateEnabled(enabled: Boolean) {\n        AppConfig.isAutoCheckUpdateEnabled = enabled\n    }\n\n    override fun setStartAtBootEnabled(enabled: Boolean) {\n        AppConfig.isStartAtBootEnabled = enabled\n    }\n\n    override fun setWakeLockEnabled(enabled: Boolean) {\n        AppConfig.isWakeLockEnabled = enabled\n    }\n}"
  },
  {
    "path": "android/app/src/main/kotlin/com/openlist/mobile/bridge/CommonBridge.kt",
    "content": "package com.openlist.mobile.bridge\n\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Build\nimport com.openlist.mobile.BuildConfig\nimport com.openlist.mobile.utils.ToastUtils.longToast\nimport com.openlist.mobile.utils.ToastUtils.toast\nimport com.openlist.pigeon.GeneratedApi\n\nclass CommonBridge(private val context: Context) : GeneratedApi.NativeCommon {\n    override fun startActivityFromUri(intentUri: String): Boolean {\n        val intent = Intent.parseUri(intentUri, Intent.URI_INTENT_SCHEME)\n        return if (intent.resolveActivity(context.packageManager) != null){\n            context.startActivity(intent)\n            true\n        }else{\n            false\n        }\n    }\n\n    override fun getDeviceSdkInt(): Long {\n        return Build.VERSION.SDK_INT.toLong()\n    }\n\n\n    override fun getDeviceCPUABI(): String {\n        return Build.SUPPORTED_ABIS[0]\n    }\n\n    override fun getVersionName() = BuildConfig.VERSION_NAME\n    override fun getVersionCode() = BuildConfig.VERSION_CODE.toLong()\n\n\n    override fun toast(msg: String) {\n        context.toast(msg)\n    }\n\n    override fun longToast(msg: String) {\n        context.longToast(msg)\n    }\n}"
  },
  {
    "path": "android/app/src/main/kotlin/com/openlist/mobile/bridge/ServiceBridge.kt",
    "content": "package com.openlist.mobile.bridge\n\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Build\nimport android.util.Log\nimport com.openlist.mobile.OpenListService\nimport com.openlist.mobile.config.AppConfig\nimport com.openlist.mobile.utils.BatteryOptimizationUtils\nimport io.flutter.plugin.common.MethodCall\nimport io.flutter.plugin.common.MethodChannel\nimport io.flutter.plugin.common.MethodChannel.MethodCallHandler\nimport io.flutter.plugin.common.MethodChannel.Result\nimport openlistlib.Openlistlib\n\n/**\n * 服务桥接类 - 连接Flutter和Android服务\n */\nclass ServiceBridge(private val context: Context, private val channel: MethodChannel) : MethodCallHandler {\n    companion object {\n        private const val TAG = \"ServiceBridge\"\n        private const val CHANNEL_NAME = \"com.openlist.mobile/service\"\n    }\n\n    init {\n        channel.setMethodCallHandler(this)\n        Log.d(TAG, \"ServiceBridge initialized\")\n    }\n\n    override fun onMethodCall(call: MethodCall, result: Result) {\n        try {\n            when (call.method) {\n                \"startService\" -> {\n                    val success = startOpenListService()\n                    result.success(success)\n                }\n                \n                \"stopService\" -> {\n                    val success = stopOpenListService()\n                    result.success(success)\n                }\n                \n                \"isServiceRunning\" -> {\n                    val isRunning = isOpenListServiceRunning()\n                    result.success(isRunning)\n                }\n                \n                \"isBatteryOptimizationIgnored\" -> {\n                    val isIgnored = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n                        BatteryOptimizationUtils.isIgnoringBatteryOptimizations(context)\n                    } else {\n                        true\n                    }\n                    result.success(isIgnored)\n                }\n                \n                \"requestIgnoreBatteryOptimization\" -> {\n                    val success = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n                        BatteryOptimizationUtils.requestIgnoreBatteryOptimizations(context)\n                    } else {\n                        true\n                    }\n                    result.success(success)\n                }\n                \n                \"openBatteryOptimizationSettings\" -> {\n                    val success = BatteryOptimizationUtils.openBatteryOptimizationSettings(context)\n                    result.success(success)\n                }\n                \n                \"openAutoStartSettings\" -> {\n                    val success = BatteryOptimizationUtils.openAutoStartSettings(context)\n                    result.success(success)\n                }\n                \n                \"getServiceAddress\" -> {\n                    val address = getServiceAddress()\n                    result.success(address)\n                }\n                \n                else -> {\n                    Log.w(TAG, \"Unknown method: ${call.method}\")\n                    result.notImplemented()\n                }\n            }\n        } catch (e: Exception) {\n            Log.e(TAG, \"Error handling method call: ${call.method}\", e)\n            result.error(\"ERROR\", e.message, e.toString())\n        }\n    }\n\n    /**\n     * 启动OpenList服务\n     */\n    private fun startOpenListService(): Boolean {\n        return try {\n            // 清除手动停止标志，表示用户手动启动了服务\n            AppConfig.isManuallyStoppedByUser = false\n            \n            val intent = Intent(context, OpenListService::class.java)\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n                context.startForegroundService(intent)\n            } else {\n                context.startService(intent)\n            }\n            \n            Log.d(TAG, \"OpenList service start command sent, manual stop flag cleared\")\n            true\n        } catch (e: Exception) {\n            Log.e(TAG, \"Failed to start OpenList service\", e)\n            false\n        }\n    }\n\n    /**\n     * 停止OpenList服务\n     */\n    private fun stopOpenListService(): Boolean {\n        return try {\n            // 设置手动停止标志，阻止保活机制重启服务\n            AppConfig.isManuallyStoppedByUser = true\n            \n            // 首先尝试通过服务实例直接停止OpenList\n            val serviceInstance = OpenListService.serviceInstance\n            if (serviceInstance != null && OpenListService.isRunning) {\n                Log.d(TAG, \"Calling service stopOpenListService method directly\")\n                // 直接调用服务的停止方法\n                serviceInstance.stopOpenListService()\n            } else {\n                Log.w(TAG, \"Service instance not available or not running, using stopService\")\n                // 如果服务实例不可用，直接停止服务\n                val intent = Intent(context, OpenListService::class.java)\n                context.stopService(intent)\n            }\n            \n            Log.d(TAG, \"OpenList service stop command sent, manual stop flag set\")\n            true\n        } catch (e: Exception) {\n            Log.e(TAG, \"Failed to stop OpenList service\", e)\n            false\n        }\n    }\n\n    /**\n     * 检查OpenList服务是否运行\n     */\n    private fun isOpenListServiceRunning(): Boolean {\n        return try {\n            OpenListService.isRunning\n        } catch (e: Exception) {\n            Log.e(TAG, \"Failed to check service status\", e)\n            false\n        }\n    }\n\n    /**\n     * 获取服务地址\n     */\n    private fun getServiceAddress(): String {\n        return try {\n            if (OpenListService.isRunning) {\n                Openlistlib.getOutboundIPString()\n            } else {\n                \"\"\n            }\n        } catch (e: Exception) {\n            Log.e(TAG, \"Failed to get service address\", e)\n            \"\"\n        }\n    }\n\n    /**\n     * 通知Flutter服务状态变化\n     */\n    fun notifyServiceStatusChanged(isRunning: Boolean) {\n        try {\n            val arguments = mapOf(\"isRunning\" to isRunning)\n            channel.invokeMethod(\"onServiceStatusChanged\", arguments)\n            Log.d(TAG, \"Service status change notified: $isRunning\")\n        } catch (e: Exception) {\n            Log.e(TAG, \"Failed to notify service status change\", e)\n        }\n    }\n}"
  },
  {
    "path": "android/app/src/main/kotlin/com/openlist/mobile/config/AppConfig.kt",
    "content": "package com.openlist.mobile.config\n\nimport com.cioccarellia.ksprefs.KsPrefs\nimport com.cioccarellia.ksprefs.dynamic\nimport com.openlist.mobile.app\n\nobject AppConfig {\n    val prefs by lazy { KsPrefs(app, \"app\") }\n\n    var isSilentJumpAppEnabled by prefs.dynamic(\"isSilentJumpAppEnabled\", fallback = false)\n\n    var isWakeLockEnabled: Boolean by prefs.dynamic(\"isWakeLockEnabled\", fallback = false)\n    var isStartAtBootEnabled: Boolean by prefs.dynamic(\"isStartAtBootEnabled\", fallback = false)\n    var isAutoCheckUpdateEnabled: Boolean by prefs.dynamic(\n        \"isAutoCheckUpdateEnabled\",\n        fallback = false\n    )\n\n    var isAutoOpenWebPageEnabled: Boolean by prefs.dynamic(\n        \"isAutoOpenWebPageEnabled\",\n        fallback = false\n    )\n\n    // 用户手动停止服务的标志，当为true时，保活机制不会重启服务\n    var isManuallyStoppedByUser: Boolean by prefs.dynamic(\"isManuallyStoppedByUser\", fallback = false)\n\n    val defaultDataDir by lazy { app.getExternalFilesDir(\"data\")?.absolutePath!! }\n\n    private var mDataDir: String by prefs.dynamic(\"dataDir\", fallback = defaultDataDir)\n\n\n    var dataDir: String\n        get() {\n            if (mDataDir.isBlank()) mDataDir = defaultDataDir\n            return mDataDir\n        }\n        set(value) {\n            if (value.isBlank()) {\n                mDataDir = defaultDataDir\n                return\n            }\n\n            mDataDir = value\n        }\n\n}"
  },
  {
    "path": "android/app/src/main/kotlin/com/openlist/mobile/constant/AppConst.kt",
    "content": "package com.openlist.mobile.constant\n\nimport androidx.localbroadcastmanager.content.LocalBroadcastManager\nimport com.openlist.mobile.app\nimport kotlinx.serialization.ExperimentalSerializationApi\nimport kotlinx.serialization.json.Json\n\nobject AppConst {\n    @OptIn(ExperimentalSerializationApi::class)\n    val json = Json {\n        ignoreUnknownKeys = true\n        allowStructuredMapKeys = true\n        prettyPrint = true\n        isLenient = true\n        explicitNulls = false\n    }\n\n    val localBroadcast by lazy {\n        LocalBroadcastManager.getInstance(app)\n    }\n\n//    val fileProviderAuthor = BuildConfig.APPLICATION_ID + \".fileprovider\"\n}"
  },
  {
    "path": "android/app/src/main/kotlin/com/openlist/mobile/constant/LogLevel.kt",
    "content": "package com.openlist.mobile.constant\n\nimport androidx.annotation.IntDef\n\n@IntDef(\n    LogLevel.PANIC,\n    LogLevel.FATAL,\n    LogLevel.ERROR,\n    LogLevel.WARN,\n    LogLevel.INFO,\n    LogLevel.DEBUG,\n    LogLevel.TRACE\n)\nannotation class LogLevel {\n    companion object {\n        const val PANIC = 0\n        const val FATAL = 1\n        const val ERROR = 2\n        const val WARN = 3\n        const val INFO = 4\n        const val DEBUG = 5\n        const val TRACE = 6\n\n        fun Int.toLevelString(): String {\n            return when (this) {\n                PANIC -> \"PANIC\"\n                FATAL -> \"FATAL\"\n                ERROR -> \"ERROR\"\n                WARN -> \"WARN\"\n                INFO -> \"INFO\"\n                DEBUG -> \"DEBUG\"\n                TRACE -> \"TRACE\"\n                else -> \"UNKNOWN\"\n            }\n        }\n    }\n\n}"
  },
  {
    "path": "android/app/src/main/kotlin/com/openlist/mobile/data/AppDatabase.kt",
    "content": "/*\npackage com.openlist.mobile.data\n\nimport androidx.room.AutoMigration\nimport androidx.room.Database\nimport androidx.room.Room\nimport androidx.room.RoomDatabase\nimport com.openlist.openlistandroid.data.dao.ServerLogDao\nimport com.openlist.mobile.data.entities.ServerLog\nimport com.openlist.mobile.App.Companion.app\n\nval appDb by lazy { AppDatabase.create() }\n\n@Database(\n    version = 2,\n    entities = [ServerLog::class],\n    autoMigrations = [\n        AutoMigration(from = 1, to = 2)\n    ]\n)\nabstract class AppDatabase : RoomDatabase() {\n    abstract val serverLogDao: ServerLogDao\n\n    companion object {\n        fun create() = Room.databaseBuilder(\n            app,\n            AppDatabase::class.java,\n            \"openlistandroid.db\"\n        )\n            .allowMainThreadQueries()\n            .build()\n    }\n}*/\n"
  },
  {
    "path": "android/app/src/main/kotlin/com/openlist/mobile/data/entities/ServerLog.kt",
    "content": "package com.openlist.mobile.data.entities\n\nimport com.openlist.mobile.constant.LogLevel\n\ndata class ServerLog(\n\n    @LogLevel val level: Int,\n    val message: String,\n    val time: String,\n) {\n    companion object {\n\n        @Suppress(\"RegExpRedundantEscape\")\n        fun String.evalLog(): ServerLog? {\n            val logPattern = \"\"\"(\\w+)\\[(\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2})\\] (.+)\"\"\".toRegex()\n            val result = logPattern.find(this)\n            if (result != null) {\n                val (level, time, msg) = result.destructured\n                val l = when (level[0].toString()) {\n                    \"D\" -> LogLevel.DEBUG\n                    \"I\" -> LogLevel.INFO\n                    \"W\" -> LogLevel.WARN\n                    \"E\" -> LogLevel.ERROR\n                    else -> LogLevel.INFO\n                }\n                return ServerLog(level = l, message = msg, time = time)\n            }\n            return null\n        }\n    }\n}\n"
  },
  {
    "path": "android/app/src/main/kotlin/com/openlist/mobile/model/ShortCuts.kt",
    "content": "package com.openlist.mobile.model\n\nimport android.content.Context\nimport android.content.Intent\nimport androidx.core.content.pm.ShortcutInfoCompat\nimport androidx.core.content.pm.ShortcutManagerCompat\nimport androidx.core.graphics.drawable.IconCompat\nimport com.openlist.mobile.R\nimport com.openlist.mobile.SwitchServerActivity\n\n\nobject ShortCuts {\n    private inline fun <reified T> buildIntent(context: Context): Intent {\n        val intent = Intent(context, T::class.java)\n        intent.action = Intent.ACTION_VIEW\n        return intent\n    }\n\n\n    private fun buildOpenListSwitchShortCutInfo(context: Context): ShortcutInfoCompat {\n        val msSwitchIntent = buildIntent<SwitchServerActivity>(context)\n        return ShortcutInfoCompat.Builder(context, \"openlist_switch\")\n            .setShortLabel(context.getString(R.string.app_switch))\n            .setLongLabel(context.getString(R.string.app_switch))\n            .setIcon(IconCompat.createWithResource(context, R.drawable.openlist_switch))\n            .setIntent(msSwitchIntent)\n            .build()\n    }\n\n\n    fun buildShortCuts(context: Context) {\n        ShortcutManagerCompat.setDynamicShortcuts(\n            context, listOf(\n                buildOpenListSwitchShortCutInfo(context),\n            )\n        )\n    }\n\n\n}"
  },
  {
    "path": "android/app/src/main/kotlin/com/openlist/mobile/model/UpdateResult.kt",
    "content": "package com.openlist.openlistandroid.model\n\ndata class UpdateResult(\n    val version: String = \"\",\n    val time: String = \"\",\n    val content: String = \"\",\n    val downloadUrl: String = \"\",\n    val size: Long = 0,\n) {\n    fun hasUpdate() = version.isNotBlank() && downloadUrl.isNotBlank()\n}"
  },
  {
    "path": "android/app/src/main/kotlin/com/openlist/mobile/model/openlist/Logger.kt",
    "content": "package com.openlist.mobile.model.openlist\n\nobject Logger {\n    private var listeners = mutableListOf<Listener>()\n\n    fun addListener(listener: Listener) {\n        listeners.add(listener)\n    }\n\n    fun removeListener(listener: Listener) {\n        listeners.remove(listener)\n    }\n\n    interface Listener {\n        fun onLog(level: Int, time: String, msg: String)\n    }\n\n    fun log(level: Int, time: String, msg: String) {\n        listeners.forEach {\n            it.onLog(level, time, msg)\n        }\n    }\n}"
  },
  {
    "path": "android/app/src/main/kotlin/com/openlist/mobile/model/openlist/OpenList.kt",
    "content": "package com.openlist.mobile.model.openlist\n\nimport openlistlib.Openlistlib\nimport openlistlib.Event\nimport openlistlib.LogCallback\nimport android.annotation.SuppressLint\nimport android.util.Log\nimport com.openlist.mobile.R\nimport com.openlist.mobile.app\nimport com.openlist.mobile.config.AppConfig\nimport com.openlist.mobile.constant.LogLevel\nimport com.openlist.mobile.utils.ToastUtils.longToast\nimport java.io.File\nimport java.text.SimpleDateFormat\nimport java.util.Locale\n\nobject OpenList : Event, LogCallback {\n    const val TAG = \"OpenList\"\n\n    val context = app\n\n    val dataDir: String\n        get() = AppConfig.dataDir\n\n    val configPath: String\n        get() = \"$dataDir${File.separator}config.json\"\n\n\n    fun init() {\n        runCatching {\n            Openlistlib.setConfigData(dataDir)\n            Openlistlib.setConfigLogStd(true)\n            Openlistlib.init(this, this)\n        }.onFailure {\n            Log.e(TAG, \"init:\", it)\n        }\n    }\n\n    interface Listener {\n        fun onShutdown(type: String)\n    }\n\n    private val mListeners = mutableListOf<Listener>()\n\n    fun addListener(listener: Listener) {\n        mListeners.add(listener)\n    }\n\n    fun removeListener(listener: Listener) {\n        mListeners.remove(listener)\n    }\n\n    override fun onShutdown(p0: String) {\n        Log.d(TAG, \"onShutdown: $p0\")\n        mListeners.forEach { it.onShutdown(p0) }\n    }\n\n    override fun onStartError(type: String, msg: String) {\n        Log.e(TAG, \"onStartError: $type, $msg\")\n        Logger.log(LogLevel.FATAL, type, msg)\n    }\n\n    private val mDateFormatter by lazy  { SimpleDateFormat(\"MM-dd HH:mm:ss\", Locale.getDefault())}\n\n    override fun onLog(level: Short, time: Long, log: String) {\n        Log.d(TAG, \"onLog: $level, $time, $log\")\n        Logger.log(level.toInt(), mDateFormatter.format(time), log)\n    }\n\n    override fun onProcessExit(code: Long) {\n\n    }\n\n    fun isRunning(): Boolean {\n        return Openlistlib.isRunning(\"\")\n    }\n\n    fun setAdminPassword(pwd: String) {\n        if (!isRunning()) init()\n\n        Log.d(TAG, \"setAdminPassword: $dataDir\")\n        Openlistlib.setConfigData(dataDir)\n        Openlistlib.setAdminPassword(pwd)\n    }\n\n\n    fun shutdown() {\n        Log.d(TAG, \"shutdown\")\n        runCatching {\n            Openlistlib.shutdown(5000)\n        }.onFailure {\n            context.longToast(R.string.shutdown_failed)\n        }\n    }\n\n    /**\n     * Force database synchronization (WAL checkpoint)\n     * This ensures SQLite WAL files are merged into the main database file\n     */\n    fun forceDatabaseSync() {\n        Log.d(TAG, \"forceDatabaseSync\")\n        runCatching {\n            Openlistlib.forceDBSync()\n            Log.d(TAG, \"Database sync completed successfully\")\n        }.onFailure { e ->\n            Log.e(TAG, \"Failed to sync database\", e)\n        }\n    }\n\n    @SuppressLint(\"SdCardPath\")\n    @Synchronized\n    fun startup() {\n        Log.d(TAG, \"startup: $dataDir\")\n        try {\n            // 确保数据目录存在\n            val dataDirFile = File(dataDir)\n            if (!dataDirFile.exists()) {\n                dataDirFile.mkdirs()\n                Log.d(TAG, \"Created data directory: $dataDir\")\n            }\n            \n            // 重新初始化以确保配置正确\n            init()\n            \n            // 多重检查是否已经在运行，防止重复启动\n            if (isRunning()) {\n                Log.w(TAG, \"OpenList is already running, skipping startup\")\n                return\n            }\n            \n            // 再次检查以确保安全\n            Thread.sleep(100) // 短暂等待以避免竞态条件\n            if (isRunning()) {\n                Log.w(TAG, \"OpenList started by another thread, skipping startup\")\n                return\n            }\n            \n            Log.d(TAG, \"Starting OpenList...\")\n            Openlistlib.start()\n            \n            // 验证启动是否成功\n            Thread.sleep(1000) // 等待1秒让服务完全启动\n            if (isRunning()) {\n                Log.d(TAG, \"OpenList started successfully and confirmed running\")\n            } else {\n                Log.w(TAG, \"OpenList startup command sent but status check failed\")\n            }\n        } catch (e: Exception) {\n            Log.e(TAG, \"Failed to start OpenList\", e)\n            throw e\n        } catch (t: Throwable) {\n            Log.e(TAG, \"Fatal error starting OpenList\", t)\n            throw RuntimeException(\"Fatal error starting OpenList\", t)\n        }\n    }\n\n    fun getHttpPort(): Int {\n        return OpenListConfigManager.config().scheme.httpPort\n    }\n}"
  },
  {
    "path": "android/app/src/main/kotlin/com/openlist/mobile/model/openlist/OpenListConfig.kt",
    "content": "package com.openlist.mobile.model.openlist\n\nimport kotlinx.serialization.SerialName\nimport kotlinx.serialization.Serializable\n\n\n@Serializable\ndata class OpenListConfig(\n    @SerialName(\"bleve_dir\")\n    val bleveDir: String = \"\", // /storage/emulated/0/Android/data/com.openlist.openlistandroid.debug/files/data/bleve\n    @SerialName(\"cdn\")\n    val cdn: String = \"\",\n//    @SerialName(\"database\")\n//    val database: Database = Database(),\n    @SerialName(\"delayed_start\")\n    val delayedStart: Int = 0, // 0\n    @SerialName(\"force\")\n    val force: Boolean = false, // false\n    @SerialName(\"jwt_secret\")\n    val jwtSecret: String = \"\", //\n//    @SerialName(\"log\")\n//    val log: Log = Log(),\n    @SerialName(\"max_connections\")\n    val maxConnections: Int = 0, // 0\n    @SerialName(\"scheme\")\n    val scheme: Scheme = Scheme(),\n    @SerialName(\"site_url\")\n    val siteUrl: String = \"\",\n    @SerialName(\"temp_dir\")\n    val tempDir: String = \"\", // /storage/emulated/0/Android/data/com.openlist.openlistandroid.debug/files/data/temp\n    @SerialName(\"tls_insecure_skip_verify\")\n    val tlsInsecureSkipVerify: Boolean = true, // true\n    @SerialName(\"token_expires_in\")\n    val tokenExpiresIn: Int = 48 // 48\n) {\n    @Serializable\n    data class Database(\n        @SerialName(\"db_file\")\n        val dbFile: String = \"\", // /storage/emulated/0/Android/data/com.openlist.openlistandroid.debug/files/data/data.db\n        @SerialName(\"host\")\n        val host: String = \"\",\n        @SerialName(\"name\")\n        val name: String = \"\",\n        @SerialName(\"password\")\n        val password: String = \"\",\n        @SerialName(\"port\")\n        val port: Int = 0, // 0\n        @SerialName(\"ssl_mode\")\n        val sslMode: String = \"\",\n        @SerialName(\"table_prefix\")\n        val tablePrefix: String = \"x_\", // x_\n        @SerialName(\"type\")\n        val type: String = \"sqlite3\", // sqlite3\n        @SerialName(\"user\")\n        val user: String = \"\"\n    )\n\n    @Serializable\n    data class Log(\n        @SerialName(\"compress\")\n        val compress: Boolean = false, // false\n        @SerialName(\"enable\")\n        val enable: Boolean = true, // true\n        @SerialName(\"max_age\")\n        val maxAge: Int = 28, // 28\n        @SerialName(\"max_backups\")\n        val maxBackups: Int = 5, // 5\n        @SerialName(\"max_size\")\n        val maxSize: Int = 10, // 10\n        @SerialName(\"name\")\n        val name: String = \"\" // /storage/emulated/0/Android/data/com.openlist.openlistandroid.debug/files/data/log/log.log\n    )\n\n    @Serializable\n    data class Scheme(\n        @SerialName(\"address\")\n        val address: String = \"0.0.0.0\", // 0.0.0.0\n        @SerialName(\"cert_file\")\n        val certFile: String = \"\",\n        @SerialName(\"force_https\")\n        val forceHttps: Boolean = false, // false\n        @SerialName(\"http_port\")\n        val httpPort: Int = 5244, // 5244\n        @SerialName(\"https_port\")\n        val httpsPort: Int = -1, // -1\n        @SerialName(\"key_file\")\n        val keyFile: String = \"\",\n        @SerialName(\"unix_file\")\n        val unixFile: String = \"\",\n        @SerialName(\"unix_file_perm\")\n        val unixFilePerm: String = \"\"\n    )\n}"
  },
  {
    "path": "android/app/src/main/kotlin/com/openlist/mobile/model/openlist/OpenListConfigManager.kt",
    "content": "package com.openlist.mobile.model.openlist\n\nimport android.os.FileObserver\nimport android.util.Log\nimport com.openlist.mobile.app\nimport com.openlist.mobile.constant.AppConst\nimport com.openlist.mobile.utils.ToastUtils.longToast\nimport kotlinx.coroutines.CancellationException\nimport kotlinx.coroutines.awaitCancellation\nimport kotlinx.coroutines.coroutineScope\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.channelFlow\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.runBlocking\nimport kotlinx.serialization.ExperimentalSerializationApi\nimport kotlinx.serialization.json.decodeFromStream\nimport kotlinx.serialization.json.encodeToStream\nimport java.io.File\n\n@Suppress(\"DEPRECATION\")\nobject OpenListConfigManager {\n    const val TAG = \"OpenListConfigManager\"\n\n    val context\n        get() = app\n\n    suspend fun flowConfig(): Flow<OpenListConfig> = channelFlow {\n        val obs = object : FileObserver(OpenList.configPath) {\n            override fun onEvent(event: Int, p1: String?) {\n                if (listOf(CLOSE_NOWRITE, CLOSE_WRITE).contains(event))\n                    runBlocking {\n                        Log.d(TAG, \"config.json changed: $event\")\n                        send((config()))\n                    }\n            }\n        }\n        coroutineScope {\n            val waitJob = launch {\n                obs.startWatching()\n                try {\n                    awaitCancellation()\n                } catch (_: CancellationException) {\n                }\n\n                obs.stopWatching()\n            }\n            waitJob.join()\n        }\n    }\n\n    @OptIn(ExperimentalSerializationApi::class)\n    fun config(): OpenListConfig {\n        try {\n            File(OpenList.configPath).inputStream().use {\n                return AppConst.json.decodeFromStream<OpenListConfig>(it)\n            }\n        } catch (e: Exception) {\n            OpenList.context.longToast(\"读取 config.json 失败：\\n$e\")\n            return OpenListConfig()\n        }\n    }\n\n    @OptIn(ExperimentalSerializationApi::class)\n    fun update(cfg: OpenListConfig) {\n        try {\n            File(OpenList.configPath).outputStream().use {\n                AppConst.json.encodeToStream(cfg, it)\n            }\n        } catch (e: Exception) {\n            OpenList.context.longToast(\"更新 config.json 失败：\\n$e\")\n        }\n    }\n\n}"
  },
  {
    "path": "android/app/src/main/kotlin/com/openlist/mobile/utils/AndroidUtils.kt",
    "content": "package com.openlist.mobile.utils\n\nimport android.content.BroadcastReceiver\nimport android.content.Context\nimport android.content.Context.RECEIVER_EXPORTED\nimport android.content.IntentFilter\nimport android.os.Build\n\nobject AndroidUtils {\n    fun Context.registerReceiverCompat(\n        receiver: BroadcastReceiver,\n        vararg actions: String\n    ) {\n        val intentFilter = IntentFilter()\n        actions.forEach { intentFilter.addAction(it) }\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {\n            registerReceiver(receiver, intentFilter, RECEIVER_EXPORTED)\n        } else {\n            registerReceiver(receiver, intentFilter)\n        }\n    }\n}"
  },
  {
    "path": "android/app/src/main/kotlin/com/openlist/mobile/utils/BatteryOptimizationUtils.kt",
    "content": "package com.openlist.mobile.utils\n\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport android.content.Intent\nimport android.net.Uri\nimport android.os.Build\nimport android.os.PowerManager\nimport android.provider.Settings\nimport android.util.Log\nimport androidx.annotation.RequiresApi\n\n/**\n * 电池优化管理工具类\n * 帮助应用获得电池优化白名单，提高后台服务存活率\n */\nobject BatteryOptimizationUtils {\n    private const val TAG = \"BatteryOptimization\"\n\n    /**\n     * 检查是否在电池优化白名单中\n     */\n    @RequiresApi(Build.VERSION_CODES.M)\n    fun isIgnoringBatteryOptimizations(context: Context): Boolean {\n        return try {\n            val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager\n            powerManager.isIgnoringBatteryOptimizations(context.packageName)\n        } catch (e: Exception) {\n            Log.e(TAG, \"Failed to check battery optimization status\", e)\n            false\n        }\n    }\n\n    /**\n     * 请求忽略电池优化\n     */\n    @SuppressLint(\"BatteryLife\")\n    @RequiresApi(Build.VERSION_CODES.M)\n    fun requestIgnoreBatteryOptimizations(context: Context): Boolean {\n        return try {\n            if (isIgnoringBatteryOptimizations(context)) {\n                Log.d(TAG, \"Already ignoring battery optimizations\")\n                return true\n            }\n\n            val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {\n                data = Uri.parse(\"package:${context.packageName}\")\n            }\n            \n            if (intent.resolveActivity(context.packageManager) != null) {\n                context.startActivity(intent)\n                Log.d(TAG, \"Battery optimization request sent\")\n                true\n            } else {\n                Log.w(TAG, \"No activity found to handle battery optimization request\")\n                false\n            }\n        } catch (e: Exception) {\n            Log.e(TAG, \"Failed to request battery optimization exemption\", e)\n            false\n        }\n    }\n\n    /**\n     * 打开电池优化设置页面\n     */\n    fun openBatteryOptimizationSettings(context: Context): Boolean {\n        return try {\n            val intent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n                Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS)\n            } else {\n                Intent(Settings.ACTION_APPLICATION_SETTINGS)\n            }\n            \n            if (intent.resolveActivity(context.packageManager) != null) {\n                context.startActivity(intent)\n                Log.d(TAG, \"Battery optimization settings opened\")\n                true\n            } else {\n                Log.w(TAG, \"No activity found to handle battery optimization settings\")\n                false\n            }\n        } catch (e: Exception) {\n            Log.e(TAG, \"Failed to open battery optimization settings\", e)\n            false\n        }\n    }\n\n    /**\n     * 打开应用详情页面\n     */\n    fun openAppDetailsSettings(context: Context): Boolean {\n        return try {\n            val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {\n                data = Uri.parse(\"package:${context.packageName}\")\n            }\n            \n            if (intent.resolveActivity(context.packageManager) != null) {\n                context.startActivity(intent)\n                Log.d(TAG, \"App details settings opened\")\n                true\n            } else {\n                Log.w(TAG, \"No activity found to handle app details settings\")\n                false\n            }\n        } catch (e: Exception) {\n            Log.e(TAG, \"Failed to open app details settings\", e)\n            false\n        }\n    }\n\n    /**\n     * 尝试打开自启动管理页面（针对不同厂商）\n     */\n    fun openAutoStartSettings(context: Context): Boolean {\n        return try {\n            val autoStartIntents = listOf(\n                // 华为\n                Intent().setClassName(\n                    \"com.huawei.systemmanager\",\n                    \"com.huawei.systemmanager.startupmgr.ui.StartupNormalAppListActivity\"\n                ),\n                Intent().setClassName(\n                    \"com.huawei.systemmanager\",\n                    \"com.huawei.systemmanager.optimize.process.ProtectActivity\"\n                ),\n                // 小米\n                Intent().setClassName(\n                    \"com.miui.securitycenter\",\n                    \"com.miui.permcenter.autostart.AutoStartManagementActivity\"\n                ),\n                Intent().setClassName(\n                    \"com.xiaomi.mipicks\",\n                    \"com.xiaomi.mipicks.ui.AppPicksTabActivity\"\n                ),\n                // OPPO\n                Intent().setClassName(\n                    \"com.coloros.safecenter\",\n                    \"com.coloros.safecenter.permission.startup.FakeActivity\"\n                ),\n                Intent().setClassName(\n                    \"com.oppo.safe\",\n                    \"com.oppo.safe.permission.startup.StartupAppListActivity\"\n                ),\n                // Vivo\n                Intent().setClassName(\n                    \"com.iqoo.secure\",\n                    \"com.iqoo.secure.ui.phoneoptimize.AddWhiteListActivity\"\n                ),\n                Intent().setClassName(\n                    \"com.vivo.permissionmanager\",\n                    \"com.vivo.permissionmanager.activity.BgStartUpManagerActivity\"\n                ),\n                // 魅族\n                Intent().setClassName(\n                    \"com.meizu.safe\",\n                    \"com.meizu.safe.security.SHOW_APPSEC\"\n                ).apply {\n                    addCategory(Intent.CATEGORY_DEFAULT)\n                    putExtra(\"packageName\", context.packageName)\n                },\n                // 三星\n                Intent().setClassName(\n                    \"com.samsung.android.lool\",\n                    \"com.samsung.android.sm.ui.battery.BatteryActivity\"\n                ),\n                // 一加\n                Intent().setClassName(\n                    \"com.oneplus.security\",\n                    \"com.oneplus.security.chainlaunch.view.ChainLaunchAppListActivity\"\n                )\n            )\n\n            for (intent in autoStartIntents) {\n                try {\n                    if (intent.resolveActivity(context.packageManager) != null) {\n                        context.startActivity(intent)\n                        Log.d(TAG, \"Auto start settings opened: ${intent.component}\")\n                        return true\n                    }\n                } catch (e: Exception) {\n                    Log.d(TAG, \"Failed to open auto start settings: ${intent.component}\", e)\n                }\n            }\n\n            Log.w(TAG, \"No auto start settings activity found\")\n            false\n        } catch (e: Exception) {\n            Log.e(TAG, \"Failed to open auto start settings\", e)\n            false\n        }\n    }\n\n    /**\n     * 获取电池优化状态描述\n     */\n    fun getBatteryOptimizationStatus(context: Context): String {\n        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n            if (isIgnoringBatteryOptimizations(context)) {\n                \"已加入电池优化白名单\"\n            } else {\n                \"未加入电池优化白名单\"\n            }\n        } else {\n            \"系统版本过低，无需设置\"\n        }\n    }\n\n    /**\n     * 检查是否需要设置电池优化\n     */\n    fun needsBatteryOptimizationSetup(context: Context): Boolean {\n        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n            !isIgnoringBatteryOptimizations(context)\n        } else {\n            false\n        }\n    }\n}"
  },
  {
    "path": "android/app/src/main/kotlin/com/openlist/mobile/utils/ClipBoardUtils.kt",
    "content": "package com.openlist.mobile.utils\n\nimport android.content.ClipData\nimport android.content.ClipboardManager\nimport android.content.ClipboardManager.OnPrimaryClipChangedListener\nimport android.content.Context\nimport com.openlist.mobile.app\n\n\n/**\n * <pre>\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2016/09/25\n * desc  : utils about clipboard\n</pre> *\n */\nobject ClipboardUtils {\n\n    /**\n     * Copy the text to clipboard.\n     *\n     * The label equals name of package.\n     *\n     * @param text The text.\n     */\n    fun copyText(text: CharSequence?) {\n        val cm = app.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager\n        cm.setPrimaryClip(ClipData.newPlainText(app.getPackageName(), text))\n    }\n\n    /**\n     * Copy the text to clipboard.\n     *\n     * @param label The label.\n     * @param text  The text.\n     */\n    fun copyText(label: CharSequence?, text: CharSequence?) {\n        val cm = app.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager\n        cm.setPrimaryClip(ClipData.newPlainText(label, text))\n    }\n\n    /**\n     * Clear the clipboard.\n     */\n    fun clear() {\n        val cm = app.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager\n        cm.setPrimaryClip(ClipData.newPlainText(null, \"\"))\n    }\n\n    /**\n     * Return the label for clipboard.\n     *\n     * @return the label for clipboard\n     */\n    fun getLabel(): CharSequence {\n        val cm = app\n            .getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager\n        val des = cm.primaryClipDescription ?: return \"\"\n        return des.label ?: return \"\"\n    }\n\n    /**\n     * Return the text for clipboard.\n     *\n     * @return the text for clipboard\n     */\n    val text: CharSequence\n        get() {\n            val cm =\n                app.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager\n            val clip = cm.primaryClip\n            if (clip != null && clip.itemCount > 0) {\n                val text = clip.getItemAt(0).coerceToText(app)\n                if (text != null) {\n                    return text\n                }\n            }\n            return \"\"\n        }\n\n    /**\n     * Add the clipboard changed listener.\n     */\n    fun addChangedListener(listener: OnPrimaryClipChangedListener?) {\n        val cm = app.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager\n        cm.addPrimaryClipChangedListener(listener)\n    }\n\n    /**\n     * Remove the clipboard changed listener.\n     */\n    fun removeChangedListener(listener: OnPrimaryClipChangedListener?) {\n        val cm = app.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager\n        cm.removePrimaryClipChangedListener(listener)\n    }\n}"
  },
  {
    "path": "android/app/src/main/kotlin/com/openlist/mobile/utils/FileUtils.kt",
    "content": "package com.openlist.mobile.utils\n\nimport java.io.File\nimport java.io.InputStream\nimport java.net.URLConnection\n\nobject FileUtils {\n    val File.mimeType: String?\n        get() {\n            val fileNameMap = URLConnection.getFileNameMap()\n            return fileNameMap.getContentTypeFor(name)\n        }\n\n    /**\n     * 按行读取txt\n     */\n    fun InputStream.readAllText(): String {\n        val bufferedReader = this.bufferedReader()\n        val buffer = StringBuffer(\"\")\n        var str: String?\n        while (bufferedReader.readLine().also { str = it } != null) {\n            buffer.append(str)\n            buffer.append(\"\\n\")\n        }\n        return buffer.toString()\n    }\n\n    fun copyFolder(src: File, target: File, overwrite: Boolean = true) {\n        val folder = File(target.absolutePath + File.separator + src.name)\n        folder.mkdirs()\n\n        src.listFiles()?.forEach {\n            if (it.isFile) {\n                val newFile = File(folder.absolutePath + File.separator + it.name)\n                it.copyTo(newFile, overwrite)\n            } else if (it.isDirectory) {\n                copyFolder(it, folder)\n            }\n        }\n\n    }\n}"
  },
  {
    "path": "android/app/src/main/kotlin/com/openlist/mobile/utils/MyTools.kt",
    "content": "package com.openlist.mobile.utils\n\nimport android.annotation.SuppressLint\nimport android.app.PendingIntent\nimport android.content.Context\nimport android.content.Intent\nimport android.content.pm.ShortcutInfo\nimport android.content.pm.ShortcutManager\nimport android.graphics.drawable.Icon\nimport android.net.Uri\nimport android.os.Build\nimport android.provider.Settings\nimport com.openlist.mobile.utils.ToastUtils.longToast\nimport splitties.systemservices.powerManager\n\nobject MyTools {\n    fun Context.isIgnoringBatteryOptimizations(): Boolean {\n        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&\n                powerManager.isIgnoringBatteryOptimizations(packageName)\n    }\n\n    @SuppressLint(\"BatteryLife\")\n    fun Context.killBattery() {\n        runCatching {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !isIgnoringBatteryOptimizations()) {\n                startActivity(Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {\n                    data = Uri.parse(\"package:$packageName\")\n                })\n            }\n        }\n    }\n\n    /* 添加快捷方式 */\n    @SuppressLint(\"UnspecifiedImmutableFlag\")\n    @Suppress(\"DEPRECATION\")\n    fun addShortcut(\n        ctx: Context,\n        name: String,\n        id: String,\n        iconResId: Int,\n        launcherIntent: Intent\n    ) {\n        ctx.longToast(\"如失败 请手动授予权限\")\n        if (Build.VERSION.SDK_INT < 26) { /* Android8.0 */\n            val addShortcutIntent = Intent(\"com.android.launcher.action.INSTALL_SHORTCUT\")\n            // 不允许重复创建\n            addShortcutIntent.putExtra(\"duplicate\", false) // 经测试不是根据快捷方式的名字判断重复的\n            addShortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, name)\n            addShortcutIntent.putExtra(\n                Intent.EXTRA_SHORTCUT_ICON_RESOURCE,\n                Intent.ShortcutIconResource.fromContext(\n                    ctx, iconResId\n                )\n            )\n\n            launcherIntent.action = Intent.ACTION_MAIN\n            launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER)\n            addShortcutIntent\n                .putExtra(Intent.EXTRA_SHORTCUT_INTENT, launcherIntent)\n\n            // 发送广播\n            ctx.sendBroadcast(addShortcutIntent)\n        } else {\n            val shortcutManager: ShortcutManager = ctx.getSystemService(ShortcutManager::class.java)\n            if (shortcutManager.isRequestPinShortcutSupported) {\n                launcherIntent.action = Intent.ACTION_VIEW\n                val pinShortcutInfo = ShortcutInfo.Builder(ctx, id)\n                    .setIcon(\n                        Icon.createWithResource(ctx, iconResId)\n                    )\n                    .setIntent(launcherIntent)\n                    .setShortLabel(name)\n                    .build()\n                val pinnedShortcutCallbackIntent = shortcutManager\n                    .createShortcutResultIntent(pinShortcutInfo)\n                //Get notified when a shortcut is pinned successfully//\n                val pendingIntentFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {\n                    PendingIntent.FLAG_IMMUTABLE\n                } else {\n                    0\n                }\n                val successCallback = PendingIntent.getBroadcast(\n                    ctx, 0, pinnedShortcutCallbackIntent, pendingIntentFlags\n                )\n                shortcutManager.requestPinShortcut(\n                    pinShortcutInfo, successCallback.intentSender\n                )\n            }\n        }\n    }\n}"
  },
  {
    "path": "android/app/src/main/kotlin/com/openlist/mobile/utils/StringUtils.kt",
    "content": "package com.openlist.mobile.utils\n\nobject StringUtils {\n    private fun paramsParseInternal(params: String): HashMap<String, String> {\n        val parameters: HashMap<String, String> = hashMapOf()\n        if (params.isBlank()) return parameters\n\n        for (param in params.split(\"&\")) {\n            val entry = param.split(\"=\".toRegex()).dropLastWhile { it.isEmpty() }\n            if (entry.size > 1) {\n                parameters[entry[0]] = entry[1]\n            } else {\n                parameters[entry[0]] = \"\"\n            }\n        }\n        return parameters\n    }\n\n    fun String.paramsParse() = paramsParseInternal(this)\n\n    fun String.toNumberInt(): Int {\n        return this.replace(Regex(\"[^0-9]\"), \"\").toIntOrNull() ?: 0\n    }\n\n    private val mAnsiRegex = Regex(\"\"\"\\x1b(\\[.*?[@-~]|].*?(\\x07|\\x1b\\\\))\"\"\")\n    fun String.removeAnsiCodes(): String {\n        return mAnsiRegex.replace(this, \"\")\n    }\n\n    fun String.parseToMap(): Map<String, String> {\n        return this.split(\";\").associate {\n            val ss = it.trim().split(\"=\")\n            if (ss.size != 2) return@associate \"\" to \"\"\n\n            val key = ss[0]\n            val value = ss[1]\n            key.trim() to value.trim()\n        }\n    }\n}"
  },
  {
    "path": "android/app/src/main/kotlin/com/openlist/mobile/utils/ToastUtils.kt",
    "content": "package com.openlist.mobile.utils\n\nimport android.content.Context\nimport android.widget.Toast\nimport androidx.annotation.StringRes\nimport kotlinx.coroutines.DelicateCoroutinesApi\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.GlobalScope\nimport kotlinx.coroutines.launch\n\nobject ToastUtils {\n    @OptIn(DelicateCoroutinesApi::class)\n    fun runMain(block: () -> Unit) {\n        GlobalScope.launch(Dispatchers.Main) {\n            block()\n        }\n    }\n\n    fun Context.toast(str: String) {\n        runMain {\n            Toast.makeText(this, str, Toast.LENGTH_SHORT).show()\n        }\n    }\n\n    fun Context.toast(@StringRes strId: Int, vararg args: Any) {\n        runMain {\n            Toast.makeText(\n                this,\n                getString(strId, *args),\n                Toast.LENGTH_SHORT\n            ).show()\n        }\n    }\n\n    fun Context.longToast(str: String) {\n        runMain {\n            Toast.makeText(this, str, Toast.LENGTH_LONG).show()\n        }\n    }\n\n    fun Context.longToast(@StringRes strId: Int) {\n        runMain {\n            Toast.makeText(this, strId, Toast.LENGTH_LONG).show()\n        }\n    }\n\n    fun Context.longToast(@StringRes strId: Int, vararg args: Any) {\n        runMain {\n            Toast.makeText(\n                this,\n                getString(strId, *args),\n                Toast.LENGTH_LONG\n            ).show()\n        }\n    }\n}"
  },
  {
    "path": "android/app/src/main/res/drawable/ic_download.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\" android:height=\"24dp\" android:viewportHeight=\"1024\" android:viewportWidth=\"1024\" android:width=\"24dp\">\n      \n    <path android:fillColor=\"#000000\" android:pathData=\"M853.3,853.3a42.7,42.7 0,0 1,0 85.3H170.7a42.7,42.7 0,0 1,0 -85.3h682.7zM512,85.5a42.7,42.7 0,0 1,42.7 42.7v515.4l204.4,-204.4a42.7,42.7 0,0 1,63.9 56.3l-3.6,4 -277.4,277.5a42.7,42.7 0,0 1,-56.3 3.6l-4,-3.5 -277.1,-276.7a42.7,42.7 0,0 1,56.2 -64l4,3.5L469.3,644.1V128.2a42.7,42.7 0,0 1,42.7 -42.7z\"/>\n    \n</vector>\n"
  },
  {
    "path": "android/app/src/main/res/drawable/ic_female.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:pathData=\"M512,105.5c-220.6,0 -399.5,178.9 -399.5,399.5S291.4,904.5 512,904.5 911.5,725.6 911.5,505 732.6,105.5 512,105.5zM712,727.4L561.5,727.4v75.1h-99.1v-75.1L312,727.4L312,627.3h150.4v-6.2c-33.6,-8.6 -65.5,-25.9 -91.8,-52.2 -78,-78 -78,-204.5 0,-282.5s204.5,-78 282.5,0 78,204.5 0,282.5c-26.3,26.3 -58.1,43.7 -91.7,52.2v6.2h150.4v100.1h0.2z\"\n      android:fillColor=\"#FF4D94\"/>\n  <path\n      android:pathData=\"M582.4,356.1c-39.1,-39.1 -102.5,-39.1 -141.6,0 -39.1,39.1 -39.1,102.4 0,141.5 39.1,39.1 102.5,39.1 141.6,0 39.1,-39 39.1,-102.4 0,-141.5z\"\n      android:fillColor=\"#FF4D94\"/>\n</vector>\n"
  },
  {
    "path": "android/app/src/main/res/drawable/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\" android:height=\"72dp\" android:viewportHeight=\"1024\" android:viewportWidth=\"1024\" android:width=\"72dp\">\n      \n    <path android:fillColor=\"#79ddd1\" android:pathData=\"M437,120.7C469.2,115.4 502.2,116 534.7,118.1C564.4,120.7 593.5,127.6 622.1,135.8C622.8,136.3 623.7,136.9 624.5,137.4C627.6,137.8 630.5,138.8 633.5,139.7C634.1,140.3 634.7,140.8 635.3,141.4C661.1,149.6 685.1,162.8 708.5,176.3C732.4,191 754.2,209 774.7,228C779.2,232.9 784.3,237.3 788.9,242.1C794.2,250.3 802.4,256.2 807.5,264.5C826.6,287.6 842.2,313.4 854.9,340.5C856.5,344.1 858.7,347.6 859.8,351.5C861.6,356.5 865.3,361.1 865.5,366.6C866.2,367.3 866.8,368 867.5,368.7C867.4,369.7 867.4,370.7 867.4,371.7C867.9,372.1 868.9,372.8 869.3,373.2C869.4,374.3 869.4,375.3 869.5,376.4C879.5,401.7 884.9,429 889.4,455.9C896.5,515.1 891,576 871.8,632.5C870.1,639.4 867,645.9 865,652.6C864.7,653.1 863.9,653.9 863.6,654.3C863.2,658 861.4,661.3 859.7,664.5C858.2,670.1 854.6,674.9 852.9,680.5C847.9,689.2 844.1,698.7 839.1,707.5C838.7,707.7 837.9,708.3 837.5,708.6C837.4,709.1 837.4,709.7 837.4,710.3C835.1,712.9 833.9,716.2 831.7,718.8C824.1,731.5 815.8,744.2 805.4,754.8C805.5,755.2 805.5,755.7 805.6,756.2C777.8,787.7 747.6,817.7 712.1,840.5C711.4,840.4 710,840.3 709.3,840.2C709.3,840.8 709.3,842 709.3,842.6C699.5,848.4 689.7,854.4 680.1,860.4C679.5,860.4 678.5,860.4 677.9,860.3C670.2,866.3 660.5,869.2 652.1,874.1C647,875.2 642.6,878 638,880.1C635.9,880.4 633.9,881.2 632.1,882.4C631.2,882.4 630.4,882.5 629.6,882.5C628.1,883.7 626.4,884.3 624.6,884.7C599.8,893.7 574.2,900.2 548,903.2C522.7,907.5 496.7,906.5 471,906.3C454.7,904.1 437.4,905 422.1,898.8C391.1,889 373.1,850.2 387.2,820.4C393.1,809.5 399.4,798.7 406.6,788.5C406.6,787.9 406.7,786.9 406.7,786.3C410.5,783.7 411.6,779 414.3,775.4C427.4,755.4 439.4,734.7 452.4,714.5C452.5,713.8 452.5,713.1 452.6,712.4C453,712.1 453.8,711.6 454.2,711.3C472.1,683.2 489.8,655 507.3,626.6C520.2,607.3 531.8,587.1 544.7,567.8C555.7,550.2 566.3,532.3 578.6,515.5C579.1,515.3 580,514.9 580.5,514.7C584.9,507.5 593.4,504.8 599.8,499.8C605.8,498.6 611.5,495.6 617.7,495.4C628.5,493.3 639.3,496.1 649.3,499.8C660.3,505.7 670.5,513.5 676.9,524.4C678,527.2 679.5,529.9 681,532.6C685.7,545.7 688.3,560.6 683.2,574.1C678.9,590 668.1,602.9 659.8,616.9C655,624.4 650.4,632.1 645.5,639.6C645.5,640.1 645.3,641.1 645.2,641.6C634.3,655.9 625.8,672 615.6,686.8C615.5,687.3 615.3,688.2 615.1,688.6C608.6,697.9 602.9,707.8 596.7,717.4C591.7,725.2 586.6,732.9 581.9,740.9C576.1,749.3 571.2,758.3 565.9,767C563.2,771.9 559.3,776.2 557,781.3C555.3,783.1 553.9,785.1 553.8,787.7C573,786.5 590.7,777.8 608.5,771.3C614.9,767.9 621.8,765.4 628,761.7C628.5,761.7 629.6,761.7 630.1,761.6C632.9,758.7 637.2,757.7 640.6,755.5C673.6,736.7 702.3,710.5 725.2,680.3C734.4,666.3 744,652.5 751.5,637.6C758.9,622.3 765.1,606.4 769.9,590.2C771,586.6 773.2,583.2 772.3,579.3C772.6,579.4 773.4,579.6 773.7,579.7C778,558 782,536.2 782.7,514C783.1,471.7 774.7,429.5 758.3,390.5C750.9,376.6 745,361.5 734.6,349.4C734.5,348.7 734.5,348 734.4,347.4C718.5,325.8 701.1,304.5 679.2,288.7C679,288.3 678.5,287.5 678.2,287C671.8,283.1 666.5,277.4 659.7,274C653.2,268.6 645.3,265.2 638.2,260.6C637.6,260.6 637.1,260.5 636.6,260.4C635.9,259.7 635.3,259 634.7,258.4C634.1,258.3 633.1,258.2 632.6,258.2C627.3,254.6 621.5,252 615.5,249.9C612.6,248.2 609.5,246.8 606.2,246.1C605.8,245.7 605.1,245 604.7,244.6C580.8,236.3 556.3,229 531,226.4C511.5,222.9 491.7,225 472,224.5C444.7,227.4 417.6,232 392,242.4C391.5,242.4 390.4,242.4 389.9,242.3C382.6,247 373.3,249.1 366,254.3C365.4,254.3 364.1,254.3 363.5,254.4C349.6,263.8 334.2,271.3 321.3,282.4C317.7,285.4 314,288.4 310,291C305,297.8 297.1,302 292.3,309.2C273.7,328 258,349.5 245,372.5C241.8,377 240.4,382.6 237.5,387C237.4,387.5 237.4,388.5 237.4,389C232.1,396.1 229.8,405.8 222,411C217.8,416.3 211.3,418.7 205.9,422.4C204.8,422.4 203.6,422.4 202.5,422.4C201.7,423 200.9,423.7 200.1,424.3C186.4,426.3 171.5,425.9 159.4,418.5C158.9,418.5 157.9,418.3 157.3,418.2C153.4,413.6 147.4,411.2 143.9,406C139.3,402 137.8,396 134.5,391.1C134.5,390.4 134.6,389.1 134.7,388.4C134,387.3 133.4,386.2 132.7,385.1C132.6,378.4 130.4,371.7 131.8,365C132.3,355 137.2,346 141,337C142.9,332.7 145.8,328.9 147.2,324.4C148.9,322.3 150.3,320.1 151,317.5C179.3,267.7 217.7,223.3 263.9,189.2C269,185.4 274.7,182.3 279.3,177.8C279.8,177.8 280.9,177.6 281.5,177.5C299.9,165.1 320,155.4 340,145.9C342.9,145.1 345.7,144.2 348.5,143.3C376.5,131.3 406.7,124.5 437,120.7Z\"/>\n      \n    <path android:fillColor=\"#10b0ea\" android:pathData=\"M432.3,344.5C457.7,340.5 485,356.2 493.2,380.8C495.3,389.3 495.9,398.1 496.6,406.8C495.9,422.4 485.7,435.1 477.8,447.8C412,551.9 345.1,655.4 279.7,759.7C272,773.3 259.2,784.2 243.8,787.8C214.4,797.1 179.9,777.2 172.8,747.2C169.9,733 171.3,717.5 178.8,704.8C219.3,640.2 261.4,576.5 301.6,511.6C331.8,465 361,417.9 391.3,371.3C400.3,356.9 415.5,346.9 432.3,344.5Z\"/>\n    \n</vector>\n"
  },
  {
    "path": "android/app/src/main/res/drawable/launch_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Modify this file to customize your launch splash screen -->\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@android:color/white\" />\n\n    <!-- You can insert your own image assets here -->\n    <!-- <item>\n        <bitmap\n            android:gravity=\"center\"\n            android:src=\"@mipmap/launch_image\" />\n    </item> -->\n</layer-list>\n"
  },
  {
    "path": "android/app/src/main/res/drawable/openlist_logo.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"1024dp\"\n    android:height=\"1024dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n  <path\n      android:pathData=\"M437,120.7C469.2,115.4 502.2,116 534.7,118.1C564.4,120.7 593.5,127.6 622.1,135.8C622.8,136.3 623.7,136.9 624.5,137.4C627.6,137.8 630.5,138.8 633.5,139.7C634.1,140.3 634.7,140.8 635.3,141.4C661.1,149.6 685.1,162.8 708.5,176.3C732.4,191 754.2,209 774.7,228C779.2,232.9 784.3,237.3 788.9,242.1C794.2,250.3 802.4,256.2 807.5,264.5C826.6,287.6 842.2,313.4 854.9,340.5C856.5,344.1 858.7,347.6 859.8,351.5C861.6,356.5 865.3,361.1 865.5,366.6C866.2,367.3 866.8,368 867.5,368.7C867.4,369.7 867.4,370.7 867.4,371.7C867.9,372.1 868.9,372.8 869.3,373.2C869.4,374.3 869.4,375.3 869.5,376.4C879.5,401.7 884.9,429 889.4,455.9C896.5,515.1 891,576 871.8,632.5C870.1,639.4 867,645.9 865,652.6C864.7,653.1 863.9,653.9 863.6,654.3C863.2,658 861.4,661.3 859.7,664.5C858.2,670.1 854.6,674.9 852.9,680.5C847.9,689.2 844.1,698.7 839.1,707.5C838.7,707.7 837.9,708.3 837.5,708.6C837.4,709.1 837.4,709.7 837.4,710.3C835.1,712.9 833.9,716.2 831.7,718.8C824.1,731.5 815.8,744.2 805.4,754.8C805.5,755.2 805.5,755.7 805.6,756.2C777.8,787.7 747.6,817.7 712.1,840.5C711.4,840.4 710,840.3 709.3,840.2C709.3,840.8 709.3,842 709.3,842.6C699.5,848.4 689.7,854.4 680.1,860.4C679.5,860.4 678.5,860.4 677.9,860.3C670.2,866.3 660.5,869.2 652.1,874.1C647,875.2 642.6,878 638,880.1C635.9,880.4 633.9,881.2 632.1,882.4C631.2,882.4 630.4,882.5 629.6,882.5C628.1,883.7 626.4,884.3 624.6,884.7C599.8,893.7 574.2,900.2 548,903.2C522.7,907.5 496.7,906.5 471,906.3C454.7,904.1 437.4,905 422.1,898.8C391.1,889 373.1,850.2 387.2,820.4C393.1,809.5 399.4,798.7 406.6,788.5C406.6,787.9 406.7,786.9 406.7,786.3C410.5,783.7 411.6,779 414.3,775.4C427.4,755.4 439.4,734.7 452.4,714.5C452.5,713.8 452.5,713.1 452.6,712.4C453,712.1 453.8,711.6 454.2,711.3C472.1,683.2 489.8,655 507.3,626.6C520.2,607.3 531.8,587.1 544.7,567.8C555.7,550.2 566.3,532.3 578.6,515.5C579.1,515.3 580,514.9 580.5,514.7C584.9,507.5 593.4,504.8 599.8,499.8C605.8,498.6 611.5,495.6 617.7,495.4C628.5,493.3 639.3,496.1 649.3,499.8C660.3,505.7 670.5,513.5 676.9,524.4C678,527.2 679.5,529.9 681,532.6C685.7,545.7 688.3,560.6 683.2,574.1C678.9,590 668.1,602.9 659.8,616.9C655,624.4 650.4,632.1 645.5,639.6C645.5,640.1 645.3,641.1 645.2,641.6C634.3,655.9 625.8,672 615.6,686.8C615.5,687.3 615.3,688.2 615.1,688.6C608.6,697.9 602.9,707.8 596.7,717.4C591.7,725.2 586.6,732.9 581.9,740.9C576.1,749.3 571.2,758.3 565.9,767C563.2,771.9 559.3,776.2 557,781.3C555.3,783.1 553.9,785.1 553.8,787.7C573,786.5 590.7,777.8 608.5,771.3C614.9,767.9 621.8,765.4 628,761.7C628.5,761.7 629.6,761.7 630.1,761.6C632.9,758.7 637.2,757.7 640.6,755.5C673.6,736.7 702.3,710.5 725.2,680.3C734.4,666.3 744,652.5 751.5,637.6C758.9,622.3 765.1,606.4 769.9,590.2C771,586.6 773.2,583.2 772.3,579.3C772.6,579.4 773.4,579.6 773.7,579.7C778,558 782,536.2 782.7,514C783.1,471.7 774.7,429.5 758.3,390.5C750.9,376.6 745,361.5 734.6,349.4C734.5,348.7 734.5,348 734.4,347.4C718.5,325.8 701.1,304.5 679.2,288.7C679,288.3 678.5,287.5 678.2,287C671.8,283.1 666.5,277.4 659.7,274C653.2,268.6 645.3,265.2 638.2,260.6C637.6,260.6 637.1,260.5 636.6,260.4C635.9,259.7 635.3,259 634.7,258.4C634.1,258.3 633.1,258.2 632.6,258.2C627.3,254.6 621.5,252 615.5,249.9C612.6,248.2 609.5,246.8 606.2,246.1C605.8,245.7 605.1,245 604.7,244.6C580.8,236.3 556.3,229 531,226.4C511.5,222.9 491.7,225 472,224.5C444.7,227.4 417.6,232 392,242.4C391.5,242.4 390.4,242.4 389.9,242.3C382.6,247 373.3,249.1 366,254.3C365.4,254.3 364.1,254.3 363.5,254.4C349.6,263.8 334.2,271.3 321.3,282.4C317.7,285.4 314,288.4 310,291C305,297.8 297.1,302 292.3,309.2C273.7,328 258,349.5 245,372.5C241.8,377 240.4,382.6 237.5,387C237.4,387.5 237.4,388.5 237.4,389C232.1,396.1 229.8,405.8 222,411C217.8,416.3 211.3,418.7 205.9,422.4C204.8,422.4 203.6,422.4 202.5,422.4C201.7,423 200.9,423.7 200.1,424.3C186.4,426.3 171.5,425.9 159.4,418.5C158.9,418.5 157.9,418.3 157.3,418.2C153.4,413.6 147.4,411.2 143.9,406C139.3,402 137.8,396 134.5,391.1C134.5,390.4 134.6,389.1 134.7,388.4C134,387.3 133.4,386.2 132.7,385.1C132.6,378.4 130.4,371.7 131.8,365C132.3,355 137.2,346 141,337C142.9,332.7 145.8,328.9 147.2,324.4C148.9,322.3 150.3,320.1 151,317.5C179.3,267.7 217.7,223.3 263.9,189.2C269,185.4 274.7,182.3 279.3,177.8C279.8,177.8 280.9,177.6 281.5,177.5C299.9,165.1 320,155.4 340,145.9C342.9,145.1 345.7,144.2 348.5,143.3C376.5,131.3 406.7,124.5 437,120.7Z\"\n      android:fillColor=\"#79ddd1\"/>\n  <path\n      android:pathData=\"M432.3,344.5C457.7,340.5 485,356.2 493.2,380.8C495.3,389.3 495.9,398.1 496.6,406.8C495.9,422.4 485.7,435.1 477.8,447.8C412,551.9 345.1,655.4 279.7,759.7C272,773.3 259.2,784.2 243.8,787.8C214.4,797.1 179.9,777.2 172.8,747.2C169.9,733 171.3,717.5 178.8,704.8C219.3,640.2 261.4,576.5 301.6,511.6C331.8,465 361,417.9 391.3,371.3C400.3,356.9 415.5,346.9 432.3,344.5Z\"\n      android:fillColor=\"#10b0ea\"/>\n</vector>\n"
  },
  {
    "path": "android/app/src/main/res/drawable/openlist_switch.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:tint=\"@android:color/holo_blue_bright\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M15,9L9,9c-0.55,0 -1,0.45 -1,1v12c0,0.55 0.45,1 1,1h6c0.55,0 1,-0.45 1,-1L16,10c0,-0.55 -0.45,-1 -1,-1zM12,15c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM7.05,6.05l1.41,1.41C9.37,6.56 10.62,6 12,6s2.63,0.56 3.54,1.46l1.41,-1.41C15.68,4.78 13.93,4 12,4s-3.68,0.78 -4.95,2.05zM12,0C8.96,0 6.21,1.23 4.22,3.22l1.41,1.41C7.26,3.01 9.51,2 12,2s4.74,1.01 6.36,2.64l1.41,-1.41C17.79,1.23 15.04,0 12,0z\" />\n</vector>\n"
  },
  {
    "path": "android/app/src/main/res/drawable/server.xml",
    "content": "<vector android:autoMirrored=\"true\" android:height=\"24dp\"\n    android:viewportHeight=\"1024\" android:viewportWidth=\"1024\"\n    android:width=\"24dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"#FF000000\" android:pathData=\"M170.7,42.7 L853.3,42.7C876.8,42.7 896,61.9 896,85.3L896,256C896,279.5 876.8,298.7 853.3,298.7L170.7,298.7C147.2,298.7 128,279.5 128,256L128,85.3C128,61.9 147.2,42.7 170.7,42.7M170.7,384 L853.3,384C876.8,384 896,403.2 896,426.7L896,597.3C896,620.8 876.8,640 853.3,640L170.7,640C147.2,640 128,620.8 128,597.3L128,426.7C128,403.2 147.2,384 170.7,384M170.7,725.3 L853.3,725.3C876.8,725.3 896,744.5 896,768L896,938.7C896,962.1 876.8,981.3 853.3,981.3L170.7,981.3C147.2,981.3 128,962.1 128,938.7L128,768C128,744.5 147.2,725.3 170.7,725.3M384,213.3 L426.7,213.3 426.7,128 384,128 384,213.3M384,554.7 L426.7,554.7 426.7,469.3 384,469.3 384,554.7M384,896 L426.7,896 426.7,810.7 384,810.7 384,896M213.3,128 L213.3,213.3 298.7,213.3 298.7,128 213.3,128M213.3,469.3 L213.3,554.7 298.7,554.7 298.7,469.3 213.3,469.3M213.3,810.7 L213.3,896 298.7,896 298.7,810.7 213.3,810.7Z\"/>\n</vector>\n"
  },
  {
    "path": "android/app/src/main/res/drawable/server2.xml",
    "content": "<vector android:height=\"24dp\" android:tint=\"#000000\"\n    android:viewportHeight=\"24\" android:viewportWidth=\"24\"\n    android:width=\"24dp\" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <path android:fillColor=\"@android:color/white\" android:pathData=\"M15,9L9,9c-0.55,0 -1,0.45 -1,1v12c0,0.55 0.45,1 1,1h6c0.55,0 1,-0.45 1,-1L16,10c0,-0.55 -0.45,-1 -1,-1zM12,15c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM7.05,6.05l1.41,1.41C9.37,6.56 10.62,6 12,6s2.63,0.56 3.54,1.46l1.41,-1.41C15.68,4.78 13.93,4 12,4s-3.68,0.78 -4.95,2.05zM12,0C8.96,0 6.21,1.23 4.22,3.22l1.41,1.41C7.26,3.01 9.51,2 12,2s4.74,1.01 6.36,2.64l1.41,-1.41C17.79,1.23 15.04,0 12,0z\"/>\n</vector>\n"
  },
  {
    "path": "android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/ic_launcher_background\"/>\n    <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\n</adaptive-icon>"
  },
  {
    "path": "android/app/src/main/res/values/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"ic_launcher_background\">#FFFFFF</color>\n</resources>"
  },
  {
    "path": "android/app/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"app_name\">OpenList Mobile</string>\n\n    <string name=\"open_source_license\">开源许可</string>\n    <string name=\"back\">返回</string>\n    <string name=\"no_file_selected\">未选择文件</string>\n    <string name=\"error\">❌ 错误</string>\n    <string name=\"ok\">确定</string>\n    <string name=\"description\">描述</string>\n    <string name=\"log\">日志</string>\n    <string name=\"openlist_server\">OpenList服务器</string>\n    <string name=\"add_desktop_shortcut\">添加桌面快捷方式</string>\n    <string name=\"shutdown\">关闭</string>\n    <string name=\"copy_address\">复制地址</string>\n    <string name=\"openlist_server_running\">OpenList运行中</string>\n    <string name=\"cancel\">取消</string>\n    <string name=\"admin_password_set_to\">admin 密码已设为：\\n %1$s</string>\n    <string name=\"admin_password\">admin 密码</string>\n    <string name=\"server_shutdown_failed\">关闭失败：%1$s</string>\n    <string name=\"address_copied\">已复制地址</string>\n    <string name=\"set_admin_pwd_for_not_running\">⚠️启动服务器才可设置admin密码</string>\n    <string name=\"openlist_config\">OpenList配置</string>\n    <string name=\"settings\">设置</string>\n    <string name=\"password\">密码</string>\n    <string name=\"openlist_starting\">启动中</string>\n    <string name=\"openlist_shut_downing\">关闭中</string>\n    <string name=\"app_switch\">开关</string>\n    <string name=\"moreOptions\">更多选项</string>\n    <string name=\"about\">关于</string>\n    <string name=\"listen_address\">监听地址</string>\n    <string name=\"edit_config_json\">编辑 config.json</string>\n    <string name=\"no_server_enable_warn_msg\">请至少启用一个服务器！</string>\n    <string name=\"openlist_provider\">OpenList提供者</string>\n    <string name=\"provider_root_summary\">account</string>\n    <string name=\"path_copied\">路径已复制</string>\n    <string name=\"check_update\">检查更新</string>\n    <string name=\"start\">启动</string>\n    <string name=\"all_files_manage_permission\">所有文件访问权限</string>\n    <string name=\"files_permission_desc\">挂载本地存储时必须打开，否则无权限读写文件。</string>\n    <string name=\"read_external_storage_permission\">读取存储权限</string>\n    <string name=\"write_external_storage_permission\">写入存储权限</string>\n    <string name=\"grant_battery_whiltelist\">请求电池优化白名单</string>\n    <string name=\"grant_battery_whiltelist_desc\">如果程序在后台运行时被系统杀死，可以尝试设置。</string>\n    <string name=\"close\">关闭</string>\n    <string name=\"autoCheckForUpdates\">自动检查更新</string>\n    <string name=\"autoCheckForUpdatesDesc\">打开程序主界面时从Github检查更新</string>\n    <string name=\"wake_lock\">唤醒锁</string>\n    <string name=\"wake_lock_desc\">打开可防止锁屏后CPU休眠，但在部分系统可能会导致杀后台</string>\n    <string name=\"importent_settings\">重要设置</string>\n    <string name=\"open_data_folder\">打开data文件夹</string>\n    <string name=\"open_data_folder_tips\">点按上方路径选择“MT管理器”打开data文件夹</string>\n    <string name=\"web\">网页</string>\n    <string name=\"togo_app_failed\">跳转失败: %1$s</string>\n    <string name=\"clear_web_data\">清空网页数据</string>\n    <string name=\"clear_web_cache\">清空网页缓存</string>\n    <string name=\"clear_web_data_desc\">清空网页数据库、Cookie、DomStorage。</string>\n    <string name=\"clear_web_cache_desc\">仅清空资源缓存，不影响用户数据。</string>\n    <string name=\"cleared\">已清除</string>\n    <string name=\"confirm\">确定</string>\n    <string name=\"auto_open_web\">自动打开网页界面</string>\n    <string name=\"auto_open_web_desc\">打开主界面时，自动跳转到网页界面。</string>\n    <string name=\"download_file\">下载文件</string>\n    <string name=\"system_downloader\">系统下载器</string>\n    <string name=\"open_url\">打开链接</string>\n    <string name=\"url_copied\">已复制链接</string>\n    <string name=\"starting\">启动中</string>\n    <string name=\"shutdowned\">已关闭: %1$s</string>\n    <string name=\"browser\">浏览器</string>\n    <string name=\"select_downloader\">选择下载器</string>\n    <string name=\"last_used\">上次使用</string>\n    <string name=\"start_at_boot\">开机自启动服务</string>\n    <string name=\"start_at_boot_desc\">在开机时自动开启OpenList服务。</string>\n    <string name=\"shutdown_failed\">关闭失败</string>\n</resources>\n"
  },
  {
    "path": "android/app/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->\n    <style name=\"LaunchTheme\" parent=\"@android:style/Theme.Light.NoTitleBar\">\n        <!-- Show a splash screen on the activity. Automatically removed when\n             the Flutter engine draws its first frame -->\n        <item name=\"android:windowBackground\">@drawable/launch_background</item>\n    </style>\n    <!-- Theme applied to the Android Window as soon as the process has started.\n         This theme determines the color of the Android Window while your\n         Flutter UI initializes, as well as behind your Flutter UI while its\n         running.\n\n         This Theme is only used starting with V2 of Flutter's Android embedding. -->\n    <style name=\"NormalTheme\" parent=\"@android:style/Theme.Light.NoTitleBar\">\n        <item name=\"android:windowBackground\">?android:colorBackground</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "android/app/src/main/res/values/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <style name=\"Theme.OpenList\" parent=\"android:Theme.Material.Light.NoActionBar\"/>\n\n\n</resources>"
  },
  {
    "path": "android/app/src/main/res/values-en/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"app_name\">OpenList Mobile</string>\n\n    <string name=\"open_source_license\">Open Source License</string>\n    <string name=\"back\">Back</string>\n    <string name=\"no_file_selected\">No file selected</string>\n    <string name=\"error\">❌ Error</string>\n    <string name=\"ok\">OK</string>\n    <string name=\"description\">Description</string>\n    <string name=\"log\">Log</string>\n    <string name=\"openlist_server\">OpenList Server</string>\n    <string name=\"add_desktop_shortcut\">Add Desktop Shortcut</string>\n    <string name=\"shutdown\">Shutdown</string>\n    <string name=\"copy_address\">Copy Address</string>\n    <string name=\"openlist_server_running\">OpenList Running</string>\n    <string name=\"cancel\">Cancel</string>\n    <string name=\"admin_password_set_to\">Admin password set to:\\n %1$s</string>\n    <string name=\"admin_password\">Admin Password</string>\n    <string name=\"server_shutdown_failed\">Shutdown failed: %1$s</string>\n    <string name=\"address_copied\">Address copied</string>\n    <string name=\"set_admin_pwd_for_not_running\">⚠️Start server to set admin password</string>\n    <string name=\"openlist_config\">OpenList Config</string>\n    <string name=\"settings\">Settings</string>\n    <string name=\"password\">Password</string>\n    <string name=\"openlist_starting\">Starting</string>\n    <string name=\"openlist_shut_downing\">Shutting down</string>\n    <string name=\"app_switch\">Switch</string>\n    <string name=\"moreOptions\">More Options</string>\n    <string name=\"about\">About</string>\n    <string name=\"listen_address\">Listen Address</string>\n    <string name=\"edit_config_json\">Edit config.json</string>\n    <string name=\"no_server_enable_warn_msg\">Please enable at least one server!</string>\n    <string name=\"openlist_provider\">OpenList Provider</string>\n    <string name=\"provider_root_summary\">account</string>\n    <string name=\"path_copied\">Path copied</string>\n    <string name=\"check_update\">Check Update</string>\n    <string name=\"start\">Start</string>\n    <string name=\"all_files_manage_permission\">All Files Access Permission</string>\n    <string name=\"files_permission_desc\">Must be enabled when mounting local storage, otherwise no permission to read and write files.</string>\n    <string name=\"read_external_storage_permission\">Read Storage Permission</string>\n    <string name=\"write_external_storage_permission\">Write Storage Permission</string>\n    <string name=\"grant_battery_whiltelist\">Request Battery Optimization Whitelist</string>\n    <string name=\"grant_battery_whiltelist_desc\">If the program is killed by the system when running in the background, you can try to set this.</string>\n    <string name=\"close\">Close</string>\n    <string name=\"autoCheckForUpdates\">Auto Check for Updates</string>\n    <string name=\"autoCheckForUpdatesDesc\">Check for updates from Github when opening the main interface</string>\n    <string name=\"wake_lock\">Wake Lock</string>\n    <string name=\"wake_lock_desc\">Enable to prevent CPU from sleeping after screen lock, but may cause background killing on some systems</string>\n    <string name=\"importent_settings\">Important Settings</string>\n    <string name=\"open_data_folder\">Open Data Folder</string>\n    <string name=\"open_data_folder_tips\">Click the path above and select \\\"MT Manager\\\" to open the data folder</string>\n    <string name=\"web\">Web</string>\n    <string name=\"togo_app_failed\">Jump failed: %1$s</string>\n    <string name=\"clear_web_data\">Clear Web Data</string>\n    <string name=\"clear_web_cache\">Clear Web Cache</string>\n    <string name=\"clear_web_data_desc\">Clear web database, cookies, DomStorage.</string>\n    <string name=\"clear_web_cache_desc\">Only clear resource cache, does not affect user data.</string>\n    <string name=\"cleared\">Cleared</string>\n    <string name=\"confirm\">Confirm</string>\n    <string name=\"auto_open_web\">Auto Open Web Interface</string>\n    <string name=\"auto_open_web_desc\">Automatically jump to web interface when opening main interface.</string>\n    <string name=\"download_file\">Download File</string>\n    <string name=\"system_downloader\">System Downloader</string>\n    <string name=\"open_url\">Open URL</string>\n    <string name=\"url_copied\">URL copied</string>\n    <string name=\"starting\">Starting</string>\n    <string name=\"shutdowned\">Shutdown: %1$s</string>\n    <string name=\"browser\">Browser</string>\n    <string name=\"select_downloader\">Select Downloader</string>\n    <string name=\"last_used\">Last Used</string>\n    <string name=\"start_at_boot\">Auto Start Service at Boot</string>\n    <string name=\"start_at_boot_desc\">Automatically start OpenList service at boot.</string>\n    <string name=\"shutdown_failed\">Shutdown Failed</string>\n</resources>\n"
  },
  {
    "path": "android/app/src/main/res/values-night/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->\n    <style name=\"LaunchTheme\" parent=\"@android:style/Theme.Black.NoTitleBar\">\n        <!-- Show a splash screen on the activity. Automatically removed when\n             the Flutter engine draws its first frame -->\n        <item name=\"android:windowBackground\">@drawable/launch_background</item>\n    </style>\n    <!-- Theme applied to the Android Window as soon as the process has started.\n         This theme determines the color of the Android Window while your\n         Flutter UI initializes, as well as behind your Flutter UI while its\n         running.\n\n         This Theme is only used starting with V2 of Flutter's Android embedding. -->\n    <style name=\"NormalTheme\" parent=\"@android:style/Theme.Black.NoTitleBar\">\n        <item name=\"android:windowBackground\">?android:colorBackground</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "android/app/src/main/res/xml/backup_rules.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<full-backup-content/>\n"
  },
  {
    "path": "android/app/src/main/res/xml/data_extraction_rules.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<data-extraction-rules>\n    <cloud-backup/>\n</data-extraction-rules>\n"
  },
  {
    "path": "android/app/src/main/res/xml/file_path_data.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<paths>\n    <files-path name=\"files\" path=\"./data\"/>\n</paths>\n"
  },
  {
    "path": "android/app/src/main/res/xml/file_paths.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<paths>\n    <external-path name=\"external\" path=\".\"/>\n    <external-files-path name=\"external_files\" path=\".\"/>\n    <cache-path name=\"cache\" path=\".\"/>\n    <external-cache-path name=\"external_cache\" path=\".\"/>\n    <files-path name=\"files\" path=\".\"/>\n</paths>\n"
  },
  {
    "path": "android/app/src/main/res/xml/network_security_config.xml",
    "content": "<network-security-config xmlns:tools=\"http://schemas.android.com/tools\">\n    <base-config\n        cleartextTrafficPermitted=\"true\"\n        tools:ignore=\"InsecureBaseConfiguration\" />\n</network-security-config>"
  },
  {
    "path": "android/app/src/profile/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <!-- The INTERNET permission is required for development. Specifically,\n         the Flutter tool needs it to communicate with the running application\n         to allow setting breakpoints, to provide hot reload, etc.\n    -->\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n</manifest>\n"
  },
  {
    "path": "android/build/reports/problems/problems-report.html",
    "content": "<!DOCTYPE html>\n\n<html lang=\"en\">\n<head>\n    <!-- Required meta tags -->\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n\n                    <style type=\"text/css\">\n                /*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */\nhtml {\n    line-height: 1.15;\n    -ms-text-size-adjust: 100%;\n    -webkit-text-size-adjust: 100%\n}\n\nbody {\n    margin: 0\n}\n\narticle, aside, footer, header, nav, section {\n    display: block\n}\n\nh1 {\n    font-size: 2em;\n    margin: .67em 0\n}\n\nfigcaption, figure, main {\n    display: block\n}\n\nfigure {\n    margin: 1em 40px\n}\n\nhr {\n    box-sizing: content-box;\n    height: 0;\n    overflow: visible\n}\n\npre {\n    font-family: monospace, monospace;\n    font-size: 1em\n}\n\na {\n    background-color: transparent;\n    -webkit-text-decoration-skip: objects\n}\n\nabbr[title] {\n    border-bottom: none;\n    text-decoration: underline;\n    text-decoration: underline dotted\n}\n\nb, strong {\n    font-weight: inherit\n}\n\nb, strong {\n    font-weight: bolder\n}\n\ncode, kbd, samp {\n    font-family: monospace, monospace;\n    font-size: 1em\n}\n\ndfn {\n    font-style: italic\n}\n\nmark {\n    background-color: #ff0;\n    color: #000\n}\n\nsmall {\n    font-size: 80%\n}\n\nsub, sup {\n    font-size: 75%;\n    line-height: 0;\n    position: relative;\n    vertical-align: baseline\n}\n\nsub {\n    bottom: -.25em\n}\n\nsup {\n    top: -.5em\n}\n\naudio, video {\n    display: inline-block\n}\n\naudio:not([controls]) {\n    display: none;\n    height: 0\n}\n\nimg {\n    border-style: none\n}\n\nsvg:not(:root) {\n    overflow: hidden\n}\n\nbutton, input, optgroup, select, textarea {\n    font-family: sans-serif;\n    font-size: 100%;\n    line-height: 1.15;\n    margin: 0\n}\n\nbutton, input {\n    overflow: visible\n}\n\nbutton, select {\n    text-transform: none\n}\n\n[type=reset], [type=submit], button, html [type=button] {\n    -webkit-appearance: button\n}\n\n[type=button]::-moz-focus-inner, [type=reset]::-moz-focus-inner, [type=submit]::-moz-focus-inner, button::-moz-focus-inner {\n    border-style: none;\n    padding: 0\n}\n\n[type=button]:-moz-focusring, [type=reset]:-moz-focusring, [type=submit]:-moz-focusring, button:-moz-focusring {\n    outline: 1px dotted ButtonText\n}\n\nfieldset {\n    padding: .35em .75em .625em\n}\n\nlegend {\n    box-sizing: border-box;\n    color: inherit;\n    display: table;\n    max-width: 100%;\n    padding: 0;\n    white-space: normal\n}\n\nprogress {\n    display: inline-block;\n    vertical-align: baseline\n}\n\ntextarea {\n    overflow: auto\n}\n\n[type=checkbox], [type=radio] {\n    box-sizing: border-box;\n    padding: 0\n}\n\n[type=number]::-webkit-inner-spin-button, [type=number]::-webkit-outer-spin-button {\n    height: auto\n}\n\n[type=search] {\n    -webkit-appearance: textfield;\n    outline-offset: -2px\n}\n\n[type=search]::-webkit-search-cancel-button, [type=search]::-webkit-search-decoration {\n    -webkit-appearance: none\n}\n\n::-webkit-file-upload-button {\n    -webkit-appearance: button;\n    font: inherit\n}\n\ndetails, menu {\n    display: block\n}\n\nsummary {\n    display: list-item\n}\n\ncanvas {\n    display: inline-block\n}\n\ntemplate {\n    display: none\n}\n\n[hidden] {\n    display: none\n}\n\n/* configuration cache styles */\n\n.report-wrapper {\n    margin: 0;\n    padding: 0 24px;\n}\n\n.gradle-logo {\n    width: 32px;\n    height: 24px;\n    background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAYCAYAAACbU/80AAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAIKADAAQAAAABAAAAGAAAAAA915G0AAAD5klEQVRIDbVWC0xTZxT+emmhVUEeA1/ROh/tFAFFGK7oJisIKsNVoOwBbJPowEWHzikRxeiMRpwwjDWRBHQLIzOmiRhe22BT40TitiyaMBQFfMEeLMIEaSmk+/+rvd7be4no6Elu7n++c/5zzv845/wyOyG4iGyDgzCdNOPLM9W41n4bnmNUiHo5DNsz0hGsmcV6lbkyAOOWXJjrz4qWp1C4o3z/LqzWL4VcJB1FIHmZHn/f78a6pDcxbeIEfNvQiPwTZbDZBpC24zOEaGfDpTsgtZby6u+QlrubFWUY3nh6AH39/ahr/Bn1jZfxW3ML2js60dtvgbtcQVblj8CZM7A0PBSrol6Ft+c4KZ8iTB1nwN0//8IEP9/hA2i924Gir0/iq8oa/NvbJzLiDKiUSqTE6pGVbEBY4BxnsYAPSnwXTa3tLCZ5BF3dPdAkGNHzoFcwcaRMnC4CeZkZiAgKFE252nITC1Pew9Dj5GNEGgS4Rbb5eZ1Te7UXG6FLX4cV6zeh5kIDaDpSunL9Boyf5nLOpwT4Sx+BxWrFK8QAnTAapPRQwofcj86uLoG59cbVEOzA0NAQNh38Atn5RSjY8rFAmc/I3dyQvOx1PsSNVy7Roa3ajHDePbBYLSLn1MaGd5KFAXy07xAOl59C6elK+I73hIHcbGd6wXs8qkyH8FZcjLOI5X/9/TrOnLsAldJDUu4As1NToFFPe3IEpm/M2HigwCFnU6t4Zw6Ck1JhGRhgcXq5juXloKyqFnlHirmz5CaNcEAv59kSE9wVikcB3O78A/MSU0Fznk/H9+yAetJEnPr+B8RFLsLcGS8ia28+qQuX+WrPNNZOV+Nc6VH4+3iz89g0pEaLzRUiQ3LGDWsM8Qidq2WL0PGKKlgf74ZIeQTAfFJ6a44WIsDXh9OW/dPdY58aawC9KK6kpOgolO7JxViVSuBGXnvxksudZ5F0O5yzGYxMJnBOGaau4fnPU2RNAtCFBKFoa7akczaAptY2iWmjB33+yQa4kZwfjpi2ex3Dyf43vuAljWQ/4Btmei1WPj+q45hF4U+1J4fEizCEvNf0EWHoIW244sfzoN1RipaT2kDfdjfv3MNpojdISjmfIheE8Fnp8WR9vJ2Zr+O+bYUmO+kJ9KnIUtf9bnvY2x9wcqrrvnCJvfL8Tw4V9v9LU7PdKzJaoNdy645AR4ph1JMncZHRKrVvYyYY5kmP8iO1v2T3dk6HDtYmrgJtOnwKnaPFrg8z+BBX7QSgEyOPJfX9Qd9DFs40GgTOHbrBs2ch4bXFuEG2mmFkeD9hpUMk+NMXEe0TNtsg/Ly94DVurEAuxfwHC1WiVbe0U7MAAAAASUVORK5CYII=\");\n    background-size: contain;\n}\n\n.header {\n    display: flex;\n    flex-wrap: wrap;\n    position: fixed;\n    top: 0;\n    left: 0;\n    width: 100%;\n    padding: 24px 24px 0 24px;\n    background-color: white;\n    z-index: 1;\n}\n\n.learn-more {\n    margin-left: auto;\n    align-self: center;\n    font-size: 0.875rem;\n    font-weight: normal;\n}\n\n.title {\n    display: flex;\n    align-items: center;\n    padding: 18px 0 24px 0;\n    flex: 1 0 100%;\n}\n\n.content {\n    font-size: 0.875rem;\n    padding: 240px 0 48px;\n    overflow-x: auto;\n    white-space: nowrap;\n}\n\n.content ol:first-of-type {\n    margin: 0;\n}\n\n.tree-btn {\n    cursor: pointer;\n    display: inline-block;\n    width: 16px;\n    height: 16px;\n    background-size: contain;\n    background-repeat: no-repeat;\n    vertical-align: middle;\n    margin-top: -0.2em;\n}\n\n.tree-btn.collapsed {\n    background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 192 512\"><path d=\"M166.9 264.5l-117.8 116c-4.7 4.7-12.3 4.7-17 0l-7.1-7.1c-4.7-4.7-4.7-12.3 0-17L127.3 256 25.1 155.6c-4.7-4.7-4.7-12.3 0-17l7.1-7.1c4.7-4.7 12.3-4.7 17 0l117.8 116c4.6 4.7 4.6 12.3-.1 17z\" fill=\"%23999999\" stroke=\"%23999999\"/></svg>');\n}\n\n.tree-btn.expanded {\n    background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 256 512\"><path d=\"M119.5 326.9L3.5 209.1c-4.7-4.7-4.7-12.3 0-17l7.1-7.1c4.7-4.7 12.3-4.7 17 0L128 287.3l100.4-102.2c4.7-4.7 12.3-4.7 17 0l7.1 7.1c4.7 4.7 4.7 12.3 0 17L136.5 327c-4.7 4.6-12.3 4.6-17-.1z\" fill=\"%23999999\" stroke=\"%23999999\"/></svg>');\n}\n\nul .tree-btn {\n    margin-right: 3px;\n}\n\n.leaf-icon {\n    display: inline-block;\n    width: 16px;\n    height: 16px;\n    background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 256 512\"><path d=\"M32 256 H224\" stroke=\"%23999999\" stroke-width=\"48\" stroke-linecap=\"round\"/></svg>');\n    background-size: contain;\n    background-repeat: no-repeat;\n    vertical-align: middle;\n    margin-top: -0.2em;\n}\n\n.invisible-text {\n    user-select: all; /* Allow the text to be selectable */\n    color: transparent; /* Hide the text */\n    text-indent: -9999px; /* Move the text out of view */\n    position: relative;\n    white-space: pre; /* Preserve meaningful whitespace in the invisible text for copying */\n}\n\n.text-for-copy {\n    display: inline-block;\n}\n\n.enum-icon {\n    display: inline-block;\n    width: 16px;\n    height: 16px;\n    background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 1024 1024\"><circle cx=\"512\" cy=\"512\" r=\"200\" /></svg>');\n    background-size: contain;\n    background-repeat: no-repeat;\n    vertical-align: middle;\n    margin-inline-start: 0.5ex;\n    margin-inline-end: 0.5ex;\n    margin-top: -0.2em;\n}\n\n.error-icon {\n    display: inline-block;\n    width: 16px;\n    height: 16px;\n    background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 512\"><path d=\"M193.94 256L296.5 153.44l21.15-21.15c3.12-3.12 3.12-8.19 0-11.31l-22.63-22.63c-3.12-3.12-8.19-3.12-11.31 0L160 222.06 36.29 98.34c-3.12-3.12-8.19-3.12-11.31 0L2.34 120.97c-3.12 3.12-3.12 8.19 0 11.31L126.06 256 2.34 379.71c-3.12 3.12-3.12 8.19 0 11.31l22.63 22.63c3.12 3.12 8.19 3.12 11.31 0L160 289.94 262.56 392.5l21.15 21.15c3.12 3.12 8.19 3.12 11.31 0l22.63-22.63c3.12-3.12 3.12-8.19 0-11.31L193.94 256z\" fill=\"%23FC461E\" stroke=\"%23FC461E\"/></svg>');\n    background-size: contain;\n    background-repeat: no-repeat;\n    vertical-align: middle;\n    margin-inline-start: 0.5ex;\n    margin-inline-end: 0.5ex;\n    margin-top: -0.2em;\n}\n\n.advice-icon {\n    display: inline-block;\n    width: 16px;\n    height: 16px;\n    background-image: url('data:image/svg+xml;utf8,<svg width=\"800px\" height=\"800px\" viewBox=\"-4.93 0 122.88 122.88\" version=\"1.1\" id=\"Layer_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"  style=\"enable-background:new 0 0 113.01 122.88\" xml:space=\"preserve\"><g><path d=\"M44.13,102.06c-1.14,0.03-2.14-0.81-2.3-1.96c-0.17-1.2,0.64-2.31,1.82-2.54c-1.3-7.37-4.85-11.43-8.6-15.72 c-2.92-3.34-5.95-6.81-8.34-11.92c-2.35-5.03-3.64-10.23-3.6-15.63c0.05-5.4,1.42-10.96,4.4-16.71c0.02-0.04,0.04-0.07,0.06-0.11 l0,0c3.91-6.62,9.38-11.04,15.47-13.52c5.11-2.09,10.66-2.8,16.1-2.3c5.42,0.5,10.73,2.2,15.37,4.94 c5.9,3.49,10.75,8.67,13.42,15.21c1.44,3.54,2.42,7.49,2.54,11.82c0.12,4.31-0.62,8.96-2.61,13.88 c-2.66,6.59-6.18,10.68-9.47,14.51c-3.03,3.53-5.85,6.81-7.42,11.84c0.89,0.21,1.59,0.94,1.73,1.9c0.17,1.24-0.7,2.39-1.94,2.56 l-0.77,0.11c-0.14,1.09-0.23,2.26-0.27,3.51l0.25-0.04c1.24-0.17,2.39,0.7,2.56,1.94c0.17,1.24-0.7,2.39-1.94,2.56l-0.78,0.11 c0.01,0.15,0.02,0.3,0.03,0.45l0,0c0.07,0.88,0.08,1.73,0.03,2.54l0.13-0.02c1.25-0.15,2.38,0.74,2.54,1.98 c0.15,1.25-0.74,2.38-1.98,2.54l-1.68,0.21c-1.2,3.11-3.34,5.48-5.87,6.94c-1.74,1.01-3.67,1.59-5.61,1.71 c-1.97,0.12-3.96-0.25-5.78-1.13c-2.08-1.02-3.94-2.71-5.29-5.14c-0.65-0.33-1.13-0.97-1.23-1.75c-0.04-0.31-0.01-0.61,0.07-0.89 c-0.39-1.16-0.68-2.43-0.87-3.83l-0.07,0.01c-1.24,0.17-2.39-0.7-2.56-1.94c-0.17-1.24,0.7-2.39,1.94-2.56l0.54-0.08 C44.19,104.32,44.18,103.16,44.13,102.06L44.13,102.06z M2.18,58.86C1.01,58.89,0.04,57.98,0,56.81c-0.04-1.17,0.88-2.14,2.05-2.18 l8.7-0.3c1.17-0.04,2.14,0.88,2.18,2.05c0.04,1.17-0.88,2.14-2.05,2.18L2.18,58.86L2.18,58.86z M110.68,50.25 c1.16-0.12,2.2,0.73,2.32,1.89c0.12,1.16-0.73,2.2-1.89,2.32l-8.66,0.91c-1.16,0.12-2.2-0.73-2.32-1.89 c-0.12-1.16,0.73-2.2,1.89-2.32L110.68,50.25L110.68,50.25z M94.91,14.78c0.65-0.97,1.96-1.23,2.93-0.58 c0.97,0.65,1.23,1.96,0.58,2.93l-4.84,7.24c-0.65,0.97-1.96,1.23-2.93,0.58c-0.97-0.65-1.23-1.96-0.58-2.93L94.91,14.78 L94.91,14.78z M57.63,2.06c0.03-1.17,1-2.09,2.16-2.06c1.17,0.03,2.09,1,2.06,2.16l-0.22,8.7c-0.03,1.17-1,2.09-2.16,2.06 c-1.17-0.03-2.09-1-2.06-2.16L57.63,2.06L57.63,2.06z M13.88,15.53c-0.86-0.8-0.9-2.14-0.11-2.99c0.8-0.86,2.14-0.9,2.99-0.11 l6.37,5.94c0.86,0.8,0.9,2.14,0.11,2.99c-0.8,0.86-2.14,0.9-2.99,0.11L13.88,15.53L13.88,15.53z M47.88,96.95l18.49-2.63 c1.59-6.7,5.05-10.73,8.8-15.08c3.08-3.58,6.36-7.4,8.76-13.34c1.76-4.35,2.41-8.43,2.31-12.19c-0.1-3.75-0.96-7.21-2.24-10.34 c-2.3-5.63-6.51-10.11-11.65-13.15c-4.11-2.43-8.8-3.94-13.59-4.37c-4.77-0.44-9.64,0.19-14.13,2.02 c-5.26,2.15-9.99,5.97-13.39,11.72c-2.64,5.12-3.86,10.02-3.9,14.73c-0.04,4.74,1.11,9.33,3.2,13.8c2.13,4.56,4.97,7.8,7.69,10.92 C42.47,83.9,46.48,88.49,47.88,96.95L47.88,96.95z M65.62,99.02l-17.27,2.45c0.05,1.1,0.07,2.25,0.05,3.47l17.05-2.42 C65.47,101.29,65.52,100.12,65.62,99.02L65.62,99.02z M48.49,109.52c0.12,0.92,0.3,1.76,0.53,2.54l16.55-2.04 c0.11-0.86,0.13-1.77,0.05-2.74l0,0l0-0.02l-0.01-0.17L48.49,109.52L48.49,109.52z M51.37,116.36c0.64,0.67,1.35,1.19,2.1,1.55 c1.15,0.56,2.42,0.79,3.67,0.72c1.29-0.08,2.57-0.47,3.74-1.15c1.1-0.64,2.09-1.53,2.88-2.65L51.37,116.36L51.37,116.36z\"/></g></svg>');\n    background-size: contain;\n    background-repeat: no-repeat;\n    vertical-align: middle;\n    margin-inline-start: 0.5ex;\n    margin-inline-end: 0.5ex;\n    margin-top: -0.2em;\n}\n\n.warning-icon {\n    display: inline-block;\n    width: 13px;\n    height: 13px;\n    background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 576 512\"><path d=\"M270.2 160h35.5c3.4 0 6.1 2.8 6 6.2l-7.5 196c-.1 3.2-2.8 5.8-6 5.8h-20.5c-3.2 0-5.9-2.5-6-5.8l-7.5-196c-.1-3.4 2.6-6.2 6-6.2zM288 388c-15.5 0-28 12.5-28 28s12.5 28 28 28 28-12.5 28-28-12.5-28-28-28zm281.5 52L329.6 24c-18.4-32-64.7-32-83.2 0L6.5 440c-18.4 31.9 4.6 72 41.6 72H528c36.8 0 60-40 41.5-72zM528 480H48c-12.3 0-20-13.3-13.9-24l240-416c6.1-10.6 21.6-10.7 27.7 0l240 416c6.2 10.6-1.5 24-13.8 24z\" fill=\"%23DEAD22\" stroke=\"%23DEAD22\"/></svg>');\n    background-size: contain;\n    background-repeat: no-repeat;\n    vertical-align: middle;\n    margin-inline-start: 0.3ex;\n    margin-inline-end: 1.1ex;\n    margin-top: -0.1em;\n}\n\n.documentation-button {\n    cursor: pointer;\n    display: inline-block;\n    width: 13px;\n    height: 13px;\n    background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path d=\"M256 340c-15.464 0-28 12.536-28 28s12.536 28 28 28 28-12.536 28-28-12.536-28-28-28zm7.67-24h-16c-6.627 0-12-5.373-12-12v-.381c0-70.343 77.44-63.619 77.44-107.408 0-20.016-17.761-40.211-57.44-40.211-29.144 0-44.265 9.649-59.211 28.692-3.908 4.98-11.054 5.995-16.248 2.376l-13.134-9.15c-5.625-3.919-6.86-11.771-2.645-17.177C185.658 133.514 210.842 116 255.67 116c52.32 0 97.44 29.751 97.44 80.211 0 67.414-77.44 63.849-77.44 107.408V304c0 6.627-5.373 12-12 12zM256 40c118.621 0 216 96.075 216 216 0 119.291-96.61 216-216 216-119.244 0-216-96.562-216-216 0-119.203 96.602-216 216-216m0-32C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8z\" fill=\"%23999999\" stroke=\"%23999999\"/></svg>');\n    background-size: contain;\n    background-repeat: no-repeat;\n    vertical-align: middle;\n    margin-inline-start: 0.5ex;\n    margin-inline-end: 0.5ex;\n    margin-top: -0.2em;\n}\n\n.documentation-button::selection {\n    color: transparent;\n}\n\n.documentation-button:hover {\n    color: transparent;\n}\n\n.copy-button {\n    cursor: pointer;\n    display: inline-block;\n    width: 12px;\n    height: 12px;\n    background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"><path d=\"M433.941 193.941l-51.882-51.882A48 48 0 0 0 348.118 128H320V80c0-26.51-21.49-48-48-48h-66.752C198.643 13.377 180.858 0 160 0s-38.643 13.377-45.248 32H48C21.49 32 0 53.49 0 80v288c0 26.51 21.49 48 48 48h80v48c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48V227.882a48 48 0 0 0-14.059-33.941zm-22.627 22.627a15.888 15.888 0 0 1 4.195 7.432H352v-63.509a15.88 15.88 0 0 1 7.431 4.195l51.883 51.882zM160 30c9.941 0 18 8.059 18 18s-8.059 18-18 18-18-8.059-18-18 8.059-18 18-18zM48 384c-8.822 0-16-7.178-16-16V80c0-8.822 7.178-16 16-16h66.752c6.605 18.623 24.389 32 45.248 32s38.643-13.377 45.248-32H272c8.822 0 16 7.178 16 16v48H176c-26.51 0-48 21.49-48 48v208H48zm352 96H176c-8.822 0-16-7.178-16-16V176c0-8.822 7.178-16 16-16h144v72c0 13.2 10.8 24 24 24h72v208c0 8.822-7.178 16-16 16z\" fill=\"%23999999\" stroke=\"%23999999\"/></svg>');\n    background-size: contain;\n    background-repeat: no-repeat;\n    vertical-align: middle;\n    margin-inline-start: 0.5ex;\n    margin-top: -0.2em;\n}\n\n.groups{\n    display: flex;\n    border-bottom: 1px solid #EDEEEF;\n    flex: 1 0 100%;\n}\n\n.uncategorized {\n    display: flex;\n    border-top: 4px solid #EDEEEF;\n    flex: 1 0 100%;\n}\n\n.group-selector {\n    padding: 0 52px 24px 0;\n    font-size: 0.9rem;\n    font-weight: bold;\n    color: #999999;\n    cursor: pointer;\n}\n\n.group-selector__count {\n    margin: 0 8px;\n    border-radius: 8px;\n    background-color: #999;\n    color: #fff;\n    padding: 1px 8px 2px;\n    font-size: 0.75rem;\n}\n\n.group-selector--active {\n    color: #02303A;\n    cursor: auto;\n}\n\n.group-selector--active .group-selector__count {\n    background-color: #686868;\n}\n\n.group-selector--disabled {\n    cursor: not-allowed;\n}\n\n.accordion-header {\n    cursor: pointer;\n}\n\n.container {\n    padding-left: 0.5em;\n    padding-right: 0.5em;\n}\n\n.stacktrace {\n    border-radius: 4px;\n    overflow-x: auto;\n    padding: 0.5rem;\n    margin-bottom: 0;\n    min-width: 1000px;\n}\n\n/* Lato (bold, regular) */\n@font-face {\n    font-display: swap;\n    font-family: Lato;\n    font-weight: 500;\n    font-style: normal;\n    src: url(\"https://assets.gradle.com/lato/fonts/lato-semibold/lato-semibold.woff2\") format(\"woff2\"),\n    url(\"https://assets.gradle.com/lato/fonts/lato-semibold/lato-semibold.woff\") format(\"woff\");\n}\n\n@font-face {\n    font-display: swap;\n    font-family: Lato;\n    font-weight: bold;\n    font-style: normal;\n    src: url(\"https://assets.gradle.com/lato/fonts/lato-bold/lato-bold.woff2\") format(\"woff2\"),\n    url(\"https://assets.gradle.com/lato/fonts/lato-bold/lato-bold.woff\") format(\"woff\");\n}\n\n* {\n    -webkit-box-sizing: border-box;\n    -moz-box-sizing: border-box;\n    box-sizing: border-box;\n}\n\nhtml,\nbody {\n    margin: 0;\n    padding: 0;\n}\n\nhtml {\n    font-family: \"Lato\", \"Helvetica Neue\", Arial, sans-serif;\n    font-size: 16px;\n    font-weight: 400;\n    line-height: 1.5;\n}\n\nbody {\n    color: #02303A;\n    background-color: #ffffff;\n    -webkit-text-size-adjust: 100%;\n    -ms-text-size-adjust: 100%;\n    -webkit-font-smoothing: antialiased;\n}\n\n\n/* typography */\nh1, h2, h3, h4, h5, h6 {\n    color: #02303A;\n    text-rendering: optimizeLegibility;\n    margin: 0;\n}\n\nh1 {\n    font-size: 1rem;\n}\n\nh2 {\n    font-size: 0.9rem;\n}\n\nh3 {\n    font-size: 1.125rem;\n}\n\nh4, h5, h6 {\n    font-size: 0.875rem;\n}\n\nh1 code {\n    font-weight: bold;\n}\n\nul, ol, dl {\n    list-style-position: outside;\n    line-height: 1.6;\n    padding: 0;\n    margin: 0 0 0 20px;\n    list-style-type: none;\n}\n\nli {\n    line-height: 2;\n}\n\na {\n    color: #1DA2BD;\n    text-decoration: none;\n    transition: all 0.3s ease, visibility 0s;\n}\n\na:hover {\n    color: #35c1e4;\n}\n\n/* code */\ncode, pre {\n    font-family: Inconsolata, Monaco, \"Courier New\", monospace;\n    font-style: normal;\n    font-variant-ligatures: normal;\n    font-variant-caps: normal;\n    font-variant-numeric: normal;\n    font-variant-east-asian: normal;\n    font-weight: normal;\n    font-stretch: normal;\n    color: #686868;\n}\n\n*:not(pre) > code {\n    letter-spacing: 0;\n    padding: 0.1em 0.5ex;\n    text-rendering: optimizeSpeed;\n    word-spacing: -0.15em;\n    word-wrap: break-word;\n}\n\npre {\n    font-size: 0.75rem;\n    line-height: 1.8;\n    margin-top: 0;\n    margin-bottom: 1.5em;\n    padding: 1rem;\n}\n\npre code {\n    background-color: transparent;\n    color: inherit;\n    line-height: 1.8;\n    font-size: 100%;\n    padding: 0;\n}\n\na code {\n    color: #1BA8CB;\n}\n\npre.code, pre.programlisting, pre.screen, pre.tt {\n    background-color: #f7f7f8;\n    border-radius: 4px;\n    font-size: 1em;\n    line-height: 1.45;\n    margin-bottom: 1.25em;\n    overflow-x: auto;\n    padding: 1rem;\n}\n\nli em, p em {\n    padding: 0 1px;\n}\n\ncode em, tt em {\n    text-decoration: none;\n}\n\ncode + .copy-button {\n    margin-inline-start: 0.2ex;\n}\n\n.java-exception {\n    font-size: 0.75rem;\n    padding-left: 24px;\n}\n\n.java-exception ul {\n    margin: 0;\n    line-height: inherit;\n}\n\n.java-exception code {\n    white-space: pre;\n}\n\n.java-exception-part-toggle {\n    user-select: none;\n    cursor: pointer;\n    border-radius: 2px;\n    padding: 0.1em 0.2em;\n    background: azure;\n    color: #686868;\n}\n\n                </style>\n    <!-- Inconsolata is used as a default monospace font in the report. -->\n    <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Inconsolata:400,700\" />\n\n    <title>Gradle Configuration Cache</title>\n</head>\n<body>\n\n<div id=\"playground\"></div>\n\n<div class=\"report\" id=\"report\">\n    Loading...\n</div>\n\n<script type=\"text/javascript\">\nfunction configurationCacheProblems() { return (\n// begin-report-data\n{\"diagnostics\":[{\"locations\":[{\"pluginId\":\"kotlin-android\"}],\"problem\":[{\"text\":\"The org.gradle.api.plugins.Convention type has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 9.0.\"}],\"contextualLabel\":\"The org.gradle.api.plugins.Convention type has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.13/userguide/upgrading_version_8.html#deprecated_access_to_conventions\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"the-org-gradle-api-plugins-convention-type-has-been-deprecated\",\"displayName\":\"The org.gradle.api.plugins.Convention type has been deprecated.\"}]},{\"locations\":[{\"path\":\"build file 'D:\\\\Codes\\\\GitHub\\\\OpenList\\\\Private\\\\OpenListFlutter\\\\android\\\\app\\\\build.gradle'\",\"line\":32}],\"problem\":[{\"text\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 10.0.\"}],\"contextualLabel\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.13/userguide/upgrading_version_8.html#groovy_space_assignment_syntax\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"solutions\":[[{\"text\":\"Use assignment ('namespace = <value>') instead.\"}]]},{\"locations\":[{\"path\":\"build file 'D:\\\\Codes\\\\GitHub\\\\OpenList\\\\Private\\\\OpenListFlutter\\\\android\\\\app\\\\build.gradle'\",\"line\":53}],\"problem\":[{\"text\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 10.0.\"}],\"contextualLabel\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.13/userguide/upgrading_version_8.html#groovy_space_assignment_syntax\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"solutions\":[[{\"text\":\"Use assignment ('minSdk = <value>') instead.\"}]]},{\"locations\":[{\"path\":\"build file 'D:\\\\Codes\\\\GitHub\\\\OpenList\\\\Private\\\\OpenListFlutter\\\\android\\\\app\\\\build.gradle'\",\"line\":54}],\"problem\":[{\"text\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 10.0.\"}],\"contextualLabel\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.13/userguide/upgrading_version_8.html#groovy_space_assignment_syntax\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"solutions\":[[{\"text\":\"Use assignment ('targetSdk = <value>') instead.\"}]]},{\"locations\":[{\"path\":\"build file 'D:\\\\Codes\\\\GitHub\\\\OpenList\\\\Private\\\\OpenListFlutter\\\\android\\\\app\\\\build.gradle'\",\"line\":64}],\"problem\":[{\"text\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 10.0.\"}],\"contextualLabel\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.13/userguide/upgrading_version_8.html#groovy_space_assignment_syntax\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"solutions\":[[{\"text\":\"Use assignment ('buildConfig = <value>') instead.\"}]]},{\"locations\":[{\"path\":\"build file 'D:\\\\Codes\\\\GitHub\\\\OpenList\\\\Private\\\\OpenListFlutter\\\\android\\\\app\\\\build.gradle'\",\"line\":71}],\"problem\":[{\"text\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 10.0.\"}],\"contextualLabel\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.13/userguide/upgrading_version_8.html#groovy_space_assignment_syntax\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"solutions\":[[{\"text\":\"Use assignment ('coreLibraryDesugaringEnabled = <value>') instead.\"}]]},{\"locations\":[{\"path\":\"build file 'D:\\\\Codes\\\\GitHub\\\\OpenList\\\\Private\\\\OpenListFlutter\\\\android\\\\app\\\\build.gradle'\",\"line\":88}],\"problem\":[{\"text\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 10.0.\"}],\"contextualLabel\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.13/userguide/upgrading_version_8.html#groovy_space_assignment_syntax\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"solutions\":[[{\"text\":\"Use assignment ('enable = <value>') instead.\"}]]},{\"locations\":[{\"path\":\"build file 'D:\\\\Codes\\\\GitHub\\\\OpenList\\\\Private\\\\OpenListFlutter\\\\android\\\\app\\\\build.gradle'\",\"line\":91}],\"problem\":[{\"text\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 10.0.\"}],\"contextualLabel\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.13/userguide/upgrading_version_8.html#groovy_space_assignment_syntax\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"solutions\":[[{\"text\":\"Use assignment ('universalApk = <value>') instead.\"}]]},{\"locations\":[{\"path\":\"build file 'D:\\\\Codes\\\\GitHub\\\\OpenList\\\\Private\\\\OpenListFlutter\\\\android\\\\app\\\\build.gradle'\",\"line\":130}],\"problem\":[{\"text\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 10.0.\"}],\"contextualLabel\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.13/userguide/upgrading_version_8.html#groovy_space_assignment_syntax\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"solutions\":[[{\"text\":\"Use assignment ('source = <value>') instead.\"}]]},{\"locations\":[{\"pluginId\":\"dev.flutter.flutter-gradle-plugin\"}],\"problem\":[{\"text\":\"The CopyProcessingSpec.setFileMode(Integer) method has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 9.0.\"}],\"contextualLabel\":\"The CopyProcessingSpec.setFileMode(Integer) method has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.13/userguide/upgrading_version_8.html#unix_file_permissions_deprecated\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"the-copyprocessingspec-setfilemode-integer-method-has-been-deprecated\",\"displayName\":\"The CopyProcessingSpec.setFileMode(Integer) method has been deprecated.\"}],\"solutions\":[[{\"text\":\"Please use the filePermissions(Action) method instead.\"}]]},{\"problem\":[{\"text\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 10.0.\"}],\"contextualLabel\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.13/userguide/upgrading_version_8.html#groovy_space_assignment_syntax\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"solutions\":[[{\"text\":\"Use assignment ('group = <value>') instead.\"}]]},{\"problem\":[{\"text\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 10.0.\"}],\"contextualLabel\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.13/userguide/upgrading_version_8.html#groovy_space_assignment_syntax\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"solutions\":[[{\"text\":\"Use assignment ('version = <value>') instead.\"}]]},{\"problem\":[{\"text\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 10.0.\"}],\"contextualLabel\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.13/userguide/upgrading_version_8.html#groovy_space_assignment_syntax\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"solutions\":[[{\"text\":\"Use assignment ('compileSdk = <value>') instead.\"}]]},{\"problem\":[{\"text\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 10.0.\"}],\"contextualLabel\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.13/userguide/upgrading_version_8.html#groovy_space_assignment_syntax\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"solutions\":[[{\"text\":\"Use assignment ('namespace = <value>') instead.\"}]]},{\"problem\":[{\"text\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 10.0.\"}],\"contextualLabel\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.13/userguide/upgrading_version_8.html#groovy_space_assignment_syntax\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"solutions\":[[{\"text\":\"Use assignment ('minSdk = <value>') instead.\"}]]},{\"locations\":[{\"pluginId\":\"org.jetbrains.kotlin.android\"}],\"problem\":[{\"text\":\"The org.gradle.api.plugins.Convention type has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 9.0.\"}],\"contextualLabel\":\"The org.gradle.api.plugins.Convention type has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.13/userguide/upgrading_version_8.html#deprecated_access_to_conventions\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"the-org-gradle-api-plugins-convention-type-has-been-deprecated\",\"displayName\":\"The org.gradle.api.plugins.Convention type has been deprecated.\"}]},{\"problem\":[{\"text\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 10.0.\"}],\"contextualLabel\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.13/userguide/upgrading_version_8.html#groovy_space_assignment_syntax\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"solutions\":[[{\"text\":\"Use assignment ('coreLibraryDesugaringEnabled = <value>') instead.\"}]]},{\"problem\":[{\"text\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"severity\":\"WARNING\",\"problemDetails\":[{\"text\":\"This is scheduled to be removed in Gradle 10.0.\"}],\"contextualLabel\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\",\"documentationLink\":\"https://docs.gradle.org/8.13/userguide/upgrading_version_8.html#groovy_space_assignment_syntax\",\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"solutions\":[[{\"text\":\"Use assignment ('multiDexEnabled = <value>') instead.\"}]]}],\"problemsReport\":{\"totalProblemCount\":18,\"buildName\":\"android\",\"requestedTasks\":\"\",\"documentationLink\":\"https://docs.gradle.org/8.13/userguide/reporting_problems.html\",\"documentationLinkCaption\":\"Problem report\",\"summaries\":[{\"problemId\":[{\"name\":\"deprecation\",\"displayName\":\"Deprecation\"},{\"name\":\"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl\",\"displayName\":\"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.\"}],\"count\":2}]}}\n// end-report-data\n);}\n</script>\n                <script type=\"text/javascript\">\n                !function(n,t){\"object\"==typeof exports&&\"object\"==typeof module?module.exports=t():\"function\"==typeof define&&define.amd?define([],t):\"object\"==typeof exports?exports[\"configuration-cache-report\"]=t():n[\"configuration-cache-report\"]=t()}(this,(()=>(({70:function(){void 0===ArrayBuffer.isView&&(ArrayBuffer.isView=function(n){return null!=n&&null!=n.__proto__&&n.__proto__.__proto__===Int8Array.prototype.__proto__}),void 0===Math.imul&&(Math.imul=function(n,t){return(4294901760&n)*(65535&t)+(65535&n)*(0|t)|0}),this[\"configuration-cache-report\"]=function(n){\"use strict\";var t,r,i,e,u,o,f,s,c,a,h,l,_,v,d,g,w,b,p,m,k,y,q,B,C,x,j,P,I,S,z,E,T,L,N,A,M,F,D,O,R,H,$,G,U,V,Q,Z,Y,W,K,X,J,nn,tn,rn,en,un,on,fn,sn,cn,an,hn,ln,_n,vn,dn,gn,wn,bn,pn,mn,kn,yn,qn,Bn,Cn,xn,jn,Pn,In,Sn,zn=Math.imul,En=ArrayBuffer.isView;function Tn(n,t){if(!(t>=0))throw fu(ie(\"Requested element count \"+t+\" is less than zero.\"));return function(n,t){if(!(t>=0))throw fu(ie(\"Requested element count \"+t+\" is less than zero.\"));if(0===t)return pt();if(t>=n.length)return function(n){switch(n.length){case 0:return pt();case 1:return gr(n[0]);default:return function(n){return Ar(function(n){return new qt(n,!1)}(n))}(n)}}(n);if(1===t)return gr(n[0]);var r=0,i=Nr(),e=0,u=n.length;n:for(;e<u;){var o=n[e];if(e=e+1|0,i.d(o),(r=r+1|0)===t)break n}return i}(n,Wn(n.length-t|0,0))}function Ln(n,t,r,i,e,u,o){return t=t===A?\", \":t,r=r===A?\"\":r,i=i===A?\"\":i,e=e===A?-1:e,u=u===A?\"...\":u,o=o===A?null:o,function(n,t,r,i,e,u,o,f){r=r===A?\", \":r,i=i===A?\"\":i,e=e===A?\"\":e,u=u===A?-1:u,o=o===A?\"...\":o,f=f===A?null:f,t.e(i);var s=0,c=0,a=n.length;n:for(;c<a;){var h=n[c];if(c=c+1|0,(s=s+1|0)>1&&t.e(r),!(u<0||s<=u))break n;Qt(t,h,f)}return u>=0&&s>u&&t.e(o),t.e(e),t}(n,bi(),t,r,i,e,u,o).toString()}function Nn(n){return n.length-1|0}function An(n,t){if(null==t){var r=0,i=n.length-1|0;if(r<=i)do{var e=r;if(r=r+1|0,null==n[e])return e}while(r<=i)}else{var u=0,o=n.length-1|0;if(u<=o)do{var f=u;if(u=u+1|0,oe(t,n[f]))return f}while(u<=o)}return-1}function Mn(n,t,r,i,e,u,o){return t=t===A?\", \":t,r=r===A?\"\":r,i=i===A?\"\":i,e=e===A?-1:e,u=u===A?\"...\":u,o=o===A?null:o,Fn(n,bi(),t,r,i,e,u,o).toString()}function Fn(n,t,r,i,e,u,o,f){r=r===A?\", \":r,i=i===A?\"\":i,e=e===A?\"\":e,u=u===A?-1:u,o=o===A?\"...\":o,f=f===A?null:f,t.e(i);var s=0,c=n.f();n:for(;c.g();){var a=c.h();if((s=s+1|0)>1&&t.e(r),!(u<0||s<=u))break n;Qt(t,a,f)}return u>=0&&s>u&&t.e(o),t.e(e),t}function Dn(n){if(n.i())throw mu(\"List is empty.\");return n.j(0)}function On(n){return new Yn(n)}function Rn(n){if(Ue(n,Ti)){var t;switch(n.k()){case 0:t=pt();break;case 1:t=gr(Ue(n,Ei)?n.j(0):n.f().h());break;default:t=Hn(n)}return t}return mt(Vn(n))}function Hn(n){return Ar(n)}function $n(n){if(Ue(n,Ti)&&n.k()<=1)return Rn(n);var t=Vn(n);return function(n){var t=(n.k()/2|0)-1|0;if(t<0)return lr();var r=kt(n),i=0;if(i<=t)do{var e=i;i=i+1|0;var u=n.j(e);n.f4(e,n.j(r)),n.f4(r,u),r=r-1|0}while(e!==t)}(t),t}function Gn(n,t){if(!(t>=0))throw fu(ie(\"Requested element count \"+t+\" is less than zero.\"));return function(n,t){if(!(t>=0))throw fu(ie(\"Requested element count \"+t+\" is less than zero.\"));if(0===t)return pt();if(Ue(n,Ti)){if(t>=n.k())return Rn(n);if(1===t)return gr(function(n){if(Ue(n,Ei))return Dn(n);var t=n.f();if(!t.g())throw mu(\"Collection is empty.\");return t.h()}(n))}var r=0,i=Nr(),e=n.f();n:for(;e.g();){var u=e.h();if(i.d(u),(r=r+1|0)===t)break n}return mt(i)}(n,Wn(n.k()-t|0,0))}function Un(n,t){if(!(t>=0))throw fu(ie(\"Requested element count \"+t+\" is less than zero.\"));if(0===t)return pt();var r=n.k();if(t>=r)return Rn(n);if(1===t)return gr(Qn(n));var i=Nr();if(Ue(n,li)){var e=r-t|0;if(e<r)do{var u=e;e=e+1|0,i.d(n.j(u))}while(e<r)}else for(var o=n.l(r-t|0);o.g();){var f=o.h();i.d(f)}return i}function Vn(n){return Ue(n,Ti)?Hn(n):Zn(n,Lr())}function Qn(n){if(n.i())throw mu(\"List is empty.\");return n.j(kt(n))}function Zn(n,t){for(var r=n.f();r.g();){var i=r.h();t.d(i)}return t}function Yn(n){this.n_1=n}function Wn(n,t){return n<t?t:n}function Kn(n,t){return n>t?t:n}function Xn(n,t){return Ut().q(n,t,-1)}function Jn(n,t){return new Et(n,t)}function nt(n){var t=n.f();if(!t.g())return pt();var r=t.h();if(!t.g())return gr(r);var i=Lr();for(i.d(r);t.g();)i.d(t.h());return i}function tt(n){this.r_1=n}function rt(n,t){this.s_1=n,this.t_1=t}function it(){}function et(n){this.x_1=n,this.w_1=0}function ut(n,t){this.a1_1=n,et.call(this,n),ft().b1(t,this.a1_1.k()),this.w_1=t}function ot(){t=this}function ft(){return null==t&&new ot,t}function st(){ft(),it.call(this)}function ct(n){this.h1_1=n}function at(n,t){return t===n?\"(this Map)\":Oi(t)}function ht(n,t){var r;n:{for(var i=n.o().f();i.g();){var e=i.h();if(oe(e.j1(),t)){r=e;break n}}r=null}return r}function lt(){r=this}function _t(){return null==r&&new lt,r}function vt(n){this.q1_1=n,it.call(this)}function dt(){_t(),this.n1_1=null,this.o1_1=null}function gt(){i=this}function wt(){return null==i&&new gt,i}function bt(n){return n.length>0?nu(n):pt()}function pt(){return null==e&&new yt,e}function mt(n){switch(n.k()){case 0:return pt();case 1:return gr(n.j(0));default:return n}}function kt(n){return n.k()-1|0}function yt(){e=this,this.z1_1=new de(-1478467534,-1720727600)}function qt(n,t){this.b2_1=n,this.c2_1=t}function Bt(){u=this}function Ct(){return null==u&&new Bt,u}function xt(n,t){return Ue(n,Ti)?n.k():t}function jt(n,t){if(Ue(t,Ti))return n.m(t);for(var r=!1,i=t.f();i.g();){var e=i.h();n.d(e)&&(r=!0)}return r}function Pt(){}function It(n,t){this.h2_1=n,this.g2_1=n.i2_1.l(function(n,t){if(!(0<=t&&t<=n.k()))throw cu(\"Position index \"+t+\" must be in range [\"+Oe(0,n.k())+\"].\");return n.k()-t|0}(n,t))}function St(n){st.call(this),this.i2_1=n}function zt(n){this.k2_1=n,this.j2_1=n.l2_1.f()}function Et(n,t){this.l2_1=n,this.m2_1=t}function Tt(n){for(;n.n2_1.g();){var t=n.n2_1.h();if(n.q2_1.t2_1(t)===n.q2_1.s2_1)return n.p2_1=t,n.o2_1=1,lr()}n.o2_1=0}function Lt(n){this.q2_1=n,this.n2_1=n.r2_1.f(),this.o2_1=-1,this.p2_1=null}function Nt(n,t,r){t=t===A||t,this.r2_1=n,this.s2_1=t,this.t2_1=r}function At(){return null==o&&new Mt,o}function Mt(){o=this,this.u2_1=new de(1993859828,793161749)}function Ft(n,t,r){return Dt(Dt(n,r)-Dt(t,r)|0,r)}function Dt(n,t){var r=n%t|0;return r>=0?r:r+t|0}function Ot(){f=this,this.p_1=new Ht(1,0)}function Rt(){return null==f&&new Ot,f}function Ht(n,t){Rt(),Vt.call(this,n,t,1)}function $t(n,t,r){Pt.call(this),this.d3_1=r,this.e3_1=t,this.f3_1=this.d3_1>0?n<=t:n>=t,this.g3_1=this.f3_1?n:this.e3_1}function Gt(){s=this}function Ut(){return null==s&&new Gt,s}function Vt(n,t,r){if(Ut(),0===r)throw fu(\"Step must be non-zero.\");if(r===vr().MIN_VALUE)throw fu(\"Step must be greater than Int.MIN_VALUE to avoid overflow on negation.\");this.z2_1=n,this.a3_1=function(n,t,r){var i;if(r>0)i=n>=t?t:t-Ft(t,n,r)|0;else{if(!(r<0))throw fu(\"Step is zero.\");i=n<=t?t:t+Ft(n,t,0|-r)|0}return i}(n,t,r),this.b3_1=r}function Qt(n,t,r){null!=r?n.e(r(t)):null==t||Ze(t)?n.e(t):t instanceof zi?n.i3(t.h3_1):n.e(Oi(t))}function Zt(n,t,r){if(n===t)return!0;if(!(r=r!==A&&r))return!1;var i=mi(n),e=mi(t);return i===e||oe(new zi(Zi(Pi(i).toLowerCase(),0)),new zi(Zi(Pi(e).toLowerCase(),0)))}function Yt(n){return Wi(n)-1|0}function Wt(n,t,r,i){return r=r===A?0:r,(i=i!==A&&i)||\"string\"!=typeof n?Kt(n,t,r,Wi(n),i):n.indexOf(t,r)}function Kt(n,t,r,i,e,u){var o=(u=u!==A&&u)?Xn(Kn(r,Yt(n)),Wn(i,0)):Oe(Wn(r,0),Kn(i,Wi(n)));if(\"string\"==typeof n&&\"string\"==typeof t){var f=o.z2_1,s=o.a3_1,c=o.b3_1;if(c>0&&f<=s||c<0&&s<=f)do{var a=f;if(f=f+c|0,xi(t,0,n,a,Wi(t),e))return a}while(a!==s)}else{var h=o.z2_1,l=o.a3_1,_=o.b3_1;if(_>0&&h<=l||_<0&&l<=h)do{var v=h;if(h=h+_|0,tr(t,0,n,v,Wi(t),e))return v}while(v!==l)}return-1}function Xt(n){var t=0,r=Wi(n)-1|0,i=!1;n:for(;t<=r;){var e=ki(Zi(n,i?r:t));if(i){if(!e)break n;r=r-1|0}else e?t=t+1|0:i=!0}return Ki(n,t,r+1|0)}function Jt(n,t){return ie(Ki(n,t.y2(),t.c3()+1|0))}function nr(n,t,r,i,e){r=r===A?0:r,i=i!==A&&i,rr(e=e===A?0:e);var u,o,f=nu(t);return new ur(n,r,e,(u=f,o=i,function(n,t){var r=function(n,t,r,i){if(!i&&1===t.k()){var e=function(n){if(Ue(n,Ei))return function(n){var t;switch(n.k()){case 0:throw mu(\"List is empty.\");case 1:t=n.j(0);break;default:throw fu(\"List has more than one element.\")}return t}(n);var t=n.f();if(!t.g())throw mu(\"Collection is empty.\");var r=t.h();if(t.g())throw fu(\"Collection has more than one element.\");return r}(t),u=Wt(n,e,r);return u<0?null:fr(u,e)}var o=Oe(Wn(r,0),Wi(n));if(\"string\"==typeof n){var f=o.z2_1,s=o.a3_1,c=o.b3_1;if(c>0&&f<=s||c<0&&s<=f)do{var a,h=f;f=f+c|0;n:{for(var l=t.f();l.g();){var _=l.h();if(xi(_,0,n,h,_.length,i)){a=_;break n}}a=null}if(null!=a)return fr(h,a)}while(h!==s)}else{var v=o.z2_1,d=o.a3_1,g=o.b3_1;if(g>0&&v<=d||g<0&&d<=v)do{var w,b=v;v=v+g|0;n:{for(var p=t.f();p.g();){var m=p.h();if(tr(m,0,n,b,m.length,i)){w=m;break n}}w=null}if(null!=w)return fr(b,w)}while(b!==d)}return null}(n,u,t,o);return null==r?null:fr(r.t3_1,r.u3_1.length)}))}function tr(n,t,r,i,e,u){if(i<0||t<0||t>(Wi(n)-e|0)||i>(Wi(r)-e|0))return!1;var o=0;if(o<e)do{var f=o;if(o=o+1|0,!Zt(Zi(n,t+f|0),Zi(r,i+f|0),u))return!1}while(o<e);return!0}function rr(n){if(!(n>=0))throw fu(ie(\"Limit must be non-negative, but was \"+n))}function ir(n){if(n.l3_1<0)n.j3_1=0,n.m3_1=null;else{var t;if(n.o3_1.r3_1>0?(n.n3_1=n.n3_1+1|0,t=n.n3_1>=n.o3_1.r3_1):t=!1,t||n.l3_1>Wi(n.o3_1.p3_1))n.m3_1=Oe(n.k3_1,Yt(n.o3_1.p3_1)),n.l3_1=-1;else{var r=n.o3_1.s3_1(n.o3_1.p3_1,n.l3_1);if(null==r)n.m3_1=Oe(n.k3_1,Yt(n.o3_1.p3_1)),n.l3_1=-1;else{var i=r.v3(),e=r.w3();n.m3_1=function(n,t){return t<=vr().MIN_VALUE?Rt().p_1:Oe(n,t-1|0)}(n.k3_1,i),n.k3_1=i+e|0,n.l3_1=n.k3_1+(0===e?1:0)|0}}n.j3_1=1}}function er(n){this.o3_1=n,this.j3_1=-1,this.k3_1=function(n,t,r){if(0>r)throw fu(\"Cannot coerce value to an empty range: maximum \"+r+\" is less than minimum 0.\");return n<0?0:n>r?r:n}(n.q3_1,0,Wi(n.p3_1)),this.l3_1=this.k3_1,this.m3_1=null,this.n3_1=0}function ur(n,t,r,i){this.p3_1=n,this.q3_1=t,this.r3_1=r,this.s3_1=i}function or(n,t){this.t3_1=n,this.u3_1=t}function fr(n,t){return new or(n,t)}function sr(){}function cr(){}function ar(){}function hr(){c=this}function lr(){return null==c&&new hr,c}function _r(){a=this,this.MIN_VALUE=-2147483648,this.MAX_VALUE=2147483647,this.SIZE_BYTES=4,this.SIZE_BITS=32}function vr(){return null==a&&new _r,a}function dr(n){for(var t=[],r=n.f();r.g();)t.push(r.h());return t}function gr(n){return 0===(t=[n]).length?Lr():Ar(new qt(t,!0));var t}function wr(n){return n<0&&function(){throw yu(\"Index overflow has happened.\")}(),n}function br(n){return void 0!==n.toArray?n.toArray():dr(n)}function pr(n){return function(n,t){for(var r=0,i=n.length;r<i;){var e=n[r];r=r+1|0,t.d(e)}return t}(t=[n],(r=t.length,i=ce(se(Zr)),function(n,t,r){zr.call(r),Zr.call(r),r.y5_1=function(n){return Ur(n,0,ce(se(Vr)))}(n)}(r,0,i),i));var t,r,i}function mr(){it.call(this)}function kr(n){this.j4_1=n,this.h4_1=0,this.i4_1=-1}function yr(n,t){this.n4_1=n,kr.call(this,n),ft().b1(t,this.n4_1.k()),this.h4_1=t}function qr(){mr.call(this),this.o4_1=0}function Br(n){this.r4_1=n}function Cr(n){this.s4_1=n}function xr(n,t){this.t4_1=n,this.u4_1=t}function jr(){zr.call(this)}function Pr(n){this.x4_1=n,zr.call(this)}function Ir(n){this.e5_1=n,mr.call(this)}function Sr(){dt.call(this),this.c5_1=null,this.d5_1=null}function zr(){mr.call(this)}function Er(){h=this;var n=Nr();n.c_1=!0,this.i5_1=n}function Tr(){return null==h&&new Er,h}function Lr(){return n=ce(se(Fr)),t=[],Fr.call(n,t),n;var n,t}function Nr(n){return t=ce(se(Fr)),r=[],Fr.call(t,r),t;var t,r}function Ar(n){return function(n,t){var r;return r=br(n),Fr.call(t,r),t}(n,ce(se(Fr)))}function Mr(n,t){return ft().e1(t,n.k()),t}function Fr(n){Tr(),qr.call(this),this.b_1=n,this.c_1=!1}function Dr(n,t,r,i,e){if(r===i)return n;var u=(r+i|0)/2|0,o=Dr(n,t,r,u,e),f=Dr(n,t,u+1|0,i,e),s=o===t?n:t,c=r,a=u+1|0,h=r;if(h<=i)do{var l=h;if(h=h+1|0,c<=u&&a<=i){var _=o[c],v=f[a];e.compare(_,v)<=0?(s[l]=_,c=c+1|0):(s[l]=v,a=a+1|0)}else c<=u?(s[l]=o[c],c=c+1|0):(s[l]=f[a],a=a+1|0)}while(l!==i);return s}function Or(n,t){return(3&n)-(3&t)|0}function Rr(){_=this}function Hr(n){this.n5_1=n,jr.call(this)}function $r(n){return function(n,t){Sr.call(t),Vr.call(t),t.t5_1=n,t.u5_1=n.w5()}(new Jr((null==_&&new Rr,_)),n),n}function Gr(){return $r(ce(se(Vr)))}function Ur(n,t,r){if($r(r),!(n>=0))throw fu(ie(\"Negative initial capacity: \"+n));if(!(t>=0))throw fu(ie(\"Non-positive load factor: \"+t));return r}function Vr(){this.v5_1=null}function Qr(n,t){return zr.call(t),Zr.call(t),t.y5_1=n,t}function Zr(){}function Yr(n,t){var r=Kr(n,n.h6_1.m5(t));if(null==r)return null;var i=r;if(null!=i&&Ve(i))return Wr(i,n,t);var e=i;return n.h6_1.l5(e.j1(),t)?e:null}function Wr(n,t,r){var i;n:{for(var e=0,u=n.length;e<u;){var o=n[e];if(e=e+1|0,t.h6_1.l5(o.j1(),r)){i=o;break n}}i=null}return i}function Kr(n,t){var r=n.i6_1[t];return void 0===r?null:r}function Xr(n){this.g6_1=n,this.z5_1=-1,this.a6_1=Object.keys(n.i6_1),this.b6_1=-1,this.c6_1=null,this.d6_1=!1,this.e6_1=-1,this.f6_1=null}function Jr(n){this.h6_1=n,this.i6_1=this.k6(),this.j6_1=0}function ni(){}function ti(n){this.n6_1=n,this.l6_1=null,this.m6_1=null,this.m6_1=this.n6_1.y6_1.v6_1}function ri(){v=this;var n,t=(fi(0,0,n=ce(se(si))),n);t.x6_1=!0,this.e7_1=t}function ii(){return null==v&&new ri,v}function ei(n,t,r){this.d7_1=n,xr.call(this,t,r),this.b7_1=null,this.c7_1=null}function ui(n){this.y6_1=n,jr.call(this)}function oi(){return $r(n=ce(se(si))),si.call(n),n.w6_1=Gr(),n;var n}function fi(n,t,r){return Ur(n,t,r),si.call(r),r.w6_1=Gr(),r}function si(){ii(),this.v6_1=null,this.x6_1=!1}function ci(){d=this;var n=ai(0),t=n.y5_1;(t instanceof si?t:_e()).j5(),this.f7_1=n}function ai(n){return function(n,t){return function(n,t,r){Qr(function(n,t){return fi(n,t,ce(se(si)))}(n,t),r),hi.call(r)}(n,0,t),t}(n,ce(se(hi)))}function hi(){null==d&&new ci}function li(){}function _i(){}function vi(n){_i.call(this),this.k7_1=n}function di(){gi.call(this)}function gi(){_i.call(this),this.m7_1=\"\"}function wi(){if(!w){w=!0;var n=\"undefined\"!=typeof process&&process.versions&&!!process.versions.node;g=n?new vi(process.stdout):new di}}function bi(){return n=ce(se(pi)),pi.call(n,\"\"),n;var n}function pi(n){this.o7_1=void 0!==n?n:\"\"}function mi(n){var t=Pi(n).toUpperCase();return t.length>1?n:Zi(t,0)}function ki(n){return function(n){return 9<=n&&n<=13||28<=n&&n<=32||160===n||n>4096&&(5760===n||8192<=n&&n<=8202||8232===n||8233===n||8239===n||8287===n||12288===n)}(n)}function yi(){b=this,this.q7_1=new RegExp(\"[\\\\\\\\^$*+?.()|[\\\\]{}]\",\"g\"),this.r7_1=new RegExp(\"[\\\\\\\\$]\",\"g\"),this.s7_1=new RegExp(\"\\\\$\",\"g\")}function qi(){return null==b&&new yi,b}function Bi(n,t){qi(),this.v7_1=n,this.w7_1=function(n){if(Ue(n,Ti)){var t;switch(n.k()){case 0:t=At();break;case 1:t=pr(Ue(n,Ei)?n.j(0):n.f().h());break;default:t=Zn(n,ai(n.k()))}return t}return function(n){switch(n.k()){case 0:return At();case 1:return pr(n.f().h());default:return n}}(Zn(n,(r=ce(se(hi)),Qr(oi(),r),hi.call(r),r)));var r}(t),this.x7_1=new RegExp(n,Mn(t,\"\",\"gu\",A,A,A,Ci)),this.y7_1=null,this.z7_1=null}function Ci(n){return n.d8_1}function xi(n,t,r,i,e,u){return tr(n,t,r,i,e,u=u!==A&&u)}function ji(n,t){return n-t|0}function Pi(n){return String.fromCharCode(n)}function Ii(){p=this,this.e8_1=0,this.f8_1=65535,this.g8_1=55296,this.h8_1=56319,this.i8_1=56320,this.j8_1=57343,this.k8_1=55296,this.l8_1=57343,this.m8_1=2,this.n8_1=16}function Si(){return null==p&&new Ii,p}function zi(n){Si(),this.h3_1=n}function Ei(){}function Ti(){}function Li(){}function Ni(){}function Ai(){}function Mi(){}function Fi(){m=this}function Di(n,t){null==m&&new Fi,this.p8_1=n,this.q8_1=t}function Oi(n){var t=null==n?null:ie(n);return null==t?\"null\":t}function Ri(n){return new Hi(n)}function Hi(n){this.t8_1=n,this.s8_1=0}function $i(){return Qi(),k}function Gi(){return Qi(),y}function Ui(){return Qi(),q}function Vi(){return Qi(),B}function Qi(){x||(x=!0,k=new ArrayBuffer(8),y=new Float64Array($i()),new Float32Array($i()),q=new Int32Array($i()),Gi()[0]=-1,B=0!==Ui()[0]?1:0,C=1-Vi()|0)}function Zi(n,t){var r;if(Yi(n)){var i,e=n.charCodeAt(t);if(Si(),e<0?i=!0:(Si(),i=e>65535),i)throw fu(\"Invalid Char code: \"+e);r=De(e)}else r=n.y3(t);return r}function Yi(n){return\"string\"==typeof n}function Wi(n){return Yi(n)?n.length:n.x3()}function Ki(n,t,r){return Yi(n)?n.substring(t,r):n.z3(t,r)}function Xi(n){return ie(n)}function Ji(n,t){var r;switch(typeof n){case\"number\":r=\"number\"==typeof t?ne(n,t):t instanceof de?ne(n,t.w8()):te(n,t);break;case\"string\":case\"boolean\":r=te(n,t);break;default:r=function(n,t){return n.a4(t)}(n,t)}return r}function ne(n,t){var r;if(n<t)r=-1;else if(n>t)r=1;else if(n===t){var i;if(0!==n)i=0;else{var e=1/n;i=e===1/t?0:e<0?-1:1}r=i}else r=n!=n?t!=t?0:1:-1;return r}function te(n,t){return n<t?-1:n>t?1:0}function re(n){if(!(\"kotlinHashCodeValue$\"in n)){var t=4294967296*Math.random()|0,r=new Object;r.value=t,r.enumerable=!1,Object.defineProperty(n,\"kotlinHashCodeValue$\",r)}return n.kotlinHashCodeValue$}function ie(n){return null==n?\"null\":function(n){return!!$e(n)||En(n)}(n)?\"[...]\":n.toString()}function ee(n){if(null==n)return 0;var t;switch(typeof n){case\"object\":t=\"function\"==typeof n.hashCode?n.hashCode():re(n);break;case\"function\":t=re(n);break;case\"number\":t=function(n){return Qi(),(0|n)===n?Fe(n):(Gi()[0]=n,zn(Ui()[(Qi(),C)],31)+Ui()[Vi()]|0)}(n);break;case\"boolean\":t=n?1:0;break;default:t=ue(String(n))}return t}function ue(n){var t=0,r=0,i=n.length-1|0;if(r<=i)do{var e=r;r=r+1|0;var u=n.charCodeAt(e);t=zn(t,31)+u|0}while(e!==i);return t}function oe(n,t){return null==n?null==t:null!=t&&(\"object\"==typeof n&&\"function\"==typeof n.equals?n.equals(t):n!=n?t!=t:\"number\"==typeof n&&\"number\"==typeof t?n===t&&(0!==n||1/n==1/t):n===t)}function fe(n,t){null!=Error.captureStackTrace?Error.captureStackTrace(n,t):n.stack=(new Error).stack}function se(n){return n.prototype}function ce(n){return Object.create(n)}function ae(n,t,r){Error.call(n),function(n,t,r){var i=Xe(Object.getPrototypeOf(n));if(!(1&i)){var e;if(null==t){var u;if(null!==t){var o=null==r?null:r.toString();u=null==o?A:o}else u=A;e=u}else e=t;n.message=e}2&i||(n.cause=r),n.name=Object.getPrototypeOf(n).constructor.name}(n,t,r)}function he(n){var t;return null==n?function(){throw ju()}():t=n,t}function le(){throw Iu()}function _e(){throw zu()}function ve(){j=this,this.x8_1=new de(0,-2147483648),this.y8_1=new de(-1,2147483647),this.z8_1=8,this.a9_1=64}function de(n,t){null==j&&new ve,ar.call(this),this.u8_1=n,this.v8_1=t}function ge(){return Me(),P}function we(){return Me(),I}function be(){return Me(),S}function pe(){return Me(),E}function me(){return Me(),T}function ke(n,t){if(Me(),xe(n,t))return 0;var r=Ie(n),i=Ie(t);return r&&!i?-1:!r&&i?1:Ie(qe(n,t))?-1:1}function ye(n,t){Me();var r=n.v8_1>>>16|0,i=65535&n.v8_1,e=n.u8_1>>>16|0,u=65535&n.u8_1,o=t.v8_1>>>16|0,f=65535&t.v8_1,s=t.u8_1>>>16|0,c=0,a=0,h=0,l=0;return c=(c=c+((a=(a=a+((h=(h=h+((l=l+(u+(65535&t.u8_1)|0)|0)>>>16|0)|0)+(e+s|0)|0)>>>16|0)|0)+(i+f|0)|0)>>>16|0)|0)+(r+o|0)|0,new de((h&=65535)<<16|(l&=65535),(c&=65535)<<16|(a&=65535))}function qe(n,t){return Me(),ye(n,t.e9())}function Be(n,t){if(Me(),Se(n))return ge();if(Se(t))return ge();if(xe(n,pe()))return ze(t)?pe():ge();if(xe(t,pe()))return ze(n)?pe():ge();if(Ie(n))return Ie(t)?Be(Ee(n),Ee(t)):Ee(Be(Ee(n),t));if(Ie(t))return Ee(Be(n,Ee(t)));if(Te(n,me())&&Te(t,me()))return Le(Ce(n)*Ce(t));var r=n.v8_1>>>16|0,i=65535&n.v8_1,e=n.u8_1>>>16|0,u=65535&n.u8_1,o=t.v8_1>>>16|0,f=65535&t.v8_1,s=t.u8_1>>>16|0,c=65535&t.u8_1,a=0,h=0,l=0,_=0;return l=l+((_=_+zn(u,c)|0)>>>16|0)|0,_&=65535,h=(h=h+((l=l+zn(e,c)|0)>>>16|0)|0)+((l=(l&=65535)+zn(u,s)|0)>>>16|0)|0,l&=65535,a=(a=(a=a+((h=h+zn(i,c)|0)>>>16|0)|0)+((h=(h&=65535)+zn(e,s)|0)>>>16|0)|0)+((h=(h&=65535)+zn(u,f)|0)>>>16|0)|0,h&=65535,a=a+(((zn(r,c)+zn(i,s)|0)+zn(e,f)|0)+zn(u,o)|0)|0,new de(l<<16|_,(a&=65535)<<16|h)}function Ce(n){return Me(),4294967296*n.v8_1+function(n){return Me(),n.u8_1>=0?n.u8_1:4294967296+n.u8_1}(n)}function xe(n,t){return Me(),n.v8_1===t.v8_1&&n.u8_1===t.u8_1}function je(n,t){if(Me(),t<2||36<t)throw vu(\"radix out of range: \"+t);if(Se(n))return\"0\";if(Ie(n)){if(xe(n,pe())){var r=Pe(t),i=n.d9(r),e=qe(Be(i,r),n).g9();return je(i,t)+e.toString(t)}return\"-\"+je(Ee(n),t)}for(var u=2===t?31:t<=10?9:t<=21?7:t<=35?6:5,o=Le(Math.pow(t,u)),f=n,s=\"\";;){var c=f.d9(o),a=qe(f,Be(c,o)).g9().toString(t);if(Se(f=c))return a+s;for(;a.length<u;)a=\"0\"+a;s=a+s}}function Pe(n){return Me(),new de(n,n<0?-1:0)}function Ie(n){return Me(),n.v8_1<0}function Se(n){return Me(),0===n.v8_1&&0===n.u8_1}function ze(n){return Me(),!(1&~n.u8_1)}function Ee(n){return Me(),n.e9()}function Te(n,t){return Me(),ke(n,t)<0}function Le(n){if(Me(),(t=n)!=t)return ge();if(n<=-0x8000000000000000)return pe();if(n+1>=0x8000000000000000)return Me(),z;if(n<0)return Ee(Le(-n));var t,r=4294967296;return new de(n%r|0,n/r|0)}function Ne(n,t){return Me(),ke(n,t)>0}function Ae(n,t){return Me(),ke(n,t)>=0}function Me(){L||(L=!0,P=Pe(0),I=Pe(1),S=Pe(-1),z=new de(-1,2147483647),E=new de(0,-2147483648),T=Pe(16777216))}function Fe(n){return n instanceof de?n.g9():function(n){return n>2147483647?2147483647:n<-2147483648?-2147483648:0|n}(n)}function De(n){var t;return t=function(n){return n<<16>>16}(Fe(n)),function(n){return 65535&n}(t)}function Oe(n,t){return new Ht(n,t)}function Re(n,t,r,i){return He(\"class\",n,t,r,i,null)}function He(n,t,r,i,e,u){return{kind:n,simpleName:t,associatedObjectKey:r,associatedObjects:i,suspendArity:e,$kClass$:A,iid:u}}function $e(n){return Array.isArray(n)}function Ge(n,t,r,i,e,u,o,f){null!=i&&(n.prototype=Object.create(i.prototype),n.prototype.constructor=n);var s=r(t,u,o,null==f?[]:f);n.$metadata$=s,null!=e&&((null!=s.iid?n:n.prototype).$imask$=function(n){for(var t=1,r=[],i=0,e=n.length;i<e;){var u=n[i];i=i+1|0;var o=t,f=u.prototype.$imask$,s=null==f?u.$imask$:f;null!=s&&(r.push(s),o=s.length);var c=u.$metadata$.iid,a=null==c?null:(l=void 0,v=1<<(31&(h=c)),(l=new Int32Array(1+(h>>5)|0))[_=h>>5]=l[_]|v,l);null!=a&&(r.push(a),o=Math.max(o,a.length)),o>t&&(t=o)}var h,l,_,v;return function(n,t){for(var r=0,i=new Int32Array(n);r<n;){for(var e=r,u=0,o=0,f=t.length;o<f;){var s=t[o];o=o+1|0,e<s.length&&(u|=s[e])}i[e]=u,r=r+1|0}return i}(t,r)}(e))}function Ue(n,t){return function(n,t){var r=n.$imask$;return null!=r&&function(n,t){var r=t>>5;if(r>n.length)return!1;var i=1<<(31&t);return!!(n[r]&i)}(r,t)}(n,t.$metadata$.iid)}function Ve(n){return!!$e(n)&&!n.$type$}function Qe(n){var t;switch(typeof n){case\"string\":case\"number\":case\"boolean\":case\"function\":t=!0;break;default:t=n instanceof Object}return t}function Ze(n){return\"string\"==typeof n||Ue(n,sr)}function Ye(n,t,r,i){return He(\"interface\",n,t,r,i,(null==N&&(N=0),N=We()+1|0,We()))}function We(){if(null!=N)return N;!function(){throw Tu(\"lateinit property iid has not been initialized\")}()}function Ke(n,t,r,i){return He(\"object\",n,t,r,i,null)}function Xe(n){var t=n.constructor,r=null==t?null:t.$metadata$,i=null==r?null:r.errorInfo;if(null!=i)return i;var e,u=0;if(Je(n,\"message\")&&(u|=1),Je(n,\"cause\")&&(u|=2),3!==u){var o=(e=n,Object.getPrototypeOf(e));o!=Error.prototype&&(u|=Xe(o))}return null!=r&&(r.errorInfo=u),u}function Je(n,t){return n.hasOwnProperty(t)}function nu(n){return new Fr(n)}function tu(n,t,r){for(var i=new Int32Array(r),e=0,u=0,o=0,f=0,s=n.length;f<s;){var c=Zi(n,f);f=f+1|0;var a=t[c];if(u|=(31&a)<<o,a<32){var h=e;e=h+1|0,i[h]=u,u=0,o=0}else o=o+5|0}return i}function ru(n,t){for(var r=0,i=n.length-1|0,e=-1,u=0;r<=i;)if(t>(u=n[e=(r+i|0)/2|0]))r=e+1|0;else{if(t===u)return e;i=e-1|0}return e-(t<u?1:0)|0}function iu(){M=this;var n=\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\",t=new Int32Array(128),r=0,i=Wi(n)-1|0;if(r<=i)do{var e=r;r=r+1|0,t[Zi(n,e)]=e}while(r<=i);var u=tu(\"hCgBpCQGYHZH5BRpBPPPPPPRMP5BPPlCPP6BkEPPPPcPXPzBvBrB3BOiDoBHwD+E3DauCnFmBmB2D6E1BlBTiBmBlBP5BhBiBrBvBjBqBnBPRtBiCmCtBlB0BmB5BiB7BmBgEmChBZgCoEoGVpBSfRhBPqKQ2BwBYoFgB4CJuTiEvBuCuDrF5DgEgFlJ1DgFmBQtBsBRGsB+BPiBlD1EIjDPRPPPQPPPPPGQSQS/DxENVNU+B9zCwBwBPPCkDPNnBPqDYY1R8B7FkFgTgwGgwUwmBgKwBuBScmEP/BPPPPPPrBP8B7F1B/ErBqC6B7BiBmBfQsBUwCw/KwqIwLwETPcPjQgJxFgBlBsD\",t,222),o=new Int32Array(u.length),f=0,s=u.length-1|0;if(f<=s)do{var c=f;f=f+1|0,o[c]=0===c?u[c]:o[c-1|0]+u[c]|0}while(f<=s);this.h9_1=o,this.i9_1=tu(\"aaMBXHYH5BRpBPPPPPPRMP5BPPlCPPzBDOOPPcPXPzBvBjB3BOhDmBBpB7DoDYxB+EiBP1DoExBkBQhBekBPmBgBhBctBiBMWOOXhCsBpBkBUV3Ba4BkB0DlCgBXgBtD4FSdBfPhBPpKP0BvBXjEQ2CGsT8DhBtCqDpFvD1D3E0IrD2EkBJrBDOBsB+BPiBlB1EIjDPPPPPPPPPPPGPPMNLsBNPNPKCvBvBPPCkDPBmBPhDXXgD4B6FzEgDguG9vUtkB9JcuBSckEP/BPPPPPPBPf4FrBjEhBpC3B5BKaWPrBOwCk/KsCuLqDHPbPxPsFtEaaqDL\",t,222),this.j9_1=tu(\"GFjgggUHGGFFZZZmzpz5qB6s6020B60ptltB6smt2sB60mz22B1+vv+8BZZ5s2850BW5q1ymtB506smzBF3q1q1qB1q1q1+Bgii4wDTm74g3KiggxqM60q1q1Bq1o1q1BF1qlrqrBZ2q5wprBGFZWWZGHFsjiooLowgmOowjkwCkgoiIk7ligGogiioBkwkiYkzj2oNoi+sbkwj04DghhkQ8wgiYkgoioDsgnkwC4gikQ//v+85BkwvoIsgoyI4yguI0whiwEowri4CoghsJowgqYowgm4DkwgsY/nwnzPowhmYkg6wI8yggZswikwHgxgmIoxgqYkwgk4DkxgmIkgoioBsgssoBgzgyI8g9gL8g9kI0wgwJoxgkoC0wgioFkw/wI0w53iF4gioYowjmgBHGq1qkgwBF1q1q8qBHwghuIwghyKk0goQkwgoQk3goQHGFHkyg0pBgxj6IoinkxDswno7Ikwhz9Bo0gioB8z48Rwli0xN0mpjoX8w78pDwltoqKHFGGwwgsIHFH3q1q16BFHWFZ1q10q1B2qlwq1B1q10q1B2q1yq1B6q1gq1Biq1qhxBir1qp1Bqt1q1qB1g1q1+B//3q16B///q1qBH/qlqq9Bholqq9B1i00a1q10qD1op1HkwmigEigiy6Cptogq1Bixo1kDq7/j00B2qgoBWGFm1lz50B6s5q1+BGWhggzhwBFFhgk4//Bo2jigE8wguI8wguI8wgugUog1qoB4qjmIwwi2KgkYHHH4lBgiFWkgIWoghssMmz5smrBZ3q1y50B5sm7gzBtz1smzB5smz50BqzqtmzB5sgzqzBF2/9//5BowgoIwmnkzPkwgk4C8ys65BkgoqI0wgy6FghquZo2giY0ghiIsgh24B4ghsQ8QF/v1q1OFs0O8iCHHF1qggz/B8wg6Iznv+//B08QgohsjK0QGFk7hsQ4gB\",t,222)}function eu(){return null==M&&new iu,M}function uu(){F=this,this.k9_1=new Int32Array([170,186,688,704,736,837,890,7468,7544,7579,8305,8319,8336,8560,9424,11388,42652,42864,43e3,43868]),this.l9_1=new Int32Array([1,1,9,2,5,1,1,63,1,37,1,1,13,16,26,2,2,1,2,4])}function ou(){return null==F&&new uu,F}function fu(n){var t=function(n,t){return wu(n,t),su.call(t),t}(n,ce(se(su)));return fe(t,fu),t}function su(){fe(this,su)}function cu(n){var t=function(n,t){return wu(n,t),au.call(t),t}(n,ce(se(au)));return fe(t,cu),t}function au(){fe(this,au)}function hu(n){var t=function(n,t){return wu(n,t),lu.call(t),t}(n,ce(se(lu)));return fe(t,hu),t}function lu(){fe(this,lu)}function _u(n,t){return ae(t,n),du.call(t),t}function vu(n){var t=_u(n,ce(se(du)));return fe(t,vu),t}function du(){fe(this,du)}function gu(n){return function(n){ae(n),du.call(n)}(n),bu.call(n),n}function wu(n,t){return _u(n,t),bu.call(t),t}function bu(){fe(this,bu)}function pu(){var n,t=(gu(n=ce(se(ku))),ku.call(n),n);return fe(t,pu),t}function mu(n){var t=function(n,t){return wu(n,t),ku.call(t),t}(n,ce(se(ku)));return fe(t,mu),t}function ku(){fe(this,ku)}function yu(n){var t=function(n,t){return wu(n,t),qu.call(t),t}(n,ce(se(qu)));return fe(t,yu),t}function qu(){fe(this,qu)}function Bu(){var n,t=(gu(n=ce(se(xu))),xu.call(n),n);return fe(t,Bu),t}function Cu(n){var t=function(n,t){return wu(n,t),xu.call(t),t}(n,ce(se(xu)));return fe(t,Cu),t}function xu(){fe(this,xu)}function ju(){var n,t=(gu(n=ce(se(Pu))),Pu.call(n),n);return fe(t,ju),t}function Pu(){fe(this,Pu)}function Iu(){var n,t=(gu(n=ce(se(Su))),Su.call(n),n);return fe(t,Iu),t}function Su(){fe(this,Su)}function zu(){var n,t=(gu(n=ce(se(Eu))),Eu.call(n),n);return fe(t,zu),t}function Eu(){fe(this,Eu)}function Tu(n){var t=function(n,t){return wu(n,t),Lu.call(t),t}(n,ce(se(Lu)));return fe(t,Tu),t}function Lu(){fe(this,Lu)}function Nu(n,t){var r,i=n.className;return(r=\"(^|.*\\\\s+)\"+t+\"($|\\\\s+.*)\",function(n,t){return Bi.call(t,n,At()),t}(r,ce(se(Bi)))).a8(i)}function Au(n,t){this.o9_1=n,this.p9_1=t}function Mu(n){this.q9_1=n}function Fu(n,t,r){var i,e=Ff(),u=Wu(),o=Mf().ga(t),f=Wu();if(0===Wi(r))i=Lf();else{var s=n.ia_1,c=null==s?null:new Mu(s).v9(r,\"Copy reference to the clipboard\");i=null==c?Lf():c}return e.ja([u,o,f,i])}function Du(n){n=n===A?null:n,this.ia_1=n}function Ou(n,t,r){Yu.call(this),this.ma_1=n,this.na_1=t,this.oa_1=r}function Ru(n,t){this.ra_1=n,this.sa_1=t}function Hu(n,t){Yu.call(this),this.va_1=n,this.wa_1=t}function $u(n,t){Yu.call(this),this.xa_1=n,this.ya_1=t}function Gu(n){Yu.call(this),this.za_1=n}function Uu(n){Yu.call(this),this.ab_1=n}function Vu(n){Yu.call(this),this.bb_1=n}function Qu(n,t){Yu.call(this),this.cb_1=n,this.db_1=t}function Zu(n){Yu.call(this),this.eb_1=n}function Yu(){}function Wu(){return ro(),D}function Ku(){return ro(),O}function Xu(){return ro(),R}function Ju(){return ro(),H}function no(n){return ro(),Ff().fb(Jf(to),n)}function to(n){return ro(),n.gb([\"invisible-text\",\"text-for-copy\"]),lr()}function ro(){$||($=!0,D=no(\"`\"),O=no(\" \"),R=no(\"(\"),H=no(\")\"))}function io(n,t){Yu.call(this),this.hb_1=n,this.ib_1=t}function eo(n){Yu.call(this),this.jb_1=n}function uo(n,t){Yu.call(this),this.kb_1=n,this.lb_1=t}function oo(n){Yu.call(this),this.mb_1=n}function fo(n){Yu.call(this),this.nb_1=n}function so(n){Yu.call(this),this.ob_1=n}function co(n,t,r){Yu.call(this),this.pb_1=n,this.qb_1=t,this.rb_1=r}function ao(n){Yu.call(this),this.sb_1=n}function ho(n){Yu.call(this),this.tb_1=n}function lo(n){return n.xb_1.vb_1.k()}function _o(){if(Z)return lr();Z=!0,G=new ko(\"Inputs\",0,\"Build configuration inputs\"),U=new ko(\"ByMessage\",1,\"Problems grouped by message\"),V=new ko(\"ByLocation\",2,\"Problems grouped by location\"),Q=new ko(\"IncompatibleTasks\",3,\"Incompatible tasks\")}function vo(n){yc.call(this),this.yb_1=n}function go(n){yc.call(this),this.ac_1=n}function wo(n){yc.call(this),this.bc_1=n}function bo(n){yc.call(this),this.cc_1=n}function po(n){yo.call(this),this.dc_1=n}function mo(n,t,r,i,e,u,o,f){this.ec_1=n,this.fc_1=t,this.gc_1=r,this.hc_1=i,this.ic_1=e,this.jc_1=u,this.kc_1=o,this.lc_1=f}function ko(n,t,r){Di.call(this,n,t),this.qc_1=r}function yo(){Bc.call(this)}function qo(n,t){var r=Af(),i=Jf(Lo),e=Af().y9(Jf(No),[]),u=function(n,t){var r,i=Af(),e=Jf(Oo),u=Ff().ga(\"Learn more about the \"),o=$f();return i.y9(e,[u,o.fb(Jf((r=t,function(n){return n.bd(r.tc_1),lr()})),t.sc_1),Ff().ga(\".\")])}(0,t.gc_1),o=Af().y9(Jf(Ao),[Co(0,t)]),f=Af();return r.y9(i,[e,u,o,f.y9(Jf(Mo),[Io(0,Ro(),t.lc_1,lo(t.jc_1)),Io(0,Ho(),t.lc_1,lo(t.hc_1)),Io(0,$o(),t.lc_1,lo(t.ic_1)),Io(0,Go(),t.lc_1,lo(t.kc_1))])])}function Bo(n,t){var r,i,e=Af(),u=Jf(Fo);switch(t.lc_1.q8_1){case 0:r=zo(0,t.jc_1,((i=function(n){return new wo(n)}).callableName=\"<init>\",i));break;case 3:r=zo(0,t.kc_1,function(){var n=function(n){return new bo(n)};return n.callableName=\"<init>\",n}());break;case 1:r=zo(0,t.hc_1,function(){var n=function(n){return new go(n)};return n.callableName=\"<init>\",n}());break;case 2:r=zo(0,t.ic_1,function(){var n=function(n){return new vo(n)};return n.callableName=\"<init>\",n}());break;default:le()}return e.y9(u,[r])}function Co(n,t){return Af().ja([Po(0,t),xo(0,t)])}function xo(n,t){for(var r=Af(),i=t.fc_1,e=Lr(),u=0,o=i.f();o.g();){var f=o.h(),s=u;u=s+1|0,jt(e,0===wr(s)?gr(jo(Vo(),f)):bt([Gf().ja([]),jo(Vo(),f)]))}return r.ha(e)}function jo(n,t){return Df().ja([Ic(t)])}function Po(n,t){return Nf().ja([jc().ka(t.ec_1)])}function Io(n,t,r,i){var e,u,o;return Af().y9(Jf((e=i,u=t,o=r,function(n){return n.t9(\"group-selector\"),0===e?(n.t9(\"group-selector--disabled\"),lr()):u.equals(o)?(n.t9(\"group-selector--active\"),lr()):(n.u9(function(n){return function(t){return new po(n)}}(u)),lr()),lr()})),[Ff().rc(t.qc_1,[So(0,i)])])}function So(n,t){return Ff().y9(Jf(Do),[Ku(),Xu(),Ff().ga(\"\"+t),Ju()])}function zo(n,t,r){return function(n,t,r){var i,e=Af(),u=Of();return e.ja([u.ha(ys(t,(i=r,function(n){var t,r=n.cd().ub_1;return r instanceof Hu?Nc(i,(Vo(),(t=function(n){return Eo(0,n)}).callableName=\"viewNode\",t),n,r.va_1,r.wa_1,Cc()):r instanceof $u?Nc(i,function(){var n=function(n){return Eo(0,n)};return n.callableName=\"viewNode\",n}(Vo()),n,r.xa_1,r.ya_1,xc()):r instanceof io?Nc(i,function(){var n=function(n){return Eo(0,n)};return n.callableName=\"viewNode\",n}(Vo()),n,r.hb_1,r.ib_1,A,So(Vo(),n.cd().vb_1.k())):r instanceof Ou?Lc(i,n,r):Nc(i,function(){var n=function(n){return Eo(0,n)};return n.callableName=\"viewNode\",n}(Vo()),n,r)})))])}(0,t.xb_1.uc().vc(),r)}function Eo(n,t){var r;return t instanceof eo?Sc((r=t,function(n){return n.ed(\"project \"),n.fd(r.jb_1),lr()})):t instanceof co?Sc(function(n){return function(t){return t.ed(n.pb_1+\" \"),t.fd(n.qb_1),t.ed(\" of \"),t.fd(n.rb_1),lr()}}(t)):t instanceof so?Sc(function(n){return function(t){return t.ed(\"system property \"),t.fd(n.ob_1),lr()}}(t)):t instanceof uo?Sc(function(n){return function(t){return t.ed(\"task \"),t.fd(n.kb_1),t.ed(\" of type \"),t.fd(n.lb_1),lr()}}(t)):t instanceof fo?Sc(function(n){return function(t){return t.ed(\"bean of type \"),t.fd(n.nb_1),lr()}}(t)):t instanceof ao?Sc(function(n){return function(t){return t.ed(n.sb_1),lr()}}(t)):t instanceof ho?Sc(function(n){return function(t){return t.ed(\"class \"),t.fd(n.tb_1),lr()}}(t)):t instanceof Zu?Sc(function(n){return function(t){return t.ed(n.eb_1),lr()}}(t)):t instanceof Gu?Ic(t.za_1):t instanceof Qu?Qo(t):Ff().ga(ie(t))}function To(n){return n.t9(\"report-wrapper\"),lr()}function Lo(n){return n.t9(\"header\"),lr()}function No(n){return n.t9(\"gradle-logo\"),lr()}function Ao(n){return n.t9(\"title\"),lr()}function Mo(n){return n.t9(\"groups\"),lr()}function Fo(n){return n.t9(\"content\"),lr()}function Do(n){return n.t9(\"group-selector__count\"),lr()}function Oo(n){return n.t9(\"learn-more\"),lr()}function Ro(){return _o(),G}function Ho(){return _o(),U}function $o(){return _o(),V}function Go(){return _o(),Q}function Uo(){Y=this}function Vo(){return null==Y&&new Uo,Y}function Qo(n){var t;return $f().fb(Jf((t=n,function(n){return n.t9(\"documentation-button\"),n.bd(t.cb_1),lr()})),n.db_1)}function Zo(n,t,r){this.kd_1=n,this.ld_1=t,this.md_1=r}function Yo(n,t,r){this.nd_1=n,this.od_1=t,this.pd_1=r}function Wo(n,t){for(var r=vf(n),i=t.trace,e=Nr(i.length),u=0,o=i.length;u<o;){var f,s=i[u];u=u+1|0,f=Jo(s),e.d(f)}return new Zo(t,r,e)}function Ko(n,t){var r,i=null==(r=t.kd_1.error)?null:nf(r);null==i||n.d(i)}function Xo(n){return function(n,t,r){var i=null==n.error?null:new Hu(t,r);return null==i?new $u(t,r):i}(n.kd_1,new Gu(n.ld_1),ef(n.kd_1))}function Jo(n){var t;switch(n.kind){case\"Project\":t=new eo(n.path);break;case\"Task\":t=new uo(n.path,n.type);break;case\"TaskPath\":t=new oo(n.path);break;case\"Bean\":t=new fo(n.type);break;case\"Field\":t=new co(\"field\",n.name,n.declaringType);break;case\"InputProperty\":t=new co(\"input property\",n.name,n.task);break;case\"OutputProperty\":t=new co(\"output property\",n.name,n.task);break;case\"SystemProperty\":t=new so(n.name);break;case\"PropertyUsage\":t=new co(\"property\",n.name,n.from);break;case\"BuildLogic\":t=new ao(n.location);break;case\"BuildLogicClass\":t=new ho(n.type);break;default:t=new Zu(\"Gradle runtime\")}return t}function nf(n){var t=n.parts;if(null==t){var r=n.summary;return null==r?null:new Gu(vf(r))}for(var i=n.summary,e=null==i?null:vf(i),u=Lr(),o=Ri(t);o.g();){var f=rf(o.h());null==f||u.d(f)}for(var s=Mn(u,\"\\n\"),c=Lr(),a=Ri(t);a.g();){var h=tf(a.h());null==h||c.d(h)}return new Ou(e,s,c)}function tf(n){var t=rf(n);if(null==t)return null;var r,i,e=nt(new Nt(function(n,t,r,i){var e;return Jn(nr(n,[\"\\r\\n\",\"\\n\",\"\\r\"],A,r=r!==A&&r,i=i===A?0:i),(e=n,function(n){return Jt(e,n)}))}(t),!0,lf));return new Ru(e,(r=!(null==n.internalText),i=e.k(),r&&i>1?ps():null))}function rf(n){var t=n.text;return null==t?n.internalText:t}function ef(n){var t=n.documentationLink;return null==t?null:new Qu(t,\"\")}function uf(n,t){return new cs(of(n,jf().sd(t),ps()))}function of(n,t,r){return new ks(n,function(n,t){var r,i=Jn(On(n.o()),If);return nt(Jn(new rt(i,new ff(_f)),(r=t,function(n){return of(n.v3(),n.w3().wd_1,r)})))}(t,1===Pf(t)?ms():ps()),0===Pf(t)?ps():r)}function ff(n){this.td_1=n}function sf(n){var t=Lr(),r=n.ld_1,i=Dn(r.ca_1).fa_1,e=ie(Xt(Ze(i)?i:_e())),u=r.vd(function(n,t){var r;if(!(t>=0))throw fu(ie(\"Requested element count \"+t+\" is less than zero.\"));if(0===t)return Rn(n);if(Ue(n,Ti)){var i=n.k()-t|0;if(i<=0)return pt();if(1===i)return gr(function(n){if(Ue(n,Ei))return Qn(n);var t=n.f();if(!t.g())throw mu(\"Collection is empty.\");for(var r=t.h();t.g();)r=t.h();return r}(n));if(r=Nr(),Ue(n,Ei)){if(Ue(n,li)){var e=t,u=n.k();if(e<u)do{var o=e;e=e+1|0,r.d(n.j(o))}while(e<u)}else for(var f=n.l(t);f.g();){var s=f.h();r.d(s)}return r}}else r=Lr();for(var c=0,a=n.f();a.g();){var h=a.h();c>=t?r.d(h):c=c+1|0}return mt(r)}(r.ca_1,1));return t.d(new io(new Zu(e),ef(n.kd_1))),t.d(new Gu(u)),t.m(n.md_1),t.j5()}function cf(n){var t=Lr(),r=n.ld_1,i=r.vd(r.ca_1);return t.d(new $u(new Gu(i),ef(n.kd_1))),t.j5()}function af(n){var t=Lr();return t.d(Xo(n)),t.m(n.md_1),Ko(t,n),t.j5()}function hf(n){var t=Lr();return t.m(new St(n.md_1)),t.d(Xo(n)),Ko(t,n),t.j5()}function lf(n){return Wi(n)>0}function _f(n,t){return function(n,t){return n===t?0:null==n?-1:null==t?1:Ji(null!=n&&(\"string\"==(i=typeof(r=n))||\"boolean\"===i||function(n){return\"number\"==typeof n||n instanceof de}(r)||Ue(r,cr))?n:_e(),t);var r,i}(Oi(n.v3()),Oi(t.v3()))}function vf(n){var t;return Bf().qd((t=n,function(n){for(var r=t,i=0,e=r.length;i<e;){var u=r[i];i=i+1|0;var o=u.text;null==o||n.ed(o);var f=u.name;null==f||(n.fd(f),lr())}return lr()}))}function df(n,t){return(0!==(r=n)?r.toString():\"No\")+\" \"+gf(t,n)+\" \"+wf(n)+\" found\";var r}function gf(n,t){return t<2?n:n+\"s\"}function wf(n){return n<=1?\"was\":\"were\"}function bf(n,t){this.sc_1=n,this.tc_1=t}function pf(n){kf.call(this),this.fa_1=n}function mf(n,t){kf.call(this),this.da_1=n,this.ea_1=t}function kf(){}function yf(){this.dd_1=Lr()}function qf(){W=this}function Bf(){return null==W&&new qf,W}function Cf(n){Bf(),this.ca_1=n}function xf(){K=this}function jf(){return null==K&&new xf,K}function Pf(n){return n.k()}function If(n){var t=n.j1(),r=n.i1();return fr(t,new Sf(Ue(r,Ni)?r:_e()))}function Sf(n){jf(),this.wd_1=n}function zf(n,t,r){var i;Tf(t,n,r),i=\"Component mounted at #\"+n.id+\".\",wi(),(wi(),g).j7(i)}function Ef(n){var t=document.getElementById(n);if(null==t)throw hu(\"'\"+n+\"' element missing\");return t}function Tf(n,t,r){var i,e,u;i=n.z9(r),e=t,u=function(n,t,r){return function(i){return Tf(n,r,n.ba(i,t)),lr()}}(n,r,t),fs(),e.innerHTML=\"\",us(e,i,u)}function Lf(){return fs(),X}function Nf(){return fs(),J}function Af(){return fs(),nn}function Mf(){return fs(),tn}function Ff(){return fs(),rn}function Df(){return fs(),en}function Of(){return fs(),un}function Rf(){return fs(),on}function Hf(){return fs(),fn}function $f(){return fs(),sn}function Gf(){return fs(),cn}function Uf(n){this.x9_1=n}function Vf(){an=this}function Qf(){return null==an&&new Vf,an}function Zf(){hn=this,Xf.call(this)}function Yf(){return null==hn&&new Zf,hn}function Wf(n,t,r,i){t=t===A?pt():t,r=r===A?null:r,i=i===A?pt():i,Xf.call(this),this.be_1=n,this.ce_1=t,this.de_1=r,this.ee_1=i}function Kf(){}function Xf(){Qf()}function Jf(n){fs();var t,r=Lr();return n(new ns((t=r,function(n){return t.d(n),lr()}))),r}function ns(n){this.r9_1=n}function ts(n,t){es.call(this),this.fe_1=n,this.ge_1=t}function rs(n){es.call(this),this.he_1=n}function is(n,t){es.call(this),this.ie_1=n,this.je_1=t}function es(){}function us(n,t,r){if(fs(),t instanceof Wf)!function(n,t,r){var i=function(n,t,r){var i=n.createElement(t);return r(i),i}(he(n.ownerDocument),t,r);n.appendChild(i)}(n,t.be_1,(e=t,u=r,function(n){for(var t=e.ce_1.f();t.g();)os(n,t.h(),u);var r=e.de_1;null==r||function(n,t){n.appendChild(he(n.ownerDocument).createTextNode(t))}(n,r);for(var i=e.ee_1.f();i.g();)us(n,i.h(),u);return lr()}));else if(t instanceof Kf){var i=t instanceof Kf?t:_e();us(n,i.ke_1,function(n,t){return function(r){return n(t.le_1(r)),lr()}}(r,i))}else if(oe(t,Yf()))return lr();var e,u}function os(n,t,r){var i,e;fs(),t instanceof is?n.setAttribute(t.ie_1,t.je_1):t instanceof rs?function(n,t){for(var r=Lr(),i=0,e=t.length;i<e;){var u=t[i];i=i+1|0,Nu(n,u)||r.d(u)}var o=r;if(!o.i()){var f=n.className,s=ie(Xt(Ze(f)?f:_e())),c=bi();c.p7(s),0!==Wi(s)&&c.p7(\" \"),Fn(o,c,\" \"),n.className=c.toString()}}(n,[t.he_1]):t instanceof ts&&n.addEventListener(t.fe_1,(i=r,e=t,function(n){return n.stopPropagation(),i(e.ge_1(n)),lr()}))}function fs(){ln||(ln=!0,X=Yf(),new Uf(\"hr\"),J=new Uf(\"h1\"),new Uf(\"h2\"),nn=new Uf(\"div\"),new Uf(\"pre\"),tn=new Uf(\"code\"),rn=new Uf(\"span\"),en=new Uf(\"small\"),un=new Uf(\"ol\"),on=new Uf(\"ul\"),fn=new Uf(\"li\"),sn=new Uf(\"a\"),cn=new Uf(\"br\"),new Uf(\"p\"))}function ss(n){as.call(this),this.ne_1=n}function cs(n){this.xb_1=n}function as(){}function hs(n){return n.me(A,A,n.wb_1.ad())}function ls(){_n=this}function _s(){return null==_n&&new ls,_n}function vs(){if(gn)return lr();gn=!0,vn=new ws(\"Collapsed\",0),dn=new ws(\"Expanded\",1)}function ds(n){bs.call(this),this.ve_1=n}function gs(n,t,r){bs.call(this),this.se_1=n,this.te_1=t,this.ue_1=r}function ws(n,t){Di.call(this,n,t)}function bs(){}function ps(){return vs(),vn}function ms(){return vs(),dn}function ks(n,t,r){t=t===A?pt():t,r=r===A?ps():r,this.ub_1=n,this.vb_1=t,this.wb_1=r}function ys(n,t){return nt(Jn(n,(r=t,function(n){return function(n,t){var r,i=n.cd(),e=Hf(),u=t(n),o=i.vb_1;r=null==(i.wb_1.equals(ms())&&!o.i()?o:null)?null:function(n,t){return Rf().ha(function(n,t){return ys(n.vc(),t)}(n,t))}(n,t);var f=r;return e.ja([u,null==f?Lf():f])}(n,r)})));var r}function qs(){if(kn)return lr();kn=!0,bn=new Bs(\"ByMessage\",0,\"Messages\"),pn=new Bs(\"ByGroup\",1,\"Group\"),mn=new Bs(\"ByFileLocation\",2,\"Locations\")}function Bs(n,t,r){Di.call(this,n,t),this.cf_1=r}function Cs(n,t){this.df_1=n,this.ef_1=t}function xs(n,t){this.ff_1=n,this.gf_1=t}function js(n){return new ks(new Gu(Bf().rd(n+\" more problem\"+(n>1?\"s have\":\" has\")+\" been skipped\")))}function Ps(n,t,r,i){var e,u,o=n.v1(t);if(null==o){var f=Lr(),s=fr(new ks(new Vs(Bf().qd((u=t,function(n){return n.fd(u),lr()}))),f,ms()),f);n.h5(t,s),e=s}else e=o;e.u3_1.d(Es(r,i))}function Is(n,t,r,i){var e;if(t=t===A?Lr():t,r=r===A?oi():r,i===A){var u=wn;wn=u+1|0,e=u}else e=i;i=e,this.hf_1=n,this.if_1=t,this.jf_1=r,this.kf_1=i}function Ss(n,t){if(t.i())return null;for(var r,i=n,e=null,u=t.f();u.g();){var o=u.h();r=e;var f,s=i,c=o.ef_1+\" (\"+o.df_1+\")\",a=s.v1(c);if(null==a){var h=Lr(),l=new Is(new ks(new Vs(Bf().qd(Fs(o))),h,ms()),h);s.h5(c,l),f=l}else f=a;e=f,null==r||he(r).if_1.u(he(e).hf_1)||he(r).if_1.d(he(e).hf_1),i=he(e).jf_1}return e}function zs(n,t){if(n.k()===t.length){var r;n:{var i=function(n,t){var r=t.length,i=xt(n,10),e=Nr(Math.min(i,r)),u=0,o=n.f();t:for(;o.g();){var f,s=o.h();if(u>=r)break t;var c=u;u=c+1|0,f=fr(s,t[c]),e.d(f)}return e}(n,t);if(Ue(i,Ti)&&i.i())r=!0;else{for(var e=i.f();e.g();){var u=e.h();if(u.t3_1.df_1!==u.u3_1.name||u.t3_1.ef_1!==u.u3_1.displayName){r=!1;break n}}r=!0}}return r}return!1}function Es(n,t,r){var i=function(n,t,r){t=t===A?null:t;var i=Ns(function(n,t){return n&&null!=t.contextualLabel?he(t.contextualLabel):Ts(t)}(r=r!==A&&r,n),t).j5();return Ls(n,new Gu(i))}(n,t=t===A?null:t,r=r!==A&&r),e=function(n,t,r){r=r!==A&&r;var i,e=n.problemDetails;if(null==e)i=null;else{var u,o=e[0].text,f=null==o?null:function(n,t,r,i){if(r=r!==A&&r,i=i===A?0:i,1===t.length){var e=t[0];if(0!==Wi(e))return function(n,t,r,i){rr(i);var e=0,u=Wt(n,t,e,r);if(-1===u||1===i)return gr(ie(n));var o,f=i>0,s=Nr(f&&Kn(i,10));n:do{var c;if(c=ie(Ki(n,e,u)),s.d(c),e=u+t.length|0,f&&s.k()===(i-1|0))break n;u=Wt(n,t,e,r)}while(-1!==u);return o=ie(Ki(n,e,Wi(n))),s.d(o),s}(n,e,r,i)}for(var u=function(n){return new tt(n)}(nr(n,t,A,r,i)),o=Nr(xt(u,10)),f=u.f();f.g();){var s;s=Jt(n,f.h()),o.d(s)}return o}(o,[\"\\n\"]);if(null==f)u=null;else{for(var s=Nr(xt(f,10)),c=f.f();c.g();){var a,h=c.h();a=Ms(n)?Bf().qd(Os(h)):Bf().rd(h),s.d(a)}u=s}var l,_=u;if(null==_)l=null;else{for(var v=Nr(xt(_,10)),d=_.f();d.g();){var g;g=new ks(new Gu(d.h())),v.d(g)}l=v}var w=null==l?null:Hn(l);i=null==w?Lr():w}var b=i,p=null==b?Lr():b;r||null==n.contextualLabel||p.d(new ks(new Gu(Bf().rd(he(n.contextualLabel)))));var m=function(n){var t=n.solutions;if(null==t||0===t.length)return null;for(var r=new Vu(Bf().rd(\"Solutions\")),i=he(n.solutions),e=Nr(i.length),u=0,o=i.length;u<o;){var f,s=i[u];u=u+1|0,f=new ks(new Uu(vf(s))),e.d(f)}return new ks(r,e)}(n);null==m||p.d(m);var k,y=n.error,q=null==y?null:nf(y);if(null==q||p.d(new ks(q)),t){var B=n.locations;k=!(null==B||0===B.length)}else k=!1;return k&&p.d(function(n){var t,r=n.locations;if(null==r)t=null;else{for(var i=Nr(r.length),e=Ri(r);e.g();){var u,o=e.h();u=new ks(new Gu(Bf().qd(Rs(o)))),i.d(u)}t=i}var f=t;return new ks(new Zu(\"Locations\"),null==f?pt():f)}(n)),p}(n,null==t,r);return new ks(i,e)}function Ts(n){return function(n){if(0===n.length)throw mu(\"Array is empty.\");return n[Nn(n)]}(n.problemId).displayName}function Ls(n,t){var r;switch(n.severity){case\"WARNING\":var i=n.documentationLink;r=new $u(t,null==i?null:new Qu(i,\"\"));break;case\"ERROR\":var e=n.documentationLink;r=new Hu(t,null==e?null:new Qu(e,\"\"));break;case\"ADVICE\":var u=n.documentationLink;r=new Qs(t,null==u?null:new Qu(u,\"\"));break;default:console.error(\"no severity \"+n.severity),r=t}return r}function Ns(n,t){t=t===A?null:t;var r,i=new yf;if(i.ed(n),null==t);else{if(null!=t.line){var e=As(t);i.xd(e+(null==(r=t).line||null==r.length?\"\":\"-\"+r.length),\"\"+t.path+e)}var u=t.taskPath;null==u||i.fd(u);var o=t.pluginId;null!=o&&i.fd(o)}return i}function As(n){var t;if(null==n.line)t=null;else{var r,i=\":\"+n.line,e=n.column;t=i+(null==(r=null==e?null:\":\"+e)?\"\":r)}return null==t?\"\":t}function Ms(n){var t,r,i=n.problemId;n:{for(var e=0,u=i.length;e<u;){var o=i[e];if(e=e+1|0,\"compilation\"===o.name){r=o;break n}}r=null}if(null!=r){var f,s=n.problemId;n:{for(var c=0,a=s.length;c<a;){var h=s[c];if(c=c+1|0,\"java\"===h.name){f=h;break n}}f=null}t=!(null==f)}else t=!1;return t}function Fs(n){return function(t){return t.ed(n.ef_1),t.fd(n.df_1),lr()}}function Ds(n){return n.name}function Os(n){return function(t){return t.xd(function(n,t,r,i){i=i!==A&&i;var e=new RegExp(qi().t7(\" \"),i?\"gui\":\"gu\"),u=qi().u7(\" \");return n.replace(e,u)}(n),\"\"),lr()}}function Rs(n){return function(t){return t.ed(\"- \"),t.fd(\"\"+n.path+As(n)),lr()}}function Hs(){return qs(),bn}function $s(){return qs(),pn}function Gs(){return qs(),mn}function Us(n){Zs.call(this),this.lf_1=n}function Vs(n,t){t=t!==A&&t,Zs.call(this),this.mf_1=n,this.nf_1=t}function Qs(n,t){t=t===A?null:t,Yu.call(this),this.of_1=n,this.pf_1=t}function Zs(){Yu.call(this)}function Ys(n){yc.call(this),this.qf_1=n}function Ws(n){yc.call(this),this.rf_1=n}function Ks(n){yc.call(this),this.sf_1=n}function Xs(n){nc.call(this),this.tf_1=n}function Js(n,t,r,i,e,u,o,f){this.uf_1=n,this.vf_1=t,this.wf_1=r,this.xf_1=i,this.yf_1=e,this.zf_1=u,this.ag_1=o,this.bg_1=f}function nc(){Bc.call(this)}function tc(n,t){var r=Lr();lo(t.xf_1)>0&&r.d(fc(0,Hs(),t.bg_1,t.ag_1)),lo(t.yf_1)>0&&r.d(fc(0,$s(),t.bg_1,t.ag_1)),lo(t.zf_1)>0&&r.d(fc(0,Gs(),t.bg_1,t.ag_1));var i=Af(),e=Jf(hc),u=Af().y9(Jf(lc),[]),o=function(n,t){var r,i=Af(),e=Jf(wc),u=Ff().ga(\"Learn more about \"),o=$f();return i.y9(e,[u,o.fb(Jf((r=t,function(n){return n.bd(r.tc_1),lr()})),t.sc_1),Ff().ga(\".\")])}(0,t.wf_1),f=Af().y9(Jf(_c),[ic(0,t)]),s=Af();return i.y9(e,[u,o,f,s.zd(Jf(vc),r)])}function rc(n,t){var r,i,e=Af(),u=Jf(dc);switch(t.bg_1.q8_1){case 0:r=sc(0,t.xf_1,((i=function(n){return new Ys(n)}).callableName=\"<init>\",i));break;case 1:r=sc(0,t.yf_1,function(){var n=function(n){return new Ws(n)};return n.callableName=\"<init>\",n}());break;case 2:r=sc(0,t.zf_1,function(){var n=function(n){return new Ks(n)};return n.callableName=\"<init>\",n}());break;default:le()}return e.y9(u,[r])}function ic(n,t){return Af().ja([oc(0,t),ec(0,t)])}function ec(n,t){for(var r=Af(),i=t.vf_1,e=Lr(),u=0,o=i.f();o.g();){var f=o.h(),s=u;u=s+1|0,jt(e,0===wr(s)?gr(uc(pc(),f)):bt([Gf().ja([]),uc(pc(),f)]))}return r.ha(e)}function uc(n,t){return Df().ja([Ic(t)])}function oc(n,t){return Nf().ja([jc().ka(t.uf_1)])}function fc(n,t,r,i){var e,u,o,f;return Af().y9(Jf((e=i,u=t,o=r,function(n){return n.t9(\"group-selector\"),0===e?(n.t9(\"group-selector--disabled\"),lr()):u.equals(o)?(n.t9(\"group-selector--active\"),lr()):(n.u9(function(n){return function(t){return new Xs(n)}}(u)),lr()),lr()})),[Ff().rc(t.cf_1,[(f=i,Ff().y9(Jf(gc),[Ku(),Xu(),Ff().ga(\"\"+f),Ju()]))])])}function sc(n,t,r){return function(n,t,r){var i,e=Af(),u=Of();return e.ja([u.ha(ys(t,(i=r,function(n){return function(n,t,r,i){var e,u;return t instanceof Us?Ic(Bf().rd(t.lf_1)):t instanceof Vs?Af().y9(Jf((u=t,function(n){return u.nf_1&&(n.t9(\"uncategorized\"),lr()),lr()})),[Af().ja([zc(r,i),Ic(t.mf_1)])]):t instanceof Ou?Lc(i,r,t):t instanceof Gu?Ic(t.za_1):t instanceof Uu?Af().ja([(Vc(),xn),Ic(t.ab_1)]):t instanceof Vu?Af().ja([zc(r,i),Ic(t.bb_1)]):t instanceof Hu?Nc(i,((e=function(n){return cc(0,n)}).callableName=\"viewIt\",e),r,t.va_1,t.wa_1,Cc()):t instanceof Qs?Nc(i,function(){var n=function(n){return cc(0,n)};return n.callableName=\"viewIt\",n}(),r,t.of_1,t.pf_1,(Vc(),Bn)):t instanceof $u?Nc(i,function(){var n=function(n){return cc(0,n)};return n.callableName=\"viewIt\",n}(),r,t.xa_1,t.ya_1,xc()):t instanceof Zu?Af().ja([zc(r,i),Ic(Bf().rd(t.eb_1))]):Ff().ga(\"Unknown node type viewNode: \"+t)}(pc(),n.cd().ub_1,n,i)})))])}(0,t.xb_1.uc().vc(),r)}function cc(n,t){var r;if(t instanceof Qu)r=Qo(t);else if(t instanceof Zu)r=Ic(Bf().rd(t.eb_1));else if(t instanceof Gu)r=Ic(t.za_1);else{var i=\"Unknown node type viewIt: \"+t;console.error(i),r=Ff().ga(i)}return r}function ac(n){return n.t9(\"report-wrapper\"),lr()}function hc(n){return n.t9(\"header\"),lr()}function lc(n){return n.t9(\"gradle-logo\"),lr()}function _c(n){return n.t9(\"title\"),lr()}function vc(n){return n.t9(\"groups\"),lr()}function dc(n){return n.t9(\"content\"),lr()}function gc(n){return n.t9(\"group-selector__count\"),lr()}function wc(n){return n.t9(\"learn-more\"),lr()}function bc(){yn=this,document.title=\"Gradle - Problems Report\"}function pc(){return null==yn&&new bc,yn}function mc(n,t,r){return n.pe(t.zb().oe(),r)}function kc(n){Bc.call(this),this.hd_1=n}function yc(){Bc.call(this)}function qc(n,t){Bc.call(this),this.wc_1=n,this.xc_1=t}function Bc(){}function Cc(){return Vc(),qn}function xc(){return Vc(),Cn}function jc(){return Vc(),jn}function Pc(){return Vc(),Pn}function Ic(n){return Vc(),Pc().ka(n)}function Sc(n){return Vc(),Pc().ka(Bf().qd(n))}function zc(n,t){return Vc(),n.cd().ye()?Ac(n,t):function(n){return Vc(),Ff().fb(Jf(Gc),Mc(n))}(n)}function Ec(n,t,r,i){var e,u,o;return Vc(),Ff().fb(Jf((e=r,u=t,o=i,function(n){return n.t9(\"java-exception-part-toggle\"),n.u9(function(n,t){return function(r){return new qc(n,t())}}(u,o)),n.s9(\"Click to \"+function(n){var t;switch(Vc(),n.q8_1){case 0:t=\"show\";break;case 1:t=\"hide\";break;default:le()}return t}(e)),lr()})),\"(\"+n+\" internal \"+gf(\"line\",n)+\" \"+function(n){var t;switch(Vc(),n.q8_1){case 0:t=\"hidden\";break;case 1:t=\"shown\";break;default:le()}return t}(r)+\")\")}function Tc(n,t){t=t===A?Lf():t,Vc();for(var r=Rf(),i=Nr(xt(n,10)),e=0,u=n.f();u.g();){var o,f=e;e=f+1|0,s=u.h(),c=(c=0===wr(f)?t:Lf())===A?Lf():c,Vc(),o=Hf().ja([Mf().ga(s),c]),i.d(o)}var s,c;return r.ha(i)}function Lc(n,t,r){Vc();var i,e,u,o=Af(),f=Ac(t,n),s=Ff().ga(\"Exception\"),c=Ff().ja([(Vc(),In).v9(r.na_1,\"Copy exception to the clipboard\")]),a=null==r.ma_1?null:Ff().ga(\" \"),h=null==a?Lf():a,l=r.ma_1,_=null==l?null:Ic(l),v=null==_?Lf():_;switch(t.cd().wb_1.q8_1){case 0:i=Lf();break;case 1:i=function(n,t){Vc();for(var r=Af(),i=Jf(Uc),e=n.oa_1,u=Nr(xt(e,10)),o=0,f=e.f();f.g();){var s,c=f.h(),a=o;o=a+1|0;var h,l=wr(a);if(null!=c.sa_1){var _,v=Ec(c.ra_1.k(),l,c.sa_1,t),d=c.sa_1;switch(null==d?-1:d.q8_1){case 0:_=Tc(Un(c.ra_1,1),v);break;case 1:_=Tc(c.ra_1,v);break;default:le()}h=_}else h=Tc(c.ra_1);s=h,u.d(s)}return r.zd(i,u)}(r,(e=n,u=t,function(){return e(new ss(u))}));break;default:le()}return o.ja([f,s,c,h,v,i])}function Nc(n,t,r,i,e,u,o){e=e===A?null:e,u=u===A?Lf():u,o=o===A?Lf():o,Vc();var f=Af(),s=zc(r,n),c=t(i),a=null==e?null:t(e);return f.ja([s,u,c,null==a?Lf():a,o])}function Ac(n,t){var r,i;return Vc(),Ff().fb(Jf((r=n,i=t,function(n){return n.gb([\"invisible-text\",\"tree-btn\"]),r.cd().wb_1===ps()&&(n.t9(\"collapsed\"),lr()),r.cd().wb_1===ms()&&(n.t9(\"expanded\"),lr()),n.s9(\"Click to \"+function(n){var t;switch(Vc(),n.q8_1){case 0:t=\"expand\";break;case 1:t=\"collapse\";break;default:le()}return t}(r.cd().wb_1)),n.u9(function(n,t){return function(r){return n(new ss(t))}}(i,r)),lr()})),Mc(n))}function Mc(n){return Vc(),function(n,t){var r;if(!(t>=0))throw fu(ie(\"Count 'n' must be non-negative, but was \"+t+\".\"));switch(t){case 0:r=\"\";break;case 1:r=ie(n);break;default:var i=\"\";if(0!==Wi(n))for(var e=ie(n),u=t;1&~u||(i+=e),0!=(u=u>>>1|0);)e+=e;return i}return r}(\"    \",n.we()-1|0)+\"- \"}function Fc(n){return Vc(),n.gb([\"invisible-text\",\"error-icon\"]),lr()}function Dc(n){return Vc(),n.gb([\"invisible-text\",\"advice-icon\"]),lr()}function Oc(n){return Vc(),n.gb([\"invisible-text\",\"warning-icon\"]),lr()}function Rc(n){return Vc(),n.gb([\"invisible-text\",\"enum-icon\"]),lr()}function Hc(n){return Vc(),new kc(n)}function $c(n){return Vc(),new kc(n)}function Gc(n){return Vc(),n.gb([\"invisible-text\",\"leaf-icon\"]),lr()}function Uc(n){return Vc(),n.t9(\"java-exception\"),lr()}function Vc(){if(!Sn){Sn=!0;var n=Ff();qn=n.fb(Jf(Fc),\"[error] \");var t=Ff();Bn=t.fb(Jf(Dc),\"[advice] \");var r=Ff();Cn=r.fb(Jf(Oc),\"[warn]  \");var i=Ff();xn=i.fb(Jf(Rc),\"[enum]  \"),jn=new Du,Pn=new Du(Hc),In=new Mu($c)}}return Ge(Yn,A,Re),Ge(tt,A,Re),Ge(rt,A,Re),Ge(Ti,\"Collection\",Ye),Ge(it,\"AbstractCollection\",Re,A,[Ti]),Ge(et,\"IteratorImpl\",Re),Ge(ut,\"ListIteratorImpl\",Re,et),Ge(ot,\"Companion\",Ke),Ge(Ei,\"List\",Ye,A,[Ti]),Ge(st,\"AbstractList\",Re,it,[it,Ei]),Ge(ct,A,Re),Ge(lt,\"Companion\",Ke),Ge(vt,A,Re,it),Ge(Ni,\"Map\",Ye),Ge(dt,\"AbstractMap\",Re,A,[Ni]),Ge(gt,\"Companion\",Ke),Ge(li,\"RandomAccess\",Ye),Ge(yt,\"EmptyList\",Ke,A,[Ei,li]),Ge(qt,\"ArrayAsCollection\",Re,A,[Ti]),Ge(Bt,\"EmptyIterator\",Ke),Ge(Pt,\"IntIterator\",Re),Ge(It,A,Re),Ge(St,\"ReversedListReadOnly\",Re,st),Ge(zt,A,Re),Ge(Et,\"TransformingSequence\",Re),Ge(Lt,A,Re),Ge(Nt,\"FilteringSequence\",Re),Ge(Mi,\"Set\",Ye,A,[Ti]),Ge(Mt,\"EmptySet\",Ke,A,[Mi]),Ge(Ot,\"Companion\",Ke),Ge(Vt,\"IntProgression\",Re),Ge(Ht,\"IntRange\",Re,Vt),Ge($t,\"IntProgressionIterator\",Re,Pt),Ge(Gt,\"Companion\",Ke),Ge(er,A,Re),Ge(ur,\"DelimitedRangesSequence\",Re),Ge(or,\"Pair\",Re),Ge(sr,\"CharSequence\",Ye),Ge(cr,\"Comparable\",Ye),Ge(ar,\"Number\",Re),Ge(hr,\"Unit\",Ke),Ge(_r,\"IntCompanionObject\",Ke),Ge(mr,\"AbstractMutableCollection\",Re,it,[it,Ti]),Ge(kr,\"IteratorImpl\",Re),Ge(yr,\"ListIteratorImpl\",Re,kr),Ge(qr,\"AbstractMutableList\",Re,mr,[mr,Ti,Ei]),Ge(Br,A,Re),Ge(Cr,A,Re),Ge(Li,\"Entry\",Ye),Ge(Ai,\"MutableEntry\",Ye,A,[Li]),Ge(xr,\"SimpleEntry\",Re,A,[Ai]),Ge(zr,\"AbstractMutableSet\",Re,mr,[mr,Mi,Ti]),Ge(jr,\"AbstractEntrySet\",Re,zr),Ge(Pr,A,Re,zr),Ge(Ir,A,Re,mr),Ge(Sr,\"AbstractMutableMap\",Re,dt,[dt,Ni]),Ge(Er,\"Companion\",Ke),Ge(Fr,\"ArrayList\",Re,qr,[qr,Ti,Ei,li]),Ge(Rr,\"HashCode\",Ke),Ge(Hr,\"EntrySet\",Re,jr),Ge(Vr,\"HashMap\",Re,Sr,[Sr,Ni]),Ge(Zr,\"HashSet\",Re,zr,[zr,Mi,Ti]),Ge(Xr,A,Re),Ge(ni,\"InternalMap\",Ye),Ge(Jr,\"InternalHashCodeMap\",Re,A,[ni]),Ge(ti,\"EntryIterator\",Re),Ge(ri,\"Companion\",Ke),Ge(ei,\"ChainEntry\",Re,xr),Ge(ui,\"EntrySet\",Re,jr),Ge(si,\"LinkedHashMap\",Re,Vr,[Vr,Ni]),Ge(ci,\"Companion\",Ke),Ge(hi,\"LinkedHashSet\",Re,Zr,[Zr,Mi,Ti]),Ge(_i,\"BaseOutput\",Re),Ge(vi,\"NodeJsOutput\",Re,_i),Ge(gi,\"BufferedOutput\",Re,_i),Ge(di,\"BufferedOutputToConsoleLog\",Re,gi),Ge(pi,\"StringBuilder\",Re,A,[sr]),Ge(yi,\"Companion\",Ke),Ge(Bi,\"Regex\",Re),Ge(Ii,\"Companion\",Ke),Ge(zi,\"Char\",Re,A,[cr]),Ge(Fi,\"Companion\",Ke),Ge(Di,\"Enum\",Re,A,[cr]),Ge(Hi,A,Re),Ge(ve,\"Companion\",Ke),Ge(de,\"Long\",Re,ar,[ar,cr]),Ge(iu,\"Letter\",Ke),Ge(uu,\"OtherLowercase\",Ke),Ge(du,\"Exception\",Re,Error),Ge(bu,\"RuntimeException\",Re,du),Ge(su,\"IllegalArgumentException\",Re,bu),Ge(au,\"IndexOutOfBoundsException\",Re,bu),Ge(lu,\"IllegalStateException\",Re,bu),Ge(ku,\"NoSuchElementException\",Re,bu),Ge(qu,\"ArithmeticException\",Re,bu),Ge(xu,\"UnsupportedOperationException\",Re,bu),Ge(Pu,\"NullPointerException\",Re,bu),Ge(Su,\"NoWhenBranchMatchedException\",Re,bu),Ge(Eu,\"ClassCastException\",Re,bu),Ge(Lu,\"UninitializedPropertyAccessException\",Re,bu),Ge(Au,\"Model\",Re),Ge(Mu,\"CopyButtonComponent\",Re),Ge(Du,\"PrettyTextComponent\",Re),Ge(Yu,\"ProblemNode\",Re),Ge(Ou,\"Exception\",Re,Yu),Ge(Ru,\"StackTracePart\",Re),Ge(Hu,\"Error\",Re,Yu),Ge($u,\"Warning\",Re,Yu),Ge(Gu,\"Message\",Re,Yu),Ge(Uu,\"ListElement\",Re,Yu),Ge(Vu,\"TreeNode\",Re,Yu),Ge(Qu,\"Link\",Re,Yu),Ge(Zu,\"Label\",Re,Yu),Ge(io,\"Info\",Re,Yu),Ge(eo,\"Project\",Re,Yu),Ge(uo,\"Task\",Re,Yu),Ge(oo,\"TaskPath\",Re,Yu),Ge(fo,\"Bean\",Re,Yu),Ge(so,\"SystemProperty\",Re,Yu),Ge(co,\"Property\",Re,Yu),Ge(ao,\"BuildLogic\",Re,Yu),Ge(ho,\"BuildLogicClass\",Re,Yu),Ge(Bc,\"BaseIntent\",Re),Ge(yc,\"TreeIntent\",Re,Bc),Ge(vo,\"TaskTreeIntent\",Re,yc),Ge(go,\"MessageTreeIntent\",Re,yc),Ge(wo,\"InputTreeIntent\",Re,yc),Ge(bo,\"IncompatibleTaskTreeIntent\",Re,yc),Ge(yo,\"Intent\",Re,Bc),Ge(po,\"SetTab\",Re,yo),Ge(mo,\"Model\",Re),Ge(ko,\"Tab\",Re,Di),Ge(Uo,\"ConfigurationCacheReportPage\",Ke),Ge(Zo,\"ImportedProblem\",Re),Ge(Yo,\"ImportedDiagnostics\",Re),Ge(ff,\"sam$kotlin_Comparator$0\",Re),Ge(bf,\"LearnMore\",Re),Ge(kf,\"Fragment\",Re),Ge(pf,\"Text\",Re,kf),Ge(mf,\"Reference\",Re,kf),Ge(yf,\"Builder\",Re),Ge(qf,\"Companion\",Ke),Ge(Cf,\"PrettyText\",Re),Ge(xf,\"Companion\",Ke),Ge(Sf,\"Trie\",Re),Ge(Uf,\"ViewFactory\",Re),Ge(Vf,\"Companion\",Ke),Ge(Xf,\"View\",Re),Ge(Zf,\"Empty\",Ke,Xf),Ge(Wf,\"Element\",Re,Xf),Ge(Kf,\"MappedView\",Re,Xf),Ge(ns,\"Attributes\",Re),Ge(es,\"Attribute\",Re),Ge(ts,\"OnEvent\",Re,es),Ge(rs,\"ClassName\",Re,es),Ge(is,\"Named\",Re,es),Ge(as,\"Intent\",Re),Ge(ss,\"Toggle\",Re,as),Ge(cs,\"Model\",Re),Ge(ls,\"TreeView\",Ke),Ge(bs,\"Focus\",Re),Ge(ds,\"Original\",Re,bs),Ge(gs,\"Child\",Re,bs),Ge(ws,\"ViewState\",Re,Di),Ge(ks,\"Tree\",Re),Ge(Bs,\"Tab\",Re,Di),Ge(Cs,\"ProblemIdElement\",Re),Ge(xs,\"ProblemSummary\",Re),Ge(Is,\"ProblemNodeGroup\",Re),Ge(Zs,\"ProblemApiNode\",Re,Yu),Ge(Us,\"Text\",Re,Zs),Ge(Vs,\"ProblemIdNode\",Re,Zs),Ge(Qs,\"Advice\",Re,Yu),Ge(Ys,\"MessageTreeIntent\",Re,yc),Ge(Ws,\"ProblemIdTreeIntent\",Re,yc),Ge(Ks,\"FileLocationTreeIntent\",Re,yc),Ge(nc,\"Intent\",Re,Bc),Ge(Xs,\"SetTab\",Re,nc),Ge(Js,\"Model\",Re),Ge(bc,\"ProblemsReportPage\",Ke),Ge(kc,\"Copy\",Re,Bc),Ge(qc,\"ToggleStackTracePart\",Re,Bc),se(Yn).f=function(){return this.n_1.f()},se(tt).f=function(){return this.r_1.f()},se(rt).f=function(){var n,t,r=function(n,t){for(var r=n.f();r.g();){var i=r.h();t.d(i)}return t}(this.s_1,Lr());return n=r,t=this.t_1,function(n,t){if(n.k()<=1)return lr();var r=br(n);!function(n,t){if(function(){if(null!=l)return l;l=!1;var n=[],t=0;if(t<600)do{var r=t;t=t+1|0,n.push(r)}while(t<600);var i=Or;n.sort(i);var e=1,u=n.length;if(e<u)do{var o=e;e=e+1|0;var f=n[o-1|0],s=n[o];if((3&f)==(3&s)&&f>=s)return!1}while(e<u);return l=!0,!0}()){var r=(i=t,function(n,t){return i.compare(n,t)});n.sort(r)}else!function(n,t,r,i){var e=n.length,u=function(n){var t=0,r=n.length-1|0;if(t<=r)do{var i=t;t=t+1|0,n[i]=null}while(i!==r);return n}(Array(e)),o=Dr(n,u,0,r,i);if(o!==n){var f=0;if(f<=r)do{var s=f;f=f+1|0,n[s]=o[s]}while(s!==r)}}(n,0,Nn(n),t);var i}(r,t);var i=0,e=r.length;if(i<e)do{var u=i;i=i+1|0,n.f4(u,r[u])}while(i<e)}(n,t),r.f()},se(it).u=function(n){var t;n:if(Ue(this,Ti)&&this.i())t=!1;else{for(var r=this.f();r.g();)if(oe(r.h(),n)){t=!0;break n}t=!1}return t},se(it).v=function(n){var t;n:if(Ue(n,Ti)&&n.i())t=!0;else{for(var r=n.f();r.g();){var i=r.h();if(!this.u(i)){t=!1;break n}}t=!0}return t},se(it).i=function(){return 0===this.k()},se(it).toString=function(){return Mn(this,\", \",\"[\",\"]\",A,A,(n=this,function(t){return t===n?\"(this Collection)\":Oi(t)}));var n},se(it).toArray=function(){return dr(this)},se(et).g=function(){return this.w_1<this.x_1.k()},se(et).h=function(){if(!this.g())throw pu();var n=this.w_1;return this.w_1=n+1|0,this.x_1.j(n)},se(ut).c1=function(){return this.w_1>0},se(ut).d1=function(){if(!this.c1())throw pu();return this.w_1=this.w_1-1|0,this.a1_1.j(this.w_1)},se(ot).e1=function(n,t){if(n<0||n>=t)throw cu(\"index: \"+n+\", size: \"+t)},se(ot).b1=function(n,t){if(n<0||n>t)throw cu(\"index: \"+n+\", size: \"+t)},se(ot).f1=function(n){for(var t=1,r=n.f();r.g();){var i=r.h(),e=zn(31,t),u=null==i?null:ee(i);t=e+(null==u?0:u)|0}return t},se(ot).g1=function(n,t){if(n.k()!==t.k())return!1;for(var r=t.f(),i=n.f();i.g();)if(!oe(i.h(),r.h()))return!1;return!0},se(st).f=function(){return new et(this)},se(st).l=function(n){return new ut(this,n)},se(st).equals=function(n){return n===this||!(null==n||!Ue(n,Ei))&&ft().g1(this,n)},se(st).hashCode=function(){return ft().f1(this)},se(ct).g=function(){return this.h1_1.g()},se(ct).h=function(){return this.h1_1.h().i1()},se(lt).k1=function(n){var t=n.j1(),r=null==t?null:ee(t),i=null==r?0:r,e=n.i1(),u=null==e?null:ee(e);return i^(null==u?0:u)},se(lt).l1=function(n){return Oi(n.j1())+\"=\"+Oi(n.i1())},se(lt).m1=function(n,t){return!(null==t||!Ue(t,Li))&&!!oe(n.j1(),t.j1())&&oe(n.i1(),t.i1())},se(vt).r1=function(n){return this.q1_1.s1(n)},se(vt).u=function(n){return!(null!=n&&!Qe(n))&&this.r1(null==n||Qe(n)?n:_e())},se(vt).f=function(){return new ct(this.q1_1.o().f())},se(vt).k=function(){return this.q1_1.k()},se(dt).t1=function(n){return!(null==ht(this,n))},se(dt).s1=function(n){var t;n:{var r=this.o();if(Ue(r,Ti)&&r.i())t=!1;else{for(var i=r.f();i.g();)if(oe(i.h().i1(),n)){t=!0;break n}t=!1}}return t},se(dt).u1=function(n){if(null==n||!Ue(n,Li))return!1;var t=n.j1(),r=n.i1(),i=(Ue(this,Ni)?this:_e()).v1(t);return!(!oe(r,i)||null==i&&!(Ue(this,Ni)?this:_e()).t1(t))},se(dt).equals=function(n){if(n===this)return!0;if(null==n||!Ue(n,Ni))return!1;if(this.k()!==n.k())return!1;var t;n:{var r=n.o();if(Ue(r,Ti)&&r.i())t=!0;else{for(var i=r.f();i.g();){var e=i.h();if(!this.u1(e)){t=!1;break n}}t=!0}}return t},se(dt).v1=function(n){var t=ht(this,n);return null==t?null:t.i1()},se(dt).hashCode=function(){return ee(this.o())},se(dt).i=function(){return 0===this.k()},se(dt).k=function(){return this.o().k()},se(dt).toString=function(){var n;return Mn(this.o(),\", \",\"{\",\"}\",A,A,(n=this,function(t){return n.p1(t)}))},se(dt).p1=function(n){return at(this,n.j1())+\"=\"+at(this,n.i1())},se(dt).w1=function(){return null==this.o1_1&&(this.o1_1=new vt(this)),he(this.o1_1)},se(gt).x1=function(n){for(var t=0,r=n.f();r.g();){var i=r.h(),e=t,u=null==i?null:ee(i);t=e+(null==u?0:u)|0}return t},se(gt).y1=function(n,t){return n.k()===t.k()&&n.v(t)},se(yt).equals=function(n){return!(null==n||!Ue(n,Ei))&&n.i()},se(yt).hashCode=function(){return 1},se(yt).toString=function(){return\"[]\"},se(yt).k=function(){return 0},se(yt).i=function(){return!0},se(yt).a2=function(n){return n.i()},se(yt).v=function(n){return this.a2(n)},se(yt).j=function(n){throw cu(\"Empty list doesn't contain element at index \"+n+\".\")},se(yt).f=function(){return Ct()},se(yt).l=function(n){if(0!==n)throw cu(\"Index: \"+n);return Ct()},se(qt).k=function(){return this.b2_1.length},se(qt).i=function(){return 0===this.b2_1.length},se(qt).d2=function(n){return function(n,t){return An(n,t)>=0}(this.b2_1,n)},se(qt).e2=function(n){var t;n:if(Ue(n,Ti)&&n.i())t=!0;else{for(var r=n.f();r.g();){var i=r.h();if(!this.d2(i)){t=!1;break n}}t=!0}return t},se(qt).v=function(n){return this.e2(n)},se(qt).f=function(){return Ri(this.b2_1)},se(Bt).g=function(){return!1},se(Bt).c1=function(){return!1},se(Bt).h=function(){throw pu()},se(Bt).d1=function(){throw pu()},se(Pt).h=function(){return this.f2()},se(It).g=function(){return this.g2_1.c1()},se(It).c1=function(){return this.g2_1.g()},se(It).h=function(){return this.g2_1.d1()},se(It).d1=function(){return this.g2_1.h()},se(St).k=function(){return this.i2_1.k()},se(St).j=function(n){return this.i2_1.j(function(n,t){if(!(0<=t&&t<=kt(n)))throw cu(\"Element index \"+t+\" must be in range [\"+Oe(0,kt(n))+\"].\");return kt(n)-t|0}(this,n))},se(St).f=function(){return this.l(0)},se(St).l=function(n){return new It(this,n)},se(zt).h=function(){return this.k2_1.m2_1(this.j2_1.h())},se(zt).g=function(){return this.j2_1.g()},se(Et).f=function(){return new zt(this)},se(Lt).h=function(){if(-1===this.o2_1&&Tt(this),0===this.o2_1)throw pu();var n=this.p2_1;return this.p2_1=null,this.o2_1=-1,null==n||Qe(n)?n:_e()},se(Lt).g=function(){return-1===this.o2_1&&Tt(this),1===this.o2_1},se(Nt).f=function(){return new Lt(this)},se(Mt).equals=function(n){return!(null==n||!Ue(n,Mi))&&n.i()},se(Mt).hashCode=function(){return 0},se(Mt).toString=function(){return\"[]\"},se(Mt).k=function(){return 0},se(Mt).i=function(){return!0},se(Mt).a2=function(n){return n.i()},se(Mt).v=function(n){return this.a2(n)},se(Mt).f=function(){return Ct()},se(Ht).y2=function(){return this.z2_1},se(Ht).c3=function(){return this.a3_1},se(Ht).i=function(){return this.z2_1>this.a3_1},se(Ht).equals=function(n){return n instanceof Ht&&(!(!this.i()||!n.i())||this.z2_1===n.z2_1&&this.a3_1===n.a3_1)},se(Ht).hashCode=function(){return this.i()?-1:zn(31,this.z2_1)+this.a3_1|0},se(Ht).toString=function(){return this.z2_1+\"..\"+this.a3_1},se($t).g=function(){return this.f3_1},se($t).f2=function(){var n=this.g3_1;if(n===this.e3_1){if(!this.f3_1)throw pu();this.f3_1=!1}else this.g3_1=this.g3_1+this.d3_1|0;return n},se(Gt).q=function(n,t,r){return new Vt(n,t,r)},se(Vt).f=function(){return new $t(this.z2_1,this.a3_1,this.b3_1)},se(Vt).i=function(){return this.b3_1>0?this.z2_1>this.a3_1:this.z2_1<this.a3_1},se(Vt).equals=function(n){return n instanceof Vt&&(!(!this.i()||!n.i())||this.z2_1===n.z2_1&&this.a3_1===n.a3_1&&this.b3_1===n.b3_1)},se(Vt).hashCode=function(){return this.i()?-1:zn(31,zn(31,this.z2_1)+this.a3_1|0)+this.b3_1|0},se(Vt).toString=function(){return this.b3_1>0?this.z2_1+\"..\"+this.a3_1+\" step \"+this.b3_1:this.z2_1+\" downTo \"+this.a3_1+\" step \"+(0|-this.b3_1)},se(er).h=function(){if(-1===this.j3_1&&ir(this),0===this.j3_1)throw pu();var n=this.m3_1,t=n instanceof Ht?n:_e();return this.m3_1=null,this.j3_1=-1,t},se(er).g=function(){return-1===this.j3_1&&ir(this),1===this.j3_1},se(ur).f=function(){return new er(this)},se(or).toString=function(){return\"(\"+this.t3_1+\", \"+this.u3_1+\")\"},se(or).v3=function(){return this.t3_1},se(or).w3=function(){return this.u3_1},se(or).hashCode=function(){var n=null==this.t3_1?0:ee(this.t3_1);return zn(n,31)+(null==this.u3_1?0:ee(this.u3_1))|0},se(or).equals=function(n){if(this===n)return!0;if(!(n instanceof or))return!1;var t=n instanceof or?n:_e();return!!oe(this.t3_1,t.t3_1)&&!!oe(this.u3_1,t.u3_1)},se(hr).toString=function(){return\"kotlin.Unit\"},se(_r).b4=function(){return this.MIN_VALUE},se(_r).c4=function(){return this.MAX_VALUE},se(_r).d4=function(){return this.SIZE_BYTES},se(_r).e4=function(){return this.SIZE_BITS},se(mr).m=function(n){this.g4();for(var t=!1,r=n.f();r.g();){var i=r.h();this.d(i)&&(t=!0)}return t},se(mr).toJSON=function(){return this.toArray()},se(mr).g4=function(){},se(kr).g=function(){return this.h4_1<this.j4_1.k()},se(kr).h=function(){if(!this.g())throw pu();var n=this.h4_1;return this.h4_1=n+1|0,this.i4_1=n,this.j4_1.j(this.i4_1)},se(yr).c1=function(){return this.h4_1>0},se(yr).d1=function(){if(!this.c1())throw pu();return this.h4_1=this.h4_1-1|0,this.i4_1=this.h4_1,this.n4_1.j(this.i4_1)},se(qr).d=function(n){return this.g4(),this.p4(this.k(),n),!0},se(qr).f=function(){return new kr(this)},se(qr).u=function(n){return this.q4(n)>=0},se(qr).q4=function(n){var t=0,r=kt(this);if(t<=r)do{var i=t;if(t=t+1|0,oe(this.j(i),n))return i}while(i!==r);return-1},se(qr).l=function(n){return new yr(this,n)},se(qr).equals=function(n){return n===this||!(null==n||!Ue(n,Ei))&&ft().g1(this,n)},se(qr).hashCode=function(){return ft().f1(this)},se(Br).g=function(){return this.r4_1.g()},se(Br).h=function(){return this.r4_1.h().j1()},se(Cr).g=function(){return this.s4_1.g()},se(Cr).h=function(){return this.s4_1.h().i1()},se(xr).j1=function(){return this.t4_1},se(xr).i1=function(){return this.u4_1},se(xr).v4=function(n){var t=this.u4_1;return this.u4_1=n,t},se(xr).hashCode=function(){return _t().k1(this)},se(xr).toString=function(){return _t().l1(this)},se(xr).equals=function(n){return _t().m1(this,n)},se(jr).u=function(n){return this.w4(n)},se(Pr).y4=function(n){throw Cu(\"Add is not supported on keys\")},se(Pr).d=function(n){return this.y4(null==n||Qe(n)?n:_e())},se(Pr).z4=function(n){return this.x4_1.t1(n)},se(Pr).u=function(n){return!(null!=n&&!Qe(n))&&this.z4(null==n||Qe(n)?n:_e())},se(Pr).f=function(){return new Br(this.x4_1.o().f())},se(Pr).k=function(){return this.x4_1.k()},se(Pr).g4=function(){return this.x4_1.g4()},se(Ir).f5=function(n){throw Cu(\"Add is not supported on values\")},se(Ir).d=function(n){return this.f5(null==n||Qe(n)?n:_e())},se(Ir).r1=function(n){return this.e5_1.s1(n)},se(Ir).u=function(n){return!(null!=n&&!Qe(n))&&this.r1(null==n||Qe(n)?n:_e())},se(Ir).f=function(){return new Cr(this.e5_1.o().f())},se(Ir).k=function(){return this.e5_1.k()},se(Ir).g4=function(){return this.e5_1.g4()},se(Sr).g5=function(){return null==this.c5_1&&(this.c5_1=new Pr(this)),he(this.c5_1)},se(Sr).w1=function(){return null==this.d5_1&&(this.d5_1=new Ir(this)),he(this.d5_1)},se(Sr).g4=function(){},se(zr).equals=function(n){return n===this||!(null==n||!Ue(n,Mi))&&wt().y1(this,n)},se(zr).hashCode=function(){return wt().x1(this)},se(Fr).j5=function(){return this.g4(),this.c_1=!0,this.k()>0?this:Tr().i5_1},se(Fr).k=function(){return this.b_1.length},se(Fr).j=function(n){var t=this.b_1[Mr(this,n)];return null==t||Qe(t)?t:_e()},se(Fr).f4=function(n,t){this.g4(),Mr(this,n);var r=this.b_1[n];this.b_1[n]=t;var i=r;return null==i||Qe(i)?i:_e()},se(Fr).d=function(n){return this.g4(),this.b_1.push(n),this.o4_1=this.o4_1+1|0,!0},se(Fr).p4=function(n,t){this.g4(),this.b_1.splice(function(n,t){return ft().b1(t,n.k()),t}(this,n),0,t),this.o4_1=this.o4_1+1|0},se(Fr).m=function(n){if(this.g4(),n.i())return!1;for(var t,r,i,e=(t=this,r=n.k(),i=t.k(),t.b_1.length=t.k()+r|0,i),u=0,o=n.f();o.g();){var f=o.h(),s=u;u=s+1|0;var c=wr(s);this.b_1[e+c|0]=f}return this.o4_1=this.o4_1+1|0,!0},se(Fr).q4=function(n){return An(this.b_1,n)},se(Fr).toString=function(){return Ln(this.b_1,\", \",\"[\",\"]\",A,A,Xi)},se(Fr).k5=function(){return[].slice.call(this.b_1)},se(Fr).toArray=function(){return this.k5()},se(Fr).g4=function(){if(this.c_1)throw Bu()},se(Rr).l5=function(n,t){return oe(n,t)},se(Rr).m5=function(n){var t=null==n?null:ee(n);return null==t?0:t},se(Hr).o5=function(n){throw Cu(\"Add is not supported on entries\")},se(Hr).d=function(n){return this.o5(null!=n&&Ue(n,Ai)?n:_e())},se(Hr).w4=function(n){return this.n5_1.u1(n)},se(Hr).f=function(){return this.n5_1.t5_1.f()},se(Hr).k=function(){return this.n5_1.k()},se(Vr).t1=function(n){return this.t5_1.z4(n)},se(Vr).s1=function(n){var t;n:{var r=this.t5_1;if(Ue(r,Ti)&&r.i())t=!1;else{for(var i=r.f();i.g();){var e=i.h();if(this.u5_1.l5(e.i1(),n)){t=!0;break n}}t=!1}}return t},se(Vr).o=function(){return null==this.v5_1&&(this.v5_1=this.x5()),he(this.v5_1)},se(Vr).x5=function(){return new Hr(this)},se(Vr).v1=function(n){return this.t5_1.v1(n)},se(Vr).h5=function(n,t){return this.t5_1.h5(n,t)},se(Vr).k=function(){return this.t5_1.k()},se(Zr).d=function(n){return null==this.y5_1.h5(n,this)},se(Zr).u=function(n){return this.y5_1.t1(n)},se(Zr).i=function(){return this.y5_1.i()},se(Zr).f=function(){return this.y5_1.g5().f()},se(Zr).k=function(){return this.y5_1.k()},se(Xr).g=function(){return-1===this.z5_1&&(this.z5_1=function(n){if(null!=n.c6_1&&n.d6_1){var t=n.c6_1.length;if(n.e6_1=n.e6_1+1|0,n.e6_1<t)return 0}if(n.b6_1=n.b6_1+1|0,n.b6_1<n.a6_1.length){n.c6_1=n.g6_1.i6_1[n.a6_1[n.b6_1]];var r=n,i=n.c6_1;return r.d6_1=null!=i&&Ve(i),n.e6_1=0,0}return n.c6_1=null,1}(this)),0===this.z5_1},se(Xr).h=function(){if(!this.g())throw pu();var n=this.d6_1?this.c6_1[this.e6_1]:this.c6_1;return this.f6_1=n,this.z5_1=-1,n},se(Jr).w5=function(){return this.h6_1},se(Jr).k=function(){return this.j6_1},se(Jr).h5=function(n,t){var r=this.h6_1.m5(n),i=Kr(this,r);if(null==i)this.i6_1[r]=new xr(n,t);else{if(null==i||!Ve(i)){var e,u=i;return this.h6_1.l5(u.j1(),n)?u.v4(t):(e=[u,new xr(n,t)],this.i6_1[r]=e,this.j6_1=this.j6_1+1|0,null)}var o=i,f=Wr(o,this,n);if(null!=f)return f.v4(t);o.push(new xr(n,t))}return this.j6_1=this.j6_1+1|0,null},se(Jr).z4=function(n){return!(null==Yr(this,n))},se(Jr).v1=function(n){var t=Yr(this,n);return null==t?null:t.i1()},se(Jr).f=function(){return new Xr(this)},se(ti).g=function(){return!(null===this.m6_1)},se(ti).h=function(){if(!this.g())throw pu();var n=he(this.m6_1);this.l6_1=n;var t,r=n.b7_1;return t=r!==this.n6_1.y6_1.v6_1?r:null,this.m6_1=t,n},se(ei).v4=function(n){return this.d7_1.g4(),se(xr).v4.call(this,n)},se(ui).o5=function(n){throw Cu(\"Add is not supported on entries\")},se(ui).d=function(n){return this.o5(null!=n&&Ue(n,Ai)?n:_e())},se(ui).w4=function(n){return this.y6_1.u1(n)},se(ui).f=function(){return new ti(this)},se(ui).k=function(){return this.y6_1.k()},se(ui).g4=function(){return this.y6_1.g4()},se(si).j5=function(){var n;if(this.g4(),this.x6_1=!0,this.k()>0)n=this;else{var t=ii().e7_1;n=Ue(t,Ni)?t:_e()}return n},se(si).t1=function(n){return this.w6_1.t1(n)},se(si).s1=function(n){var t=this.v6_1;if(null==t)return!1;var r=t;do{if(oe(r.i1(),n))return!0;r=he(r.b7_1)}while(r!==this.v6_1);return!1},se(si).x5=function(){return new ui(this)},se(si).v1=function(n){var t=this.w6_1.v1(n);return null==t?null:t.i1()},se(si).h5=function(n,t){this.g4();var r=this.w6_1.v1(n);if(null==r){var i=new ei(this,n,t);return this.w6_1.h5(n,i),function(n,t){if(null!=n.b7_1||null!=n.c7_1)throw hu(ie(\"Check failed.\"));var r=t.v6_1;if(null==r)t.v6_1=n,n.b7_1=n,n.c7_1=n;else{var i=r.c7_1;if(null==i)throw hu(ie(\"Required value was null.\"));var e=i;n.c7_1=e,n.b7_1=r,r.c7_1=n,e.b7_1=n}}(i,this),null}return r.v4(t)},se(si).k=function(){return this.w6_1.k()},se(si).g4=function(){if(this.x6_1)throw Bu()},se(hi).g4=function(){return this.y5_1.g4()},se(_i).h7=function(){this.i7(\"\\n\")},se(_i).j7=function(n){this.i7(n),this.h7()},se(vi).i7=function(n){var t=String(n);this.k7_1.write(t)},se(di).i7=function(n){var t=String(n),r=t.lastIndexOf(\"\\n\",0);if(r>=0){var i=this.m7_1;this.m7_1=i+t.substring(0,r),this.n7();var e=r+1|0;t=t.substring(e)}this.m7_1=this.m7_1+t},se(di).n7=function(){console.log(this.m7_1),this.m7_1=\"\"},se(gi).i7=function(n){var t=this.m7_1;this.m7_1=t+String(n)},se(pi).x3=function(){return this.o7_1.length},se(pi).y3=function(n){var t=this.o7_1;if(!(n>=0&&n<=Yt(t)))throw cu(\"index: \"+n+\", length: \"+this.x3()+\"}\");return Zi(t,n)},se(pi).z3=function(n,t){return this.o7_1.substring(n,t)},se(pi).i3=function(n){return this.o7_1=this.o7_1+new zi(n),this},se(pi).e=function(n){return this.o7_1=this.o7_1+Oi(n),this},se(pi).p7=function(n){var t=this.o7_1;return this.o7_1=t+(null==n?\"null\":n),this},se(pi).toString=function(){return this.o7_1},se(yi).t7=function(n){var t=this.q7_1;return n.replace(t,\"\\\\$&\")},se(yi).u7=function(n){var t=this.s7_1;return n.replace(t,\"$$$$\")},se(Bi).a8=function(n){this.x7_1.lastIndex=0;var t=this.x7_1.exec(ie(n));return null!=t&&0===t.index&&this.x7_1.lastIndex===Wi(n)},se(Bi).toString=function(){return this.x7_1.toString()},se(zi).o8=function(n){return ji(this.h3_1,n)},se(zi).a4=function(n){return function(n,t){return ji(n.h3_1,t instanceof zi?t.h3_1:_e())}(this,n)},se(zi).equals=function(n){return function(n,t){return t instanceof zi&&n===t.h3_1}(this.h3_1,n)},se(zi).hashCode=function(){return this.h3_1},se(zi).toString=function(){return Pi(this.h3_1)},se(Di).r8=function(n){return Ji(this.q8_1,n.q8_1)},se(Di).a4=function(n){return this.r8(n instanceof Di?n:_e())},se(Di).equals=function(n){return this===n},se(Di).hashCode=function(){return re(this)},se(Di).toString=function(){return this.p8_1},se(Hi).g=function(){return!(this.s8_1===this.t8_1.length)},se(Hi).h=function(){if(this.s8_1===this.t8_1.length)throw mu(\"\"+this.s8_1);var n=this.s8_1;return this.s8_1=n+1|0,this.t8_1[n]},se(de).b9=function(n){return ke(this,n)},se(de).a4=function(n){return this.b9(n instanceof de?n:_e())},se(de).c9=function(n){return ye(this,n)},se(de).d9=function(n){return function(n,t){if(Me(),Se(t))throw vu(\"division by zero\");if(Se(n))return ge();if(xe(n,pe())){if(xe(t,we())||xe(t,be()))return pe();if(xe(t,pe()))return we();var r=function(n){Me();return new de(n.u8_1>>>1|n.v8_1<<31,n.v8_1>>1)}(n),i=function(n){Me();return new de(n.u8_1<<1,n.v8_1<<1|n.u8_1>>>31)}(r.d9(t));return xe(i,ge())?Ie(t)?we():be():ye(i,qe(n,Be(t,i)).d9(t))}if(xe(t,pe()))return ge();if(Ie(n))return Ie(t)?Ee(n).d9(Ee(t)):Ee(Ee(n).d9(t));if(Ie(t))return Ee(n.d9(Ee(t)));for(var e=ge(),u=n;Ae(u,t);){for(var o=Ce(u)/Ce(t),f=Math.max(1,Math.floor(o)),s=Math.ceil(Math.log(f)/Math.LN2),c=s<=48?1:Math.pow(2,s-48),a=Le(f),h=Be(a,t);Ie(h)||Ne(h,u);)h=Be(a=Le(f-=c),t);Se(a)&&(a=we()),e=ye(e,a),u=qe(u,h)}return e}(this,n)},se(de).e9=function(){return this.f9().c9(new de(1,0))},se(de).f9=function(){return new de(~this.u8_1,~this.v8_1)},se(de).g9=function(){return this.u8_1},se(de).w8=function(){return Ce(this)},se(de).valueOf=function(){return this.w8()},se(de).equals=function(n){return n instanceof de&&xe(this,n)},se(de).hashCode=function(){return Me(),this.u8_1^this.v8_1},se(de).toString=function(){return je(this,10)},se(Au).toString=function(){return\"Model(text=\"+this.o9_1+\", tooltip=\"+this.p9_1+\")\"},se(Au).hashCode=function(){var n=ue(this.o9_1);return zn(n,31)+ue(this.p9_1)|0},se(Au).equals=function(n){if(this===n)return!0;if(!(n instanceof Au))return!1;var t=n instanceof Au?n:_e();return this.o9_1===t.o9_1&&this.p9_1===t.p9_1},se(Mu).v9=function(n,t){return this.w9(new Au(n,t))},se(Mu).w9=function(n){var t,r;return Df().y9(Jf((t=n,r=this,function(n){return n.s9(t.p9_1),n.t9(\"copy-button\"),n.u9(function(n,t){return function(r){return n.q9_1(t.o9_1)}}(r,t)),lr()})),[])},se(Mu).z9=function(n){return this.w9(n instanceof Au?n:_e())},se(Mu).aa=function(n,t){return t},se(Mu).ba=function(n,t){var r=null==n||Qe(n)?n:_e();return this.aa(r,t instanceof Au?t:_e())},se(Du).ka=function(n){return function(n,t){for(var r=Ff(),i=t.ca_1,e=Nr(xt(i,10)),u=i.f();u.g();){var o,f,s=u.h();s instanceof pf?f=Ff().ga(s.fa_1):s instanceof mf?f=Fu(n,s.da_1,s.ea_1):le(),o=f,e.d(o)}return r.ha(e)}(this,n)},se(Du).z9=function(n){return this.ka(n instanceof Cf?n:_e())},se(Du).la=function(n,t){return t},se(Du).ba=function(n,t){var r=null==n||Qe(n)?n:_e();return this.la(r,t instanceof Cf?t:_e())},se(Ou).pa=function(n,t,r){return new Ou(n,t,r)},se(Ou).qa=function(n,t,r,i){return n=n===A?this.ma_1:n,t=t===A?this.na_1:t,r=r===A?this.oa_1:r,i===A?this.pa(n,t,r):i.pa.call(this,n,t,r)},se(Ou).toString=function(){return\"Exception(summary=\"+this.ma_1+\", fullText=\"+this.na_1+\", parts=\"+this.oa_1+\")\"},se(Ou).hashCode=function(){var n=null==this.ma_1?0:this.ma_1.hashCode();return n=zn(n,31)+ue(this.na_1)|0,zn(n,31)+ee(this.oa_1)|0},se(Ou).equals=function(n){if(this===n)return!0;if(!(n instanceof Ou))return!1;var t=n instanceof Ou?n:_e();return!!oe(this.ma_1,t.ma_1)&&this.na_1===t.na_1&&!!oe(this.oa_1,t.oa_1)},se(Ru).ta=function(n,t){return new Ru(n,t)},se(Ru).ua=function(n,t,r){return n=n===A?this.ra_1:n,t=t===A?this.sa_1:t,r===A?this.ta(n,t):r.ta.call(this,n,t)},se(Ru).toString=function(){return\"StackTracePart(lines=\"+this.ra_1+\", state=\"+this.sa_1+\")\"},se(Ru).hashCode=function(){var n=ee(this.ra_1);return zn(n,31)+(null==this.sa_1?0:this.sa_1.hashCode())|0},se(Ru).equals=function(n){if(this===n)return!0;if(!(n instanceof Ru))return!1;var t=n instanceof Ru?n:_e();return!!oe(this.ra_1,t.ra_1)&&!!oe(this.sa_1,t.sa_1)},se(Hu).toString=function(){return\"Error(label=\"+this.va_1+\", docLink=\"+this.wa_1+\")\"},se(Hu).hashCode=function(){var n=ee(this.va_1);return zn(n,31)+(null==this.wa_1?0:ee(this.wa_1))|0},se(Hu).equals=function(n){if(this===n)return!0;if(!(n instanceof Hu))return!1;var t=n instanceof Hu?n:_e();return!!oe(this.va_1,t.va_1)&&!!oe(this.wa_1,t.wa_1)},se($u).toString=function(){return\"Warning(label=\"+this.xa_1+\", docLink=\"+this.ya_1+\")\"},se($u).hashCode=function(){var n=ee(this.xa_1);return zn(n,31)+(null==this.ya_1?0:ee(this.ya_1))|0},se($u).equals=function(n){if(this===n)return!0;if(!(n instanceof $u))return!1;var t=n instanceof $u?n:_e();return!!oe(this.xa_1,t.xa_1)&&!!oe(this.ya_1,t.ya_1)},se(Gu).toString=function(){return\"Message(prettyText=\"+this.za_1+\")\"},se(Gu).hashCode=function(){return this.za_1.hashCode()},se(Gu).equals=function(n){if(this===n)return!0;if(!(n instanceof Gu))return!1;var t=n instanceof Gu?n:_e();return!!this.za_1.equals(t.za_1)},se(Uu).toString=function(){return\"ListElement(prettyText=\"+this.ab_1+\")\"},se(Uu).hashCode=function(){return this.ab_1.hashCode()},se(Uu).equals=function(n){if(this===n)return!0;if(!(n instanceof Uu))return!1;var t=n instanceof Uu?n:_e();return!!this.ab_1.equals(t.ab_1)},se(Vu).toString=function(){return\"TreeNode(prettyText=\"+this.bb_1+\")\"},se(Vu).hashCode=function(){return this.bb_1.hashCode()},se(Vu).equals=function(n){if(this===n)return!0;if(!(n instanceof Vu))return!1;var t=n instanceof Vu?n:_e();return!!this.bb_1.equals(t.bb_1)},se(Qu).toString=function(){return\"Link(href=\"+this.cb_1+\", label=\"+this.db_1+\")\"},se(Qu).hashCode=function(){var n=ue(this.cb_1);return zn(n,31)+ue(this.db_1)|0},se(Qu).equals=function(n){if(this===n)return!0;if(!(n instanceof Qu))return!1;var t=n instanceof Qu?n:_e();return this.cb_1===t.cb_1&&this.db_1===t.db_1},se(Zu).toString=function(){return\"Label(text=\"+this.eb_1+\")\"},se(Zu).hashCode=function(){return ue(this.eb_1)},se(Zu).equals=function(n){if(this===n)return!0;if(!(n instanceof Zu))return!1;var t=n instanceof Zu?n:_e();return this.eb_1===t.eb_1},se(io).toString=function(){return\"Info(label=\"+this.hb_1+\", docLink=\"+this.ib_1+\")\"},se(io).hashCode=function(){var n=ee(this.hb_1);return zn(n,31)+(null==this.ib_1?0:ee(this.ib_1))|0},se(io).equals=function(n){if(this===n)return!0;if(!(n instanceof io))return!1;var t=n instanceof io?n:_e();return!!oe(this.hb_1,t.hb_1)&&!!oe(this.ib_1,t.ib_1)},se(eo).toString=function(){return\"Project(path=\"+this.jb_1+\")\"},se(eo).hashCode=function(){return ue(this.jb_1)},se(eo).equals=function(n){if(this===n)return!0;if(!(n instanceof eo))return!1;var t=n instanceof eo?n:_e();return this.jb_1===t.jb_1},se(uo).toString=function(){return\"Task(path=\"+this.kb_1+\", type=\"+this.lb_1+\")\"},se(uo).hashCode=function(){var n=ue(this.kb_1);return zn(n,31)+ue(this.lb_1)|0},se(uo).equals=function(n){if(this===n)return!0;if(!(n instanceof uo))return!1;var t=n instanceof uo?n:_e();return this.kb_1===t.kb_1&&this.lb_1===t.lb_1},se(oo).toString=function(){return\"TaskPath(path=\"+this.mb_1+\")\"},se(oo).hashCode=function(){return ue(this.mb_1)},se(oo).equals=function(n){if(this===n)return!0;if(!(n instanceof oo))return!1;var t=n instanceof oo?n:_e();return this.mb_1===t.mb_1},se(fo).toString=function(){return\"Bean(type=\"+this.nb_1+\")\"},se(fo).hashCode=function(){return ue(this.nb_1)},se(fo).equals=function(n){if(this===n)return!0;if(!(n instanceof fo))return!1;var t=n instanceof fo?n:_e();return this.nb_1===t.nb_1},se(so).toString=function(){return\"SystemProperty(name=\"+this.ob_1+\")\"},se(so).hashCode=function(){return ue(this.ob_1)},se(so).equals=function(n){if(this===n)return!0;if(!(n instanceof so))return!1;var t=n instanceof so?n:_e();return this.ob_1===t.ob_1},se(co).toString=function(){return\"Property(kind=\"+this.pb_1+\", name=\"+this.qb_1+\", owner=\"+this.rb_1+\")\"},se(co).hashCode=function(){var n=ue(this.pb_1);return n=zn(n,31)+ue(this.qb_1)|0,zn(n,31)+ue(this.rb_1)|0},se(co).equals=function(n){if(this===n)return!0;if(!(n instanceof co))return!1;var t=n instanceof co?n:_e();return this.pb_1===t.pb_1&&this.qb_1===t.qb_1&&this.rb_1===t.rb_1},se(ao).toString=function(){return\"BuildLogic(location=\"+this.sb_1+\")\"},se(ao).hashCode=function(){return ue(this.sb_1)},se(ao).equals=function(n){if(this===n)return!0;if(!(n instanceof ao))return!1;var t=n instanceof ao?n:_e();return this.sb_1===t.sb_1},se(ho).toString=function(){return\"BuildLogicClass(type=\"+this.tb_1+\")\"},se(ho).hashCode=function(){return ue(this.tb_1)},se(ho).equals=function(n){if(this===n)return!0;if(!(n instanceof ho))return!1;var t=n instanceof ho?n:_e();return this.tb_1===t.tb_1},se(vo).zb=function(){return this.yb_1},se(vo).toString=function(){return\"TaskTreeIntent(delegate=\"+this.yb_1+\")\"},se(vo).hashCode=function(){return ee(this.yb_1)},se(vo).equals=function(n){if(this===n)return!0;if(!(n instanceof vo))return!1;var t=n instanceof vo?n:_e();return!!oe(this.yb_1,t.yb_1)},se(go).zb=function(){return this.ac_1},se(go).toString=function(){return\"MessageTreeIntent(delegate=\"+this.ac_1+\")\"},se(go).hashCode=function(){return ee(this.ac_1)},se(go).equals=function(n){if(this===n)return!0;if(!(n instanceof go))return!1;var t=n instanceof go?n:_e();return!!oe(this.ac_1,t.ac_1)},se(wo).zb=function(){return this.bc_1},se(wo).toString=function(){return\"InputTreeIntent(delegate=\"+this.bc_1+\")\"},se(wo).hashCode=function(){return ee(this.bc_1)},se(wo).equals=function(n){if(this===n)return!0;if(!(n instanceof wo))return!1;var t=n instanceof wo?n:_e();return!!oe(this.bc_1,t.bc_1)},se(bo).zb=function(){return this.cc_1},se(bo).toString=function(){return\"IncompatibleTaskTreeIntent(delegate=\"+this.cc_1+\")\"},se(bo).hashCode=function(){return ee(this.cc_1)},se(bo).equals=function(n){if(this===n)return!0;if(!(n instanceof bo))return!1;var t=n instanceof bo?n:_e();return!!oe(this.cc_1,t.cc_1)},se(po).toString=function(){return\"SetTab(tab=\"+this.dc_1+\")\"},se(po).hashCode=function(){return this.dc_1.hashCode()},se(po).equals=function(n){if(this===n)return!0;if(!(n instanceof po))return!1;var t=n instanceof po?n:_e();return!!this.dc_1.equals(t.dc_1)},se(mo).mc=function(n,t,r,i,e,u,o,f){return new mo(n,t,r,i,e,u,o,f)},se(mo).nc=function(n,t,r,i,e,u,o,f,s){return n=n===A?this.ec_1:n,t=t===A?this.fc_1:t,r=r===A?this.gc_1:r,i=i===A?this.hc_1:i,e=e===A?this.ic_1:e,u=u===A?this.jc_1:u,o=o===A?this.kc_1:o,f=f===A?this.lc_1:f,s===A?this.mc(n,t,r,i,e,u,o,f):s.mc.call(this,n,t,r,i,e,u,o,f)},se(mo).toString=function(){return\"Model(heading=\"+this.ec_1+\", summary=\"+this.fc_1+\", learnMore=\"+this.gc_1+\", messageTree=\"+this.hc_1+\", locationTree=\"+this.ic_1+\", inputTree=\"+this.jc_1+\", incompatibleTaskTree=\"+this.kc_1+\", tab=\"+this.lc_1+\")\"},se(mo).hashCode=function(){var n=this.ec_1.hashCode();return n=zn(n,31)+ee(this.fc_1)|0,n=zn(n,31)+this.gc_1.hashCode()|0,n=zn(n,31)+this.hc_1.hashCode()|0,n=zn(n,31)+this.ic_1.hashCode()|0,n=zn(n,31)+this.jc_1.hashCode()|0,n=zn(n,31)+this.kc_1.hashCode()|0,zn(n,31)+this.lc_1.hashCode()|0},se(mo).equals=function(n){if(this===n)return!0;if(!(n instanceof mo))return!1;var t=n instanceof mo?n:_e();return!!(this.ec_1.equals(t.ec_1)&&oe(this.fc_1,t.fc_1)&&this.gc_1.equals(t.gc_1)&&this.hc_1.equals(t.hc_1)&&this.ic_1.equals(t.ic_1)&&this.jc_1.equals(t.jc_1)&&this.kc_1.equals(t.kc_1)&&this.lc_1.equals(t.lc_1))},se(Uo).gd=function(n,t){var r,i;return n instanceof vo?r=t.nc(A,A,A,A,_s().id(n.yb_1,t.ic_1)):n instanceof go?r=t.nc(A,A,A,_s().id(n.ac_1,t.hc_1)):n instanceof wo?r=t.nc(A,A,A,A,A,_s().id(n.bc_1,t.jc_1)):n instanceof bo?r=t.nc(A,A,A,A,A,A,_s().id(n.cc_1,t.kc_1)):n instanceof qc?r=function(n,t,r,i){var e;return r instanceof go?e=n.nc(A,A,A,mc(n.hc_1,r,i)):r instanceof vo?e=n.nc(A,A,A,A,mc(n.ic_1,r,i)):r instanceof wo?e=n.nc(A,A,A,A,A,mc(n.jc_1,r,i)):r instanceof bo?e=n.nc(A,A,A,A,A,A,mc(n.kc_1,r,i)):(console.error(\"Unhandled tree intent: \"+r),e=n),e}(t,0,n.xc_1,(i=n,function(n){var t;if(!(n instanceof Ou))throw fu(ie(\"Failed requirement.\"));for(var r=n.oa_1,e=i.wc_1,u=Nr(xt(r,10)),o=0,f=r.f();f.g();){var s,c,a=f.h(),h=o;if(o=h+1|0,e===wr(h)){var l=a.sa_1;c=a.ua(A,null==l?null:l.ad())}else c=a;s=c,u.d(s)}return t=u,n.qa(A,A,t)})):n instanceof kc?(window.navigator.clipboard.writeText(n.hd_1),r=t):n instanceof po?r=t.nc(A,A,A,A,A,A,A,n.dc_1):(console.error(\"Unhandled intent: \"+n),r=t),r},se(Uo).ba=function(n,t){var r=n instanceof Bc?n:_e();return this.gd(r,t instanceof mo?t:_e())},se(Uo).jd=function(n){return Af().y9(Jf(To),[qo(0,n),Bo(0,n)])},se(Uo).z9=function(n){return this.jd(n instanceof mo?n:_e())},se(Zo).toString=function(){return\"ImportedProblem(problem=\"+this.kd_1+\", message=\"+this.ld_1+\", trace=\"+this.md_1+\")\"},se(Zo).hashCode=function(){var n=ee(this.kd_1);return n=zn(n,31)+this.ld_1.hashCode()|0,zn(n,31)+ee(this.md_1)|0},se(Zo).equals=function(n){if(this===n)return!0;if(!(n instanceof Zo))return!1;var t=n instanceof Zo?n:_e();return!!oe(this.kd_1,t.kd_1)&&!!this.ld_1.equals(t.ld_1)&&!!oe(this.md_1,t.md_1)},se(ff).ud=function(n,t){return this.td_1(n,t)},se(ff).compare=function(n,t){return this.ud(n,t)},se(bf).toString=function(){return\"LearnMore(text=\"+this.sc_1+\", documentationLink=\"+this.tc_1+\")\"},se(bf).hashCode=function(){var n=ue(this.sc_1);return zn(n,31)+ue(this.tc_1)|0},se(bf).equals=function(n){if(this===n)return!0;if(!(n instanceof bf))return!1;var t=n instanceof bf?n:_e();return this.sc_1===t.sc_1&&this.tc_1===t.tc_1},se(pf).toString=function(){return\"Text(text=\"+this.fa_1+\")\"},se(pf).hashCode=function(){return ue(this.fa_1)},se(pf).equals=function(n){if(this===n)return!0;if(!(n instanceof pf))return!1;var t=n instanceof pf?n:_e();return this.fa_1===t.fa_1},se(mf).toString=function(){return\"Reference(name=\"+this.da_1+\", clipboardString=\"+this.ea_1+\")\"},se(mf).hashCode=function(){var n=ue(this.da_1);return zn(n,31)+ue(this.ea_1)|0},se(mf).equals=function(n){if(this===n)return!0;if(!(n instanceof mf))return!1;var t=n instanceof mf?n:_e();return this.da_1===t.da_1&&this.ea_1===t.ea_1},se(yf).ed=function(n){return this.dd_1.d(new pf(n)),this},se(yf).xd=function(n,t){return this.dd_1.d(new mf(n,t)),this},se(yf).fd=function(n,t,r){return t=t===A?n:t,r===A?this.xd(n,t):r.xd.call(this,n,t)},se(yf).j5=function(){return new Cf(Rn(this.dd_1))},se(qf).rd=function(n){return new Cf(gr(new pf(n)))},se(qf).qd=function(n){var t=new yf;return n(t),t.j5()},se(Cf).vd=function(n){return new Cf(n)},se(Cf).toString=function(){return\"PrettyText(fragments=\"+this.ca_1+\")\"},se(Cf).hashCode=function(){return ee(this.ca_1)},se(Cf).equals=function(n){if(this===n)return!0;if(!(n instanceof Cf))return!1;var t=n instanceof Cf?n:_e();return!!oe(this.ca_1,t.ca_1)},se(xf).sd=function(n){return function(n){for(var t=Gr(),r=n.f();r.g();)for(var i=t,e=r.h().f();e.g();){var u,o=e.h(),f=i,s=f.v1(o);if(null==s){var c=Gr();f.h5(o,c),u=c}else u=s;i=u instanceof Vr?u:_e()}return t}(n)},se(Sf).toString=function(){return\"Trie(nestedMaps=\"+this.wd_1+\")\"},se(Sf).hashCode=function(){return ee(this.wd_1)},se(Sf).equals=function(n){return function(n,t){return t instanceof Sf&&!!oe(n,t instanceof Sf?t.wd_1:_e())}(this.wd_1,n)},se(Uf).ga=function(n){return Qf().yd(this.x9_1,A,n)},se(Uf).ha=function(n){return Qf().yd(this.x9_1,A,A,n)},se(Uf).ja=function(n){return Qf().yd(this.x9_1,A,A,nu(n))},se(Uf).y9=function(n,t){return Qf().yd(this.x9_1,n,A,nu(t))},se(Uf).zd=function(n,t){return Qf().yd(this.x9_1,n,A,t)},se(Uf).fb=function(n,t){return Qf().yd(this.x9_1,n,t)},se(Uf).rc=function(n,t){return Qf().yd(this.x9_1,A,n,nu(t))},se(Uf).toString=function(){return\"ViewFactory(elementName=\"+this.x9_1+\")\"},se(Uf).hashCode=function(){return ue(this.x9_1)},se(Uf).equals=function(n){if(this===n)return!0;if(!(n instanceof Uf))return!1;var t=n instanceof Uf?n:_e();return this.x9_1===t.x9_1},se(Vf).ae=function(n,t,r,i){return new Wf(n,t,r,i)},se(Vf).yd=function(n,t,r,i,e){return t=t===A?pt():t,r=r===A?null:r,i=i===A?pt():i,e===A?this.ae(n,t,r,i):e.ae.call(this,n,t,r,i)},se(Wf).toString=function(){return\"Element(elementName=\"+this.be_1+\", attributes=\"+this.ce_1+\", innerText=\"+this.de_1+\", children=\"+this.ee_1+\")\"},se(Wf).hashCode=function(){var n=ue(this.be_1);return n=zn(n,31)+ee(this.ce_1)|0,n=zn(n,31)+(null==this.de_1?0:ue(this.de_1))|0,zn(n,31)+ee(this.ee_1)|0},se(Wf).equals=function(n){if(this===n)return!0;if(!(n instanceof Wf))return!1;var t=n instanceof Wf?n:_e();return this.be_1===t.be_1&&!!oe(this.ce_1,t.ce_1)&&this.de_1==t.de_1&&!!oe(this.ee_1,t.ee_1)},se(ns).u9=function(n){return this.r9_1(new ts(\"click\",n))},se(ns).t9=function(n){return this.r9_1(new rs(n))},se(ns).gb=function(n){for(var t=0,r=n.length;t<r;){var i=n[t];t=t+1|0,this.r9_1(new rs(i))}return lr()},se(ns).s9=function(n){return this.r9_1(new is(\"title\",n))},se(ns).bd=function(n){return this.r9_1(new is(\"href\",n))},se(ss).oe=function(){return this.ne_1},se(ss).toString=function(){return\"Toggle(focus=\"+this.ne_1+\")\"},se(ss).hashCode=function(){return ee(this.ne_1)},se(ss).equals=function(n){if(this===n)return!0;if(!(n instanceof ss))return!1;var t=n instanceof ss?n:_e();return!!oe(this.ne_1,t.ne_1)},se(cs).pe=function(n,t){return this.re(n.qe((r=t,function(n){return n.me(r(n.ub_1))})));var r},se(cs).re=function(n){return new cs(n)},se(cs).toString=function(){return\"Model(tree=\"+this.xb_1+\")\"},se(cs).hashCode=function(){return this.xb_1.hashCode()},se(cs).equals=function(n){if(this===n)return!0;if(!(n instanceof cs))return!1;var t=n instanceof cs?n:_e();return!!this.xb_1.equals(t.xb_1)},se(ls).id=function(n,t){var r;if(n instanceof ss){var i=n.oe();r=t.re(i.qe(hs))}else le();return r},se(ds).cd=function(){return this.ve_1},se(ds).we=function(){return 0},se(ds).qe=function(n){return n(this.ve_1)},se(ds).toString=function(){return\"Original(tree=\"+this.ve_1+\")\"},se(ds).hashCode=function(){return this.ve_1.hashCode()},se(ds).equals=function(n){if(this===n)return!0;if(!(n instanceof ds))return!1;var t=n instanceof ds?n:_e();return!!this.ve_1.equals(t.ve_1)},se(gs).cd=function(){return this.ue_1},se(gs).we=function(){return this.se_1.we()+1|0},se(gs).qe=function(n){return this.se_1.qe((t=this,r=n,function(n){for(var i,e=n.vb_1,u=t.te_1,o=Nr(xt(e,10)),f=0,s=e.f();s.g();){var c,a=s.h(),h=f;f=h+1|0,c=u===wr(h)?r(a):a,o.d(c)}return i=o,n.me(A,i)}));var t,r},se(gs).toString=function(){return\"Child(parent=\"+this.se_1+\", index=\"+this.te_1+\", tree=\"+this.ue_1+\")\"},se(gs).hashCode=function(){var n=ee(this.se_1);return n=zn(n,31)+this.te_1|0,zn(n,31)+this.ue_1.hashCode()|0},se(gs).equals=function(n){if(this===n)return!0;if(!(n instanceof gs))return!1;var t=n instanceof gs?n:_e();return!!oe(this.se_1,t.se_1)&&this.te_1===t.te_1&&!!this.ue_1.equals(t.ue_1)},se(ws).ad=function(){var n;switch(this.q8_1){case 0:n=ms();break;case 1:n=ps();break;default:le()}return n},se(bs).vc=function(){var n,t;return Jn(On(Oe(0,this.cd().vb_1.k()-1|0)),(n=this,(t=function(t){return n.xe(t)}).callableName=\"child\",t))},se(bs).xe=function(n){return new gs(this,n,this.cd().vb_1.j(n))},se(ks).uc=function(){return new ds(this)},se(ks).ye=function(){return!this.vb_1.i()},se(ks).ze=function(n,t,r){return new ks(n,t,r)},se(ks).me=function(n,t,r,i){return n=n===A?this.ub_1:n,t=t===A?this.vb_1:t,r=r===A?this.wb_1:r,i===A?this.ze(n,t,r):i.ze.call(this,n,t,r)},se(ks).toString=function(){return\"Tree(label=\"+this.ub_1+\", children=\"+this.vb_1+\", state=\"+this.wb_1+\")\"},se(ks).hashCode=function(){var n=null==this.ub_1?0:ee(this.ub_1);return n=zn(n,31)+ee(this.vb_1)|0,zn(n,31)+this.wb_1.hashCode()|0},se(ks).equals=function(n){if(this===n)return!0;if(!(n instanceof ks))return!1;var t=n instanceof ks?n:_e();return!!oe(this.ub_1,t.ub_1)&&!!oe(this.vb_1,t.vb_1)&&!!this.wb_1.equals(t.wb_1)},se(Cs).toString=function(){return\"ProblemIdElement(name=\"+this.df_1+\", displayName=\"+this.ef_1+\")\"},se(Cs).hashCode=function(){var n=ue(this.df_1);return zn(n,31)+ue(this.ef_1)|0},se(Cs).equals=function(n){if(this===n)return!0;if(!(n instanceof Cs))return!1;var t=n instanceof Cs?n:_e();return this.df_1===t.df_1&&this.ef_1===t.ef_1},se(xs).toString=function(){return\"ProblemSummary(problemId=\"+this.ff_1+\", count=\"+this.gf_1+\")\"},se(xs).hashCode=function(){var n=ee(this.ff_1);return zn(n,31)+this.gf_1|0},se(xs).equals=function(n){if(this===n)return!0;if(!(n instanceof xs))return!1;var t=n instanceof xs?n:_e();return!!oe(this.ff_1,t.ff_1)&&this.gf_1===t.gf_1},se(Is).toString=function(){return\"ProblemNodeGroup(tree=\"+this.hf_1+\", children=\"+this.if_1+\", childGroups=\"+this.jf_1+\", id=\"+this.kf_1+\")\"},se(Is).hashCode=function(){var n=this.hf_1.hashCode();return n=zn(n,31)+ee(this.if_1)|0,n=zn(n,31)+ee(this.jf_1)|0,zn(n,31)+this.kf_1|0},se(Is).equals=function(n){if(this===n)return!0;if(!(n instanceof Is))return!1;var t=n instanceof Is?n:_e();return!!this.hf_1.equals(t.hf_1)&&!!oe(this.if_1,t.if_1)&&!!oe(this.jf_1,t.jf_1)&&this.kf_1===t.kf_1},se(Us).toString=function(){return\"Text(text=\"+this.lf_1+\")\"},se(Us).hashCode=function(){return ue(this.lf_1)},se(Us).equals=function(n){if(this===n)return!0;if(!(n instanceof Us))return!1;var t=n instanceof Us?n:_e();return this.lf_1===t.lf_1},se(Vs).toString=function(){return\"ProblemIdNode(prettyText=\"+this.mf_1+\", separator=\"+this.nf_1+\")\"},se(Vs).hashCode=function(){var n=this.mf_1.hashCode();return zn(n,31)+(0|this.nf_1)|0},se(Vs).equals=function(n){if(this===n)return!0;if(!(n instanceof Vs))return!1;var t=n instanceof Vs?n:_e();return!!this.mf_1.equals(t.mf_1)&&this.nf_1===t.nf_1},se(Qs).toString=function(){return\"Advice(label=\"+this.of_1+\", docLink=\"+this.pf_1+\")\"},se(Qs).hashCode=function(){var n=ee(this.of_1);return zn(n,31)+(null==this.pf_1?0:ee(this.pf_1))|0},se(Qs).equals=function(n){if(this===n)return!0;if(!(n instanceof Qs))return!1;var t=n instanceof Qs?n:_e();return!!oe(this.of_1,t.of_1)&&!!oe(this.pf_1,t.pf_1)},se(Ys).zb=function(){return this.qf_1},se(Ys).toString=function(){return\"MessageTreeIntent(delegate=\"+this.qf_1+\")\"},se(Ys).hashCode=function(){return ee(this.qf_1)},se(Ys).equals=function(n){if(this===n)return!0;if(!(n instanceof Ys))return!1;var t=n instanceof Ys?n:_e();return!!oe(this.qf_1,t.qf_1)},se(Ws).zb=function(){return this.rf_1},se(Ws).toString=function(){return\"ProblemIdTreeIntent(delegate=\"+this.rf_1+\")\"},se(Ws).hashCode=function(){return ee(this.rf_1)},se(Ws).equals=function(n){if(this===n)return!0;if(!(n instanceof Ws))return!1;var t=n instanceof Ws?n:_e();return!!oe(this.rf_1,t.rf_1)},se(Ks).zb=function(){return this.sf_1},se(Ks).toString=function(){return\"FileLocationTreeIntent(delegate=\"+this.sf_1+\")\"},se(Ks).hashCode=function(){return ee(this.sf_1)},se(Ks).equals=function(n){if(this===n)return!0;if(!(n instanceof Ks))return!1;var t=n instanceof Ks?n:_e();return!!oe(this.sf_1,t.sf_1)},se(Xs).toString=function(){return\"SetTab(tab=\"+this.tf_1+\")\"},se(Xs).hashCode=function(){return this.tf_1.hashCode()},se(Xs).equals=function(n){if(this===n)return!0;if(!(n instanceof Xs))return!1;var t=n instanceof Xs?n:_e();return!!this.tf_1.equals(t.tf_1)},se(Js).cg=function(n,t,r,i,e,u,o,f){return new Js(n,t,r,i,e,u,o,f)},se(Js).dg=function(n,t,r,i,e,u,o,f,s){return n=n===A?this.uf_1:n,t=t===A?this.vf_1:t,r=r===A?this.wf_1:r,i=i===A?this.xf_1:i,e=e===A?this.yf_1:e,u=u===A?this.zf_1:u,o=o===A?this.ag_1:o,f=f===A?this.bg_1:f,s===A?this.cg(n,t,r,i,e,u,o,f):s.cg.call(this,n,t,r,i,e,u,o,f)},se(Js).toString=function(){return\"Model(heading=\"+this.uf_1+\", summary=\"+this.vf_1+\", learnMore=\"+this.wf_1+\", messageTree=\"+this.xf_1+\", problemIdTree=\"+this.yf_1+\", fileLocationTree=\"+this.zf_1+\", problemCount=\"+this.ag_1+\", tab=\"+this.bg_1+\")\"},se(Js).hashCode=function(){var n=this.uf_1.hashCode();return n=zn(n,31)+ee(this.vf_1)|0,n=zn(n,31)+this.wf_1.hashCode()|0,n=zn(n,31)+this.xf_1.hashCode()|0,n=zn(n,31)+this.yf_1.hashCode()|0,n=zn(n,31)+this.zf_1.hashCode()|0,n=zn(n,31)+this.ag_1|0,zn(n,31)+this.bg_1.hashCode()|0},se(Js).equals=function(n){if(this===n)return!0;if(!(n instanceof Js))return!1;var t=n instanceof Js?n:_e();return!!(this.uf_1.equals(t.uf_1)&&oe(this.vf_1,t.vf_1)&&this.wf_1.equals(t.wf_1)&&this.xf_1.equals(t.xf_1)&&this.yf_1.equals(t.yf_1)&&this.zf_1.equals(t.zf_1)&&this.ag_1===t.ag_1&&this.bg_1.equals(t.bg_1))},se(bc).eg=function(n,t){var r,i;return n instanceof Ks?r=t.dg(A,A,A,A,A,_s().id(n.sf_1,t.zf_1)):n instanceof Ws?r=t.dg(A,A,A,A,_s().id(n.rf_1,t.yf_1)):n instanceof Ys?r=t.dg(A,A,A,_s().id(n.qf_1,t.xf_1)):n instanceof qc?r=function(n,t,r,i){var e;return r instanceof Ys?e=n.dg(A,A,A,mc(n.xf_1,r,i)):r instanceof Ws?e=n.dg(A,A,A,A,mc(n.yf_1,r,i)):r instanceof Ks?e=n.dg(A,A,A,A,A,mc(n.zf_1,r,i)):(console.error(\"Unhandled tree intent: \"+r),e=n),e}(t,0,n.xc_1,(i=n,function(n){var t;if(!(n instanceof Ou))throw fu(ie(\"Failed requirement.\"));for(var r=n.oa_1,e=i.wc_1,u=Nr(xt(r,10)),o=0,f=r.f();f.g();){var s,c,a=f.h(),h=o;if(o=h+1|0,e===wr(h)){var l=a.sa_1;c=a.ua(A,null==l?null:l.ad())}else c=a;s=c,u.d(s)}return t=u,n.qa(A,A,t)})):n instanceof kc?(window.navigator.clipboard.writeText(n.hd_1),r=t):n instanceof Xs?r=t.dg(A,A,A,A,A,A,A,n.tf_1):(console.error(\"Unhandled intent: \"+n),r=t),r},se(bc).ba=function(n,t){var r=n instanceof Bc?n:_e();return this.eg(r,t instanceof Js?t:_e())},se(bc).fg=function(n){return Af().y9(Jf(ac),[tc(0,n),rc(0,n)])},se(bc).z9=function(n){return this.fg(n instanceof Js?n:_e())},se(kc).toString=function(){return\"Copy(text=\"+this.hd_1+\")\"},se(kc).hashCode=function(){return ue(this.hd_1)},se(kc).equals=function(n){if(this===n)return!0;if(!(n instanceof kc))return!1;var t=n instanceof kc?n:_e();return this.hd_1===t.hd_1},se(qc).toString=function(){return\"ToggleStackTracePart(partIndex=\"+this.wc_1+\", location=\"+this.xc_1+\")\"},se(qc).hashCode=function(){var n=this.wc_1;return zn(n,31)+ee(this.xc_1)|0},se(qc).equals=function(n){if(this===n)return!0;if(!(n instanceof qc))return!1;var t=n instanceof qc?n:_e();return this.wc_1===t.wc_1&&!!oe(this.xc_1,t.xc_1)},se(Jr).k6=function(){var n=Object.create(null);return n.foo=1,delete n.foo,lr(),n},l=null,wn=0,function(){var n=configurationCacheProblems();if(null==n.problemsReport)zf(Ef(\"report\"),Vo(),function(n){var t,r,i,e,u,o,f,s,c,a,h=function(n){for(var t=Lr(),r=Lr(),i=Lr(),e=0,u=n.length;e<u;){var o=n[e];e=e+1|0;var f,s=o.input,c=null==s?null:r.d(Wo(s,o));if(null==c){var a=o.incompatibleTask;f=null==a?null:i.d(Wo(a,o))}else f=c;if(null==f){var h=he(o.problem);t.d(Wo(h,o))}}return new Yo(t,r,i)}(n.diagnostics),l=n.totalProblemCount;return new mo((f=(t=n).buildName,s=t.requestedTasks,c=null==s?null:Wt(s,\" \",A,r=r!==A&&r)>=0,a=null==c||c,Bf().qd((i=t,e=f,u=s,o=a,function(n){n.ed(function(n){var t;if(Wi(n)>0){var r,i=Zi(n,0);r=function(n){return 97<=n&&n<=122||!(ji(n,128)<0)&&function(n){var t;return t=1===function(n){var t=n,r=ru(eu().h9_1,t),i=eu().h9_1[r],e=(i+eu().i9_1[r]|0)-1|0,u=eu().j9_1[r];if(t>e)return 0;var o=3&u;if(0===o){var f=2,s=i,c=0;if(c<=1)do{if(c=c+1|0,(s=s+(u>>f&127)|0)>t)return 3;if((s=s+(u>>(f=f+7|0)&127)|0)>t)return 0;f=f+7|0}while(c<=1);return 3}if(u<=7)return o;var a=t-i|0;return u>>zn(2,u<=31?a%2|0:a)&3}(n)||function(n){var t=ru(ou().k9_1,n);return t>=0&&n<(ou().k9_1[t]+ou().l9_1[t]|0)}(n),t}(n)}(i)?function(n){return function(n){var t=Pi(n).toUpperCase();if(t.length>1){var r;if(329===n)r=t;else{var i=Zi(t,0),e=t.substring(1).toLowerCase();r=Pi(i)+e}return r}return Pi(function(n){return function(n){var t=n;return 452<=t&&t<=460||497<=t&&t<=499?De(zn(3,(t+1|0)/3|0)):4304<=t&&t<=4346||4349<=t&&t<=4351?n:mi(n)}(n)}(n))}(n)}(i):Pi(i),t=ie(r)+n.substring(1)}else t=n;return t}(i.cacheAction)+\" the configuration cache for \");var t=e;null==t||n.fd(t),null==e||n.ed(\" build and \");var r=u;return null==(null==r?null:n.fd(r))&&n.ed(\"default\"),n.ed(o?\" tasks\":\" task\"),lr()}))),function(n,t){var r=n.cacheActionDescription,i=null==r?null:vf(r),e=Bf().rd(function(n){var t=n.od_1.k(),r=df(t,\"build configuration input\");return t>0?r+\" and will cause the cache to be discarded when \"+(t<=1?\"its\":\"their\")+\" value change\":r}(t)),u=Bf().rd(function(n,t){var r=n.totalProblemCount,i=t.nd_1.k(),e=df(r,\"problem\");return r>i?e+\", only the first \"+i+\" \"+wf(i)+\" included in this report\":e}(n,t));return function(n,t){for(var r=0,i=n.length;r<i;){var e=n[r];r=r+1|0,null!=e&&t.d(e)}return t}([i,e,u],Lr())}(n,h),new bf(\"Gradle Configuration Cache\",n.documentationLink),uf(new Zu(Ho().qc_1),Jn(On(h.nd_1),af)),uf(new Zu($o().qc_1),function(n){return Jn(On(n),hf)}(h.nd_1)),uf(new Zu(Ro().qc_1),Jn(On(h.od_1),sf)),uf(new Zu(Go().qc_1),Jn(On(h.pd_1),cf)),0===l?Ro():Ho())}(n));else{var t=n.problemsReport;zf(Ef(\"report\"),pc(),function(n,t){for(var r=n.summaries,i=Nr(r.length),e=0,u=r.length;e<u;){var o,f=r[e];e=e+1|0;for(var s=f.problemId,c=Nr(s.length),a=0,h=s.length;a<h;){var l,_=s[a];a=a+1|0,l=new Cs(_.name,_.displayName),c.d(l)}o=new xs(c,f.count),i.d(o)}for(var v=i,d=function(n,t){for(var r=oi(),i=0,e=n.length;i<e;){var u=n[i];i=i+1|0;var o,f=Ln(u.problemId,\":\",A,A,A,A,Ds),s=r.v1(f);if(null==s){var c=Lr();r.h5(f,c),o=c}else o=s;o.d(u)}for(var a=r.o(),h=Nr(xt(a,10)),l=a.f();l.g();){for(var _,v=l.h(),d=v.i1(),g=Nr(xt(d,10)),w=d.f();w.g();){var b;b=Es(w.h(),null,!0),g.d(b)}var p,m=Hn(g),k=Dn(v.i1()),y=Ls(k,new Gu(Ns(Ts(k)).ed(\" (\"+v.i1().k()+\")\").j5()));n:{for(var q=t.f();q.g();){var B=q.h();if(zs(B.ff_1,k.problemId)){p=B;break n}}p=null}var C=p;null==C||m.d(js(C.gf_1)),_=new ks(y,m),h.d(_)}var x=h;return new cs(new ks(new Us(\"message tree root\"),x))}(t,v),g=function(n,t){for(var r=function(){var n=Lr();return new Is(new ks(new Vs(Bf().rd(\"Ungrouped\"),!0),n),n,oi())}(),i=oi(),e=0,u=n.length;e<u;){var o=n[e];e=e+1|0;for(var f=$n(Tn(o.problemId,1)),s=Nr(xt(f,10)),c=f.f();c.g();){var a,h=c.h();a=new Cs(h.name,h.displayName),s.d(a)}var l=Ss(i,s),_=Es(o);null==l?r.if_1.d(_):l.if_1.d(_)}for(var v=Nr(xt(t,10)),d=t.f();d.g();){var g,w=d.h();g=new xs($n(Gn(w.ff_1,1)),w.gf_1),v.d(g)}for(var b=oi(),p=v.f();p.g();){var m,k=p.h(),y=k.ff_1,q=b.v1(y);if(null==q){var B=Lr();b.h5(y,B),m=B}else m=q;m.d(k)}for(var C=b.o(),x=Nr(xt(C,10)),j=C.f();j.g();){for(var P,I=j.h(),S=I.j1(),z=0,E=I.i1().f();E.g();)z=z+E.h().gf_1|0;P=new xs(S,z),x.d(P)}for(var T=x.f();T.g();){var L=T.h(),N=Ss(i,L.ff_1),A=null==N?null:N.if_1;null==A||A.d(js(L.gf_1))}for(var M=i.w1(),F=Nr(xt(M,10)),D=M.f();D.g();){var O;O=D.h().hf_1,F.d(O)}var R=function(n,t){var r=Nr(n.k());return r.m(n),r.d(t),r}(F,r.hf_1);return new cs(new ks(new Us(\"group tree root\"),R))}(t,v),w=0,b=v.f();b.g();)w=w+b.h().gf_1|0;var p=function(n,t){for(var r=Lr(),i=oi(),e=0,u=n.length;e<u;){var o=n[e];e=e+1|0;var f=o.locations;if(null==f||0===f.length)r.d(Es(o));else{var s,c=o.locations;if(null==c)s=null;else{for(var a=Lr(),h=Ri(c);h.g();){var l=h.h();null!=l.path&&a.d(l)}s=a}if(null==s);else for(var _=s.f();_.g();){var v=_.h();Ps(i,he(v.path),o,v)}var d,g=o.locations;if(null==g)d=null;else{for(var w=Lr(),b=Ri(g);b.g();){var p=b.h();null!=p.pluginId&&w.d(p)}d=w}if(null==d);else for(var m=d.f();m.g();){var k=m.h();Ps(i,he(k.pluginId),o,k)}var y,q=o.locations;if(null==q)y=null;else{for(var B=Lr(),C=Ri(q);C.g();){var x=C.h();null!=x.taskPath&&B.d(x)}y=B}if(null==y);else for(var j=y.f();j.g();){var P=j.h();Ps(i,he(P.taskPath),o,P)}}}var I=function(n,t,r){for(var i=n.w1(),e=Nr(xt(i,10)),u=i.f();u.g();){var o;o=u.h().t3_1,e.d(o)}var f=Hn(e);return t.i()||f.d(new ks(new Vs(Bf().rd(\"no location\"),!0),t)),r>0&&f.d(js(r)),f}(i,r,t);return new cs(new ks(new Us(\"text\"),I))}(t,w);return new Js(Bf().rd(\"Problems Report\"),function(n,t){var r,i,e,u=n.description,o=null==u?null:gr(vf(u));return r=null==o?gr(Bf().qd((i=t,e=n,function(n){n.ed(i.length+\" problems have been reported during the execution\");var t=e.buildName;null==t||(n.ed(\" of build \"),n.fd(t));var r=e.requestedTasks;return null==r||(n.ed(\" for the following tasks:\"),n.fd(r),lr()),lr()}))):o,r}(n,t),new bf(\"reporting problems\",n.documentationLink),d,g,p,t.length,function(n,t,r){return lo(r)>0?Gs():lo(n)>0?Hs():lo(t)>0?$s():Hs()}(d,g,p))}(t,n.diagnostics))}}(),n}(void 0===this[\"configuration-cache-report\"]?{}:this[\"configuration-cache-report\"])}}[70](),{}))));\n//# sourceMappingURL=configuration-cache-report.js.map\n                </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "android/build.gradle",
    "content": "buildscript {\n    ext{\n        kotlin_version = '1.8.0'\n        agp_version = '8.1.4'\n        room_version = '2.6.1'\n        ksp_version = '1.9.21-1.0.16'\n    }\n\n\n    repositories {\n        google()\n        mavenCentral()\n    }\n\n    dependencies {\n        classpath \"com.android.tools.build:gradle:$agp_version\"\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\"\n        classpath \"org.jetbrains.kotlin:kotlin-serialization:$kotlin_version\"\n    }\n}\n\n\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\n\nrootProject.buildDir = '../build'\nsubprojects {\n    project.buildDir = \"${rootProject.buildDir}/${project.name}\"\n} \n\nsubprojects {\n    project.evaluationDependsOn(':app')\n}\n\ntasks.register(\"clean\", Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "android/gradle/wrapper/gradle-wrapper.properties",
    "content": "#Tue Jul 15 12:22:13 CST 2025\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.4-bin.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "android/gradle.properties",
    "content": "org.gradle.jvmargs=-Xmx4G\nandroid.useAndroidX=true\nandroid.enableJetifier=true\n"
  },
  {
    "path": "android/settings.gradle",
    "content": "pluginManagement {\n    def flutterSdkPath = {\n        def properties = new Properties()\n        file(\"local.properties\").withInputStream { properties.load(it) }\n        def flutterSdkPath = properties.getProperty(\"flutter.sdk\")\n        assert flutterSdkPath != null, \"flutter.sdk not set in local.properties\"\n        return flutterSdkPath\n    }\n    settings.ext.flutterSdkPath = flutterSdkPath()\n\n    includeBuild(\"${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle\")\n\n    repositories {\n        google()\n        mavenCentral()\n        gradlePluginPortal()\n    }\n\n    plugins {\n        id \"dev.flutter.flutter-gradle-plugin\" version \"1.0.0\" apply false\n    }\n}\n\nplugins {\n    id \"dev.flutter.flutter-plugin-loader\" version \"1.0.0\"\n    id \"com.android.application\" version '8.1.4' apply false\n}\n\ninclude \":app\"\ninclude \":utils\""
  },
  {
    "path": "android/utils/.gitignore",
    "content": "/build\n/.cxx"
  },
  {
    "path": "android/utils/build.gradle",
    "content": "plugins {\n    id(\"com.android.library\")\n    id(\"kotlin-android\")\n}\n\nandroid {\n    namespace \"com.openlist.utils\"\n    compileSdk 35\n\n    defaultConfig {\n        minSdk 21\n\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n\n    externalNativeBuild {\n        cmake {\n            path \"src/main/cpp/CMakeLists.txt\"\n            version \"3.22.1\"\n        }\n    }\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_17\n        targetCompatibility JavaVersion.VERSION_17\n    }\n    kotlinOptions {\n        jvmTarget = \"17\"\n    }\n}\n\ndependencies {\n    testImplementation(\"junit:junit:4.13.2\")\n    androidTestImplementation(\"androidx.test.ext:junit:1.1.5\")\n    androidTestImplementation(\"androidx.test.espresso:espresso-core:3.5.1\")\n}"
  },
  {
    "path": "android/utils/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "android/utils/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "android/utils/src/androidTest/java/com/github/jing332/utils/ExampleInstrumentedTest.kt",
    "content": "package com.openlist.utils\n\nimport androidx.test.platform.app.InstrumentationRegistry\nimport androidx.test.ext.junit.runners.AndroidJUnit4\n\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\nimport org.junit.Assert.*\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\n@RunWith(AndroidJUnit4::class)\nclass ExampleInstrumentedTest {\n    @Test\n    fun useAppContext() {\n        // Context of the app under test.\n        val appContext = InstrumentationRegistry.getInstrumentation().targetContext\n        assertEquals(\"com.openlist.utils.test\", appContext.packageName)\n    }\n}"
  },
  {
    "path": "android/utils/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n</manifest>"
  },
  {
    "path": "android/utils/src/main/cpp/CMakeLists.txt",
    "content": "# For more information about using CMake with Android Studio, read the\n# documentation: https://d.android.com/studio/projects/add-native-code.html.\n# For more examples on how to use CMake, see https://github.com/android/ndk-samples.\n\n# Sets the minimum CMake version required for this project.\ncmake_minimum_required(VERSION 3.22.1)\n\n# Declares the project name. The project name can be accessed via ${ PROJECT_NAME},\n# Since this is the top level CMakeLists.txt, the project name is also accessible\n# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level\n# build script scope).\nproject(\"utils\")\n\n# Creates and names a library, sets it as either STATIC\n# or SHARED, and provides the relative paths to its source code.\n# You can define multiple libraries, and CMake builds them for you.\n# Gradle automatically packages shared libraries with your APK.\n#\n# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define\n# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME}\n# is preferred for the same purpose.\n#\n# In order to load a library into your app from Java/Kotlin, you must call\n# System.loadLibrary() and pass the name of the library defined here;\n# for GameActivity/NativeActivity derived applications, the same library name must be\n# used in the AndroidManifest.xml file.\nadd_library(${CMAKE_PROJECT_NAME} SHARED\n        # List C/C++ source files with relative paths to this CMakeLists.txt.\n        utils.cpp)\n\n# Specifies libraries CMake should link to your target library. You\n# can link libraries from various origins, such as libraries defined in this\n# build script, prebuilt third-party libraries, or Android system libraries.\ntarget_link_libraries(${CMAKE_PROJECT_NAME}\n        # List libraries link to the target library\n        android\n        log)"
  },
  {
    "path": "android/utils/src/main/cpp/utils.cpp",
    "content": "#include <jni.h>\n#include <string>\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <arpa/inet.h>\n#include <string.h>\n#include <sys/ioctl.h>\n#include <net/if.h>\n#include <netinet/in.h>\n#include <unistd.h>\n\n#define RUN_SUCCESS 0\n#define RUN_FAIL 1\n\n\nint get_local_ip_using_ifconf(char *str_ip)\n{\n    int sock_fd, intrface;\n    struct ifreq buf[INET_ADDRSTRLEN];\n    struct ifconf ifc;\n    char *local_ip = NULL;\n    int status = RUN_FAIL;\n\n    if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) >= 0)\n    {\n        ifc.ifc_len = sizeof(buf);\n        ifc.ifc_buf = (caddr_t)buf;\n        if (!ioctl(sock_fd, SIOCGIFCONF, (char *)&ifc))\n        {\n            intrface = ifc.ifc_len/sizeof(struct ifreq);\n            while (intrface-- > 0)\n            {\n                if (!(ioctl(sock_fd, SIOCGIFADDR, (char *)&buf[intrface])))\n                {\n                    local_ip = NULL;\n                    local_ip = inet_ntoa(((struct sockaddr_in*)(&buf[intrface].ifr_addr))->sin_addr);\n                    if(local_ip)\n                    {\n                        strcpy(str_ip, local_ip);\n                        status = RUN_SUCCESS;\n                        if(strcmp(\"127.0.0.1\", str_ip))\n                        {\n                            break;\n                        }\n                    }\n\n                }\n            }\n        }\n        close(sock_fd);\n    }\n    return status;\n}\n\nextern \"C\" JNIEXPORT jstring JNICALL\nJava_com_github_openlistteam_utils_NativeLib_getLocalIp(\n        JNIEnv* env,\n        jobject /* this */) {\n    std::string hello = \"Hello from C++\";\n    char str_ip[INET_ADDRSTRLEN];\n    int status = get_local_ip_using_ifconf(str_ip);\n\n    return env->NewStringUTF(str_ip);\n}\n"
  },
  {
    "path": "android/utils/src/main/java/com/github/jing332/utils/NativeLib.kt",
    "content": "package com.openlist.utils\n\nobject NativeLib {\n    external fun getLocalIp(): String\n\n    init {\n        System.loadLibrary(\"utils\")\n    }\n\n}"
  },
  {
    "path": "android/utils/src/test/java/com/github/jing332/utils/ExampleUnitTest.kt",
    "content": "package com.openlist.utils\n\nimport org.junit.Test\n\nimport org.junit.Assert.*\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\nclass ExampleUnitTest {\n    @Test\n    fun addition_isCorrect() {\n        assertEquals(4, 2 + 2)\n    }\n}"
  },
  {
    "path": "ios/.gitignore",
    "content": "**/dgph\n*.mode1v3\n*.mode2v3\n*.moved-aside\n*.pbxuser\n*.perspectivev3\n**/*sync/\n.sconsign.dblite\n.tags*\n**/.vagrant/\n**/DerivedData/\nIcon?\n**/Pods/\n**/.symlinks/\nprofile\nxcuserdata\n**/.generated/\nFlutter/App.framework\nFlutter/Flutter.framework\nFlutter/Flutter.podspec\nFlutter/Generated.xcconfig\nFlutter/ephemeral/\nFlutter/app.flx\nFlutter/app.zip\nFlutter/flutter_assets/\nFlutter/flutter_export_environment.sh\nServiceDefinitions.json\nRunner/GeneratedPluginRegistrant.*\n\n# Exceptions to above rules.\n!default.mode1v3\n!default.mode2v3\n!default.pbxuser\n!default.perspectivev3\n"
  },
  {
    "path": "ios/Flutter/AppFrameworkInfo.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n  <key>CFBundleDevelopmentRegion</key>\n  <string>en</string>\n  <key>CFBundleExecutable</key>\n  <string>App</string>\n  <key>CFBundleIdentifier</key>\n  <string>io.flutter.flutter.app</string>\n  <key>CFBundleInfoDictionaryVersion</key>\n  <string>6.0</string>\n  <key>CFBundleName</key>\n  <string>App</string>\n  <key>CFBundlePackageType</key>\n  <string>FMWK</string>\n  <key>CFBundleShortVersionString</key>\n  <string>1.0</string>\n  <key>CFBundleSignature</key>\n  <string>????</string>\n  <key>CFBundleVersion</key>\n  <string>1.0</string>\n  <key>MinimumOSVersion</key>\n  <string>12.0</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/Flutter/Debug.xcconfig",
    "content": "#include \"Generated.xcconfig\"\n"
  },
  {
    "path": "ios/Flutter/Release.xcconfig",
    "content": "#include \"Generated.xcconfig\"\n"
  },
  {
    "path": "ios/Podfile",
    "content": "# Uncomment this line to define a global platform for your project\nplatform :ios, '12.0'\n\n# CocoaPods analytics sends network stats synchronously affecting flutter build latency.\nENV['COCOAPODS_DISABLE_STATS'] = 'true'\n\nproject 'Runner', {\n  'Debug' => :debug,\n  'Profile' => :release,\n  'Release' => :release,\n}\n\ndef flutter_root\n  generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)\n  unless File.exist?(generated_xcode_build_settings_path)\n    raise \"#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first\"\n  end\n\n  File.foreach(generated_xcode_build_settings_path) do |line|\n    matches = line.match(/FLUTTER_ROOT\\=(.*)/)\n    return matches[1].strip if matches\n  end\n  raise \"FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get\"\nend\n\nrequire File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)\n\nflutter_ios_podfile_setup\n\ntarget 'Runner' do\n  use_frameworks!\n  use_modular_headers!\n\n  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))\nend\n\npost_install do |installer|\n  installer.pods_project.targets.each do |target|\n    flutter_additional_ios_build_settings(target)\n  end\n  \n  # Embed xcframeworks into Runner target\n  installer.aggregate_targets.each do |aggregate_target|\n    aggregate_target.user_project.targets.each do |target|\n      next unless target.name == 'Runner'\n      \n      # Add xcframeworks to build settings\n      target.build_configurations.each do |config|\n        # Add framework search paths\n        config.build_settings['FRAMEWORK_SEARCH_PATHS'] ||= ['$(inherited)']\n        config.build_settings['FRAMEWORK_SEARCH_PATHS'] << '$(PROJECT_DIR)/Frameworks'\n        \n        # Add system libraries needed by Go mobile\n        config.build_settings['OTHER_LDFLAGS'] ||= ['$(inherited)']\n        config.build_settings['OTHER_LDFLAGS'] << '-lresolv'\n      end\n      \n      # Add xcframeworks as embedded frameworks\n      frameworks_dir = File.join(File.dirname(__FILE__), 'Frameworks')\n      if Dir.exist?(frameworks_dir)\n        frameworks_build_phase = target.frameworks_build_phase\n        \n        Dir.glob(\"#{frameworks_dir}/*.xcframework\").each do |path|\n          framework_ref = aggregate_target.user_project.new(Xcodeproj::Project::Object::PBXFileReference)\n          framework_ref.name = File.basename(path)\n          framework_ref.path = \"Frameworks/#{File.basename(path)}\"\n          framework_ref.source_tree = '<group>'\n          framework_ref.last_known_file_type = 'wrapper.xcframework'\n          \n          # Add to Frameworks group\n          frameworks_group = aggregate_target.user_project.frameworks_group\n          frameworks_group.children << framework_ref unless frameworks_group.children.include?(framework_ref)\n          \n          # Add to build phase\n          build_file = frameworks_build_phase.add_file_reference(framework_ref)\n          build_file.settings = { 'ATTRIBUTES' => ['CodeSignOnCopy', 'RemoveHeadersOnCopy'] }\n        end\n        \n        aggregate_target.user_project.save\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "ios/README_iOS_CONFIG.md",
    "content": "# iOS特定配置指南\n\n## 1. 应用图标配置\n\n### 自动生成图标（推荐）\n使用提供的Python脚本自动从Logo文件夹生成所有所需尺寸的iOS图标：\n\n```bash\n# 在项目根目录运行\npython ios/scripts/generate_ios_icons.py\n```\n\n该脚本会自动：\n- 从 `../Logo/logo/` 目录读取源图标\n- 生成所有iOS所需尺寸的PNG图标\n- 使用高质量的LANCZOS重采样算法\n- 自动优化输出的PNG文件\n\n### 手动替换图标\n如需手动替换，在 `ios/Runner/Assets.xcassets/AppIcon.appiconset/` 目录中替换以下图标文件：\n\n### iPhone图标尺寸：\n- Icon-App-20x20@2x.png (40x40)\n- Icon-App-20x20@3x.png (60x60)\n- Icon-App-29x29@1x.png (29x29)\n- Icon-App-29x29@2x.png (58x58)\n- Icon-App-29x29@3x.png (87x87)\n- Icon-App-40x40@2x.png (80x80)\n- Icon-App-40x40@3x.png (120x120)\n- Icon-App-60x60@2x.png (120x120)\n- Icon-App-60x60@3x.png (180x180)\n\n### iPad图标尺寸：\n- Icon-App-20x20@1x.png (20x20)\n- Icon-App-40x40@1x.png (40x40)\n- Icon-App-76x76@1x.png (76x76)\n- Icon-App-76x76@2x.png (152x152)\n- Icon-App-83.5x83.5@2x.png (167x167)\n\n### App Store图标：\n- Icon-App-1024x1024@1x.png (1024x1024)\n\n## 2. 启动画面配置\n在 `ios/Runner/Base.lproj/LaunchScreen.storyboard` 中自定义启动画面\n\n## 3. 代码签名配置（发布时需要）\n在Xcode中配置：\n1. 打开 `ios/Runner.xcworkspace`\n2. 选择Runner项目 → Signing & Capabilities\n3. 配置Team和Bundle Identifier\n4. 选择适当的Provisioning Profile\n\n## 4. App Store Connect配置\n1. 创建App Store Connect记录\n2. 配置应用元数据\n3. 上传应用截图\n4. 设置应用描述和关键词\n\n## 5. 推送通知配置（如需要）\n1. 在Apple Developer Portal启用Push Notifications\n2. 在Info.plist中添加推送权限\n3. 在AppDelegate.swift中配置推送处理\n\n## 6. 深度链接配置（如需要）\n在Info.plist中添加URL Schemes：\n```xml\n<key>CFBundleURLTypes</key>\n<array>\n    <dict>\n        <key>CFBundleURLName</key>\n        <string>com.openlist.mobile</string>\n        <key>CFBundleURLSchemes</key>\n        <array>\n            <string>openlist</string>\n        </array>\n    </dict>\n</array>\n```\n\n## 7. 网络安全配置\n### ⚠️ 重要：本地HTTP访问配置\nOpenList需要访问本地HTTP服务（如 `http://127.0.0.1`），这在iOS中需要特殊配置。\n\n已配置的网络安全设置：\n```xml\n<key>NSAppTransportSecurity</key>\n<dict>\n    <key>NSExceptionDomains</key>\n    <dict>\n        <key>localhost</key>\n        <dict>\n            <key>NSExceptionAllowsInsecureHTTPLoads</key>\n            <true/>\n            <key>NSExceptionMinimumTLSVersion</key>\n            <string>TLSv1.0</string>\n        </dict>\n        <key>127.0.0.1</key>\n        <dict>\n            <key>NSExceptionAllowsInsecureHTTPLoads</key>\n            <true/>\n            <key>NSExceptionMinimumTLSVersion</key>\n            <string>TLSv1.0</string>\n        </dict>\n    </dict>\n</dict>\n```\n\n### 为什么需要这个配置？\n1. **iOS App Transport Security (ATS)** 默认阻止所有HTTP连接\n2. **本地服务访问** - OpenList需要连接到 `http://127.0.0.1` 的本地服务\n3. **开发和调试** - 允许连接到本地开发服务器\n4. **安全性平衡** - 仅为本地环回地址提供例外，保持 ATS 默认保护\n\n### 支持的本地地址：\n- `localhost` - 标准本地主机名\n- `127.0.0.1` - IPv4回环地址\n\n### 生产环境建议：\n如果生产版本不需要访问本地HTTP服务，可以进一步移除 `NSExceptionDomains` 的本地例外配置。\n\n## 8. 后台模式配置（如需要）\n在Info.plist中添加后台模式：\n```xml\n<key>UIBackgroundModes</key>\n<array>\n    <string>background-fetch</string>\n    <string>background-processing</string>\n</array>\n```\n\n## 9. 隐私权限说明\n已配置的权限说明：\n- 相册访问权限\n- 相机访问权限\n- 麦克风访问权限\n- 文档文件夹访问权限\n- 下载文件夹访问权限\n\n## 10. 构建配置\n- 最低iOS版本：12.0\n- 支持设备：arm64架构\n- 状态栏样式：默认样式\n\n## 构建命令\n```bash\n# 调试构建\nflutter build ios --debug\n\n# 发布构建（无代码签名）\nflutter build ios --release --no-codesign\n\n# 发布构建（带代码签名）\nflutter build ios --release\n```\n\n## 发布到App Store\n1. 在Xcode中Archive项目\n2. 通过Xcode Organizer上传到App Store Connect\n3. 在App Store Connect中提交审核"
  },
  {
    "path": "ios/Runner/AppDelegate.swift",
    "content": "import Flutter\nimport UIKit\n\n@main\n@objc class AppDelegate: FlutterAppDelegate {\n  var eventAPI: Event?\n  private var backgroundTask: UIBackgroundTaskIdentifier = .invalid\n  private var appStoreUpdateBridge: AppStoreUpdateBridge?\n  \n  override func application(\n    _ application: UIApplication,\n    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?\n  ) -> Bool {\n    GeneratedPluginRegistrant.register(with: self)\n    \n    // Setup Pigeon APIs\n    guard let controller = window?.rootViewController as? FlutterViewController else {\n      print(\"[AppDelegate] Failed to get FlutterViewController\")\n      return super.application(application, didFinishLaunchingWithOptions: launchOptions)\n    }\n    \n    let messenger = controller.binaryMessenger\n    \n    // Register Pigeon API implementations\n    AppConfigSetup.setUp(binaryMessenger: messenger, api: AppConfigBridge())\n    AndroidSetup.setUp(binaryMessenger: messenger, api: OpenListBridge())\n    NativeCommonSetup.setUp(binaryMessenger: messenger, api: CommonBridge(viewController: controller))\n\n    let appStoreChannel = FlutterMethodChannel(\n      name: \"openlist/app_store_update\",\n      binaryMessenger: messenger\n    )\n    let appStoreBridge = AppStoreUpdateBridge(viewController: controller)\n    appStoreUpdateBridge = appStoreBridge\n    appStoreChannel.setMethodCallHandler { call, result in\n      switch call.method {\n      case \"checkAndShowUpdate\":\n        appStoreBridge.checkAndShowUpdate(result: result)\n      default:\n        result(FlutterMethodNotImplemented)\n      }\n    }\n    \n    // Setup Event API for Flutter callbacks\n    eventAPI = Event(binaryMessenger: messenger)\n    \n    // Initialize OpenList core (if XCFramework is available)\n    #if canImport(Openlistlib)\n    let eventHandler = OpenListEventHandler()\n    let logCallback = OpenListLogCallback()\n    eventHandler.eventAPI = eventAPI\n    logCallback.eventAPI = eventAPI\n    \n    do {\n      try OpenListManager.shared.initialize(event: eventHandler, logger: logCallback)\n      print(\"[AppDelegate] OpenList core initialized\")\n    } catch {\n      print(\"[AppDelegate] OpenList core initialization failed: \\(error)\")\n      // Continue without core - will work in Flutter-only mode\n    }\n    #else\n    print(\"[AppDelegate] OpenList core not available - running in Flutter-only mode\")\n    #endif\n    \n    print(\"[AppDelegate] Pigeon APIs registered successfully\")\n    \n    return super.application(application, didFinishLaunchingWithOptions: launchOptions)\n  }\n  \n  // MARK: - Application Lifecycle\n  \n  override func applicationWillTerminate(_ application: UIApplication) {\n    // Cleanup OpenList core\n    OpenListManager.shared.stopServer()\n    \n    // End background task if still active\n    endBackgroundTask()\n    \n    super.applicationWillTerminate(application)\n  }\n  \n  override func applicationDidEnterBackground(_ application: UIApplication) {\n    // Begin background task to prevent WebView process suspension\n    backgroundTask = application.beginBackgroundTask { [weak self] in\n      // Background task is about to expire, clean up\n      self?.endBackgroundTask()\n    }\n  }\n  \n  override func applicationWillEnterForeground(_ application: UIApplication) {\n    // End background task when returning to foreground\n    endBackgroundTask()\n  }\n  \n  // MARK: - Background Task Management\n  \n  private func endBackgroundTask() {\n    if backgroundTask != .invalid {\n      UIApplication.shared.endBackgroundTask(backgroundTask)\n      backgroundTask = .invalid\n    }\n  }\n}\n"
  },
  {
    "path": "ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-20x20@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-20x20@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-29x29@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-29x29@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-29x29@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-40x40@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-40x40@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"60x60\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-60x60@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"60x60\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-60x60@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-20x20@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-20x20@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-29x29@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-29x29@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-40x40@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-40x40@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"76x76\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-76x76@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"76x76\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-76x76@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"83.5x83.5\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-83.5x83.5@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"1024x1024\",\n      \"idiom\" : \"ios-marketing\",\n      \"filename\" : \"Icon-App-1024x1024@1x.png\",\n      \"scale\" : \"1x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}\n"
  },
  {
    "path": "ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"LaunchImage.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"LaunchImage@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"LaunchImage@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}\n"
  },
  {
    "path": "ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md",
    "content": "# Launch Screen Assets\n\nYou can customize the launch screen with your own desired assets by replacing the image files in this directory.\n\nYou can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images."
  },
  {
    "path": "ios/Runner/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"12121\" systemVersion=\"16G29\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"12089\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <layoutGuides>\n                        <viewControllerLayoutGuide type=\"top\" id=\"Ydg-fD-yQy\"/>\n                        <viewControllerLayoutGuide type=\"bottom\" id=\"xbc-2k-c8Z\"/>\n                    </layoutGuides>\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <imageView opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" image=\"LaunchImage\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"YRO-k0-Ey4\">\n                            </imageView>\n                        </subviews>\n                        <color key=\"backgroundColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <constraints>\n                            <constraint firstItem=\"YRO-k0-Ey4\" firstAttribute=\"centerX\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"centerX\" id=\"1a2-6s-vTC\"/>\n                            <constraint firstItem=\"YRO-k0-Ey4\" firstAttribute=\"centerY\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"centerY\" id=\"4X2-HB-R7a\"/>\n                        </constraints>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n    <resources>\n        <image name=\"LaunchImage\" width=\"168\" height=\"185\"/>\n    </resources>\n</document>\n"
  },
  {
    "path": "ios/Runner/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"10117\" systemVersion=\"15F34\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" initialViewController=\"BYZ-38-t0r\">\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"10085\"/>\n    </dependencies>\n    <scenes>\n        <!--Flutter View Controller-->\n        <scene sceneID=\"tne-QT-ifu\">\n            <objects>\n                <viewController id=\"BYZ-38-t0r\" customClass=\"FlutterViewController\" sceneMemberID=\"viewController\">\n                    <layoutGuides>\n                        <viewControllerLayoutGuide type=\"top\" id=\"y3c-jy-aDJ\"/>\n                        <viewControllerLayoutGuide type=\"bottom\" id=\"wfy-db-euE\"/>\n                    </layoutGuides>\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"8bC-Xf-vdC\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"600\" height=\"600\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"calibratedWhite\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "ios/Runner/Bridges/AppConfigBridge.swift",
    "content": "import Flutter\nimport Foundation\n\n/// Bridge implementation for App Configuration APIs\nclass AppConfigBridge: NSObject, AppConfig {\n    private let defaults = UserDefaults.standard\n    \n    // Keys for UserDefaults\n    private enum Keys {\n        static let wakeLock = \"app_config_wake_lock\"\n        static let startAtBoot = \"app_config_start_at_boot\"\n        static let autoCheckUpdate = \"app_config_auto_check_update\"\n        static let autoOpenWebPage = \"app_config_auto_open_web_page\"\n        static let dataDir = \"app_config_data_dir\"\n        static let silentJumpApp = \"app_config_silent_jump_app\"\n    }\n    \n    func isWakeLockEnabled() throws -> Bool {\n        return defaults.bool(forKey: Keys.wakeLock)\n    }\n    \n    func setWakeLockEnabled(enabled: Bool) throws {\n        defaults.set(enabled, forKey: Keys.wakeLock)\n        print(\"[AppConfigBridge] Wake lock enabled: \\(enabled)\")\n    }\n    \n    func isStartAtBootEnabled() throws -> Bool {\n        return defaults.bool(forKey: Keys.startAtBoot)\n    }\n    \n    func setStartAtBootEnabled(enabled: Bool) throws {\n        defaults.set(enabled, forKey: Keys.startAtBoot)\n        print(\"[AppConfigBridge] Start at boot enabled: \\(enabled)\")\n    }\n    \n    func isAutoCheckUpdateEnabled() throws -> Bool {\n        return defaults.bool(forKey: Keys.autoCheckUpdate)\n    }\n    \n    func setAutoCheckUpdateEnabled(enabled: Bool) throws {\n        defaults.set(enabled, forKey: Keys.autoCheckUpdate)\n        print(\"[AppConfigBridge] Auto check update enabled: \\(enabled)\")\n    }\n    \n    func isAutoOpenWebPageEnabled() throws -> Bool {\n        return defaults.bool(forKey: Keys.autoOpenWebPage)\n    }\n    \n    func setAutoOpenWebPageEnabled(enabled: Bool) throws {\n        defaults.set(enabled, forKey: Keys.autoOpenWebPage)\n        print(\"[AppConfigBridge] Auto open web page enabled: \\(enabled)\")\n    }\n    \n    func getDataDir() throws -> String {\n        if let customDir = defaults.string(forKey: Keys.dataDir), !customDir.isEmpty {\n            print(\"[AppConfigBridge] Using custom data directory: \\(customDir)\")\n            return customDir\n        }\n        \n        // Default to app's document directory with openlist_data subdirectory\n        // This follows iOS app data storage guidelines\n        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)\n        let documentsDirectory = paths[0]\n        let openlistDataDir = documentsDirectory.appendingPathComponent(\"openlist_data\")\n        \n        // Create directory if not exists\n        if !FileManager.default.fileExists(atPath: openlistDataDir.path) {\n            do {\n                try FileManager.default.createDirectory(at: openlistDataDir, withIntermediateDirectories: true, attributes: nil)\n                print(\"[AppConfigBridge] Created data directory: \\(openlistDataDir.path)\")\n            } catch {\n                print(\"[AppConfigBridge] Failed to create data directory: \\(error)\")\n                throw error\n            }\n        }\n        \n        print(\"[AppConfigBridge] Data directory: \\(openlistDataDir.path)\")\n        return openlistDataDir.path\n    }\n    \n    func setDataDir(dir: String) throws {\n        // On iOS, we should not allow users to change data directory arbitrarily\n        // But we keep the method for compatibility\n        if dir.isEmpty {\n            defaults.removeObject(forKey: Keys.dataDir)\n            print(\"[AppConfigBridge] Data directory reset to default\")\n        } else {\n            // iOS: Only allow setting within app's container\n            let appDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].path\n            if dir.hasPrefix(appDir) {\n                defaults.set(dir, forKey: Keys.dataDir)\n                print(\"[AppConfigBridge] Data directory set to: \\(dir)\")\n            } else {\n                print(\"[AppConfigBridge] Rejected invalid data directory (outside app container): \\(dir)\")\n                throw NSError(domain: \"AppConfigBridge\", code: -2, \n                            userInfo: [NSLocalizedDescriptionKey: \"Data directory must be within app container\"])\n            }\n        }\n    }\n    \n    func isSilentJumpAppEnabled() throws -> Bool {\n        return defaults.bool(forKey: Keys.silentJumpApp)\n    }\n    \n    func setSilentJumpAppEnabled(enabled: Bool) throws {\n        defaults.set(enabled, forKey: Keys.silentJumpApp)\n        print(\"[AppConfigBridge] Silent jump app enabled: \\(enabled)\")\n    }\n}\n"
  },
  {
    "path": "ios/Runner/Bridges/AppStoreUpdateBridge.swift",
    "content": "import Foundation\nimport StoreKit\nimport UIKit\nimport Flutter\n\nfinal class AppStoreUpdateBridge: NSObject, SKStoreProductViewControllerDelegate {\n    private weak var viewController: UIViewController?\n\n    init(viewController: UIViewController?) {\n        self.viewController = viewController\n        super.init()\n    }\n\n    func checkAndShowUpdate(result: @escaping FlutterResult) {\n        guard let bundleId = Bundle.main.bundleIdentifier else {\n            result(false)\n            return\n        }\n\n        let currentVersion = Bundle.main.infoDictionary?[\"CFBundleShortVersionString\"] as? String ?? \"0\"\n        let urlString = \"https://itunes.apple.com/lookup?bundleId=\\(bundleId)\"\n        guard let url = URL(string: urlString) else {\n            result(false)\n            return\n        }\n\n        URLSession.shared.dataTask(with: url) { [weak self] data, _, error in\n            if let error = error {\n                print(\"[AppStoreUpdateBridge] Apple API error: \\(error)\")\n                result(false)\n                return\n            }\n\n            guard\n                let data = data,\n                let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],\n                let results = json[\"results\"] as? [[String: Any]],\n                let first = results.first,\n                let latestVersion = first[\"version\"] as? String,\n                let trackId = first[\"trackId\"] as? Int\n            else {\n                result(false)\n                return\n            }\n\n            let hasUpdate = self?.isVersionNewer(latest: latestVersion, current: currentVersion) ?? false\n            if !hasUpdate {\n                result(false)\n                return\n            }\n\n            self?.presentStoreProduct(trackId: trackId) { presented in\n                result(presented)\n            }\n        }.resume()\n    }\n\n    private func presentStoreProduct(trackId: Int, completion: @escaping (Bool) -> Void) {\n        DispatchQueue.main.async { [weak self] in\n            guard let presenter = self?.viewController ?? UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.rootViewController else {\n                completion(false)\n                return\n            }\n\n            let storeVC = SKStoreProductViewController()\n            storeVC.delegate = self\n            let parameters = [SKStoreProductParameterITunesItemIdentifier: NSNumber(value: trackId)]\n            storeVC.loadProduct(withParameters: parameters) { loaded, error in\n                if let error = error {\n                    print(\"[AppStoreUpdateBridge] Failed to load product: \\(error)\")\n                    completion(false)\n                    return\n                }\n                if loaded {\n                    presenter.present(storeVC, animated: true) {\n                        completion(true)\n                    }\n                } else {\n                    completion(false)\n                }\n            }\n        }\n    }\n\n    private func isVersionNewer(latest: String, current: String) -> Bool {\n        let latestParts = latest.split(separator: \".\").map { Int($0) ?? 0 }\n        let currentParts = current.split(separator: \".\").map { Int($0) ?? 0 }\n        let count = max(latestParts.count, currentParts.count)\n\n        for index in 0..<count {\n            let latestValue = index < latestParts.count ? latestParts[index] : 0\n            let currentValue = index < currentParts.count ? currentParts[index] : 0\n            if latestValue != currentValue {\n                return latestValue > currentValue\n            }\n        }\n        return false\n    }\n\n    func productViewControllerDidFinish(_ viewController: SKStoreProductViewController) {\n        viewController.dismiss(animated: true)\n    }\n}\n"
  },
  {
    "path": "ios/Runner/Bridges/CommonBridge.swift",
    "content": "import Flutter\nimport Foundation\nimport UIKit\n\n/// Bridge implementation for common native APIs\nclass CommonBridge: NSObject, NativeCommon {\n    private let viewController: UIViewController?\n    \n    init(viewController: UIViewController? = nil) {\n        self.viewController = viewController\n        super.init()\n    }\n    \n    func startActivityFromUri(intentUri: String) throws -> Bool {\n        print(\"[CommonBridge] startActivityFromUri: \\(intentUri)\")\n        \n        guard let url = URL(string: intentUri) else {\n            print(\"[CommonBridge] Invalid URL: \\(intentUri)\")\n            return false\n        }\n        \n        // Check if the URL can be opened\n        guard UIApplication.shared.canOpenURL(url) else {\n            print(\"[CommonBridge] Cannot open URL: \\(intentUri)\")\n            return false\n        }\n        \n        // Open the URL\n        UIApplication.shared.open(url, options: [:]) { success in\n            print(\"[CommonBridge] Open URL result: \\(success)\")\n        }\n        \n        return true\n    }\n    \n    func getDeviceSdkInt() throws -> Int64 {\n        // iOS doesn't have SDK int like Android, return iOS major version\n        let systemVersion = UIDevice.current.systemVersion\n        let majorVersion = systemVersion.components(separatedBy: \".\").first ?? \"0\"\n        let version = Int64(majorVersion) ?? 0\n        print(\"[CommonBridge] Device iOS version: \\(version)\")\n        return version\n    }\n    \n    func getDeviceCPUABI() throws -> String {\n        // Get CPU architecture\n        var systemInfo = utsname()\n        uname(&systemInfo)\n        let machineMirror = Mirror(reflecting: systemInfo.machine)\n        let identifier = machineMirror.children.reduce(\"\") { identifier, element in\n            guard let value = element.value as? Int8, value != 0 else { return identifier }\n            return identifier + String(UnicodeScalar(UInt8(value)))\n        }\n        \n        print(\"[CommonBridge] Device CPU ABI: \\(identifier)\")\n        return identifier\n    }\n    \n    func getVersionName() throws -> String {\n        let version = Bundle.main.infoDictionary?[\"CFBundleShortVersionString\"] as? String ?? \"1.0.0\"\n        print(\"[CommonBridge] Version name: \\(version)\")\n        return version\n    }\n    \n    func getVersionCode() throws -> Int64 {\n        let build = Bundle.main.infoDictionary?[\"CFBundleVersion\"] as? String ?? \"1\"\n        let code = Int64(build) ?? 1\n        print(\"[CommonBridge] Version code: \\(code)\")\n        return code\n    }\n    \n    func toast(msg: String) throws {\n        print(\"[CommonBridge] Toast: \\(msg)\")\n        showToast(message: msg, duration: 2.0)\n    }\n    \n    func longToast(msg: String) throws {\n        print(\"[CommonBridge] Long toast: \\(msg)\")\n        showToast(message: msg, duration: 4.0)\n    }\n    \n    // MARK: - Toast Helper\n    \n    private func showToast(message: String, duration: TimeInterval) {\n        DispatchQueue.main.async { [weak self] in\n            guard let window = UIApplication.shared.windows.first(where: { $0.isKeyWindow }) else {\n                print(\"[CommonBridge] No key window found for toast\")\n                return\n            }\n            \n            let toastLabel = UILabel()\n            toastLabel.backgroundColor = UIColor.black.withAlphaComponent(0.7)\n            toastLabel.textColor = UIColor.white\n            toastLabel.textAlignment = .center\n            toastLabel.font = UIFont.systemFont(ofSize: 14)\n            toastLabel.text = message\n            toastLabel.alpha = 0.0\n            toastLabel.layer.cornerRadius = 10\n            toastLabel.clipsToBounds = true\n            toastLabel.numberOfLines = 0\n            \n            let maxSize = CGSize(width: window.frame.width - 80, height: window.frame.height)\n            let expectedSize = toastLabel.sizeThatFits(maxSize)\n            toastLabel.frame = CGRect(\n                x: (window.frame.width - expectedSize.width - 20) / 2,\n                y: window.frame.height - 150,\n                width: expectedSize.width + 20,\n                height: expectedSize.height + 20\n            )\n            \n            window.addSubview(toastLabel)\n            \n            UIView.animate(withDuration: 0.3, animations: {\n                toastLabel.alpha = 1.0\n            }) { _ in\n                UIView.animate(withDuration: 0.3, delay: duration, options: [], animations: {\n                    toastLabel.alpha = 0.0\n                }) { _ in\n                    toastLabel.removeFromSuperview()\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "ios/Runner/Bridges/OpenListBridge.swift",
    "content": "import Flutter\nimport Foundation\n\n/// Bridge implementation for Android-specific APIs (iOS equivalent)\nclass OpenListBridge: NSObject, Android {\n    private let registrar: FlutterPluginRegistrar?\n    \n    init(registrar: FlutterPluginRegistrar? = nil) {\n        self.registrar = registrar\n        super.init()\n    }\n    \n    func addShortcut() throws {\n        print(\"[OpenListBridge] addShortcut called - iOS does not support shortcuts like Android\")\n        // iOS doesn't have the same shortcut system as Android\n        // This is a no-op on iOS\n    }\n    \n    func startService() throws {\n        print(\"[OpenListBridge] startService called\")\n        OpenListManager.shared.startServer()\n    }\n    \n    func setAdminPwd(pwd: String) throws {\n        print(\"[OpenListBridge] setAdminPwd called\")\n        try OpenListManager.shared.setAdminPassword(pwd)\n    }\n    \n    func getOpenListHttpPort() throws -> Int64 {\n        let port = OpenListManager.shared.getHttpPort()\n        print(\"[OpenListBridge] getOpenListHttpPort: \\(port)\")\n        return Int64(port)\n    }\n    \n    func isRunning() throws -> Bool {\n        let running = OpenListManager.shared.isRunning()\n        print(\"[OpenListBridge] isRunning: \\(running)\")\n        return running\n    }\n    \n    func getOpenListVersion() throws -> String {\n        // Get version from build configuration or Info.plist\n        let version = Bundle.main.infoDictionary?[\"OpenListVersion\"] as? String ?? \"dev\"\n        print(\"[OpenListBridge] getOpenListVersion: \\(version)\")\n        return version\n    }\n}\n"
  },
  {
    "path": "ios/Runner/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleDisplayName</key>\n\t<string>OpenList</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>openlist_mobile</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>$(FLUTTER_BUILD_NAME)</string>\n\t<key>CFBundleSignature</key>\n\t<string>????</string>\n\t<key>CFBundleVersion</key>\n\t<string>$(FLUTTER_BUILD_NUMBER)</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIMainStoryboardFile</key>\n\t<string>Main</string>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>CADisableMinimumFrameDurationOnPhone</key>\n\t<true/>\n\t<key>UIApplicationSupportsIndirectInputEvents</key>\n\t<true/>\n\t<key>NSPhotoLibraryUsageDescription</key>\n\t<string>This app needs access to photo library to select files</string>\n\t<key>NSCameraUsageDescription</key>\n\t<string>This app needs access to camera to take photos</string>\n\t<key>NSMicrophoneUsageDescription</key>\n\t<string>This app needs access to microphone for recording</string>\n\t<key>NSDocumentsFolderUsageDescription</key>\n\t<string>This app needs access to documents folder to manage files</string>\n\t<key>NSDownloadsFolderUsageDescription</key>\n\t<string>This app needs access to downloads folder to manage files</string>\n\t<key>MinimumOSVersion</key>\n\t<string>12.0</string>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>arm64</string>\n\t</array>\n\t<key>UIStatusBarStyle</key>\n\t<string>UIStatusBarStyleDefault</string>\n\t<key>UIViewControllerBasedStatusBarAppearance</key>\n\t<false/>\n\t<key>NSAppTransportSecurity</key>\n\t<dict>\n\t\t<key>NSExceptionDomains</key>\n\t\t<dict>\n\t\t\t<key>localhost</key>\n\t\t\t<dict>\n\t\t\t\t<key>NSExceptionAllowsInsecureHTTPLoads</key>\n\t\t\t\t<true/>\n\t\t\t\t<key>NSExceptionMinimumTLSVersion</key>\n\t\t\t\t<string>TLSv1.0</string>\n\t\t\t</dict>\n\t\t\t<key>127.0.0.1</key>\n\t\t\t<dict>\n\t\t\t\t<key>NSExceptionAllowsInsecureHTTPLoads</key>\n\t\t\t\t<true/>\n\t\t\t\t<key>NSExceptionMinimumTLSVersion</key>\n\t\t\t\t<string>TLSv1.0</string>\n\t\t\t</dict>\n\t\t</dict>\n\t</dict>\n\t<key>UIBackgroundModes</key>\n\t<array>\n\t\t<string>fetch</string>\n\t</array>\n\t<key>OpenListVersion</key>\n\t<string>dev-ios-beta</string>\n\t<key>WKAppBoundDomains</key>\n\t<array>\n\t\t<string>localhost</string>\n\t\t<string>127.0.0.1</string>\n\t</array>\n\t<key>UIFileSharingEnabled</key>\n\t<true/>\n\t<key>LSSupportsOpeningDocumentsInPlace</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/Runner/OpenListManager.swift",
    "content": "import Foundation\nimport Openlistlib\n\n/// Manages OpenList core server lifecycle\nclass OpenListManager: NSObject {\n    static let shared = OpenListManager()\n    \n    private var isInitialized = false\n    private var isServerRunning = false\n    private var dataDir: String?\n    \n    // Keep strong references to prevent deallocation\n    var eventHandler: OpenListEventHandler?\n    var logCallback: OpenListLogCallback?\n    \n    private override init() {\n        super.init()\n    }\n\n    private func ensureInitializedForConfig() throws {\n        if isInitialized {\n            return\n        }\n\n        let eventHandler = OpenListEventHandler()\n        let logCallback = OpenListLogCallback()\n\n        let appDelegate = UIApplication.shared.delegate as? AppDelegate\n        eventHandler.eventAPI = appDelegate?.eventAPI\n        logCallback.eventAPI = appDelegate?.eventAPI\n\n        self.eventHandler = eventHandler\n        self.logCallback = logCallback\n\n        try initialize(event: eventHandler, logger: logCallback)\n    }\n    \n    // MARK: - Initialization\n    \n    func initialize(event: OpenListEventHandler, logger: OpenListLogCallback) throws {\n        guard !isInitialized else {\n            print(\"[OpenListManager] Already initialized\")\n            return\n        }\n        \n        // Get data directory from AppConfigBridge\n        let appConfig = AppConfigBridge()\n        let dataDirPath: String\n        do {\n            dataDirPath = try appConfig.getDataDir()\n            self.dataDir = dataDirPath\n            print(\"[OpenListManager] Data directory: \\(dataDirPath)\")\n        } catch {\n            print(\"[OpenListManager] Failed to get data directory: \\(error)\")\n            throw error\n        }\n        \n        // Set data directory for OpenList core (no error return)\n        OpenlistlibSetConfigData(dataDirPath)\n        \n        // Enable stdout logging (no error return)\n        OpenlistlibSetConfigLogStd(true)\n        \n        var error: NSError?\n        OpenlistlibInit(event, logger, &error)\n        if let err = error {\n            print(\"[OpenListManager] Initialization failed: \\(err)\")\n            throw err\n        }\n        isInitialized = true\n        print(\"[OpenListManager] Initialized successfully with data directory: \\(dataDirPath)\")\n    }\n    \n    // MARK: - Server Control\n    \n    func startServer() {\n        print(\"[OpenListManager] Start server request received\")\n        \n        // Check if initialized, if not, try to initialize first\n        if !isInitialized {\n            print(\"[OpenListManager] Not initialized, attempting initialization...\")\n            let eventHandler = OpenListEventHandler()\n            let logCallback = OpenListLogCallback()\n            \n            // Set event API reference before initialization\n            let appDelegate = UIApplication.shared.delegate as? AppDelegate\n            eventHandler.eventAPI = appDelegate?.eventAPI\n            logCallback.eventAPI = appDelegate?.eventAPI\n            \n            // Store references globally for persistence\n            OpenListManager.shared.eventHandler = eventHandler\n            OpenListManager.shared.logCallback = logCallback\n            \n            do {\n                try initialize(event: eventHandler, logger: logCallback)\n                print(\"[OpenListManager] Initialization completed, proceeding to start server\")\n            } catch {\n                print(\"[OpenListManager] Initialization failed: \\(error), cannot start server\")\n                return\n            }\n        }\n        \n        guard !isServerRunning else {\n            print(\"[OpenListManager] Server already running\")\n            return\n        }\n        \n        print(\"[OpenListManager] Starting OpenList server with data directory: \\(dataDir ?? \"unknown\")...\")\n        DispatchQueue.global(qos: .userInitiated).async { [weak self] in\n            OpenlistlibStart()\n            \n            // Small delay to ensure server is ready\n            Thread.sleep(forTimeInterval: 0.5)\n            \n            DispatchQueue.main.async {\n                self?.isServerRunning = true\n                print(\"[OpenListManager] Server started successfully\")\n                \n                // Notify Flutter side\n                if let eventAPI = (UIApplication.shared.delegate as? AppDelegate)?.eventAPI {\n                    eventAPI.onServiceStatusChanged(isRunning: true) { result in\n                        switch result {\n                        case .failure(let error):\n                            print(\"[OpenListManager] Failed to notify Flutter of status change: \\(error)\")\n                        case .success:\n                            print(\"[OpenListManager] Status change notification sent to Flutter\")\n                        }\n                    }\n                }\n            }\n        }\n    }\n    \n    func stopServer(timeout: Int64 = 5000) {\n        guard isServerRunning else {\n            print(\"[OpenListManager] Server not running\")\n            return\n        }\n        \n        print(\"[OpenListManager] Stopping OpenList server...\")\n        var error: NSError?\n        OpenlistlibShutdown(timeout, &error)\n        if let err = error {\n            print(\"[OpenListManager] Failed to stop server: \\(err)\")\n            return\n        }\n        isServerRunning = false\n        print(\"[OpenListManager] Server stopped\")\n    }\n    \n    func isRunning() -> Bool {\n        return isServerRunning && OpenlistlibIsRunning(\"http\")\n    }\n    \n    func getHttpPort() -> Int {\n        // Default port for OpenList\n        return 5244\n    }\n\n    func setAdminPassword(_ pwd: String) throws {\n        try ensureInitializedForConfig()\n\n        if let dataDir = dataDir {\n            OpenlistlibSetConfigData(dataDir)\n        }\n\n        OpenlistlibSetAdminPassword(pwd)\n        print(\"[OpenListManager] Admin password updated\")\n    }\n    \n    func forceDBSync() {\n        var error: NSError?\n        OpenlistlibForceDBSync(&error)\n        if let err = error {\n            print(\"[OpenListManager] Database sync failed: \\(err)\")\n            return\n        }\n        print(\"[OpenListManager] Database sync completed\")\n    }\n}\n\n// MARK: - Event Handler\n\nclass OpenListEventHandler: NSObject, OpenlistlibEventProtocol {\n    weak var eventAPI: Event?\n    \n    func onStartError(_ t: String?, err: String?) {\n        print(\"[OpenListEvent] Start error - Type: \\(t ?? \"unknown\"), Error: \\(err ?? \"unknown\")\")\n        // Notify Flutter side via Event API if needed\n    }\n    \n    func onShutdown(_ t: String?) {\n        print(\"[OpenListEvent] Shutdown - Type: \\(t ?? \"unknown\")\")\n        // Notify Flutter side via Event API if needed\n    }\n    \n    func onProcessExit(_ code: Int) {\n        print(\"[OpenListEvent] Process exit - Code: \\(code)\")\n        // Handle process exit if needed\n    }\n}\n\n// MARK: - Log Callback\n\nclass OpenListLogCallback: NSObject, OpenlistlibLogCallbackProtocol {\n    weak var eventAPI: Event?\n    \n    func onLog(_ level: Int16, time: Int64, message: String?) {\n        let logMessage = message ?? \"\"\n        print(\"[OpenListLog] Level: \\(level), Message: \\(logMessage)\")\n        \n        // Forward logs to Flutter side\n        if let api = eventAPI {\n            api.onServerLog(level: Int64(level), time: \"\\(time)\", log: logMessage) { result in\n                switch result {\n                case .failure(let error):\n                    print(\"[OpenListLog] Failed to send log to Flutter: \\(error)\")\n                case .success:\n                    break // Success, no action needed\n                }\n            }\n        } else {\n            print(\"[OpenListLog] Warning: eventAPI is nil, cannot forward log to Flutter\")\n        }\n    }\n}\n"
  },
  {
    "path": "ios/Runner/PigeonApi.swift",
    "content": "// Autogenerated from Pigeon (v26.0.2), do not edit directly.\n// See also: https://pub.dev/packages/pigeon\n\nimport Foundation\n\n#if os(iOS)\n  import Flutter\n#elseif os(macOS)\n  import FlutterMacOS\n#else\n  #error(\"Unsupported platform.\")\n#endif\n\n/// Error class for passing custom error details to Dart side.\nfinal class PigeonError: Error {\n  let code: String\n  let message: String?\n  let details: Sendable?\n\n  init(code: String, message: String?, details: Sendable?) {\n    self.code = code\n    self.message = message\n    self.details = details\n  }\n\n  var localizedDescription: String {\n    return\n      \"PigeonError(code: \\(code), message: \\(message ?? \"<nil>\"), details: \\(details ?? \"<nil>\")\"\n  }\n}\n\nprivate func wrapResult(_ result: Any?) -> [Any?] {\n  return [result]\n}\n\nprivate func wrapError(_ error: Any) -> [Any?] {\n  if let pigeonError = error as? PigeonError {\n    return [\n      pigeonError.code,\n      pigeonError.message,\n      pigeonError.details,\n    ]\n  }\n  if let flutterError = error as? FlutterError {\n    return [\n      flutterError.code,\n      flutterError.message,\n      flutterError.details,\n    ]\n  }\n  return [\n    \"\\(error)\",\n    \"\\(type(of: error))\",\n    \"Stacktrace: \\(Thread.callStackSymbols)\",\n  ]\n}\n\nprivate func createConnectionError(withChannelName channelName: String) -> PigeonError {\n  return PigeonError(code: \"channel-error\", message: \"Unable to establish connection on channel: '\\(channelName)'.\", details: \"\")\n}\n\nprivate func isNullish(_ value: Any?) -> Bool {\n  return value is NSNull || value == nil\n}\n\nprivate func nilOrValue<T>(_ value: Any?) -> T? {\n  if value is NSNull { return nil }\n  return value as! T?\n}\n\n\nprivate class PigeonApiPigeonCodecReader: FlutterStandardReader {\n}\n\nprivate class PigeonApiPigeonCodecWriter: FlutterStandardWriter {\n}\n\nprivate class PigeonApiPigeonCodecReaderWriter: FlutterStandardReaderWriter {\n  override func reader(with data: Data) -> FlutterStandardReader {\n    return PigeonApiPigeonCodecReader(data: data)\n  }\n\n  override func writer(with data: NSMutableData) -> FlutterStandardWriter {\n    return PigeonApiPigeonCodecWriter(data: data)\n  }\n}\n\nclass PigeonApiPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable {\n  static let shared = PigeonApiPigeonCodec(readerWriter: PigeonApiPigeonCodecReaderWriter())\n}\n\n/// Generated protocol from Pigeon that represents a handler of messages from Flutter.\nprotocol AppConfig {\n  func isWakeLockEnabled() throws -> Bool\n  func setWakeLockEnabled(enabled: Bool) throws\n  func isStartAtBootEnabled() throws -> Bool\n  func setStartAtBootEnabled(enabled: Bool) throws\n  func isAutoCheckUpdateEnabled() throws -> Bool\n  func setAutoCheckUpdateEnabled(enabled: Bool) throws\n  func isAutoOpenWebPageEnabled() throws -> Bool\n  func setAutoOpenWebPageEnabled(enabled: Bool) throws\n  func getDataDir() throws -> String\n  func setDataDir(dir: String) throws\n  func isSilentJumpAppEnabled() throws -> Bool\n  func setSilentJumpAppEnabled(enabled: Bool) throws\n}\n\n/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.\nclass AppConfigSetup {\n  static var codec: FlutterStandardMessageCodec { PigeonApiPigeonCodec.shared }\n  /// Sets up an instance of `AppConfig` to handle messages through the `binaryMessenger`.\n  static func setUp(binaryMessenger: FlutterBinaryMessenger, api: AppConfig?, messageChannelSuffix: String = \"\") {\n    let channelSuffix = messageChannelSuffix.count > 0 ? \".\\(messageChannelSuffix)\" : \"\"\n    let isWakeLockEnabledChannel = FlutterBasicMessageChannel(name: \"dev.flutter.pigeon.openlist_mobile.AppConfig.isWakeLockEnabled\\(channelSuffix)\", binaryMessenger: binaryMessenger, codec: codec)\n    if let api = api {\n      isWakeLockEnabledChannel.setMessageHandler { _, reply in\n        do {\n          let result = try api.isWakeLockEnabled()\n          reply(wrapResult(result))\n        } catch {\n          reply(wrapError(error))\n        }\n      }\n    } else {\n      isWakeLockEnabledChannel.setMessageHandler(nil)\n    }\n    let setWakeLockEnabledChannel = FlutterBasicMessageChannel(name: \"dev.flutter.pigeon.openlist_mobile.AppConfig.setWakeLockEnabled\\(channelSuffix)\", binaryMessenger: binaryMessenger, codec: codec)\n    if let api = api {\n      setWakeLockEnabledChannel.setMessageHandler { message, reply in\n        let args = message as! [Any?]\n        let enabledArg = args[0] as! Bool\n        do {\n          try api.setWakeLockEnabled(enabled: enabledArg)\n          reply(wrapResult(nil))\n        } catch {\n          reply(wrapError(error))\n        }\n      }\n    } else {\n      setWakeLockEnabledChannel.setMessageHandler(nil)\n    }\n    let isStartAtBootEnabledChannel = FlutterBasicMessageChannel(name: \"dev.flutter.pigeon.openlist_mobile.AppConfig.isStartAtBootEnabled\\(channelSuffix)\", binaryMessenger: binaryMessenger, codec: codec)\n    if let api = api {\n      isStartAtBootEnabledChannel.setMessageHandler { _, reply in\n        do {\n          let result = try api.isStartAtBootEnabled()\n          reply(wrapResult(result))\n        } catch {\n          reply(wrapError(error))\n        }\n      }\n    } else {\n      isStartAtBootEnabledChannel.setMessageHandler(nil)\n    }\n    let setStartAtBootEnabledChannel = FlutterBasicMessageChannel(name: \"dev.flutter.pigeon.openlist_mobile.AppConfig.setStartAtBootEnabled\\(channelSuffix)\", binaryMessenger: binaryMessenger, codec: codec)\n    if let api = api {\n      setStartAtBootEnabledChannel.setMessageHandler { message, reply in\n        let args = message as! [Any?]\n        let enabledArg = args[0] as! Bool\n        do {\n          try api.setStartAtBootEnabled(enabled: enabledArg)\n          reply(wrapResult(nil))\n        } catch {\n          reply(wrapError(error))\n        }\n      }\n    } else {\n      setStartAtBootEnabledChannel.setMessageHandler(nil)\n    }\n    let isAutoCheckUpdateEnabledChannel = FlutterBasicMessageChannel(name: \"dev.flutter.pigeon.openlist_mobile.AppConfig.isAutoCheckUpdateEnabled\\(channelSuffix)\", binaryMessenger: binaryMessenger, codec: codec)\n    if let api = api {\n      isAutoCheckUpdateEnabledChannel.setMessageHandler { _, reply in\n        do {\n          let result = try api.isAutoCheckUpdateEnabled()\n          reply(wrapResult(result))\n        } catch {\n          reply(wrapError(error))\n        }\n      }\n    } else {\n      isAutoCheckUpdateEnabledChannel.setMessageHandler(nil)\n    }\n    let setAutoCheckUpdateEnabledChannel = FlutterBasicMessageChannel(name: \"dev.flutter.pigeon.openlist_mobile.AppConfig.setAutoCheckUpdateEnabled\\(channelSuffix)\", binaryMessenger: binaryMessenger, codec: codec)\n    if let api = api {\n      setAutoCheckUpdateEnabledChannel.setMessageHandler { message, reply in\n        let args = message as! [Any?]\n        let enabledArg = args[0] as! Bool\n        do {\n          try api.setAutoCheckUpdateEnabled(enabled: enabledArg)\n          reply(wrapResult(nil))\n        } catch {\n          reply(wrapError(error))\n        }\n      }\n    } else {\n      setAutoCheckUpdateEnabledChannel.setMessageHandler(nil)\n    }\n    let isAutoOpenWebPageEnabledChannel = FlutterBasicMessageChannel(name: \"dev.flutter.pigeon.openlist_mobile.AppConfig.isAutoOpenWebPageEnabled\\(channelSuffix)\", binaryMessenger: binaryMessenger, codec: codec)\n    if let api = api {\n      isAutoOpenWebPageEnabledChannel.setMessageHandler { _, reply in\n        do {\n          let result = try api.isAutoOpenWebPageEnabled()\n          reply(wrapResult(result))\n        } catch {\n          reply(wrapError(error))\n        }\n      }\n    } else {\n      isAutoOpenWebPageEnabledChannel.setMessageHandler(nil)\n    }\n    let setAutoOpenWebPageEnabledChannel = FlutterBasicMessageChannel(name: \"dev.flutter.pigeon.openlist_mobile.AppConfig.setAutoOpenWebPageEnabled\\(channelSuffix)\", binaryMessenger: binaryMessenger, codec: codec)\n    if let api = api {\n      setAutoOpenWebPageEnabledChannel.setMessageHandler { message, reply in\n        let args = message as! [Any?]\n        let enabledArg = args[0] as! Bool\n        do {\n          try api.setAutoOpenWebPageEnabled(enabled: enabledArg)\n          reply(wrapResult(nil))\n        } catch {\n          reply(wrapError(error))\n        }\n      }\n    } else {\n      setAutoOpenWebPageEnabledChannel.setMessageHandler(nil)\n    }\n    let getDataDirChannel = FlutterBasicMessageChannel(name: \"dev.flutter.pigeon.openlist_mobile.AppConfig.getDataDir\\(channelSuffix)\", binaryMessenger: binaryMessenger, codec: codec)\n    if let api = api {\n      getDataDirChannel.setMessageHandler { _, reply in\n        do {\n          let result = try api.getDataDir()\n          reply(wrapResult(result))\n        } catch {\n          reply(wrapError(error))\n        }\n      }\n    } else {\n      getDataDirChannel.setMessageHandler(nil)\n    }\n    let setDataDirChannel = FlutterBasicMessageChannel(name: \"dev.flutter.pigeon.openlist_mobile.AppConfig.setDataDir\\(channelSuffix)\", binaryMessenger: binaryMessenger, codec: codec)\n    if let api = api {\n      setDataDirChannel.setMessageHandler { message, reply in\n        let args = message as! [Any?]\n        let dirArg = args[0] as! String\n        do {\n          try api.setDataDir(dir: dirArg)\n          reply(wrapResult(nil))\n        } catch {\n          reply(wrapError(error))\n        }\n      }\n    } else {\n      setDataDirChannel.setMessageHandler(nil)\n    }\n    let isSilentJumpAppEnabledChannel = FlutterBasicMessageChannel(name: \"dev.flutter.pigeon.openlist_mobile.AppConfig.isSilentJumpAppEnabled\\(channelSuffix)\", binaryMessenger: binaryMessenger, codec: codec)\n    if let api = api {\n      isSilentJumpAppEnabledChannel.setMessageHandler { _, reply in\n        do {\n          let result = try api.isSilentJumpAppEnabled()\n          reply(wrapResult(result))\n        } catch {\n          reply(wrapError(error))\n        }\n      }\n    } else {\n      isSilentJumpAppEnabledChannel.setMessageHandler(nil)\n    }\n    let setSilentJumpAppEnabledChannel = FlutterBasicMessageChannel(name: \"dev.flutter.pigeon.openlist_mobile.AppConfig.setSilentJumpAppEnabled\\(channelSuffix)\", binaryMessenger: binaryMessenger, codec: codec)\n    if let api = api {\n      setSilentJumpAppEnabledChannel.setMessageHandler { message, reply in\n        let args = message as! [Any?]\n        let enabledArg = args[0] as! Bool\n        do {\n          try api.setSilentJumpAppEnabled(enabled: enabledArg)\n          reply(wrapResult(nil))\n        } catch {\n          reply(wrapError(error))\n        }\n      }\n    } else {\n      setSilentJumpAppEnabledChannel.setMessageHandler(nil)\n    }\n  }\n}\n/// Generated protocol from Pigeon that represents a handler of messages from Flutter.\nprotocol NativeCommon {\n  func startActivityFromUri(intentUri: String) throws -> Bool\n  func getDeviceSdkInt() throws -> Int64\n  func getDeviceCPUABI() throws -> String\n  func getVersionName() throws -> String\n  func getVersionCode() throws -> Int64\n  func toast(msg: String) throws\n  func longToast(msg: String) throws\n}\n\n/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.\nclass NativeCommonSetup {\n  static var codec: FlutterStandardMessageCodec { PigeonApiPigeonCodec.shared }\n  /// Sets up an instance of `NativeCommon` to handle messages through the `binaryMessenger`.\n  static func setUp(binaryMessenger: FlutterBinaryMessenger, api: NativeCommon?, messageChannelSuffix: String = \"\") {\n    let channelSuffix = messageChannelSuffix.count > 0 ? \".\\(messageChannelSuffix)\" : \"\"\n    let startActivityFromUriChannel = FlutterBasicMessageChannel(name: \"dev.flutter.pigeon.openlist_mobile.NativeCommon.startActivityFromUri\\(channelSuffix)\", binaryMessenger: binaryMessenger, codec: codec)\n    if let api = api {\n      startActivityFromUriChannel.setMessageHandler { message, reply in\n        let args = message as! [Any?]\n        let intentUriArg = args[0] as! String\n        do {\n          let result = try api.startActivityFromUri(intentUri: intentUriArg)\n          reply(wrapResult(result))\n        } catch {\n          reply(wrapError(error))\n        }\n      }\n    } else {\n      startActivityFromUriChannel.setMessageHandler(nil)\n    }\n    let getDeviceSdkIntChannel = FlutterBasicMessageChannel(name: \"dev.flutter.pigeon.openlist_mobile.NativeCommon.getDeviceSdkInt\\(channelSuffix)\", binaryMessenger: binaryMessenger, codec: codec)\n    if let api = api {\n      getDeviceSdkIntChannel.setMessageHandler { _, reply in\n        do {\n          let result = try api.getDeviceSdkInt()\n          reply(wrapResult(result))\n        } catch {\n          reply(wrapError(error))\n        }\n      }\n    } else {\n      getDeviceSdkIntChannel.setMessageHandler(nil)\n    }\n    let getDeviceCPUABIChannel = FlutterBasicMessageChannel(name: \"dev.flutter.pigeon.openlist_mobile.NativeCommon.getDeviceCPUABI\\(channelSuffix)\", binaryMessenger: binaryMessenger, codec: codec)\n    if let api = api {\n      getDeviceCPUABIChannel.setMessageHandler { _, reply in\n        do {\n          let result = try api.getDeviceCPUABI()\n          reply(wrapResult(result))\n        } catch {\n          reply(wrapError(error))\n        }\n      }\n    } else {\n      getDeviceCPUABIChannel.setMessageHandler(nil)\n    }\n    let getVersionNameChannel = FlutterBasicMessageChannel(name: \"dev.flutter.pigeon.openlist_mobile.NativeCommon.getVersionName\\(channelSuffix)\", binaryMessenger: binaryMessenger, codec: codec)\n    if let api = api {\n      getVersionNameChannel.setMessageHandler { _, reply in\n        do {\n          let result = try api.getVersionName()\n          reply(wrapResult(result))\n        } catch {\n          reply(wrapError(error))\n        }\n      }\n    } else {\n      getVersionNameChannel.setMessageHandler(nil)\n    }\n    let getVersionCodeChannel = FlutterBasicMessageChannel(name: \"dev.flutter.pigeon.openlist_mobile.NativeCommon.getVersionCode\\(channelSuffix)\", binaryMessenger: binaryMessenger, codec: codec)\n    if let api = api {\n      getVersionCodeChannel.setMessageHandler { _, reply in\n        do {\n          let result = try api.getVersionCode()\n          reply(wrapResult(result))\n        } catch {\n          reply(wrapError(error))\n        }\n      }\n    } else {\n      getVersionCodeChannel.setMessageHandler(nil)\n    }\n    let toastChannel = FlutterBasicMessageChannel(name: \"dev.flutter.pigeon.openlist_mobile.NativeCommon.toast\\(channelSuffix)\", binaryMessenger: binaryMessenger, codec: codec)\n    if let api = api {\n      toastChannel.setMessageHandler { message, reply in\n        let args = message as! [Any?]\n        let msgArg = args[0] as! String\n        do {\n          try api.toast(msg: msgArg)\n          reply(wrapResult(nil))\n        } catch {\n          reply(wrapError(error))\n        }\n      }\n    } else {\n      toastChannel.setMessageHandler(nil)\n    }\n    let longToastChannel = FlutterBasicMessageChannel(name: \"dev.flutter.pigeon.openlist_mobile.NativeCommon.longToast\\(channelSuffix)\", binaryMessenger: binaryMessenger, codec: codec)\n    if let api = api {\n      longToastChannel.setMessageHandler { message, reply in\n        let args = message as! [Any?]\n        let msgArg = args[0] as! String\n        do {\n          try api.longToast(msg: msgArg)\n          reply(wrapResult(nil))\n        } catch {\n          reply(wrapError(error))\n        }\n      }\n    } else {\n      longToastChannel.setMessageHandler(nil)\n    }\n  }\n}\n/// Generated protocol from Pigeon that represents a handler of messages from Flutter.\nprotocol Android {\n  func addShortcut() throws\n  func startService() throws\n  func setAdminPwd(pwd: String) throws\n  func getOpenListHttpPort() throws -> Int64\n  func isRunning() throws -> Bool\n  func getOpenListVersion() throws -> String\n}\n\n/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.\nclass AndroidSetup {\n  static var codec: FlutterStandardMessageCodec { PigeonApiPigeonCodec.shared }\n  /// Sets up an instance of `Android` to handle messages through the `binaryMessenger`.\n  static func setUp(binaryMessenger: FlutterBinaryMessenger, api: Android?, messageChannelSuffix: String = \"\") {\n    let channelSuffix = messageChannelSuffix.count > 0 ? \".\\(messageChannelSuffix)\" : \"\"\n    let addShortcutChannel = FlutterBasicMessageChannel(name: \"dev.flutter.pigeon.openlist_mobile.Android.addShortcut\\(channelSuffix)\", binaryMessenger: binaryMessenger, codec: codec)\n    if let api = api {\n      addShortcutChannel.setMessageHandler { _, reply in\n        do {\n          try api.addShortcut()\n          reply(wrapResult(nil))\n        } catch {\n          reply(wrapError(error))\n        }\n      }\n    } else {\n      addShortcutChannel.setMessageHandler(nil)\n    }\n    let startServiceChannel = FlutterBasicMessageChannel(name: \"dev.flutter.pigeon.openlist_mobile.Android.startService\\(channelSuffix)\", binaryMessenger: binaryMessenger, codec: codec)\n    if let api = api {\n      startServiceChannel.setMessageHandler { _, reply in\n        do {\n          try api.startService()\n          reply(wrapResult(nil))\n        } catch {\n          reply(wrapError(error))\n        }\n      }\n    } else {\n      startServiceChannel.setMessageHandler(nil)\n    }\n    let setAdminPwdChannel = FlutterBasicMessageChannel(name: \"dev.flutter.pigeon.openlist_mobile.Android.setAdminPwd\\(channelSuffix)\", binaryMessenger: binaryMessenger, codec: codec)\n    if let api = api {\n      setAdminPwdChannel.setMessageHandler { message, reply in\n        let args = message as! [Any?]\n        let pwdArg = args[0] as! String\n        do {\n          try api.setAdminPwd(pwd: pwdArg)\n          reply(wrapResult(nil))\n        } catch {\n          reply(wrapError(error))\n        }\n      }\n    } else {\n      setAdminPwdChannel.setMessageHandler(nil)\n    }\n    let getOpenListHttpPortChannel = FlutterBasicMessageChannel(name: \"dev.flutter.pigeon.openlist_mobile.Android.getOpenListHttpPort\\(channelSuffix)\", binaryMessenger: binaryMessenger, codec: codec)\n    if let api = api {\n      getOpenListHttpPortChannel.setMessageHandler { _, reply in\n        do {\n          let result = try api.getOpenListHttpPort()\n          reply(wrapResult(result))\n        } catch {\n          reply(wrapError(error))\n        }\n      }\n    } else {\n      getOpenListHttpPortChannel.setMessageHandler(nil)\n    }\n    let isRunningChannel = FlutterBasicMessageChannel(name: \"dev.flutter.pigeon.openlist_mobile.Android.isRunning\\(channelSuffix)\", binaryMessenger: binaryMessenger, codec: codec)\n    if let api = api {\n      isRunningChannel.setMessageHandler { _, reply in\n        do {\n          let result = try api.isRunning()\n          reply(wrapResult(result))\n        } catch {\n          reply(wrapError(error))\n        }\n      }\n    } else {\n      isRunningChannel.setMessageHandler(nil)\n    }\n    let getOpenListVersionChannel = FlutterBasicMessageChannel(name: \"dev.flutter.pigeon.openlist_mobile.Android.getOpenListVersion\\(channelSuffix)\", binaryMessenger: binaryMessenger, codec: codec)\n    if let api = api {\n      getOpenListVersionChannel.setMessageHandler { _, reply in\n        do {\n          let result = try api.getOpenListVersion()\n          reply(wrapResult(result))\n        } catch {\n          reply(wrapError(error))\n        }\n      }\n    } else {\n      getOpenListVersionChannel.setMessageHandler(nil)\n    }\n  }\n}\n/// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift.\nprotocol EventProtocol {\n  func onServiceStatusChanged(isRunning isRunningArg: Bool, completion: @escaping (Result<Void, PigeonError>) -> Void)\n  func onServerLog(level levelArg: Int64, time timeArg: String, log logArg: String, completion: @escaping (Result<Void, PigeonError>) -> Void)\n}\nclass Event: EventProtocol {\n  private let binaryMessenger: FlutterBinaryMessenger\n  private let messageChannelSuffix: String\n  init(binaryMessenger: FlutterBinaryMessenger, messageChannelSuffix: String = \"\") {\n    self.binaryMessenger = binaryMessenger\n    self.messageChannelSuffix = messageChannelSuffix.count > 0 ? \".\\(messageChannelSuffix)\" : \"\"\n  }\n  var codec: PigeonApiPigeonCodec {\n    return PigeonApiPigeonCodec.shared\n  }\n  func onServiceStatusChanged(isRunning isRunningArg: Bool, completion: @escaping (Result<Void, PigeonError>) -> Void) {\n    let channelName: String = \"dev.flutter.pigeon.openlist_mobile.Event.onServiceStatusChanged\\(messageChannelSuffix)\"\n    let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec)\n    channel.sendMessage([isRunningArg] as [Any?]) { response in\n      guard let listResponse = response as? [Any?] else {\n        completion(.failure(createConnectionError(withChannelName: channelName)))\n        return\n      }\n      if listResponse.count > 1 {\n        let code: String = listResponse[0] as! String\n        let message: String? = nilOrValue(listResponse[1])\n        let details: String? = nilOrValue(listResponse[2])\n        completion(.failure(PigeonError(code: code, message: message, details: details)))\n      } else {\n        completion(.success(()))\n      }\n    }\n  }\n  func onServerLog(level levelArg: Int64, time timeArg: String, log logArg: String, completion: @escaping (Result<Void, PigeonError>) -> Void) {\n    let channelName: String = \"dev.flutter.pigeon.openlist_mobile.Event.onServerLog\\(messageChannelSuffix)\"\n    let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec)\n    channel.sendMessage([levelArg, timeArg, logArg] as [Any?]) { response in\n      guard let listResponse = response as? [Any?] else {\n        completion(.failure(createConnectionError(withChannelName: channelName)))\n        return\n      }\n      if listResponse.count > 1 {\n        let code: String = listResponse[0] as! String\n        let message: String? = nilOrValue(listResponse[1])\n        let details: String? = nilOrValue(listResponse[2])\n        completion(.failure(PigeonError(code: code, message: message, details: details)))\n      } else {\n        completion(.success(()))\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "ios/Runner/Runner-Bridging-Header.h",
    "content": "#import \"GeneratedPluginRegistrant.h\"\n"
  },
  {
    "path": "ios/Runner.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 54;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };\n\t\t331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };\n\t\t3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };\n\t\t74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };\n\t\tAA1234561234567890ABCDE1 /* PigeonApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1234561234567890ABCDE0 /* PigeonApi.swift */; };\n\t\tAA1234561234567890ABCDE3 /* OpenListManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1234561234567890ABCDE2 /* OpenListManager.swift */; };\n\t\tAA1234561234567890ABCDE5 /* OpenListBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1234561234567890ABCDE4 /* OpenListBridge.swift */; };\n\t\tAA1234561234567890ABCDE7 /* AppConfigBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1234561234567890ABCDE6 /* AppConfigBridge.swift */; };\n\t\tAA1234561234567890ABCDE9 /* CommonBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1234561234567890ABCDE8 /* CommonBridge.swift */; };\n\t\tAA1234561234567890ABCDEF /* AppStoreUpdateBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1234561234567890ABCDEE /* AppStoreUpdateBridge.swift */; };\n\t\t97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };\n\t\t97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };\n\t\t97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXContainerItemProxy section */\n\t\t331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 97C146E61CF9000F007C117D /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = 97C146ED1CF9000F007C117D;\n\t\t\tremoteInfo = Runner;\n\t\t};\n/* End PBXContainerItemProxy section */\n\n/* Begin PBXCopyFilesBuildPhase section */\n\t\t9705A1C41CF9048500538489 /* Embed Frameworks */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 10;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tname = \"Embed Frameworks\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXCopyFilesBuildPhase section */\n\n/* Begin PBXFileReference section */\n\t\t1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = \"<group>\"; };\n\t\t1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = \"<group>\"; };\n\t\t331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = \"<group>\"; };\n\t\t331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = \"<group>\"; };\n\t\t74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = \"Runner-Bridging-Header.h\"; sourceTree = \"<group>\"; };\n\t\t74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\tAA1234561234567890ABCDE0 /* PigeonApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PigeonApi.swift; sourceTree = \"<group>\"; };\n\t\tAA1234561234567890ABCDE2 /* OpenListManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenListManager.swift; sourceTree = \"<group>\"; };\n\t\tAA1234561234567890ABCDE4 /* OpenListBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenListBridge.swift; sourceTree = \"<group>\"; };\n\t\tAA1234561234567890ABCDE6 /* AppConfigBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppConfigBridge.swift; sourceTree = \"<group>\"; };\n\t\tAA1234561234567890ABCDE8 /* CommonBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonBridge.swift; sourceTree = \"<group>\"; };\n\t\tAA1234561234567890ABCDEE /* AppStoreUpdateBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStoreUpdateBridge.swift; sourceTree = \"<group>\"; };\n\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = \"<group>\"; };\n\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = \"<group>\"; };\n\t\t9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = \"<group>\"; };\n\t\t97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\t97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\t97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t97C146EB1CF9000F007C117D /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t331C8082294A63A400263BE5 /* RunnerTests */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t331C807B294A618700263BE5 /* RunnerTests.swift */,\n\t\t\t);\n\t\t\tpath = RunnerTests;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t9740EEB11CF90186004384FC /* Flutter */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,\n\t\t\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */,\n\t\t\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */,\n\t\t\t\t9740EEB31CF90195004384FC /* Generated.xcconfig */,\n\t\t\t);\n\t\t\tname = Flutter;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146E51CF9000F007C117D = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t9740EEB11CF90186004384FC /* Flutter */,\n\t\t\t\t97C146F01CF9000F007C117D /* Runner */,\n\t\t\t\t97C146EF1CF9000F007C117D /* Products */,\n\t\t\t\t331C8082294A63A400263BE5 /* RunnerTests */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146EF1CF9000F007C117D /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t97C146EE1CF9000F007C117D /* Runner.app */,\n\t\t\t\t331C8081294A63A400263BE5 /* RunnerTests.xctest */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146F01CF9000F007C117D /* Runner */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAA1234561234567890ABCDEA /* Bridges */,\n\t\t\t\t97C146FA1CF9000F007C117D /* Main.storyboard */,\n\t\t\t\t97C146FD1CF9000F007C117D /* Assets.xcassets */,\n\t\t\t\t97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,\n\t\t\t\t97C147021CF9000F007C117D /* Info.plist */,\n\t\t\t\t1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,\n\t\t\t\t1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,\n\t\t\t\t74858FAE1ED2DC5600515810 /* AppDelegate.swift */,\n\t\t\t\t74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,\n\t\t\t\tAA1234561234567890ABCDE0 /* PigeonApi.swift */,\n\t\t\t\tAA1234561234567890ABCDE2 /* OpenListManager.swift */,\n\t\t\t);\n\t\t\tpath = Runner;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tAA1234561234567890ABCDEA /* Bridges */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAA1234561234567890ABCDEE /* AppStoreUpdateBridge.swift */,\n\t\t\t\tAA1234561234567890ABCDE4 /* OpenListBridge.swift */,\n\t\t\t\tAA1234561234567890ABCDE6 /* AppConfigBridge.swift */,\n\t\t\t\tAA1234561234567890ABCDE8 /* CommonBridge.swift */,\n\t\t\t);\n\t\t\tpath = Bridges;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t331C8080294A63A400263BE5 /* RunnerTests */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget \"RunnerTests\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t331C807D294A63A400263BE5 /* Sources */,\n\t\t\t\t331C807F294A63A400263BE5 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\t331C8086294A63A400263BE5 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = RunnerTests;\n\t\t\tproductName = RunnerTests;\n\t\t\tproductReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;\n\t\t\tproductType = \"com.apple.product-type.bundle.unit-test\";\n\t\t};\n\t\t97C146ED1CF9000F007C117D /* Runner */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget \"Runner\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t9740EEB61CF901F6004384FC /* Run Script */,\n\t\t\t\t97C146EA1CF9000F007C117D /* Sources */,\n\t\t\t\t97C146EB1CF9000F007C117D /* Frameworks */,\n\t\t\t\t97C146EC1CF9000F007C117D /* Resources */,\n\t\t\t\t9705A1C41CF9048500538489 /* Embed Frameworks */,\n\t\t\t\t3B06AD1E1E4923F5004D2608 /* Thin Binary */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = Runner;\n\t\t\tproductName = Runner;\n\t\t\tproductReference = 97C146EE1CF9000F007C117D /* Runner.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t97C146E61CF9000F007C117D /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tBuildIndependentTargetsInParallel = YES;\n\t\t\t\tLastUpgradeCheck = 1510;\n\t\t\t\tORGANIZATIONNAME = \"\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t331C8080294A63A400263BE5 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 14.0;\n\t\t\t\t\t\tTestTargetID = 97C146ED1CF9000F007C117D;\n\t\t\t\t\t};\n\t\t\t\t\t97C146ED1CF9000F007C117D = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 7.3.1;\n\t\t\t\t\t\tLastSwiftMigration = 1100;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject \"Runner\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 97C146E51CF9000F007C117D;\n\t\t\tproductRefGroup = 97C146EF1CF9000F007C117D /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t97C146ED1CF9000F007C117D /* Runner */,\n\t\t\t\t331C8080294A63A400263BE5 /* RunnerTests */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t331C807F294A63A400263BE5 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t97C146EC1CF9000F007C117D /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,\n\t\t\t\t3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,\n\t\t\t\t97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,\n\t\t\t\t97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\t3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\talwaysOutOfDate = 1;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\",\n\t\t\t);\n\t\t\tname = \"Thin Binary\";\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"/bin/sh \\\"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\\\" embed_and_thin\";\n\t\t};\n\t\t9740EEB61CF901F6004384FC /* Run Script */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\talwaysOutOfDate = 1;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\tname = \"Run Script\";\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"/bin/sh \\\"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\\\" build\";\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t331C807D294A63A400263BE5 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t97C146EA1CF9000F007C117D /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,\n\t\t\t\t1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,\n\t\t\t\tAA1234561234567890ABCDE1 /* PigeonApi.swift in Sources */,\n\t\t\t\tAA1234561234567890ABCDE3 /* OpenListManager.swift in Sources */,\n\t\t\t\tAA1234561234567890ABCDEF /* AppStoreUpdateBridge.swift in Sources */,\n\t\t\t\tAA1234561234567890ABCDE5 /* OpenListBridge.swift in Sources */,\n\t\t\t\tAA1234561234567890ABCDE7 /* AppConfigBridge.swift in Sources */,\n\t\t\t\tAA1234561234567890ABCDE9 /* CommonBridge.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXTargetDependency section */\n\t\t331C8086294A63A400263BE5 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = 97C146ED1CF9000F007C117D /* Runner */;\n\t\t\ttargetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;\n\t\t};\n/* End PBXTargetDependency section */\n\n/* Begin PBXVariantGroup section */\n\t\t97C146FA1CF9000F007C117D /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t97C146FB1CF9000F007C117D /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t97C147001CF9000F007C117D /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t249021D3217E4FDB00AE95B9 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = NO;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSUPPORTED_PLATFORMS = iphoneos;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t249021D4217E4FDB00AE95B9 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tENABLE_BITCODE = NO;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.oplist.app;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"Runner/Runner-Bridging-Header.h\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t331C8088294A63A400263BE5 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.openlist.mobile.RunnerTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t331C8089294A63A400263BE5 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.openlist.mobile.RunnerTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t331C808A294A63A400263BE5 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.openlist.mobile.RunnerTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t97C147031CF9000F007C117D /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = NO;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t97C147041CF9000F007C117D /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = NO;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 12.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSUPPORTED_PLATFORMS = iphoneos;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t97C147061CF9000F007C117D /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tENABLE_BITCODE = NO;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.oplist.app;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"Runner/Runner-Bridging-Header.h\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t97C147071CF9000F007C117D /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tENABLE_BITCODE = NO;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.oplist.app;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"Runner/Runner-Bridging-Header.h\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget \"RunnerTests\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t331C8088294A63A400263BE5 /* Debug */,\n\t\t\t\t331C8089294A63A400263BE5 /* Release */,\n\t\t\t\t331C808A294A63A400263BE5 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t97C146E91CF9000F007C117D /* Build configuration list for PBXProject \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t97C147031CF9000F007C117D /* Debug */,\n\t\t\t\t97C147041CF9000F007C117D /* Release */,\n\t\t\t\t249021D3217E4FDB00AE95B9 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t97C147061CF9000F007C117D /* Debug */,\n\t\t\t\t97C147071CF9000F007C117D /* Release */,\n\t\t\t\t249021D4217E4FDB00AE95B9 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 97C146E61CF9000F007C117D /* Project object */;\n}\n"
  },
  {
    "path": "ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>PreviewsEnabled</key>\n\t<false/>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1510\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n               BuildableName = \"Runner.app\"\n               BlueprintName = \"Runner\"\n               ReferencedContainer = \"container:Runner.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      customLLDBInitFile = \"$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n      <Testables>\n         <TestableReference\n            skipped = \"NO\"\n            parallelizable = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"331C8080294A63A400263BE5\"\n               BuildableName = \"RunnerTests.xctest\"\n               BlueprintName = \"RunnerTests\"\n               ReferencedContainer = \"container:Runner.xcodeproj\">\n            </BuildableReference>\n         </TestableReference>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      customLLDBInitFile = \"$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      enableGPUValidationMode = \"1\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Profile\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "ios/Runner.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"group:Runner.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>PreviewsEnabled</key>\n\t<false/>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/RunnerTests/RunnerTests.swift",
    "content": "import Flutter\nimport UIKit\nimport XCTest\n\nclass RunnerTests: XCTestCase {\n\n  func testExample() {\n    // If you add code to the Runner application, consider adding tests here.\n    // See https://developer.apple.com/documentation/xctest for more information about using XCTest.\n  }\n\n}\n"
  },
  {
    "path": "ios/scripts/generate_ios_icons.py",
    "content": "#!/usr/bin/env python3\n\"\"\"\niOS App Icon Generator\nGenerates all required iOS app icon sizes from source PNG files\n\"\"\"\n\nimport os\nimport sys\nfrom PIL import Image\n\n# Icon size mappings: (filename, target_size_px)\nICON_MAPPINGS = [\n    (\"Icon-App-20x20@1x.png\", 20),\n    (\"Icon-App-20x20@2x.png\", 40),\n    (\"Icon-App-20x20@3x.png\", 60),\n    (\"Icon-App-29x29@1x.png\", 29),\n    (\"Icon-App-29x29@2x.png\", 58),\n    (\"Icon-App-29x29@3x.png\", 87),\n    (\"Icon-App-40x40@1x.png\", 40),\n    (\"Icon-App-40x40@2x.png\", 80),\n    (\"Icon-App-40x40@3x.png\", 120),\n    (\"Icon-App-60x60@2x.png\", 120),\n    (\"Icon-App-60x60@3x.png\", 180),\n    (\"Icon-App-76x76@1x.png\", 76),\n    (\"Icon-App-76x76@2x.png\", 152),\n    (\"Icon-App-83.5x83.5@2x.png\", 167),\n    (\"Icon-App-1024x1024@1x.png\", 1024),\n]\n\ndef find_best_source_image(logo_dir, target_size):\n    \"\"\"Find the best source image (closest size >= target size)\"\"\"\n    available_sizes = [16, 24, 32, 48, 64, 72, 96, 120, 128, 144, 160, 192, 224, 240, 248, 256, 300, 320, 384, 512, 1024]\n    \n    # Find the smallest size that's >= target size\n    for size in available_sizes:\n        if size >= target_size:\n            source_path = os.path.join(logo_dir, f\"{size}x{size}.png\")\n            if os.path.exists(source_path):\n                return source_path\n    \n    # Fallback to largest available\n    return os.path.join(logo_dir, \"1024x1024.png\")\n\ndef generate_icon(source_path, output_path, target_size):\n    \"\"\"Resize and save icon\"\"\"\n    try:\n        img = Image.open(source_path)\n        img = img.resize((target_size, target_size), Image.Resampling.LANCZOS)\n        img.save(output_path, \"PNG\", optimize=True)\n        print(f\"✓ Generated: {os.path.basename(output_path)} ({target_size}x{target_size})\")\n        return True\n    except Exception as e:\n        print(f\"✗ Failed to generate {os.path.basename(output_path)}: {e}\")\n        return False\n\ndef main():\n    # Paths\n    script_dir = os.path.dirname(os.path.abspath(__file__))\n    ios_dir = os.path.dirname(script_dir)\n    flutter_project_root = os.path.dirname(ios_dir)\n    workspace_root = os.path.dirname(flutter_project_root)\n    logo_dir = os.path.join(workspace_root, \"Logo\", \"logo\")\n    output_dir = os.path.join(ios_dir, \"Runner\", \"Assets.xcassets\", \"AppIcon.appiconset\")\n    \n    # Verify directories\n    if not os.path.exists(logo_dir):\n        print(f\"Error: Logo directory not found: {logo_dir}\")\n        sys.exit(1)\n    \n    if not os.path.exists(output_dir):\n        print(f\"Error: Output directory not found: {output_dir}\")\n        sys.exit(1)\n    \n    print(f\"Source: {logo_dir}\")\n    print(f\"Output: {output_dir}\")\n    print(\"-\" * 60)\n    \n    # Generate all icons\n    success_count = 0\n    for filename, target_size in ICON_MAPPINGS:\n        source_path = find_best_source_image(logo_dir, target_size)\n        output_path = os.path.join(output_dir, filename)\n        \n        if generate_icon(source_path, output_path, target_size):\n            success_count += 1\n    \n    print(\"-\" * 60)\n    print(f\"✓ Successfully generated {success_count}/{len(ICON_MAPPINGS)} icons\")\n    \n    if success_count == len(ICON_MAPPINGS):\n        print(\"✓ All iOS app icons generated successfully!\")\n        return 0\n    else:\n        print(\"✗ Some icons failed to generate\")\n        return 1\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n"
  },
  {
    "path": "lib/contant/log_level.dart",
    "content": "import 'dart:ui';\n\nclass LogLevel {\n  static const int panic = 0;\n  static const int fatal = 1;\n  static const int error = 2;\n\n  static const int warn = 3;\n  static const int info = 4;\n  static const int debug = 5;\n  static const int trace = 6;\n\n  static Color toColor(int level) {\n    //Color.fromARGB(a, r, g, b)\n    return switch(level) {\n      LogLevel.panic => const Color.fromARGB(255, 255, 0, 0),\n      LogLevel.fatal => const Color.fromARGB(255, 255, 0, 0),\n      LogLevel.error => const Color.fromARGB(255, 255, 0, 0),\n      LogLevel.warn => const Color.fromARGB(255, 255, 165, 0),\n      LogLevel.info => const Color.fromARGB(255, 0, 0, 255),\n      LogLevel.debug => const Color.fromARGB(255, 0, 255, 0),\n      LogLevel.trace => const Color.fromARGB(255, 0, 255, 0),\n      _ => const Color.fromARGB(255, 0, 0, 0)\n    };\n  }\n\n  static String toStr(int level) {\n    return switch(level) {\n      LogLevel.panic => \"Panic\",\n      LogLevel.fatal => \"Fatal\",\n      LogLevel.error => \"Error\",\n      LogLevel.warn => \"Warn\",\n      LogLevel.info => \"Info\",\n      LogLevel.debug => \"Debug\",\n      LogLevel.trace => \"Trace\",\n      _ => \"\"\n    };\n  }\n}\n"
  },
  {
    "path": "lib/contant/native_bridge.dart",
    "content": "import 'package:openlist_mobile/generated_api.dart';\n\nclass NativeBridge {\n  static NativeCommon common = NativeCommon();\n  static Android android = Android();\n  static AppConfig appConfig = AppConfig();\n}\n"
  },
  {
    "path": "lib/generated/intl/messages_all.dart",
    "content": "// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart\n// This is a library that looks up messages for specific locales by\n// delegating to the appropriate library.\n\n// Ignore issues from commonly used lints in this file.\n// ignore_for_file:implementation_imports, file_names, unnecessary_new\n// ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering\n// ignore_for_file:argument_type_not_assignable, invalid_assignment\n// ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases\n// ignore_for_file:comment_references\n\nimport 'dart:async';\n\nimport 'package:flutter/foundation.dart';\nimport 'package:intl/intl.dart';\nimport 'package:intl/message_lookup_by_library.dart';\nimport 'package:intl/src/intl_helpers.dart';\n\nimport 'messages_en.dart' as messages_en;\nimport 'messages_zh.dart' as messages_zh;\n\ntypedef Future<dynamic> LibraryLoader();\nMap<String, LibraryLoader> _deferredLibraries = {\n  'en': () => new SynchronousFuture(null),\n  'zh': () => new SynchronousFuture(null),\n};\n\nMessageLookupByLibrary? _findExact(String localeName) {\n  switch (localeName) {\n    case 'en':\n      return messages_en.messages;\n    case 'zh':\n      return messages_zh.messages;\n    default:\n      return null;\n  }\n}\n\n/// User programs should call this before using [localeName] for messages.\nFuture<bool> initializeMessages(String localeName) {\n  var availableLocale = Intl.verifiedLocale(\n    localeName,\n    (locale) => _deferredLibraries[locale] != null,\n    onFailure: (_) => null,\n  );\n  if (availableLocale == null) {\n    return new SynchronousFuture(false);\n  }\n  var lib = _deferredLibraries[availableLocale];\n  lib == null ? new SynchronousFuture(false) : lib();\n  initializeInternalMessageLookup(() => new CompositeMessageLookup());\n  messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor);\n  return new SynchronousFuture(true);\n}\n\nbool _messagesExistFor(String locale) {\n  try {\n    return _findExact(locale) != null;\n  } catch (e) {\n    return false;\n  }\n}\n\nMessageLookupByLibrary? _findGeneratedMessagesFor(String locale) {\n  var actualLocale = Intl.verifiedLocale(\n    locale,\n    _messagesExistFor,\n    onFailure: (_) => null,\n  );\n  if (actualLocale == null) return null;\n  return _findExact(actualLocale);\n}\n"
  },
  {
    "path": "lib/generated/intl/messages_en.dart",
    "content": "// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart\n// This is a library that provides messages for a en locale. All the\n// messages from the main program should be duplicated here with the same\n// function name.\n\n// Ignore issues from commonly used lints in this file.\n// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new\n// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering\n// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases\n// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes\n// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes\n\nimport 'package:intl/intl.dart';\nimport 'package:intl/message_lookup_by_library.dart';\n\nfinal messages = new MessageLookup();\n\ntypedef String MessageIfAbsent(String messageStr, List<dynamic> args);\n\nclass MessageLookup extends MessageLookupByLibrary {\n  String get localeName => 'en';\n\n  static String m0(error) => \"Failed to cancel all notifications: ${error}\";\n\n  static String m1(error) => \"Failed to cancel download notification: ${error}\";\n\n  static String m2(error) => \"Failed to check install permission: ${error}\";\n\n  static String m3(error) => \"Failed to clear download directory: ${error}\";\n\n  static String m4(filename) =>\n      \"Are you sure you want to cancel downloading \\\"${filename}\\\"?\";\n\n  static String m5(filename) =>\n      \"Are you sure you want to delete file \\\"${filename}\\\"? This action cannot be undone.\";\n\n  static String m6(filename) =>\n      \"Are you sure you want to delete the download record of \\\"${filename}\\\"?\";\n\n  static String m7(error) => \"Failed to create OpenList directory: ${error}\";\n\n  static String m8(path) => \"Create OpenList download directory: ${path}\";\n\n  static String m9(count) => \"Currently ${count} files are downloading\";\n\n  static String m10(error) => \"Failed to delete file: ${error}\";\n\n  static String m11(url) => \"Download cancelled: ${url}\";\n\n  static String m12(filename) => \"Download complete: ${filename}\";\n\n  static String m13(filename) => \"Download complete: ${filename}\";\n\n  static String m14(filename) => \"${filename} download completed\";\n\n  static String m15(filename) => \"Download failed: ${filename}\";\n\n  static String m16(filename) => \"Download failed: ${filename}\";\n\n  static String m17(count) => \"Download (${count})\";\n\n  static String m18(progress) => \"Download progress: ${progress}%\";\n\n  static String m19(current, total) => \"Downloading file ${current}/${total}\";\n\n  static String m20(filename) => \"File deleted: ${filename}\";\n\n  static String m21(index) => \"File ${index} download failed\";\n\n  static String m22(size) => \"Size: ${size}\";\n\n  static String m23(time) => \"Time: ${time}\";\n\n  static String m24(error) => \"Failed to get download directory: ${error}\";\n\n  static String m25(error) => \"Failed to get download file list: ${error}\";\n\n  static String m26(line, error) =>\n      \"Invalid JSON format at line ${line}: ${error}\";\n\n  static String m27(error) => \"Load failed: ${error}\";\n\n  static String m28(count) =>\n      \"${count} files completed, click to jump to download manager\";\n\n  static String m29(payload) => \"Notification clicked: ${payload}\";\n\n  static String m30(error) =>\n      \"Failed to initialize notification manager: ${error}\";\n\n  static String m31(error) => \"Failed to open download directory: ${error}\";\n\n  static String m32(error) => \"Open file exception: ${error}\";\n\n  static String m33(error) => \"Failed to open file: ${error}\";\n\n  static String m34(error) => \"Failed to open file manager: ${error}\";\n\n  static String m35(type, message) => \"Open file result: ${type} - ${message}\";\n\n  static String m36(path) => \"OpenList download directory: ${path}\";\n\n  static String m37(error) => \"Failed to parse filename: ${error}\";\n\n  static String m38(error) => \"Restore backup failed: ${error}\";\n\n  static String m39(error) => \"Save failed: ${error}\";\n\n  static String m40(error) =>\n      \"Failed to show download complete notification: ${error}\";\n\n  static String m41(error) =>\n      \"Failed to show download progress notification: ${error}\";\n\n  static String m42(error) =>\n      \"Failed to show single file complete notification: ${error}\";\n\n  static String m43(filename) => \"Start download: ${filename}\";\n\n  static String m44(filename) => \"Start download: ${filename}\";\n\n  static String m45(path) => \"Trying to open file: ${path}\";\n\n  final messages = _notInlinedMessages(_notInlinedMessages);\n  static Map<String, Function> _notInlinedMessages(_) => <String, Function>{\n    \"about\": MessageLookupByLibrary.simpleMessage(\"About\"),\n    \"apkDownloadCompleteMessage\": MessageLookupByLibrary.simpleMessage(\n      \"APK file download completed, do you want to install?\",\n    ),\n    \"appName\": MessageLookupByLibrary.simpleMessage(\"OpenList\"),\n    \"autoCheckForUpdates\": MessageLookupByLibrary.simpleMessage(\n      \"Auto check for updates\",\n    ),\n    \"autoCheckForUpdatesDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Check for updates when app starts\",\n    ),\n    \"autoStartIssue\": MessageLookupByLibrary.simpleMessage(\n      \"Auto-Start Information\",\n    ),\n    \"autoStartIssueDesc\": MessageLookupByLibrary.simpleMessage(\n      \"When enabling auto-start, it\\'s recommended to disable battery optimization for the app. Currently, after enabling auto-start, the service will automatically start in the background after system reboot, but may not show a notification in the notification bar. Rest assured, the service is running normally. You can check the service status through the quick settings tile in the notification shade, or return to the main interface to check the service toggle to confirm if the service has started.\",\n    ),\n    \"autoStartWebPage\": MessageLookupByLibrary.simpleMessage(\n      \"Set web page as startup page\",\n    ),\n    \"autoStartWebPageDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Default page when opening main interface\",\n    ),\n    \"backupRestored\": MessageLookupByLibrary.simpleMessage(\n      \"Backup restored successfully\",\n    ),\n    \"batchDownloadComplete\": MessageLookupByLibrary.simpleMessage(\n      \"Batch download complete\",\n    ),\n    \"bootAutoStartService\": MessageLookupByLibrary.simpleMessage(\n      \"Boot auto-start service\",\n    ),\n    \"bootAutoStartServiceDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Automatically start OpenList service after boot. (Please make sure to grant auto-start permission)\",\n    ),\n    \"browserDownload\": MessageLookupByLibrary.simpleMessage(\"Browser Download\"),\n    \"browserDownloadMethod\": MessageLookupByLibrary.simpleMessage(\n      \"Browser Download\",\n    ),\n    \"browserDownloadMethodDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Use system browser\",\n    ),\n    \"cancel\": MessageLookupByLibrary.simpleMessage(\"Cancel\"),\n    \"cancelAllNotificationsFailed\": m0,\n    \"cancelDownload\": MessageLookupByLibrary.simpleMessage(\"Cancel Download\"),\n    \"cancelDownloadNotificationFailed\": m1,\n    \"cancelled\": MessageLookupByLibrary.simpleMessage(\"Cancelled\"),\n    \"cannotGetBaseDownloadDirectory\": MessageLookupByLibrary.simpleMessage(\n      \"Cannot get base download directory\",\n    ),\n    \"cannotGetDownloadDirectory\": MessageLookupByLibrary.simpleMessage(\n      \"Cannot get download directory\",\n    ),\n    \"cannotGetDownloadDirectoryError\": MessageLookupByLibrary.simpleMessage(\n      \"Cannot get download directory\",\n    ),\n    \"cannotInstallApkFile\": MessageLookupByLibrary.simpleMessage(\n      \"Cannot install APK file, you may need to enable \\\"Install unknown apps\\\" in settings\",\n    ),\n    \"cannotInstallApkNeedPermission\": MessageLookupByLibrary.simpleMessage(\n      \"Cannot install APK file, you may need to enable \\\"Install unknown apps\\\" in settings\",\n    ),\n    \"checkDownloadManagerForFiles\": MessageLookupByLibrary.simpleMessage(\n      \"Please check download manager via bottom navigation bar to view download files\",\n    ),\n    \"checkForUpdates\": MessageLookupByLibrary.simpleMessage(\n      \"Check for updates\",\n    ),\n    \"checkImageInDownloadFolder\": MessageLookupByLibrary.simpleMessage(\n      \"Please check image in download folder\",\n    ),\n    \"checkInstallPermissionFailed\": m2,\n    \"clear\": MessageLookupByLibrary.simpleMessage(\"Clear\"),\n    \"clearAll\": MessageLookupByLibrary.simpleMessage(\"Clear All\"),\n    \"clearDownloadDirectoryFailed\": m3,\n    \"clearFailed\": MessageLookupByLibrary.simpleMessage(\"Clear failed\"),\n    \"clearRecords\": MessageLookupByLibrary.simpleMessage(\"Clear Records\"),\n    \"cleared\": MessageLookupByLibrary.simpleMessage(\n      \"Download directory cleared\",\n    ),\n    \"clickToJumpToDownloadManager\": MessageLookupByLibrary.simpleMessage(\n      \"Click to jump to download manager\",\n    ),\n    \"completed\": MessageLookupByLibrary.simpleMessage(\"Completed\"),\n    \"completedTime\": MessageLookupByLibrary.simpleMessage(\"Completed time\"),\n    \"configSavedRestartRequired\": MessageLookupByLibrary.simpleMessage(\n      \"Config saved. Please restart OpenList service to take effect.\",\n    ),\n    \"confirm\": MessageLookupByLibrary.simpleMessage(\"OK\"),\n    \"confirmCancelDownload\": m4,\n    \"confirmClear\": MessageLookupByLibrary.simpleMessage(\"Confirm Clear\"),\n    \"confirmClearAllFiles\": MessageLookupByLibrary.simpleMessage(\n      \"Are you sure you want to clear all download files? This action cannot be undone.\",\n    ),\n    \"confirmDelete\": MessageLookupByLibrary.simpleMessage(\"Confirm Delete\"),\n    \"confirmDeleteFile\": m5,\n    \"confirmDeleteRecord\": m6,\n    \"confirmDownload\": MessageLookupByLibrary.simpleMessage(\"Confirm Download\"),\n    \"confirmDownloadMessage\": MessageLookupByLibrary.simpleMessage(\n      \"Do you want to download this file?\",\n    ),\n    \"confirmSaveConfigMessage\": MessageLookupByLibrary.simpleMessage(\n      \"Modifying configuration may cause service unavailable. Are you sure to save?\",\n    ),\n    \"confirmSaveConfigTitle\": MessageLookupByLibrary.simpleMessage(\n      \"Confirm Save\",\n    ),\n    \"continueDownload\": MessageLookupByLibrary.simpleMessage(\n      \"Continue Download\",\n    ),\n    \"copiedToClipboard\": MessageLookupByLibrary.simpleMessage(\n      \"Copied to clipboard\",\n    ),\n    \"createOpenListDirectoryFailed\": m7,\n    \"createOpenListDownloadDirectory\": m8,\n    \"currentDownloadingFiles\": m9,\n    \"currentIsLatestVersion\": MessageLookupByLibrary.simpleMessage(\n      \"Current is latest version\",\n    ),\n    \"currentlyDownloading\": MessageLookupByLibrary.simpleMessage(\"Downloading\"),\n    \"dataDirectory\": MessageLookupByLibrary.simpleMessage(\"data Directory\"),\n    \"databaseNotSavedIssue\": MessageLookupByLibrary.simpleMessage(\n      \"Database Not Saved Issue\",\n    ),\n    \"databaseNotSavedIssueDesc\": MessageLookupByLibrary.simpleMessage(\n      \"If you don\\'t manually close OpenList, the database may not be saved to the corresponding db file. If you encounter this issue, please manually close the app to resolve it. (The switch is located in the main program menu on the OpenList interface, as well as in the notification bar)\",\n    ),\n    \"delete\": MessageLookupByLibrary.simpleMessage(\"Delete\"),\n    \"deleteFailed\": MessageLookupByLibrary.simpleMessage(\"Delete failed\"),\n    \"deleteFile\": MessageLookupByLibrary.simpleMessage(\"Delete file\"),\n    \"deleteFileFailedLog\": m10,\n    \"deleteRecord\": MessageLookupByLibrary.simpleMessage(\"Delete record\"),\n    \"description\": MessageLookupByLibrary.simpleMessage(\"Description:\"),\n    \"desktopShortcut\": MessageLookupByLibrary.simpleMessage(\"Desktop shortcut\"),\n    \"directDownload\": MessageLookupByLibrary.simpleMessage(\"Direct Download\"),\n    \"directDownloadApk\": MessageLookupByLibrary.simpleMessage(\n      \"Direct Download APK\",\n    ),\n    \"directDownloadMethod\": MessageLookupByLibrary.simpleMessage(\n      \"Direct Download\",\n    ),\n    \"directDownloadMethodDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Use in-app downloader\",\n    ),\n    \"download\": MessageLookupByLibrary.simpleMessage(\"download\"),\n    \"downloadApk\": MessageLookupByLibrary.simpleMessage(\"Download APK\"),\n    \"downloadCancelled\": m11,\n    \"downloadCancelledStatus\": MessageLookupByLibrary.simpleMessage(\n      \"Download cancelled\",\n    ),\n    \"downloadCancelledText\": MessageLookupByLibrary.simpleMessage(\n      \"Download cancelled\",\n    ),\n    \"downloadComplete\": m12,\n    \"downloadCompleteChannel\": MessageLookupByLibrary.simpleMessage(\n      \"Download Complete\",\n    ),\n    \"downloadCompleteChannelDesc\": MessageLookupByLibrary.simpleMessage(\n      \"File download complete notification\",\n    ),\n    \"downloadCompleteFile\": m13,\n    \"downloadCompleteNotificationTitle\": m14,\n    \"downloadCompleteTitle\": MessageLookupByLibrary.simpleMessage(\n      \"Download Complete\",\n    ),\n    \"downloadDirectory\": MessageLookupByLibrary.simpleMessage(\n      \"Download Directory\",\n    ),\n    \"downloadDirectoryCleared\": MessageLookupByLibrary.simpleMessage(\n      \"Download directory cleared\",\n    ),\n    \"downloadDirectoryOpened\": MessageLookupByLibrary.simpleMessage(\n      \"Download directory opened\",\n    ),\n    \"downloadDirectoryPathUnknown\": MessageLookupByLibrary.simpleMessage(\n      \"Download directory path unknown\",\n    ),\n    \"downloadFailed\": MessageLookupByLibrary.simpleMessage(\"Download failed\"),\n    \"downloadFailedFile\": m15,\n    \"downloadFailedWithError\": m16,\n    \"downloadFunctionTest\": MessageLookupByLibrary.simpleMessage(\n      \"Download Function Test\",\n    ),\n    \"downloadInstructions\": MessageLookupByLibrary.simpleMessage(\n      \"• Files will be downloaded to system download directory\\\\n• Download progress will show notifications\\\\n• You can choose to open file after download completes\\\\n• If filename exists, a number will be added automatically\\\\n• Please check download manager via bottom navigation bar to view files\",\n    ),\n    \"downloadManager\": MessageLookupByLibrary.simpleMessage(\"Download\"),\n    \"downloadManagerWithCount\": m17,\n    \"downloadProgress\": m18,\n    \"downloadProgressChannel\": MessageLookupByLibrary.simpleMessage(\n      \"Download Progress\",\n    ),\n    \"downloadProgressDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Show file download progress\",\n    ),\n    \"downloadRecordsCleared\": MessageLookupByLibrary.simpleMessage(\n      \"Download records cleared\",\n    ),\n    \"downloadThisFile\": MessageLookupByLibrary.simpleMessage(\n      \"Download this file？\",\n    ),\n    \"downloading\": MessageLookupByLibrary.simpleMessage(\"Downloading\"),\n    \"downloadingFileProgress\": m19,\n    \"downloadingImage\": MessageLookupByLibrary.simpleMessage(\n      \"Downloading image...\",\n    ),\n    \"edit\": MessageLookupByLibrary.simpleMessage(\"Edit\"),\n    \"editOpenListConfig\": MessageLookupByLibrary.simpleMessage(\n      \"Edit OpenList Config\",\n    ),\n    \"english\": MessageLookupByLibrary.simpleMessage(\"English\"),\n    \"failed\": MessageLookupByLibrary.simpleMessage(\"Failed\"),\n    \"fileDeleted\": MessageLookupByLibrary.simpleMessage(\"File deleted\"),\n    \"fileDeletedLog\": m20,\n    \"fileDownloadFailed\": m21,\n    \"fileInfo\": MessageLookupByLibrary.simpleMessage(\"File info\"),\n    \"fileLocation\": MessageLookupByLibrary.simpleMessage(\"File Location\"),\n    \"fileLocationTip\": MessageLookupByLibrary.simpleMessage(\n      \"You can use a file manager to find this file, or try installing the appropriate app to open it.\",\n    ),\n    \"fileManagerOpened\": MessageLookupByLibrary.simpleMessage(\n      \"File manager opened\",\n    ),\n    \"fileName\": MessageLookupByLibrary.simpleMessage(\"File name\"),\n    \"fileNotFound\": MessageLookupByLibrary.simpleMessage(\n      \"File not found or has been deleted\",\n    ),\n    \"fileNotFoundWillCreateOnSave\": MessageLookupByLibrary.simpleMessage(\n      \"File not found. Will create on save.\",\n    ),\n    \"filePath\": MessageLookupByLibrary.simpleMessage(\"Path\"),\n    \"filePermissionDenied\": MessageLookupByLibrary.simpleMessage(\n      \"File permission denied. Please check app permissions.\",\n    ),\n    \"fileSavedTo\": MessageLookupByLibrary.simpleMessage(\"File saved to:\"),\n    \"fileSize\": m22,\n    \"fileTime\": m23,\n    \"findApkInDownloadFolder\": MessageLookupByLibrary.simpleMessage(\n      \"Please find APK file in download folder to install\",\n    ),\n    \"followSystem\": MessageLookupByLibrary.simpleMessage(\"Follow System\"),\n    \"general\": MessageLookupByLibrary.simpleMessage(\"General\"),\n    \"getDownloadDirectoryFailed\": m24,\n    \"getDownloadFileListFailed\": m25,\n    \"getDownloadPathFailed\": MessageLookupByLibrary.simpleMessage(\n      \"Failed to get\",\n    ),\n    \"goTo\": MessageLookupByLibrary.simpleMessage(\"GO\"),\n    \"goToSettings\": MessageLookupByLibrary.simpleMessage(\"Go to Settings\"),\n    \"grantManagerStoragePermission\": MessageLookupByLibrary.simpleMessage(\n      \"Grant [Manage external storage] permission\",\n    ),\n    \"grantNotificationPermission\": MessageLookupByLibrary.simpleMessage(\n      \"Grant [Notification] permission\",\n    ),\n    \"grantNotificationPermissionDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Used for foreground service keep alive\",\n    ),\n    \"grantStoragePermission\": MessageLookupByLibrary.simpleMessage(\n      \"Grant [external storage] permission\",\n    ),\n    \"grantStoragePermissionDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Mounting local storage is a must, otherwise no permission to read and write files\",\n    ),\n    \"imageDownloadSuccess\": MessageLookupByLibrary.simpleMessage(\n      \"Image download success\",\n    ),\n    \"importantSettings\": MessageLookupByLibrary.simpleMessage(\n      \"Important settings\",\n    ),\n    \"inProgress\": MessageLookupByLibrary.simpleMessage(\"In Progress\"),\n    \"initializingNotificationManager\": MessageLookupByLibrary.simpleMessage(\n      \"Initializing notification manager\",\n    ),\n    \"installNow\": MessageLookupByLibrary.simpleMessage(\"Install Now\"),\n    \"invalidJsonFormat\": m26,\n    \"jumpToOtherApp\": MessageLookupByLibrary.simpleMessage(\n      \"Jump to other app？\",\n    ),\n    \"language\": MessageLookupByLibrary.simpleMessage(\"Language\"),\n    \"languageSettings\": MessageLookupByLibrary.simpleMessage(\n      \"Language Settings\",\n    ),\n    \"languageSettingsDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Select app display language\",\n    ),\n    \"laterInstall\": MessageLookupByLibrary.simpleMessage(\"Install Later\"),\n    \"loadDownloadFilesFailed\": MessageLookupByLibrary.simpleMessage(\n      \"Failed to load download files\",\n    ),\n    \"loadFailed\": m27,\n    \"modifiedTime\": MessageLookupByLibrary.simpleMessage(\"Modified time\"),\n    \"modifyAdminPassword\": MessageLookupByLibrary.simpleMessage(\n      \"Modify Admin Password\",\n    ),\n    \"moreOptions\": MessageLookupByLibrary.simpleMessage(\"More options\"),\n    \"multipleFilesCompleted\": m28,\n    \"needInstallPermission\": MessageLookupByLibrary.simpleMessage(\n      \"Install Permission Required\",\n    ),\n    \"needInstallPermissionDesc\": MessageLookupByLibrary.simpleMessage(\n      \"To install APK files, install permission is required. Please enable it manually in settings.\",\n    ),\n    \"needInstallPermissionToInstallApk\": MessageLookupByLibrary.simpleMessage(\n      \"Install permission is required to install APK files\",\n    ),\n    \"newVersionFound\": MessageLookupByLibrary.simpleMessage(\n      \"New Version Found\",\n    ),\n    \"noActiveDownloads\": MessageLookupByLibrary.simpleMessage(\n      \"No active downloads\",\n    ),\n    \"noAppToOpenFile\": MessageLookupByLibrary.simpleMessage(\n      \"No app found to open this file\",\n    ),\n    \"noBackupFound\": MessageLookupByLibrary.simpleMessage(\n      \"No backup file found\",\n    ),\n    \"noCompletedDownloads\": MessageLookupByLibrary.simpleMessage(\n      \"No completed downloads\",\n    ),\n    \"noPermissionToInstallApk\": MessageLookupByLibrary.simpleMessage(\n      \"No permission to install APK file, please enable install permission in settings\",\n    ),\n    \"noPermissionToInstallApkFile\": MessageLookupByLibrary.simpleMessage(\n      \"No permission to install APK file, please enable install permission in settings\",\n    ),\n    \"noPermissionToOpenFile\": MessageLookupByLibrary.simpleMessage(\n      \"No permission to open this file\",\n    ),\n    \"notificationClicked\": m29,\n    \"notificationManagerInitFailed\": m30,\n    \"notificationManagerInitialized\": MessageLookupByLibrary.simpleMessage(\n      \"Notification manager initialized successfully\",\n    ),\n    \"ok\": MessageLookupByLibrary.simpleMessage(\"OK\"),\n    \"open\": MessageLookupByLibrary.simpleMessage(\"Open\"),\n    \"openDirectory\": MessageLookupByLibrary.simpleMessage(\"Open Directory\"),\n    \"openDownloadDirectoryFailed\": m31,\n    \"openDownloadManager\": MessageLookupByLibrary.simpleMessage(\n      \"Open Download Manager\",\n    ),\n    \"openDownloadTestPage\": MessageLookupByLibrary.simpleMessage(\n      \"Do you want to open download test page?\",\n    ),\n    \"openFile\": MessageLookupByLibrary.simpleMessage(\"Open file\"),\n    \"openFileException\": m32,\n    \"openFileFailed\": m33,\n    \"openFileManager\": MessageLookupByLibrary.simpleMessage(\n      \"Open File Manager\",\n    ),\n    \"openFileManagerFailed\": m34,\n    \"openFileResult\": m35,\n    \"openListDownloadDirectory\": m36,\n    \"openSourceLicenses\": MessageLookupByLibrary.simpleMessage(\n      \"Open Source Licenses\",\n    ),\n    \"openlist\": MessageLookupByLibrary.simpleMessage(\"OpenList\"),\n    \"openlistMobile\": MessageLookupByLibrary.simpleMessage(\"OpenList Mobile\"),\n    \"parseFilenameFailed\": m37,\n    \"pending\": MessageLookupByLibrary.simpleMessage(\"Pending\"),\n    \"preparingDownload\": MessageLookupByLibrary.simpleMessage(\n      \"Preparing download...\",\n    ),\n    \"preparingDownloadStatus\": MessageLookupByLibrary.simpleMessage(\n      \"Preparing download...\",\n    ),\n    \"preview\": MessageLookupByLibrary.simpleMessage(\"Preview\"),\n    \"refresh\": MessageLookupByLibrary.simpleMessage(\"Refresh\"),\n    \"releasePage\": MessageLookupByLibrary.simpleMessage(\"Release Page\"),\n    \"restartingService\": MessageLookupByLibrary.simpleMessage(\n      \"Restarting OpenList service...\",\n    ),\n    \"restoreBackup\": MessageLookupByLibrary.simpleMessage(\"Restore Backup\"),\n    \"restoreBackupFailed\": m38,\n    \"save\": MessageLookupByLibrary.simpleMessage(\"Save\"),\n    \"saveAndRestart\": MessageLookupByLibrary.simpleMessage(\"Save and Restart\"),\n    \"saveFailed\": m39,\n    \"saveOnly\": MessageLookupByLibrary.simpleMessage(\"Save Only\"),\n    \"saved\": MessageLookupByLibrary.simpleMessage(\"Saved\"),\n    \"selectAppToOpen\": MessageLookupByLibrary.simpleMessage(\n      \"Select app to open\",\n    ),\n    \"selectDownloadMethod\": MessageLookupByLibrary.simpleMessage(\n      \"Select download method\",\n    ),\n    \"serviceRestartFailed\": MessageLookupByLibrary.simpleMessage(\n      \"Failed to restart service. Please restart manually.\",\n    ),\n    \"serviceRestartOnlyAndroid\": MessageLookupByLibrary.simpleMessage(\n      \"Service restart is only supported on Android\",\n    ),\n    \"serviceRestartSuccess\": MessageLookupByLibrary.simpleMessage(\n      \"Service restarted successfully\",\n    ),\n    \"setAdminPassword\": MessageLookupByLibrary.simpleMessage(\n      \"Set Admin password\",\n    ),\n    \"setDefaultDirectory\": MessageLookupByLibrary.simpleMessage(\n      \"Set as default directory?\",\n    ),\n    \"settings\": MessageLookupByLibrary.simpleMessage(\"Settings\"),\n    \"shareFeatureNotImplemented\": MessageLookupByLibrary.simpleMessage(\n      \"Share feature not implemented yet\",\n    ),\n    \"shareFile\": MessageLookupByLibrary.simpleMessage(\"Share file\"),\n    \"shareLink\": MessageLookupByLibrary.simpleMessage(\"Share Link\"),\n    \"shareLinkDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Share download link\",\n    ),\n    \"showDownloadCompleteNotificationFailed\": m40,\n    \"showDownloadProgressNotificationFailed\": m41,\n    \"showInFileManager\": MessageLookupByLibrary.simpleMessage(\n      \"Show in file manager\",\n    ),\n    \"showSingleFileCompleteNotificationFailed\": m42,\n    \"silentJumpApp\": MessageLookupByLibrary.simpleMessage(\"Silent jump app\"),\n    \"silentJumpAppDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Jump to other app without prompt\",\n    ),\n    \"simplifiedChinese\": MessageLookupByLibrary.simpleMessage(\"简体中文\"),\n    \"size\": MessageLookupByLibrary.simpleMessage(\"Size\"),\n    \"startDownload\": m43,\n    \"startDownloadFile\": m44,\n    \"startTime\": MessageLookupByLibrary.simpleMessage(\"Start time\"),\n    \"testDirectDownloadFunction\": MessageLookupByLibrary.simpleMessage(\n      \"Test direct download function\",\n    ),\n    \"testDownloadJsonFile\": MessageLookupByLibrary.simpleMessage(\n      \"Test download JSON file\",\n    ),\n    \"testDownloadLargeFile\": MessageLookupByLibrary.simpleMessage(\n      \"Test download large file (1MB)\",\n    ),\n    \"testDownloadPngImage\": MessageLookupByLibrary.simpleMessage(\n      \"Test download PNG image\",\n    ),\n    \"troubleshooting\": MessageLookupByLibrary.simpleMessage(\"Troubleshooting\"),\n    \"troubleshootingDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Common issues and solutions\",\n    ),\n    \"tryToOpenFile\": m45,\n    \"uiSettings\": MessageLookupByLibrary.simpleMessage(\"UI\"),\n    \"userCancelledDownload\": MessageLookupByLibrary.simpleMessage(\n      \"User cancelled download\",\n    ),\n    \"userCancelledDownloadError\": MessageLookupByLibrary.simpleMessage(\n      \"User cancelled download\",\n    ),\n    \"view\": MessageLookupByLibrary.simpleMessage(\"View\"),\n    \"viewDownloadDirectory\": MessageLookupByLibrary.simpleMessage(\n      \"View download directory\",\n    ),\n    \"viewDownloadFiles\": MessageLookupByLibrary.simpleMessage(\n      \"View download files\",\n    ),\n    \"viewDownloads\": MessageLookupByLibrary.simpleMessage(\"View Downloads\"),\n    \"viewLocation\": MessageLookupByLibrary.simpleMessage(\"View Location\"),\n    \"viewThirdPartyLicenses\": MessageLookupByLibrary.simpleMessage(\n      \"View third-party licenses\",\n    ),\n    \"wakeLock\": MessageLookupByLibrary.simpleMessage(\"Wake lock\"),\n    \"wakeLockDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Prevent CPU from sleeping when screen is off. (May cause app killed in background on some devices)\",\n    ),\n    \"webPage\": MessageLookupByLibrary.simpleMessage(\"Web Page\"),\n  };\n}\n"
  },
  {
    "path": "lib/generated/intl/messages_zh.dart",
    "content": "// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart\n// This is a library that provides messages for a zh locale. All the\n// messages from the main program should be duplicated here with the same\n// function name.\n\n// Ignore issues from commonly used lints in this file.\n// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new\n// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering\n// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases\n// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes\n// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes\n\nimport 'package:intl/intl.dart';\nimport 'package:intl/message_lookup_by_library.dart';\n\nfinal messages = new MessageLookup();\n\ntypedef String MessageIfAbsent(String messageStr, List<dynamic> args);\n\nclass MessageLookup extends MessageLookupByLibrary {\n  String get localeName => 'zh';\n\n  static String m0(error) => \"取消所有通知失败: ${error}\";\n\n  static String m1(error) => \"取消下载通知失败: ${error}\";\n\n  static String m2(error) => \"检查安装权限失败: ${error}\";\n\n  static String m3(error) => \"清理下载目录失败: ${error}\";\n\n  static String m4(filename) => \"确定要取消下载 \\\"${filename}\\\" 吗？\";\n\n  static String m5(filename) => \"确定要删除文件 \\\"${filename}\\\" 吗？此操作不可撤销。\";\n\n  static String m6(filename) => \"确定要删除 \\\"${filename}\\\" 的下载记录吗？\";\n\n  static String m7(error) => \"创建OpenList目录失败: ${error}\";\n\n  static String m8(path) => \"创建OpenList下载目录: ${path}\";\n\n  static String m9(count) => \"当前有 ${count} 个文件在下载\";\n\n  static String m10(error) => \"删除文件失败: ${error}\";\n\n  static String m11(url) => \"下载已取消: ${url}\";\n\n  static String m12(filename) => \"下载完成: ${filename}\";\n\n  static String m13(filename) => \"下载完成: ${filename}\";\n\n  static String m14(filename) => \"${filename} 下载完毕\";\n\n  static String m15(filename) => \"下载失败: ${filename}\";\n\n  static String m16(filename) => \"下载失败: ${filename}\";\n\n  static String m17(count) => \"下载管理(${count})\";\n\n  static String m18(progress) => \"下载进度: ${progress}%\";\n\n  static String m19(current, total) => \"正在下载第 ${current}/${total} 个文件\";\n\n  static String m20(filename) => \"已删除文件: ${filename}\";\n\n  static String m21(index) => \"第 ${index} 个文件下载失败\";\n\n  static String m22(size) => \"大小: ${size}\";\n\n  static String m23(time) => \"时间: ${time}\";\n\n  static String m24(error) => \"获取下载目录失败: ${error}\";\n\n  static String m25(error) => \"获取下载文件列表失败: ${error}\";\n\n  static String m26(line, error) => \"JSON格式错误,第${line}行:${error}\";\n\n  static String m27(error) => \"加载失败:${error}\";\n\n  static String m28(count) => \"${count} 个文件已完成，点击跳转到下载管理\";\n\n  static String m29(payload) => \"通知被点击: ${payload}\";\n\n  static String m30(error) => \"通知管理器初始化失败: ${error}\";\n\n  static String m31(error) => \"打开下载目录失败: ${error}\";\n\n  static String m32(error) => \"打开文件异常: ${error}\";\n\n  static String m33(error) => \"打开文件失败: ${error}\";\n\n  static String m34(error) => \"打开文件管理器失败: ${error}\";\n\n  static String m35(type, message) => \"打开文件结果: ${type} - ${message}\";\n\n  static String m36(path) => \"OpenList下载目录: ${path}\";\n\n  static String m37(error) => \"解析文件名失败: ${error}\";\n\n  static String m38(error) => \"恢复备份失败:${error}\";\n\n  static String m39(error) => \"保存失败:${error}\";\n\n  static String m40(error) => \"显示下载完成通知失败: ${error}\";\n\n  static String m41(error) => \"显示下载进度通知失败: ${error}\";\n\n  static String m42(error) => \"显示单个文件下载完成通知失败: ${error}\";\n\n  static String m43(filename) => \"开始下载: ${filename}\";\n\n  static String m44(filename) => \"开始下载: ${filename}\";\n\n  static String m45(path) => \"尝试打开文件: ${path}\";\n\n  final messages = _notInlinedMessages(_notInlinedMessages);\n  static Map<String, Function> _notInlinedMessages(_) => <String, Function>{\n    \"about\": MessageLookupByLibrary.simpleMessage(\"关于\"),\n    \"apkDownloadCompleteMessage\": MessageLookupByLibrary.simpleMessage(\n      \"APK文件已下载完成，是否要安装？\",\n    ),\n    \"appName\": MessageLookupByLibrary.simpleMessage(\"OpenList\"),\n    \"autoCheckForUpdates\": MessageLookupByLibrary.simpleMessage(\"自动检查更新\"),\n    \"autoCheckForUpdatesDesc\": MessageLookupByLibrary.simpleMessage(\n      \"启动时自动检查更新\",\n    ),\n    \"autoStartIssue\": MessageLookupByLibrary.simpleMessage(\"自启动相关说明\"),\n    \"autoStartIssueDesc\": MessageLookupByLibrary.simpleMessage(\n      \"设置自启动时建议把app的电池优化一并关闭，当前在开启自启动后，系统重启时服务会自动在后台启动，但可能不会在通知栏弹出通知。请放心，服务已正常运行，您可以在通知栏快捷开关查看服务状态，或回到主界面查看服务开关确认服务是否已启动。\",\n    ),\n    \"autoStartWebPage\": MessageLookupByLibrary.simpleMessage(\"将网页设置为打开首页\"),\n    \"autoStartWebPageDesc\": MessageLookupByLibrary.simpleMessage(\"打开主界面时的首页\"),\n    \"backupRestored\": MessageLookupByLibrary.simpleMessage(\"备份已恢复\"),\n    \"batchDownloadComplete\": MessageLookupByLibrary.simpleMessage(\"批量下载完成\"),\n    \"bootAutoStartService\": MessageLookupByLibrary.simpleMessage(\"开机自启动服务\"),\n    \"bootAutoStartServiceDesc\": MessageLookupByLibrary.simpleMessage(\n      \"在开机后自动启动OpenList服务。（请确保授予自启动权限）\",\n    ),\n    \"browserDownload\": MessageLookupByLibrary.simpleMessage(\"浏览器下载\"),\n    \"browserDownloadMethod\": MessageLookupByLibrary.simpleMessage(\"浏览器下载\"),\n    \"browserDownloadMethodDesc\": MessageLookupByLibrary.simpleMessage(\n      \"使用系统浏览器\",\n    ),\n    \"cancel\": MessageLookupByLibrary.simpleMessage(\"取消\"),\n    \"cancelAllNotificationsFailed\": m0,\n    \"cancelDownload\": MessageLookupByLibrary.simpleMessage(\"取消下载\"),\n    \"cancelDownloadNotificationFailed\": m1,\n    \"cancelled\": MessageLookupByLibrary.simpleMessage(\"已取消\"),\n    \"cannotGetBaseDownloadDirectory\": MessageLookupByLibrary.simpleMessage(\n      \"无法获取基础下载目录\",\n    ),\n    \"cannotGetDownloadDirectory\": MessageLookupByLibrary.simpleMessage(\n      \"无法获取下载目录\",\n    ),\n    \"cannotGetDownloadDirectoryError\": MessageLookupByLibrary.simpleMessage(\n      \"无法获取下载目录\",\n    ),\n    \"cannotInstallApkFile\": MessageLookupByLibrary.simpleMessage(\n      \"无法安装 APK 文件，可能需要在设置中开启\\\"允许安装未知来源应用\\\"\",\n    ),\n    \"cannotInstallApkNeedPermission\": MessageLookupByLibrary.simpleMessage(\n      \"无法安装 APK 文件，可能需要在设置中开启\\\"允许安装未知来源应用\\\"\",\n    ),\n    \"checkDownloadManagerForFiles\": MessageLookupByLibrary.simpleMessage(\n      \"请通过底部导航栏的\\\"下载管理\\\"查看下载文件\",\n    ),\n    \"checkForUpdates\": MessageLookupByLibrary.simpleMessage(\"检查更新\"),\n    \"checkImageInDownloadFolder\": MessageLookupByLibrary.simpleMessage(\n      \"请在下载目录查看图片\",\n    ),\n    \"checkInstallPermissionFailed\": m2,\n    \"clear\": MessageLookupByLibrary.simpleMessage(\"清空\"),\n    \"clearAll\": MessageLookupByLibrary.simpleMessage(\"清空所有\"),\n    \"clearDownloadDirectoryFailed\": m3,\n    \"clearFailed\": MessageLookupByLibrary.simpleMessage(\"清空失败\"),\n    \"clearRecords\": MessageLookupByLibrary.simpleMessage(\"清空记录\"),\n    \"cleared\": MessageLookupByLibrary.simpleMessage(\"已清空下载目录\"),\n    \"clickToJumpToDownloadManager\": MessageLookupByLibrary.simpleMessage(\n      \"点击跳转到下载管理\",\n    ),\n    \"completed\": MessageLookupByLibrary.simpleMessage(\"已完成\"),\n    \"completedTime\": MessageLookupByLibrary.simpleMessage(\"完成时间\"),\n    \"configSavedRestartRequired\": MessageLookupByLibrary.simpleMessage(\n      \"配置已保存,请重启OpenList服务以生效\",\n    ),\n    \"confirm\": MessageLookupByLibrary.simpleMessage(\"确认\"),\n    \"confirmCancelDownload\": m4,\n    \"confirmClear\": MessageLookupByLibrary.simpleMessage(\"确认清空\"),\n    \"confirmClearAllFiles\": MessageLookupByLibrary.simpleMessage(\n      \"确定要清空所有下载文件吗？此操作不可撤销。\",\n    ),\n    \"confirmDelete\": MessageLookupByLibrary.simpleMessage(\"确认删除\"),\n    \"confirmDeleteFile\": m5,\n    \"confirmDeleteRecord\": m6,\n    \"confirmDownload\": MessageLookupByLibrary.simpleMessage(\"确认下载\"),\n    \"confirmDownloadMessage\": MessageLookupByLibrary.simpleMessage(\n      \"是否要下载这个文件？\",\n    ),\n    \"confirmSaveConfigMessage\": MessageLookupByLibrary.simpleMessage(\n      \"修改配置可能导致服务不可用,确定保存吗?\",\n    ),\n    \"confirmSaveConfigTitle\": MessageLookupByLibrary.simpleMessage(\"确认保存\"),\n    \"continueDownload\": MessageLookupByLibrary.simpleMessage(\"继续下载\"),\n    \"copiedToClipboard\": MessageLookupByLibrary.simpleMessage(\"已复制到剪贴板\"),\n    \"createOpenListDirectoryFailed\": m7,\n    \"createOpenListDownloadDirectory\": m8,\n    \"currentDownloadingFiles\": m9,\n    \"currentIsLatestVersion\": MessageLookupByLibrary.simpleMessage(\"已经是最新版本\"),\n    \"currentlyDownloading\": MessageLookupByLibrary.simpleMessage(\"正在下载\"),\n    \"dataDirectory\": MessageLookupByLibrary.simpleMessage(\"data 文件夹路径\"),\n    \"databaseNotSavedIssue\": MessageLookupByLibrary.simpleMessage(\"数据库未保存问题\"),\n    \"databaseNotSavedIssueDesc\": MessageLookupByLibrary.simpleMessage(\n      \"如不手动关闭OpenList，则数据库可能不会被保存到对应的db文件中，如遇到此问题，请手动关闭以解决此问题。（开关位于主程序菜单OpenList界面，以及通知栏的通知上）\",\n    ),\n    \"delete\": MessageLookupByLibrary.simpleMessage(\"删除\"),\n    \"deleteFailed\": MessageLookupByLibrary.simpleMessage(\"删除失败\"),\n    \"deleteFile\": MessageLookupByLibrary.simpleMessage(\"删除文件\"),\n    \"deleteFileFailedLog\": m10,\n    \"deleteRecord\": MessageLookupByLibrary.simpleMessage(\"删除记录\"),\n    \"description\": MessageLookupByLibrary.simpleMessage(\"说明：\"),\n    \"desktopShortcut\": MessageLookupByLibrary.simpleMessage(\"桌面快捷方式\"),\n    \"directDownload\": MessageLookupByLibrary.simpleMessage(\"直接下载\"),\n    \"directDownloadApk\": MessageLookupByLibrary.simpleMessage(\"直接下载APK\"),\n    \"directDownloadMethod\": MessageLookupByLibrary.simpleMessage(\"直接下载\"),\n    \"directDownloadMethodDesc\": MessageLookupByLibrary.simpleMessage(\n      \"使用应用内下载器\",\n    ),\n    \"download\": MessageLookupByLibrary.simpleMessage(\"下载\"),\n    \"downloadApk\": MessageLookupByLibrary.simpleMessage(\"下载APK\"),\n    \"downloadCancelled\": m11,\n    \"downloadCancelledStatus\": MessageLookupByLibrary.simpleMessage(\"下载已取消\"),\n    \"downloadCancelledText\": MessageLookupByLibrary.simpleMessage(\"下载已取消\"),\n    \"downloadComplete\": m12,\n    \"downloadCompleteChannel\": MessageLookupByLibrary.simpleMessage(\"下载完成\"),\n    \"downloadCompleteChannelDesc\": MessageLookupByLibrary.simpleMessage(\n      \"文件下载完成通知\",\n    ),\n    \"downloadCompleteFile\": m13,\n    \"downloadCompleteNotificationTitle\": m14,\n    \"downloadCompleteTitle\": MessageLookupByLibrary.simpleMessage(\"下载完成\"),\n    \"downloadDirectory\": MessageLookupByLibrary.simpleMessage(\"下载目录\"),\n    \"downloadDirectoryCleared\": MessageLookupByLibrary.simpleMessage(\"已清理下载目录\"),\n    \"downloadDirectoryOpened\": MessageLookupByLibrary.simpleMessage(\"已打开下载目录\"),\n    \"downloadDirectoryPathUnknown\": MessageLookupByLibrary.simpleMessage(\n      \"下载目录路径未知\",\n    ),\n    \"downloadFailed\": MessageLookupByLibrary.simpleMessage(\"下载失败\"),\n    \"downloadFailedFile\": m15,\n    \"downloadFailedWithError\": m16,\n    \"downloadFunctionTest\": MessageLookupByLibrary.simpleMessage(\"下载功能测试\"),\n    \"downloadInstructions\": MessageLookupByLibrary.simpleMessage(\n      \"• 文件将下载到系统下载目录\\\\n• 下载过程会显示进度通知\\\\n• 下载完成后可以选择打开文件\\\\n• 如果文件名重复会自动添加序号\\\\n• 请通过底部导航栏的\\\\\\\"下载管理\\\\\\\"查看下载文件\",\n    ),\n    \"downloadManager\": MessageLookupByLibrary.simpleMessage(\"下载管理\"),\n    \"downloadManagerWithCount\": m17,\n    \"downloadProgress\": m18,\n    \"downloadProgressChannel\": MessageLookupByLibrary.simpleMessage(\"下载进度\"),\n    \"downloadProgressDesc\": MessageLookupByLibrary.simpleMessage(\"显示文件下载进度\"),\n    \"downloadRecordsCleared\": MessageLookupByLibrary.simpleMessage(\"已清空下载记录\"),\n    \"downloadThisFile\": MessageLookupByLibrary.simpleMessage(\"下载此文件吗？\"),\n    \"downloading\": MessageLookupByLibrary.simpleMessage(\"下载中\"),\n    \"downloadingFileProgress\": m19,\n    \"downloadingImage\": MessageLookupByLibrary.simpleMessage(\"正在下载图片...\"),\n    \"edit\": MessageLookupByLibrary.simpleMessage(\"编辑\"),\n    \"editOpenListConfig\": MessageLookupByLibrary.simpleMessage(\n      \"修改OpenList配置文件\",\n    ),\n    \"english\": MessageLookupByLibrary.simpleMessage(\"English\"),\n    \"failed\": MessageLookupByLibrary.simpleMessage(\"失败\"),\n    \"fileDeleted\": MessageLookupByLibrary.simpleMessage(\"文件已删除\"),\n    \"fileDeletedLog\": m20,\n    \"fileDownloadFailed\": m21,\n    \"fileInfo\": MessageLookupByLibrary.simpleMessage(\"文件信息\"),\n    \"fileLocation\": MessageLookupByLibrary.simpleMessage(\"文件位置\"),\n    \"fileLocationTip\": MessageLookupByLibrary.simpleMessage(\n      \"您可以使用文件管理器找到此文件，或者尝试安装相应的应用来打开它。\",\n    ),\n    \"fileManagerOpened\": MessageLookupByLibrary.simpleMessage(\"已打开文件管理器\"),\n    \"fileName\": MessageLookupByLibrary.simpleMessage(\"文件名\"),\n    \"fileNotFound\": MessageLookupByLibrary.simpleMessage(\"文件不存在或已被删除\"),\n    \"fileNotFoundWillCreateOnSave\": MessageLookupByLibrary.simpleMessage(\n      \"文件不存在,保存时将创建\",\n    ),\n    \"filePath\": MessageLookupByLibrary.simpleMessage(\"路径\"),\n    \"filePermissionDenied\": MessageLookupByLibrary.simpleMessage(\n      \"文件权限被拒绝,请检查应用权限\",\n    ),\n    \"fileSavedTo\": MessageLookupByLibrary.simpleMessage(\"文件已保存到:\"),\n    \"fileSize\": m22,\n    \"fileTime\": m23,\n    \"findApkInDownloadFolder\": MessageLookupByLibrary.simpleMessage(\n      \"请在下载目录找到APK文件进行安装\",\n    ),\n    \"followSystem\": MessageLookupByLibrary.simpleMessage(\"跟随系统\"),\n    \"general\": MessageLookupByLibrary.simpleMessage(\"通用\"),\n    \"getDownloadDirectoryFailed\": m24,\n    \"getDownloadFileListFailed\": m25,\n    \"getDownloadPathFailed\": MessageLookupByLibrary.simpleMessage(\"获取失败\"),\n    \"goTo\": MessageLookupByLibrary.simpleMessage(\"前往\"),\n    \"goToSettings\": MessageLookupByLibrary.simpleMessage(\"去设置\"),\n    \"grantManagerStoragePermission\": MessageLookupByLibrary.simpleMessage(\n      \"申请【所有文件访问权限】\",\n    ),\n    \"grantNotificationPermission\": MessageLookupByLibrary.simpleMessage(\n      \"申请【通知权限】\",\n    ),\n    \"grantNotificationPermissionDesc\": MessageLookupByLibrary.simpleMessage(\n      \"用于前台服务保活\",\n    ),\n    \"grantStoragePermission\": MessageLookupByLibrary.simpleMessage(\n      \"申请【读写外置存储权限】\",\n    ),\n    \"grantStoragePermissionDesc\": MessageLookupByLibrary.simpleMessage(\n      \"挂载本地存储时必须授予，否则无权限读写文件\",\n    ),\n    \"imageDownloadSuccess\": MessageLookupByLibrary.simpleMessage(\"图片下载成功\"),\n    \"importantSettings\": MessageLookupByLibrary.simpleMessage(\"重要\"),\n    \"inProgress\": MessageLookupByLibrary.simpleMessage(\"进行中\"),\n    \"initializingNotificationManager\": MessageLookupByLibrary.simpleMessage(\n      \"初始化通知管理器\",\n    ),\n    \"installNow\": MessageLookupByLibrary.simpleMessage(\"立即安装\"),\n    \"invalidJsonFormat\": m26,\n    \"jumpToOtherApp\": MessageLookupByLibrary.simpleMessage(\"跳转到其他APP ？\"),\n    \"language\": MessageLookupByLibrary.simpleMessage(\"语言\"),\n    \"languageSettings\": MessageLookupByLibrary.simpleMessage(\"语言设置\"),\n    \"languageSettingsDesc\": MessageLookupByLibrary.simpleMessage(\"选择应用显示语言\"),\n    \"laterInstall\": MessageLookupByLibrary.simpleMessage(\"稍后安装\"),\n    \"loadDownloadFilesFailed\": MessageLookupByLibrary.simpleMessage(\"加载下载文件失败\"),\n    \"loadFailed\": m27,\n    \"modifiedTime\": MessageLookupByLibrary.simpleMessage(\"修改时间\"),\n    \"modifyAdminPassword\": MessageLookupByLibrary.simpleMessage(\"修改Admin密码\"),\n    \"moreOptions\": MessageLookupByLibrary.simpleMessage(\"更多选项\"),\n    \"multipleFilesCompleted\": m28,\n    \"needInstallPermission\": MessageLookupByLibrary.simpleMessage(\"需要安装权限\"),\n    \"needInstallPermissionDesc\": MessageLookupByLibrary.simpleMessage(\n      \"为了安装 APK 文件，需要授予安装权限。请在设置中手动开启。\",\n    ),\n    \"needInstallPermissionToInstallApk\": MessageLookupByLibrary.simpleMessage(\n      \"需要安装权限才能安装 APK 文件\",\n    ),\n    \"newVersionFound\": MessageLookupByLibrary.simpleMessage(\"发现新版本\"),\n    \"noActiveDownloads\": MessageLookupByLibrary.simpleMessage(\"暂无进行中的下载\"),\n    \"noAppToOpenFile\": MessageLookupByLibrary.simpleMessage(\"没有找到可以打开此文件的应用\"),\n    \"noBackupFound\": MessageLookupByLibrary.simpleMessage(\"未找到备份文件\"),\n    \"noCompletedDownloads\": MessageLookupByLibrary.simpleMessage(\"暂无已完成的下载\"),\n    \"noPermissionToInstallApk\": MessageLookupByLibrary.simpleMessage(\n      \"没有权限安装 APK 文件，请在设置中开启安装权限\",\n    ),\n    \"noPermissionToInstallApkFile\": MessageLookupByLibrary.simpleMessage(\n      \"没有权限安装 APK 文件，请在设置中开启安装权限\",\n    ),\n    \"noPermissionToOpenFile\": MessageLookupByLibrary.simpleMessage(\"没有权限打开此文件\"),\n    \"notificationClicked\": m29,\n    \"notificationManagerInitFailed\": m30,\n    \"notificationManagerInitialized\": MessageLookupByLibrary.simpleMessage(\n      \"通知管理器初始化成功\",\n    ),\n    \"ok\": MessageLookupByLibrary.simpleMessage(\"确定\"),\n    \"open\": MessageLookupByLibrary.simpleMessage(\"打开\"),\n    \"openDirectory\": MessageLookupByLibrary.simpleMessage(\"打开目录\"),\n    \"openDownloadDirectoryFailed\": m31,\n    \"openDownloadManager\": MessageLookupByLibrary.simpleMessage(\"打开下载管理\"),\n    \"openDownloadTestPage\": MessageLookupByLibrary.simpleMessage(\n      \"是否要打开下载测试页面？\",\n    ),\n    \"openFile\": MessageLookupByLibrary.simpleMessage(\"打开文件\"),\n    \"openFileException\": m32,\n    \"openFileFailed\": m33,\n    \"openFileManager\": MessageLookupByLibrary.simpleMessage(\"打开文件管理器\"),\n    \"openFileManagerFailed\": m34,\n    \"openFileResult\": m35,\n    \"openListDownloadDirectory\": m36,\n    \"openSourceLicenses\": MessageLookupByLibrary.simpleMessage(\"开源许可证\"),\n    \"openlist\": MessageLookupByLibrary.simpleMessage(\"OpenList\"),\n    \"openlistMobile\": MessageLookupByLibrary.simpleMessage(\"OpenList Mobile\"),\n    \"parseFilenameFailed\": m37,\n    \"pending\": MessageLookupByLibrary.simpleMessage(\"等待中\"),\n    \"preparingDownload\": MessageLookupByLibrary.simpleMessage(\"准备下载...\"),\n    \"preparingDownloadStatus\": MessageLookupByLibrary.simpleMessage(\"准备下载...\"),\n    \"preview\": MessageLookupByLibrary.simpleMessage(\"预览\"),\n    \"refresh\": MessageLookupByLibrary.simpleMessage(\"刷新\"),\n    \"releasePage\": MessageLookupByLibrary.simpleMessage(\"发布页面\"),\n    \"restartingService\": MessageLookupByLibrary.simpleMessage(\n      \"正在重启OpenList服务...\",\n    ),\n    \"restoreBackup\": MessageLookupByLibrary.simpleMessage(\"恢复备份\"),\n    \"restoreBackupFailed\": m38,\n    \"save\": MessageLookupByLibrary.simpleMessage(\"保存\"),\n    \"saveAndRestart\": MessageLookupByLibrary.simpleMessage(\"保存并重启\"),\n    \"saveFailed\": m39,\n    \"saveOnly\": MessageLookupByLibrary.simpleMessage(\"仅保存\"),\n    \"saved\": MessageLookupByLibrary.simpleMessage(\"已保存\"),\n    \"selectAppToOpen\": MessageLookupByLibrary.simpleMessage(\"选择应用打开\"),\n    \"selectDownloadMethod\": MessageLookupByLibrary.simpleMessage(\"选择下载方式\"),\n    \"serviceRestartFailed\": MessageLookupByLibrary.simpleMessage(\n      \"服务重启失败,请手动重启\",\n    ),\n    \"serviceRestartOnlyAndroid\": MessageLookupByLibrary.simpleMessage(\n      \"服务重启仅支持Android系统\",\n    ),\n    \"serviceRestartSuccess\": MessageLookupByLibrary.simpleMessage(\"服务重启成功\"),\n    \"setAdminPassword\": MessageLookupByLibrary.simpleMessage(\"设置Admin密码\"),\n    \"setDefaultDirectory\": MessageLookupByLibrary.simpleMessage(\"是否设为初始目录？\"),\n    \"settings\": MessageLookupByLibrary.simpleMessage(\"设置\"),\n    \"shareFeatureNotImplemented\": MessageLookupByLibrary.simpleMessage(\n      \"分享功能待实现\",\n    ),\n    \"shareFile\": MessageLookupByLibrary.simpleMessage(\"分享文件\"),\n    \"shareLink\": MessageLookupByLibrary.simpleMessage(\"分享链接\"),\n    \"shareLinkDesc\": MessageLookupByLibrary.simpleMessage(\"分享下载链接\"),\n    \"showDownloadCompleteNotificationFailed\": m40,\n    \"showDownloadProgressNotificationFailed\": m41,\n    \"showInFileManager\": MessageLookupByLibrary.simpleMessage(\"在文件管理器中显示\"),\n    \"showSingleFileCompleteNotificationFailed\": m42,\n    \"silentJumpApp\": MessageLookupByLibrary.simpleMessage(\"静默跳转APP\"),\n    \"silentJumpAppDesc\": MessageLookupByLibrary.simpleMessage(\"跳转APP时，不弹出提示框\"),\n    \"simplifiedChinese\": MessageLookupByLibrary.simpleMessage(\"简体中文\"),\n    \"size\": MessageLookupByLibrary.simpleMessage(\"大小\"),\n    \"startDownload\": m43,\n    \"startDownloadFile\": m44,\n    \"startTime\": MessageLookupByLibrary.simpleMessage(\"开始时间\"),\n    \"testDirectDownloadFunction\": MessageLookupByLibrary.simpleMessage(\n      \"测试直接下载功能\",\n    ),\n    \"testDownloadJsonFile\": MessageLookupByLibrary.simpleMessage(\"测试下载JSON文件\"),\n    \"testDownloadLargeFile\": MessageLookupByLibrary.simpleMessage(\n      \"测试下载大文件(1MB)\",\n    ),\n    \"testDownloadPngImage\": MessageLookupByLibrary.simpleMessage(\"测试下载PNG图片\"),\n    \"troubleshooting\": MessageLookupByLibrary.simpleMessage(\"疑难解答\"),\n    \"troubleshootingDesc\": MessageLookupByLibrary.simpleMessage(\"常见问题与解决方案\"),\n    \"tryToOpenFile\": m45,\n    \"uiSettings\": MessageLookupByLibrary.simpleMessage(\"界面\"),\n    \"userCancelledDownload\": MessageLookupByLibrary.simpleMessage(\"用户取消下载\"),\n    \"userCancelledDownloadError\": MessageLookupByLibrary.simpleMessage(\n      \"用户取消下载\",\n    ),\n    \"view\": MessageLookupByLibrary.simpleMessage(\"查看\"),\n    \"viewDownloadDirectory\": MessageLookupByLibrary.simpleMessage(\"查看下载目录\"),\n    \"viewDownloadFiles\": MessageLookupByLibrary.simpleMessage(\"查看下载文件\"),\n    \"viewDownloads\": MessageLookupByLibrary.simpleMessage(\"查看下载\"),\n    \"viewLocation\": MessageLookupByLibrary.simpleMessage(\"查看位置\"),\n    \"viewThirdPartyLicenses\": MessageLookupByLibrary.simpleMessage(\"查看第三方许可证\"),\n    \"wakeLock\": MessageLookupByLibrary.simpleMessage(\"唤醒锁\"),\n    \"wakeLockDesc\": MessageLookupByLibrary.simpleMessage(\n      \"开启防止锁屏后CPU休眠，保持进程在后台运行。（部分系统可能导致杀后台）\",\n    ),\n    \"webPage\": MessageLookupByLibrary.simpleMessage(\"网页\"),\n  };\n}\n"
  },
  {
    "path": "lib/generated/l10n.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\nimport 'package:flutter/material.dart';\nimport 'package:intl/intl.dart';\nimport 'intl/messages_all.dart';\n\n// **************************************************************************\n// Generator: Flutter Intl IDE plugin\n// Made by Localizely\n// **************************************************************************\n\n// ignore_for_file: non_constant_identifier_names, lines_longer_than_80_chars\n// ignore_for_file: join_return_with_assignment, prefer_final_in_for_each\n// ignore_for_file: avoid_redundant_argument_values, avoid_escaping_inner_quotes\n\nclass S {\n  S();\n\n  static S? _current;\n\n  static S get current {\n    assert(\n      _current != null,\n      'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.',\n    );\n    return _current!;\n  }\n\n  static const AppLocalizationDelegate delegate = AppLocalizationDelegate();\n\n  static Future<S> load(Locale locale) {\n    final name = (locale.countryCode?.isEmpty ?? false)\n        ? locale.languageCode\n        : locale.toString();\n    final localeName = Intl.canonicalizedLocale(name);\n    return initializeMessages(localeName).then((_) {\n      Intl.defaultLocale = localeName;\n      final instance = S();\n      S._current = instance;\n\n      return instance;\n    });\n  }\n\n  static S of(BuildContext context) {\n    final instance = S.maybeOf(context);\n    assert(\n      instance != null,\n      'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?',\n    );\n    return instance!;\n  }\n\n  static S? maybeOf(BuildContext context) {\n    return Localizations.of<S>(context, S);\n  }\n\n  /// `OpenList`\n  String get appName {\n    return Intl.message('OpenList', name: 'appName', desc: '', args: []);\n  }\n\n  /// `桌面快捷方式`\n  String get desktopShortcut {\n    return Intl.message('桌面快捷方式', name: 'desktopShortcut', desc: '', args: []);\n  }\n\n  /// `设置Admin密码`\n  String get setAdminPassword {\n    return Intl.message(\n      '设置Admin密码',\n      name: 'setAdminPassword',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `更多选项`\n  String get moreOptions {\n    return Intl.message('更多选项', name: 'moreOptions', desc: '', args: []);\n  }\n\n  /// `检查更新`\n  String get checkForUpdates {\n    return Intl.message('检查更新', name: 'checkForUpdates', desc: '', args: []);\n  }\n\n  /// `已经是最新版本`\n  String get currentIsLatestVersion {\n    return Intl.message(\n      '已经是最新版本',\n      name: 'currentIsLatestVersion',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `确认`\n  String get confirm {\n    return Intl.message('确认', name: 'confirm', desc: '', args: []);\n  }\n\n  /// `取消`\n  String get cancel {\n    return Intl.message('取消', name: 'cancel', desc: '', args: []);\n  }\n\n  /// `发布页面`\n  String get releasePage {\n    return Intl.message('发布页面', name: 'releasePage', desc: '', args: []);\n  }\n\n  /// `下载APK`\n  String get downloadApk {\n    return Intl.message('下载APK', name: 'downloadApk', desc: '', args: []);\n  }\n\n  /// `关于`\n  String get about {\n    return Intl.message('关于', name: 'about', desc: '', args: []);\n  }\n\n  /// `通用`\n  String get general {\n    return Intl.message('通用', name: 'general', desc: '', args: []);\n  }\n\n  /// `自动检查更新`\n  String get autoCheckForUpdates {\n    return Intl.message(\n      '自动检查更新',\n      name: 'autoCheckForUpdates',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `启动时自动检查更新`\n  String get autoCheckForUpdatesDesc {\n    return Intl.message(\n      '启动时自动检查更新',\n      name: 'autoCheckForUpdatesDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `唤醒锁`\n  String get wakeLock {\n    return Intl.message('唤醒锁', name: 'wakeLock', desc: '', args: []);\n  }\n\n  /// `开启防止锁屏后CPU休眠，保持进程在后台运行。（部分系统可能导致杀后台）`\n  String get wakeLockDesc {\n    return Intl.message(\n      '开启防止锁屏后CPU休眠，保持进程在后台运行。（部分系统可能导致杀后台）',\n      name: 'wakeLockDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `开机自启动服务`\n  String get bootAutoStartService {\n    return Intl.message(\n      '开机自启动服务',\n      name: 'bootAutoStartService',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `在开机后自动启动OpenList服务。（请确保授予自启动权限）`\n  String get bootAutoStartServiceDesc {\n    return Intl.message(\n      '在开机后自动启动OpenList服务。（请确保授予自启动权限）',\n      name: 'bootAutoStartServiceDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `网页`\n  String get webPage {\n    return Intl.message('网页', name: 'webPage', desc: '', args: []);\n  }\n\n  /// `设置`\n  String get settings {\n    return Intl.message('设置', name: 'settings', desc: '', args: []);\n  }\n\n  /// `选择应用打开`\n  String get selectAppToOpen {\n    return Intl.message('选择应用打开', name: 'selectAppToOpen', desc: '', args: []);\n  }\n\n  /// `前往`\n  String get goTo {\n    return Intl.message('前往', name: 'goTo', desc: '', args: []);\n  }\n\n  /// `下载此文件吗？`\n  String get downloadThisFile {\n    return Intl.message(\n      '下载此文件吗？',\n      name: 'downloadThisFile',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `下载`\n  String get download {\n    return Intl.message('下载', name: 'download', desc: '', args: []);\n  }\n\n  /// `已复制到剪贴板`\n  String get copiedToClipboard {\n    return Intl.message(\n      '已复制到剪贴板',\n      name: 'copiedToClipboard',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `重要`\n  String get importantSettings {\n    return Intl.message('重要', name: 'importantSettings', desc: '', args: []);\n  }\n\n  /// `界面`\n  String get uiSettings {\n    return Intl.message('界面', name: 'uiSettings', desc: '', args: []);\n  }\n\n  /// `申请【所有文件访问权限】`\n  String get grantManagerStoragePermission {\n    return Intl.message(\n      '申请【所有文件访问权限】',\n      name: 'grantManagerStoragePermission',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `挂载本地存储时必须授予，否则无权限读写文件`\n  String get grantStoragePermissionDesc {\n    return Intl.message(\n      '挂载本地存储时必须授予，否则无权限读写文件',\n      name: 'grantStoragePermissionDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `申请【读写外置存储权限】`\n  String get grantStoragePermission {\n    return Intl.message(\n      '申请【读写外置存储权限】',\n      name: 'grantStoragePermission',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `申请【通知权限】`\n  String get grantNotificationPermission {\n    return Intl.message(\n      '申请【通知权限】',\n      name: 'grantNotificationPermission',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `用于前台服务保活`\n  String get grantNotificationPermissionDesc {\n    return Intl.message(\n      '用于前台服务保活',\n      name: 'grantNotificationPermissionDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `将网页设置为打开首页`\n  String get autoStartWebPage {\n    return Intl.message(\n      '将网页设置为打开首页',\n      name: 'autoStartWebPage',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `跳转到其他APP ？`\n  String get jumpToOtherApp {\n    return Intl.message(\n      '跳转到其他APP ？',\n      name: 'jumpToOtherApp',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `打开主界面时的首页`\n  String get autoStartWebPageDesc {\n    return Intl.message(\n      '打开主界面时的首页',\n      name: 'autoStartWebPageDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `data 文件夹路径`\n  String get dataDirectory {\n    return Intl.message(\n      'data 文件夹路径',\n      name: 'dataDirectory',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `是否设为初始目录？`\n  String get setDefaultDirectory {\n    return Intl.message(\n      '是否设为初始目录？',\n      name: 'setDefaultDirectory',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `静默跳转APP`\n  String get silentJumpApp {\n    return Intl.message('静默跳转APP', name: 'silentJumpApp', desc: '', args: []);\n  }\n\n  /// `跳转APP时，不弹出提示框`\n  String get silentJumpAppDesc {\n    return Intl.message(\n      '跳转APP时，不弹出提示框',\n      name: 'silentJumpAppDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `发现新版本`\n  String get newVersionFound {\n    return Intl.message('发现新版本', name: 'newVersionFound', desc: '', args: []);\n  }\n\n  /// `直接下载APK`\n  String get directDownloadApk {\n    return Intl.message(\n      '直接下载APK',\n      name: 'directDownloadApk',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `初始化通知管理器`\n  String get initializingNotificationManager {\n    return Intl.message(\n      '初始化通知管理器',\n      name: 'initializingNotificationManager',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `下载管理`\n  String get downloadManager {\n    return Intl.message('下载管理', name: 'downloadManager', desc: '', args: []);\n  }\n\n  /// `下载管理({count})`\n  String downloadManagerWithCount(int count) {\n    return Intl.message(\n      '下载管理($count)',\n      name: 'downloadManagerWithCount',\n      desc: '',\n      args: [count],\n    );\n  }\n\n  /// `修改Admin密码`\n  String get modifyAdminPassword {\n    return Intl.message(\n      '修改Admin密码',\n      name: 'modifyAdminPassword',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `直接下载`\n  String get directDownload {\n    return Intl.message('直接下载', name: 'directDownload', desc: '', args: []);\n  }\n\n  /// `浏览器下载`\n  String get browserDownload {\n    return Intl.message('浏览器下载', name: 'browserDownload', desc: '', args: []);\n  }\n\n  /// `加载下载文件失败`\n  String get loadDownloadFilesFailed {\n    return Intl.message(\n      '加载下载文件失败',\n      name: 'loadDownloadFilesFailed',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `暂无进行中的下载`\n  String get noActiveDownloads {\n    return Intl.message(\n      '暂无进行中的下载',\n      name: 'noActiveDownloads',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `下载失败`\n  String get downloadFailed {\n    return Intl.message('下载失败', name: 'downloadFailed', desc: '', args: []);\n  }\n\n  /// `开始时间`\n  String get startTime {\n    return Intl.message('开始时间', name: 'startTime', desc: '', args: []);\n  }\n\n  /// `暂无已完成的下载`\n  String get noCompletedDownloads {\n    return Intl.message(\n      '暂无已完成的下载',\n      name: 'noCompletedDownloads',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `完成时间`\n  String get completedTime {\n    return Intl.message('完成时间', name: 'completedTime', desc: '', args: []);\n  }\n\n  /// `大小`\n  String get size {\n    return Intl.message('大小', name: 'size', desc: '', args: []);\n  }\n\n  /// `打开文件`\n  String get openFile {\n    return Intl.message('打开文件', name: 'openFile', desc: '', args: []);\n  }\n\n  /// `删除记录`\n  String get deleteRecord {\n    return Intl.message('删除记录', name: 'deleteRecord', desc: '', args: []);\n  }\n\n  /// `删除文件`\n  String get deleteFile {\n    return Intl.message('删除文件', name: 'deleteFile', desc: '', args: []);\n  }\n\n  /// `确认清空`\n  String get confirmClear {\n    return Intl.message('确认清空', name: 'confirmClear', desc: '', args: []);\n  }\n\n  /// `确定要清空所有下载文件吗？此操作不可撤销。`\n  String get confirmClearAllFiles {\n    return Intl.message(\n      '确定要清空所有下载文件吗？此操作不可撤销。',\n      name: 'confirmClearAllFiles',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `已清空下载目录`\n  String get cleared {\n    return Intl.message('已清空下载目录', name: 'cleared', desc: '', args: []);\n  }\n\n  /// `清空失败`\n  String get clearFailed {\n    return Intl.message('清空失败', name: 'clearFailed', desc: '', args: []);\n  }\n\n  /// `取消下载`\n  String get cancelDownload {\n    return Intl.message('取消下载', name: 'cancelDownload', desc: '', args: []);\n  }\n\n  /// `确定要取消下载 \"{filename}\" 吗？`\n  String confirmCancelDownload(String filename) {\n    return Intl.message(\n      '确定要取消下载 \"$filename\" 吗？',\n      name: 'confirmCancelDownload',\n      desc: '',\n      args: [filename],\n    );\n  }\n\n  /// `继续下载`\n  String get continueDownload {\n    return Intl.message('继续下载', name: 'continueDownload', desc: '', args: []);\n  }\n\n  /// `确定要删除 \"{filename}\" 的下载记录吗？`\n  String confirmDeleteRecord(String filename) {\n    return Intl.message(\n      '确定要删除 \"$filename\" 的下载记录吗？',\n      name: 'confirmDeleteRecord',\n      desc: '',\n      args: [filename],\n    );\n  }\n\n  /// `删除`\n  String get delete {\n    return Intl.message('删除', name: 'delete', desc: '', args: []);\n  }\n\n  /// `分享文件`\n  String get shareFile {\n    return Intl.message('分享文件', name: 'shareFile', desc: '', args: []);\n  }\n\n  /// `分享功能待实现`\n  String get shareFeatureNotImplemented {\n    return Intl.message(\n      '分享功能待实现',\n      name: 'shareFeatureNotImplemented',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `文件信息`\n  String get fileInfo {\n    return Intl.message('文件信息', name: 'fileInfo', desc: '', args: []);\n  }\n\n  /// `文件名`\n  String get fileName {\n    return Intl.message('文件名', name: 'fileName', desc: '', args: []);\n  }\n\n  /// `修改时间`\n  String get modifiedTime {\n    return Intl.message('修改时间', name: 'modifiedTime', desc: '', args: []);\n  }\n\n  /// `路径`\n  String get filePath {\n    return Intl.message('路径', name: 'filePath', desc: '', args: []);\n  }\n\n  /// `确认删除`\n  String get confirmDelete {\n    return Intl.message('确认删除', name: 'confirmDelete', desc: '', args: []);\n  }\n\n  /// `确定要删除文件 \"{filename}\" 吗？此操作不可撤销。`\n  String confirmDeleteFile(String filename) {\n    return Intl.message(\n      '确定要删除文件 \"$filename\" 吗？此操作不可撤销。',\n      name: 'confirmDeleteFile',\n      desc: '',\n      args: [filename],\n    );\n  }\n\n  /// `文件已删除`\n  String get fileDeleted {\n    return Intl.message('文件已删除', name: 'fileDeleted', desc: '', args: []);\n  }\n\n  /// `删除失败`\n  String get deleteFailed {\n    return Intl.message('删除失败', name: 'deleteFailed', desc: '', args: []);\n  }\n\n  /// `清空`\n  String get clear {\n    return Intl.message('清空', name: 'clear', desc: '', args: []);\n  }\n\n  /// `没有找到可以打开此文件的应用`\n  String get noAppToOpenFile {\n    return Intl.message(\n      '没有找到可以打开此文件的应用',\n      name: 'noAppToOpenFile',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `查看位置`\n  String get viewLocation {\n    return Intl.message('查看位置', name: 'viewLocation', desc: '', args: []);\n  }\n\n  /// `文件不存在或已被删除`\n  String get fileNotFound {\n    return Intl.message('文件不存在或已被删除', name: 'fileNotFound', desc: '', args: []);\n  }\n\n  /// `没有权限打开此文件`\n  String get noPermissionToOpenFile {\n    return Intl.message(\n      '没有权限打开此文件',\n      name: 'noPermissionToOpenFile',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `打开文件失败: {error}`\n  String openFileFailed(String error) {\n    return Intl.message(\n      '打开文件失败: $error',\n      name: 'openFileFailed',\n      desc: '',\n      args: [error],\n    );\n  }\n\n  /// `文件位置`\n  String get fileLocation {\n    return Intl.message('文件位置', name: 'fileLocation', desc: '', args: []);\n  }\n\n  /// `文件已保存到:`\n  String get fileSavedTo {\n    return Intl.message('文件已保存到:', name: 'fileSavedTo', desc: '', args: []);\n  }\n\n  /// `您可以使用文件管理器找到此文件，或者尝试安装相应的应用来打开它。`\n  String get fileLocationTip {\n    return Intl.message(\n      '您可以使用文件管理器找到此文件，或者尝试安装相应的应用来打开它。',\n      name: 'fileLocationTip',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `下载目录`\n  String get downloadDirectory {\n    return Intl.message('下载目录', name: 'downloadDirectory', desc: '', args: []);\n  }\n\n  /// `打开目录`\n  String get openDirectory {\n    return Intl.message('打开目录', name: 'openDirectory', desc: '', args: []);\n  }\n\n  /// `清空记录`\n  String get clearRecords {\n    return Intl.message('清空记录', name: 'clearRecords', desc: '', args: []);\n  }\n\n  /// `清空所有`\n  String get clearAll {\n    return Intl.message('清空所有', name: 'clearAll', desc: '', args: []);\n  }\n\n  /// `已清空下载记录`\n  String get downloadRecordsCleared {\n    return Intl.message(\n      '已清空下载记录',\n      name: 'downloadRecordsCleared',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `进行中`\n  String get inProgress {\n    return Intl.message('进行中', name: 'inProgress', desc: '', args: []);\n  }\n\n  /// `已完成`\n  String get completed {\n    return Intl.message('已完成', name: 'completed', desc: '', args: []);\n  }\n\n  /// `刷新`\n  String get refresh {\n    return Intl.message('刷新', name: 'refresh', desc: '', args: []);\n  }\n\n  /// `等待中`\n  String get pending {\n    return Intl.message('等待中', name: 'pending', desc: '', args: []);\n  }\n\n  /// `下载中`\n  String get downloading {\n    return Intl.message('下载中', name: 'downloading', desc: '', args: []);\n  }\n\n  /// `失败`\n  String get failed {\n    return Intl.message('失败', name: 'failed', desc: '', args: []);\n  }\n\n  /// `已取消`\n  String get cancelled {\n    return Intl.message('已取消', name: 'cancelled', desc: '', args: []);\n  }\n\n  /// `无法获取下载目录`\n  String get cannotGetDownloadDirectory {\n    return Intl.message(\n      '无法获取下载目录',\n      name: 'cannotGetDownloadDirectory',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `开始下载: {filename}`\n  String startDownload(String filename) {\n    return Intl.message(\n      '开始下载: $filename',\n      name: 'startDownload',\n      desc: '',\n      args: [filename],\n    );\n  }\n\n  /// `下载进度: {progress}%`\n  String downloadProgress(String progress) {\n    return Intl.message(\n      '下载进度: $progress%',\n      name: 'downloadProgress',\n      desc: '',\n      args: [progress],\n    );\n  }\n\n  /// `下载完成: {filename}`\n  String downloadComplete(String filename) {\n    return Intl.message(\n      '下载完成: $filename',\n      name: 'downloadComplete',\n      desc: '',\n      args: [filename],\n    );\n  }\n\n  /// `打开`\n  String get open {\n    return Intl.message('打开', name: 'open', desc: '', args: []);\n  }\n\n  /// `下载已取消: {url}`\n  String downloadCancelled(String url) {\n    return Intl.message(\n      '下载已取消: $url',\n      name: 'downloadCancelled',\n      desc: '',\n      args: [url],\n    );\n  }\n\n  /// `下载失败: {filename}`\n  String downloadFailedWithError(String filename) {\n    return Intl.message(\n      '下载失败: $filename',\n      name: 'downloadFailedWithError',\n      desc: '',\n      args: [filename],\n    );\n  }\n\n  /// `用户取消下载`\n  String get userCancelledDownload {\n    return Intl.message(\n      '用户取消下载',\n      name: 'userCancelledDownload',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `无法获取基础下载目录`\n  String get cannotGetBaseDownloadDirectory {\n    return Intl.message(\n      '无法获取基础下载目录',\n      name: 'cannotGetBaseDownloadDirectory',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `创建OpenList下载目录: {path}`\n  String createOpenListDownloadDirectory(String path) {\n    return Intl.message(\n      '创建OpenList下载目录: $path',\n      name: 'createOpenListDownloadDirectory',\n      desc: '',\n      args: [path],\n    );\n  }\n\n  /// `创建OpenList目录失败: {error}`\n  String createOpenListDirectoryFailed(String error) {\n    return Intl.message(\n      '创建OpenList目录失败: $error',\n      name: 'createOpenListDirectoryFailed',\n      desc: '',\n      args: [error],\n    );\n  }\n\n  /// `OpenList下载目录: {path}`\n  String openListDownloadDirectory(String path) {\n    return Intl.message(\n      'OpenList下载目录: $path',\n      name: 'openListDownloadDirectory',\n      desc: '',\n      args: [path],\n    );\n  }\n\n  /// `获取下载目录失败: {error}`\n  String getDownloadDirectoryFailed(String error) {\n    return Intl.message(\n      '获取下载目录失败: $error',\n      name: 'getDownloadDirectoryFailed',\n      desc: '',\n      args: [error],\n    );\n  }\n\n  /// `解析文件名失败: {error}`\n  String parseFilenameFailed(String error) {\n    return Intl.message(\n      '解析文件名失败: $error',\n      name: 'parseFilenameFailed',\n      desc: '',\n      args: [error],\n    );\n  }\n\n  /// `需要安装权限`\n  String get needInstallPermission {\n    return Intl.message(\n      '需要安装权限',\n      name: 'needInstallPermission',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `为了安装 APK 文件，需要授予安装权限。请在设置中手动开启。`\n  String get needInstallPermissionDesc {\n    return Intl.message(\n      '为了安装 APK 文件，需要授予安装权限。请在设置中手动开启。',\n      name: 'needInstallPermissionDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `去设置`\n  String get goToSettings {\n    return Intl.message('去设置', name: 'goToSettings', desc: '', args: []);\n  }\n\n  /// `需要安装权限才能安装 APK 文件`\n  String get needInstallPermissionToInstallApk {\n    return Intl.message(\n      '需要安装权限才能安装 APK 文件',\n      name: 'needInstallPermissionToInstallApk',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `检查安装权限失败: {error}`\n  String checkInstallPermissionFailed(String error) {\n    return Intl.message(\n      '检查安装权限失败: $error',\n      name: 'checkInstallPermissionFailed',\n      desc: '',\n      args: [error],\n    );\n  }\n\n  /// `尝试打开文件: {path}`\n  String tryToOpenFile(String path) {\n    return Intl.message(\n      '尝试打开文件: $path',\n      name: 'tryToOpenFile',\n      desc: '',\n      args: [path],\n    );\n  }\n\n  /// `打开文件结果: {type} - {message}`\n  String openFileResult(String type, String message) {\n    return Intl.message(\n      '打开文件结果: $type - $message',\n      name: 'openFileResult',\n      desc: '',\n      args: [type, message],\n    );\n  }\n\n  /// `无法安装 APK 文件，可能需要在设置中开启\"允许安装未知来源应用\"`\n  String get cannotInstallApkFile {\n    return Intl.message(\n      '无法安装 APK 文件，可能需要在设置中开启\"允许安装未知来源应用\"',\n      name: 'cannotInstallApkFile',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `没有权限安装 APK 文件，请在设置中开启安装权限`\n  String get noPermissionToInstallApk {\n    return Intl.message(\n      '没有权限安装 APK 文件，请在设置中开启安装权限',\n      name: 'noPermissionToInstallApk',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `打开文件异常: {error}`\n  String openFileException(String error) {\n    return Intl.message(\n      '打开文件异常: $error',\n      name: 'openFileException',\n      desc: '',\n      args: [error],\n    );\n  }\n\n  /// `获取下载文件列表失败: {error}`\n  String getDownloadFileListFailed(String error) {\n    return Intl.message(\n      '获取下载文件列表失败: $error',\n      name: 'getDownloadFileListFailed',\n      desc: '',\n      args: [error],\n    );\n  }\n\n  /// `已清理下载目录`\n  String get downloadDirectoryCleared {\n    return Intl.message(\n      '已清理下载目录',\n      name: 'downloadDirectoryCleared',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `清理下载目录失败: {error}`\n  String clearDownloadDirectoryFailed(String error) {\n    return Intl.message(\n      '清理下载目录失败: $error',\n      name: 'clearDownloadDirectoryFailed',\n      desc: '',\n      args: [error],\n    );\n  }\n\n  /// `已删除文件: {filename}`\n  String fileDeletedLog(String filename) {\n    return Intl.message(\n      '已删除文件: $filename',\n      name: 'fileDeletedLog',\n      desc: '',\n      args: [filename],\n    );\n  }\n\n  /// `删除文件失败: {error}`\n  String deleteFileFailedLog(String error) {\n    return Intl.message(\n      '删除文件失败: $error',\n      name: 'deleteFileFailedLog',\n      desc: '',\n      args: [error],\n    );\n  }\n\n  /// `准备下载...`\n  String get preparingDownload {\n    return Intl.message(\n      '准备下载...',\n      name: 'preparingDownload',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `下载已取消`\n  String get downloadCancelledStatus {\n    return Intl.message(\n      '下载已取消',\n      name: 'downloadCancelledStatus',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `通知管理器初始化成功`\n  String get notificationManagerInitialized {\n    return Intl.message(\n      '通知管理器初始化成功',\n      name: 'notificationManagerInitialized',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `通知管理器初始化失败: {error}`\n  String notificationManagerInitFailed(String error) {\n    return Intl.message(\n      '通知管理器初始化失败: $error',\n      name: 'notificationManagerInitFailed',\n      desc: '',\n      args: [error],\n    );\n  }\n\n  /// `通知被点击: {payload}`\n  String notificationClicked(String payload) {\n    return Intl.message(\n      '通知被点击: $payload',\n      name: 'notificationClicked',\n      desc: '',\n      args: [payload],\n    );\n  }\n\n  /// `当前有 {count} 个文件在下载`\n  String currentDownloadingFiles(int count) {\n    return Intl.message(\n      '当前有 $count 个文件在下载',\n      name: 'currentDownloadingFiles',\n      desc: '',\n      args: [count],\n    );\n  }\n\n  /// `显示文件下载进度`\n  String get downloadProgressDesc {\n    return Intl.message(\n      '显示文件下载进度',\n      name: 'downloadProgressDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `查看下载`\n  String get viewDownloads {\n    return Intl.message('查看下载', name: 'viewDownloads', desc: '', args: []);\n  }\n\n  /// `显示下载进度通知失败: {error}`\n  String showDownloadProgressNotificationFailed(String error) {\n    return Intl.message(\n      '显示下载进度通知失败: $error',\n      name: 'showDownloadProgressNotificationFailed',\n      desc: '',\n      args: [error],\n    );\n  }\n\n  /// `{filename} 下载完毕`\n  String downloadCompleteNotificationTitle(String filename) {\n    return Intl.message(\n      '$filename 下载完毕',\n      name: 'downloadCompleteNotificationTitle',\n      desc: '',\n      args: [filename],\n    );\n  }\n\n  /// `点击跳转到下载管理`\n  String get clickToJumpToDownloadManager {\n    return Intl.message(\n      '点击跳转到下载管理',\n      name: 'clickToJumpToDownloadManager',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `下载完成`\n  String get downloadCompleteTitle {\n    return Intl.message(\n      '下载完成',\n      name: 'downloadCompleteTitle',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `{count} 个文件已完成，点击跳转到下载管理`\n  String multipleFilesCompleted(int count) {\n    return Intl.message(\n      '$count 个文件已完成，点击跳转到下载管理',\n      name: 'multipleFilesCompleted',\n      desc: '',\n      args: [count],\n    );\n  }\n\n  /// `下载完成`\n  String get downloadCompleteChannel {\n    return Intl.message(\n      '下载完成',\n      name: 'downloadCompleteChannel',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `文件下载完成通知`\n  String get downloadCompleteChannelDesc {\n    return Intl.message(\n      '文件下载完成通知',\n      name: 'downloadCompleteChannelDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `打开下载管理`\n  String get openDownloadManager {\n    return Intl.message(\n      '打开下载管理',\n      name: 'openDownloadManager',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `显示下载完成通知失败: {error}`\n  String showDownloadCompleteNotificationFailed(String error) {\n    return Intl.message(\n      '显示下载完成通知失败: $error',\n      name: 'showDownloadCompleteNotificationFailed',\n      desc: '',\n      args: [error],\n    );\n  }\n\n  /// `显示单个文件下载完成通知失败: {error}`\n  String showSingleFileCompleteNotificationFailed(String error) {\n    return Intl.message(\n      '显示单个文件下载完成通知失败: $error',\n      name: 'showSingleFileCompleteNotificationFailed',\n      desc: '',\n      args: [error],\n    );\n  }\n\n  /// `取消下载通知失败: {error}`\n  String cancelDownloadNotificationFailed(String error) {\n    return Intl.message(\n      '取消下载通知失败: $error',\n      name: 'cancelDownloadNotificationFailed',\n      desc: '',\n      args: [error],\n    );\n  }\n\n  /// `取消所有通知失败: {error}`\n  String cancelAllNotificationsFailed(String error) {\n    return Intl.message(\n      '取消所有通知失败: $error',\n      name: 'cancelAllNotificationsFailed',\n      desc: '',\n      args: [error],\n    );\n  }\n\n  /// `在文件管理器中显示`\n  String get showInFileManager {\n    return Intl.message(\n      '在文件管理器中显示',\n      name: 'showInFileManager',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `大小: {size}`\n  String fileSize(String size) {\n    return Intl.message('大小: $size', name: 'fileSize', desc: '', args: [size]);\n  }\n\n  /// `时间: {time}`\n  String fileTime(String time) {\n    return Intl.message('时间: $time', name: 'fileTime', desc: '', args: [time]);\n  }\n\n  /// `确定`\n  String get ok {\n    return Intl.message('确定', name: 'ok', desc: '', args: []);\n  }\n\n  /// `打开文件管理器`\n  String get openFileManager {\n    return Intl.message('打开文件管理器', name: 'openFileManager', desc: '', args: []);\n  }\n\n  /// `已打开文件管理器`\n  String get fileManagerOpened {\n    return Intl.message(\n      '已打开文件管理器',\n      name: 'fileManagerOpened',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `打开文件管理器失败: {error}`\n  String openFileManagerFailed(String error) {\n    return Intl.message(\n      '打开文件管理器失败: $error',\n      name: 'openFileManagerFailed',\n      desc: '',\n      args: [error],\n    );\n  }\n\n  /// `已打开下载目录`\n  String get downloadDirectoryOpened {\n    return Intl.message(\n      '已打开下载目录',\n      name: 'downloadDirectoryOpened',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `打开下载目录失败: {error}`\n  String openDownloadDirectoryFailed(String error) {\n    return Intl.message(\n      '打开下载目录失败: $error',\n      name: 'openDownloadDirectoryFailed',\n      desc: '',\n      args: [error],\n    );\n  }\n\n  /// `下载目录路径未知`\n  String get downloadDirectoryPathUnknown {\n    return Intl.message(\n      '下载目录路径未知',\n      name: 'downloadDirectoryPathUnknown',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `无法获取下载目录`\n  String get cannotGetDownloadDirectoryError {\n    return Intl.message(\n      '无法获取下载目录',\n      name: 'cannotGetDownloadDirectoryError',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `开始下载: {filename}`\n  String startDownloadFile(String filename) {\n    return Intl.message(\n      '开始下载: $filename',\n      name: 'startDownloadFile',\n      desc: '',\n      args: [filename],\n    );\n  }\n\n  /// `下载完成: {filename}`\n  String downloadCompleteFile(String filename) {\n    return Intl.message(\n      '下载完成: $filename',\n      name: 'downloadCompleteFile',\n      desc: '',\n      args: [filename],\n    );\n  }\n\n  /// `下载失败: {filename}`\n  String downloadFailedFile(String filename) {\n    return Intl.message(\n      '下载失败: $filename',\n      name: 'downloadFailedFile',\n      desc: '',\n      args: [filename],\n    );\n  }\n\n  /// `用户取消下载`\n  String get userCancelledDownloadError {\n    return Intl.message(\n      '用户取消下载',\n      name: 'userCancelledDownloadError',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `无法安装 APK 文件，可能需要在设置中开启\"允许安装未知来源应用\"`\n  String get cannotInstallApkNeedPermission {\n    return Intl.message(\n      '无法安装 APK 文件，可能需要在设置中开启\"允许安装未知来源应用\"',\n      name: 'cannotInstallApkNeedPermission',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `没有权限安装 APK 文件，请在设置中开启安装权限`\n  String get noPermissionToInstallApkFile {\n    return Intl.message(\n      '没有权限安装 APK 文件，请在设置中开启安装权限',\n      name: 'noPermissionToInstallApkFile',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `准备下载...`\n  String get preparingDownloadStatus {\n    return Intl.message(\n      '准备下载...',\n      name: 'preparingDownloadStatus',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `下载已取消`\n  String get downloadCancelledText {\n    return Intl.message(\n      '下载已取消',\n      name: 'downloadCancelledText',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `语言`\n  String get language {\n    return Intl.message('语言', name: 'language', desc: '', args: []);\n  }\n\n  /// `语言设置`\n  String get languageSettings {\n    return Intl.message('语言设置', name: 'languageSettings', desc: '', args: []);\n  }\n\n  /// `选择应用显示语言`\n  String get languageSettingsDesc {\n    return Intl.message(\n      '选择应用显示语言',\n      name: 'languageSettingsDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `跟随系统`\n  String get followSystem {\n    return Intl.message('跟随系统', name: 'followSystem', desc: '', args: []);\n  }\n\n  /// `简体中文`\n  String get simplifiedChinese {\n    return Intl.message('简体中文', name: 'simplifiedChinese', desc: '', args: []);\n  }\n\n  /// `English`\n  String get english {\n    return Intl.message('English', name: 'english', desc: '', args: []);\n  }\n\n  /// `疑难解答`\n  String get troubleshooting {\n    return Intl.message('疑难解答', name: 'troubleshooting', desc: '', args: []);\n  }\n\n  /// `常见问题与解决方案`\n  String get troubleshootingDesc {\n    return Intl.message(\n      '常见问题与解决方案',\n      name: 'troubleshootingDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `数据库未保存问题`\n  String get databaseNotSavedIssue {\n    return Intl.message(\n      '数据库未保存问题',\n      name: 'databaseNotSavedIssue',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `如不手动关闭OpenList，则数据库可能不会被保存到对应的db文件中，如遇到此问题，请手动关闭以解决此问题。（开关位于主程序菜单OpenList界面，以及通知栏的通知上）`\n  String get databaseNotSavedIssueDesc {\n    return Intl.message(\n      '如不手动关闭OpenList，则数据库可能不会被保存到对应的db文件中，如遇到此问题，请手动关闭以解决此问题。（开关位于主程序菜单OpenList界面，以及通知栏的通知上）',\n      name: 'databaseNotSavedIssueDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `自启动相关说明`\n  String get autoStartIssue {\n    return Intl.message('自启动相关说明', name: 'autoStartIssue', desc: '', args: []);\n  }\n\n  /// `设置自启动时建议把app的电池优化一并关闭，当前在开启自启动后，系统重启时服务会自动在后台启动，但可能不会在通知栏弹出通知。请放心，服务已正常运行，您可以在通知栏快捷开关查看服务状态，或回到主界面查看服务开关确认服务是否已启动。`\n  String get autoStartIssueDesc {\n    return Intl.message(\n      '设置自启动时建议把app的电池优化一并关闭，当前在开启自启动后，系统重启时服务会自动在后台启动，但可能不会在通知栏弹出通知。请放心，服务已正常运行，您可以在通知栏快捷开关查看服务状态，或回到主界面查看服务开关确认服务是否已启动。',\n      name: 'autoStartIssueDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `正在下载`\n  String get currentlyDownloading {\n    return Intl.message(\n      '正在下载',\n      name: 'currentlyDownloading',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `下载进度`\n  String get downloadProgressChannel {\n    return Intl.message(\n      '下载进度',\n      name: 'downloadProgressChannel',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `确认下载`\n  String get confirmDownload {\n    return Intl.message('确认下载', name: 'confirmDownload', desc: '', args: []);\n  }\n\n  /// `是否要下载这个文件？`\n  String get confirmDownloadMessage {\n    return Intl.message(\n      '是否要下载这个文件？',\n      name: 'confirmDownloadMessage',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `正在下载图片...`\n  String get downloadingImage {\n    return Intl.message(\n      '正在下载图片...',\n      name: 'downloadingImage',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `稍后安装`\n  String get laterInstall {\n    return Intl.message('稍后安装', name: 'laterInstall', desc: '', args: []);\n  }\n\n  /// `立即安装`\n  String get installNow {\n    return Intl.message('立即安装', name: 'installNow', desc: '', args: []);\n  }\n\n  /// `直接下载`\n  String get directDownloadMethod {\n    return Intl.message(\n      '直接下载',\n      name: 'directDownloadMethod',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `使用应用内下载器`\n  String get directDownloadMethodDesc {\n    return Intl.message(\n      '使用应用内下载器',\n      name: 'directDownloadMethodDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `浏览器下载`\n  String get browserDownloadMethod {\n    return Intl.message(\n      '浏览器下载',\n      name: 'browserDownloadMethod',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `使用系统浏览器`\n  String get browserDownloadMethodDesc {\n    return Intl.message(\n      '使用系统浏览器',\n      name: 'browserDownloadMethodDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `分享链接`\n  String get shareLink {\n    return Intl.message('分享链接', name: 'shareLink', desc: '', args: []);\n  }\n\n  /// `分享下载链接`\n  String get shareLinkDesc {\n    return Intl.message('分享下载链接', name: 'shareLinkDesc', desc: '', args: []);\n  }\n\n  /// `查看`\n  String get view {\n    return Intl.message('查看', name: 'view', desc: '', args: []);\n  }\n\n  /// `下载功能测试`\n  String get downloadFunctionTest {\n    return Intl.message(\n      '下载功能测试',\n      name: 'downloadFunctionTest',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `测试直接下载功能`\n  String get testDirectDownloadFunction {\n    return Intl.message(\n      '测试直接下载功能',\n      name: 'testDirectDownloadFunction',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `测试下载JSON文件`\n  String get testDownloadJsonFile {\n    return Intl.message(\n      '测试下载JSON文件',\n      name: 'testDownloadJsonFile',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `测试下载PNG图片`\n  String get testDownloadPngImage {\n    return Intl.message(\n      '测试下载PNG图片',\n      name: 'testDownloadPngImage',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `测试下载大文件(1MB)`\n  String get testDownloadLargeFile {\n    return Intl.message(\n      '测试下载大文件(1MB)',\n      name: 'testDownloadLargeFile',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `查看下载文件`\n  String get viewDownloadFiles {\n    return Intl.message(\n      '查看下载文件',\n      name: 'viewDownloadFiles',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `请通过底部导航栏的\"下载管理\"查看下载文件`\n  String get checkDownloadManagerForFiles {\n    return Intl.message(\n      '请通过底部导航栏的\"下载管理\"查看下载文件',\n      name: 'checkDownloadManagerForFiles',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `查看下载目录`\n  String get viewDownloadDirectory {\n    return Intl.message(\n      '查看下载目录',\n      name: 'viewDownloadDirectory',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `获取失败`\n  String get getDownloadPathFailed {\n    return Intl.message(\n      '获取失败',\n      name: 'getDownloadPathFailed',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `是否要打开下载测试页面？`\n  String get openDownloadTestPage {\n    return Intl.message(\n      '是否要打开下载测试页面？',\n      name: 'openDownloadTestPage',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `说明：`\n  String get description {\n    return Intl.message('说明：', name: 'description', desc: '', args: []);\n  }\n\n  /// `• 文件将下载到系统下载目录\\n• 下载过程会显示进度通知\\n• 下载完成后可以选择打开文件\\n• 如果文件名重复会自动添加序号\\n• 请通过底部导航栏的\\\"下载管理\\\"查看下载文件`\n  String get downloadInstructions {\n    return Intl.message(\n      '• 文件将下载到系统下载目录\\\\n• 下载过程会显示进度通知\\\\n• 下载完成后可以选择打开文件\\\\n• 如果文件名重复会自动添加序号\\\\n• 请通过底部导航栏的\\\\\"下载管理\\\\\"查看下载文件',\n      name: 'downloadInstructions',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `选择下载方式`\n  String get selectDownloadMethod {\n    return Intl.message(\n      '选择下载方式',\n      name: 'selectDownloadMethod',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `批量下载完成`\n  String get batchDownloadComplete {\n    return Intl.message(\n      '批量下载完成',\n      name: 'batchDownloadComplete',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `图片下载成功`\n  String get imageDownloadSuccess {\n    return Intl.message(\n      '图片下载成功',\n      name: 'imageDownloadSuccess',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `请在下载目录查看图片`\n  String get checkImageInDownloadFolder {\n    return Intl.message(\n      '请在下载目录查看图片',\n      name: 'checkImageInDownloadFolder',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `请在下载目录找到APK文件进行安装`\n  String get findApkInDownloadFolder {\n    return Intl.message(\n      '请在下载目录找到APK文件进行安装',\n      name: 'findApkInDownloadFolder',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `正在下载第 {current}/{total} 个文件`\n  String downloadingFileProgress(int current, int total) {\n    return Intl.message(\n      '正在下载第 $current/$total 个文件',\n      name: 'downloadingFileProgress',\n      desc: '',\n      args: [current, total],\n    );\n  }\n\n  /// `第 {index} 个文件下载失败`\n  String fileDownloadFailed(int index) {\n    return Intl.message(\n      '第 $index 个文件下载失败',\n      name: 'fileDownloadFailed',\n      desc: '',\n      args: [index],\n    );\n  }\n\n  /// `APK文件已下载完成，是否要安装？`\n  String get apkDownloadCompleteMessage {\n    return Intl.message(\n      'APK文件已下载完成，是否要安装？',\n      name: 'apkDownloadCompleteMessage',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `OpenList`\n  String get openlist {\n    return Intl.message('OpenList', name: 'openlist', desc: '', args: []);\n  }\n\n  /// `OpenList Mobile`\n  String get openlistMobile {\n    return Intl.message(\n      'OpenList Mobile',\n      name: 'openlistMobile',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `开源许可证`\n  String get openSourceLicenses {\n    return Intl.message(\n      '开源许可证',\n      name: 'openSourceLicenses',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `查看第三方许可证`\n  String get viewThirdPartyLicenses {\n    return Intl.message(\n      '查看第三方许可证',\n      name: 'viewThirdPartyLicenses',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `修改OpenList配置文件`\n  String get editOpenListConfig {\n    return Intl.message(\n      '修改OpenList配置文件',\n      name: 'editOpenListConfig',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `保存`\n  String get save {\n    return Intl.message('保存', name: 'save', desc: '', args: []);\n  }\n\n  /// `已保存`\n  String get saved {\n    return Intl.message('已保存', name: 'saved', desc: '', args: []);\n  }\n\n  /// `编辑`\n  String get edit {\n    return Intl.message('编辑', name: 'edit', desc: '', args: []);\n  }\n\n  /// `预览`\n  String get preview {\n    return Intl.message('预览', name: 'preview', desc: '', args: []);\n  }\n\n  /// `文件不存在,保存时将创建`\n  String get fileNotFoundWillCreateOnSave {\n    return Intl.message(\n      '文件不存在,保存时将创建',\n      name: 'fileNotFoundWillCreateOnSave',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `加载失败:{error}`\n  String loadFailed(String error) {\n    return Intl.message(\n      '加载失败:$error',\n      name: 'loadFailed',\n      desc: '',\n      args: [error],\n    );\n  }\n\n  /// `保存失败:{error}`\n  String saveFailed(String error) {\n    return Intl.message(\n      '保存失败:$error',\n      name: 'saveFailed',\n      desc: '',\n      args: [error],\n    );\n  }\n\n  /// `JSON格式错误,第{line}行:{error}`\n  String invalidJsonFormat(int line, String error) {\n    return Intl.message(\n      'JSON格式错误,第$line行:$error',\n      name: 'invalidJsonFormat',\n      desc: '',\n      args: [line, error],\n    );\n  }\n\n  /// `文件权限被拒绝,请检查应用权限`\n  String get filePermissionDenied {\n    return Intl.message(\n      '文件权限被拒绝,请检查应用权限',\n      name: 'filePermissionDenied',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `配置已保存,请重启OpenList服务以生效`\n  String get configSavedRestartRequired {\n    return Intl.message(\n      '配置已保存,请重启OpenList服务以生效',\n      name: 'configSavedRestartRequired',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `确认保存`\n  String get confirmSaveConfigTitle {\n    return Intl.message(\n      '确认保存',\n      name: 'confirmSaveConfigTitle',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `修改配置可能导致服务不可用,确定保存吗?`\n  String get confirmSaveConfigMessage {\n    return Intl.message(\n      '修改配置可能导致服务不可用,确定保存吗?',\n      name: 'confirmSaveConfigMessage',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `保存并重启`\n  String get saveAndRestart {\n    return Intl.message('保存并重启', name: 'saveAndRestart', desc: '', args: []);\n  }\n\n  /// `仅保存`\n  String get saveOnly {\n    return Intl.message('仅保存', name: 'saveOnly', desc: '', args: []);\n  }\n\n  /// `恢复备份`\n  String get restoreBackup {\n    return Intl.message('恢复备份', name: 'restoreBackup', desc: '', args: []);\n  }\n\n  /// `备份已恢复`\n  String get backupRestored {\n    return Intl.message('备份已恢复', name: 'backupRestored', desc: '', args: []);\n  }\n\n  /// `未找到备份文件`\n  String get noBackupFound {\n    return Intl.message('未找到备份文件', name: 'noBackupFound', desc: '', args: []);\n  }\n\n  /// `恢复备份失败:{error}`\n  String restoreBackupFailed(String error) {\n    return Intl.message(\n      '恢复备份失败:$error',\n      name: 'restoreBackupFailed',\n      desc: '',\n      args: [error],\n    );\n  }\n\n  /// `正在重启OpenList服务...`\n  String get restartingService {\n    return Intl.message(\n      '正在重启OpenList服务...',\n      name: 'restartingService',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `服务重启成功`\n  String get serviceRestartSuccess {\n    return Intl.message(\n      '服务重启成功',\n      name: 'serviceRestartSuccess',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `服务重启失败,请手动重启`\n  String get serviceRestartFailed {\n    return Intl.message(\n      '服务重启失败,请手动重启',\n      name: 'serviceRestartFailed',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `服务重启仅支持Android系统`\n  String get serviceRestartOnlyAndroid {\n    return Intl.message(\n      '服务重启仅支持Android系统',\n      name: 'serviceRestartOnlyAndroid',\n      desc: '',\n      args: [],\n    );\n  }\n}\n\nclass AppLocalizationDelegate extends LocalizationsDelegate<S> {\n  const AppLocalizationDelegate();\n\n  List<Locale> get supportedLocales {\n    return const <Locale>[\n      Locale.fromSubtags(languageCode: 'zh'),\n      Locale.fromSubtags(languageCode: 'en'),\n    ];\n  }\n\n  @override\n  bool isSupported(Locale locale) => _isSupported(locale);\n  @override\n  Future<S> load(Locale locale) => S.load(locale);\n  @override\n  bool shouldReload(AppLocalizationDelegate old) => false;\n\n  bool _isSupported(Locale locale) {\n    for (var supportedLocale in supportedLocales) {\n      if (supportedLocale.languageCode == locale.languageCode) {\n        return true;\n      }\n    }\n    return false;\n  }\n}\n"
  },
  {
    "path": "lib/generated_api.dart",
    "content": "// Autogenerated from Pigeon (v26.0.2), do not edit directly.\n// See also: https://pub.dev/packages/pigeon\n// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers\n\nimport 'dart:async';\nimport 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;\n\nimport 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;\nimport 'package:flutter/services.dart';\n\nPlatformException _createConnectionError(String channelName) {\n  return PlatformException(\n    code: 'channel-error',\n    message: 'Unable to establish connection on channel: \"$channelName\".',\n  );\n}\n\nList<Object?> wrapResponse({Object? result, PlatformException? error, bool empty = false}) {\n  if (empty) {\n    return <Object?>[];\n  }\n  if (error == null) {\n    return <Object?>[result];\n  }\n  return <Object?>[error.code, error.message, error.details];\n}\n\n\nclass _PigeonCodec extends StandardMessageCodec {\n  const _PigeonCodec();\n  @override\n  void writeValue(WriteBuffer buffer, Object? value) {\n    if (value is int) {\n      buffer.putUint8(4);\n      buffer.putInt64(value);\n    } else {\n      super.writeValue(buffer, value);\n    }\n  }\n\n  @override\n  Object? readValueOfType(int type, ReadBuffer buffer) {\n    switch (type) {\n      default:\n        return super.readValueOfType(type, buffer);\n    }\n  }\n}\n\nclass AppConfig {\n  /// Constructor for [AppConfig].  The [binaryMessenger] named argument is\n  /// available for dependency injection.  If it is left null, the default\n  /// BinaryMessenger will be used which routes to the host platform.\n  AppConfig({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''})\n      : pigeonVar_binaryMessenger = binaryMessenger,\n        pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';\n  final BinaryMessenger? pigeonVar_binaryMessenger;\n\n  static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec();\n\n  final String pigeonVar_messageChannelSuffix;\n\n  Future<bool> isWakeLockEnabled() async {\n    final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.isWakeLockEnabled$pigeonVar_messageChannelSuffix';\n    final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(\n      pigeonVar_channelName,\n      pigeonChannelCodec,\n      binaryMessenger: pigeonVar_binaryMessenger,\n    );\n    final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);\n    final List<Object?>? pigeonVar_replyList =\n        await pigeonVar_sendFuture as List<Object?>?;\n    if (pigeonVar_replyList == null) {\n      throw _createConnectionError(pigeonVar_channelName);\n    } else if (pigeonVar_replyList.length > 1) {\n      throw PlatformException(\n        code: pigeonVar_replyList[0]! as String,\n        message: pigeonVar_replyList[1] as String?,\n        details: pigeonVar_replyList[2],\n      );\n    } else if (pigeonVar_replyList[0] == null) {\n      throw PlatformException(\n        code: 'null-error',\n        message: 'Host platform returned null value for non-null return value.',\n      );\n    } else {\n      return (pigeonVar_replyList[0] as bool?)!;\n    }\n  }\n\n  Future<void> setWakeLockEnabled(bool enabled) async {\n    final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.setWakeLockEnabled$pigeonVar_messageChannelSuffix';\n    final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(\n      pigeonVar_channelName,\n      pigeonChannelCodec,\n      binaryMessenger: pigeonVar_binaryMessenger,\n    );\n    final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[enabled]);\n    final List<Object?>? pigeonVar_replyList =\n        await pigeonVar_sendFuture as List<Object?>?;\n    if (pigeonVar_replyList == null) {\n      throw _createConnectionError(pigeonVar_channelName);\n    } else if (pigeonVar_replyList.length > 1) {\n      throw PlatformException(\n        code: pigeonVar_replyList[0]! as String,\n        message: pigeonVar_replyList[1] as String?,\n        details: pigeonVar_replyList[2],\n      );\n    } else {\n      return;\n    }\n  }\n\n  Future<bool> isStartAtBootEnabled() async {\n    final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.isStartAtBootEnabled$pigeonVar_messageChannelSuffix';\n    final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(\n      pigeonVar_channelName,\n      pigeonChannelCodec,\n      binaryMessenger: pigeonVar_binaryMessenger,\n    );\n    final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);\n    final List<Object?>? pigeonVar_replyList =\n        await pigeonVar_sendFuture as List<Object?>?;\n    if (pigeonVar_replyList == null) {\n      throw _createConnectionError(pigeonVar_channelName);\n    } else if (pigeonVar_replyList.length > 1) {\n      throw PlatformException(\n        code: pigeonVar_replyList[0]! as String,\n        message: pigeonVar_replyList[1] as String?,\n        details: pigeonVar_replyList[2],\n      );\n    } else if (pigeonVar_replyList[0] == null) {\n      throw PlatformException(\n        code: 'null-error',\n        message: 'Host platform returned null value for non-null return value.',\n      );\n    } else {\n      return (pigeonVar_replyList[0] as bool?)!;\n    }\n  }\n\n  Future<void> setStartAtBootEnabled(bool enabled) async {\n    final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.setStartAtBootEnabled$pigeonVar_messageChannelSuffix';\n    final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(\n      pigeonVar_channelName,\n      pigeonChannelCodec,\n      binaryMessenger: pigeonVar_binaryMessenger,\n    );\n    final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[enabled]);\n    final List<Object?>? pigeonVar_replyList =\n        await pigeonVar_sendFuture as List<Object?>?;\n    if (pigeonVar_replyList == null) {\n      throw _createConnectionError(pigeonVar_channelName);\n    } else if (pigeonVar_replyList.length > 1) {\n      throw PlatformException(\n        code: pigeonVar_replyList[0]! as String,\n        message: pigeonVar_replyList[1] as String?,\n        details: pigeonVar_replyList[2],\n      );\n    } else {\n      return;\n    }\n  }\n\n  Future<bool> isAutoCheckUpdateEnabled() async {\n    final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.isAutoCheckUpdateEnabled$pigeonVar_messageChannelSuffix';\n    final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(\n      pigeonVar_channelName,\n      pigeonChannelCodec,\n      binaryMessenger: pigeonVar_binaryMessenger,\n    );\n    final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);\n    final List<Object?>? pigeonVar_replyList =\n        await pigeonVar_sendFuture as List<Object?>?;\n    if (pigeonVar_replyList == null) {\n      throw _createConnectionError(pigeonVar_channelName);\n    } else if (pigeonVar_replyList.length > 1) {\n      throw PlatformException(\n        code: pigeonVar_replyList[0]! as String,\n        message: pigeonVar_replyList[1] as String?,\n        details: pigeonVar_replyList[2],\n      );\n    } else if (pigeonVar_replyList[0] == null) {\n      throw PlatformException(\n        code: 'null-error',\n        message: 'Host platform returned null value for non-null return value.',\n      );\n    } else {\n      return (pigeonVar_replyList[0] as bool?)!;\n    }\n  }\n\n  Future<void> setAutoCheckUpdateEnabled(bool enabled) async {\n    final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.setAutoCheckUpdateEnabled$pigeonVar_messageChannelSuffix';\n    final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(\n      pigeonVar_channelName,\n      pigeonChannelCodec,\n      binaryMessenger: pigeonVar_binaryMessenger,\n    );\n    final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[enabled]);\n    final List<Object?>? pigeonVar_replyList =\n        await pigeonVar_sendFuture as List<Object?>?;\n    if (pigeonVar_replyList == null) {\n      throw _createConnectionError(pigeonVar_channelName);\n    } else if (pigeonVar_replyList.length > 1) {\n      throw PlatformException(\n        code: pigeonVar_replyList[0]! as String,\n        message: pigeonVar_replyList[1] as String?,\n        details: pigeonVar_replyList[2],\n      );\n    } else {\n      return;\n    }\n  }\n\n  Future<bool> isAutoOpenWebPageEnabled() async {\n    final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.isAutoOpenWebPageEnabled$pigeonVar_messageChannelSuffix';\n    final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(\n      pigeonVar_channelName,\n      pigeonChannelCodec,\n      binaryMessenger: pigeonVar_binaryMessenger,\n    );\n    final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);\n    final List<Object?>? pigeonVar_replyList =\n        await pigeonVar_sendFuture as List<Object?>?;\n    if (pigeonVar_replyList == null) {\n      throw _createConnectionError(pigeonVar_channelName);\n    } else if (pigeonVar_replyList.length > 1) {\n      throw PlatformException(\n        code: pigeonVar_replyList[0]! as String,\n        message: pigeonVar_replyList[1] as String?,\n        details: pigeonVar_replyList[2],\n      );\n    } else if (pigeonVar_replyList[0] == null) {\n      throw PlatformException(\n        code: 'null-error',\n        message: 'Host platform returned null value for non-null return value.',\n      );\n    } else {\n      return (pigeonVar_replyList[0] as bool?)!;\n    }\n  }\n\n  Future<void> setAutoOpenWebPageEnabled(bool enabled) async {\n    final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.setAutoOpenWebPageEnabled$pigeonVar_messageChannelSuffix';\n    final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(\n      pigeonVar_channelName,\n      pigeonChannelCodec,\n      binaryMessenger: pigeonVar_binaryMessenger,\n    );\n    final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[enabled]);\n    final List<Object?>? pigeonVar_replyList =\n        await pigeonVar_sendFuture as List<Object?>?;\n    if (pigeonVar_replyList == null) {\n      throw _createConnectionError(pigeonVar_channelName);\n    } else if (pigeonVar_replyList.length > 1) {\n      throw PlatformException(\n        code: pigeonVar_replyList[0]! as String,\n        message: pigeonVar_replyList[1] as String?,\n        details: pigeonVar_replyList[2],\n      );\n    } else {\n      return;\n    }\n  }\n\n  Future<String> getDataDir() async {\n    final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.getDataDir$pigeonVar_messageChannelSuffix';\n    final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(\n      pigeonVar_channelName,\n      pigeonChannelCodec,\n      binaryMessenger: pigeonVar_binaryMessenger,\n    );\n    final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);\n    final List<Object?>? pigeonVar_replyList =\n        await pigeonVar_sendFuture as List<Object?>?;\n    if (pigeonVar_replyList == null) {\n      throw _createConnectionError(pigeonVar_channelName);\n    } else if (pigeonVar_replyList.length > 1) {\n      throw PlatformException(\n        code: pigeonVar_replyList[0]! as String,\n        message: pigeonVar_replyList[1] as String?,\n        details: pigeonVar_replyList[2],\n      );\n    } else if (pigeonVar_replyList[0] == null) {\n      throw PlatformException(\n        code: 'null-error',\n        message: 'Host platform returned null value for non-null return value.',\n      );\n    } else {\n      return (pigeonVar_replyList[0] as String?)!;\n    }\n  }\n\n  Future<void> setDataDir(String dir) async {\n    final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.setDataDir$pigeonVar_messageChannelSuffix';\n    final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(\n      pigeonVar_channelName,\n      pigeonChannelCodec,\n      binaryMessenger: pigeonVar_binaryMessenger,\n    );\n    final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[dir]);\n    final List<Object?>? pigeonVar_replyList =\n        await pigeonVar_sendFuture as List<Object?>?;\n    if (pigeonVar_replyList == null) {\n      throw _createConnectionError(pigeonVar_channelName);\n    } else if (pigeonVar_replyList.length > 1) {\n      throw PlatformException(\n        code: pigeonVar_replyList[0]! as String,\n        message: pigeonVar_replyList[1] as String?,\n        details: pigeonVar_replyList[2],\n      );\n    } else {\n      return;\n    }\n  }\n\n  Future<bool> isSilentJumpAppEnabled() async {\n    final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.isSilentJumpAppEnabled$pigeonVar_messageChannelSuffix';\n    final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(\n      pigeonVar_channelName,\n      pigeonChannelCodec,\n      binaryMessenger: pigeonVar_binaryMessenger,\n    );\n    final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);\n    final List<Object?>? pigeonVar_replyList =\n        await pigeonVar_sendFuture as List<Object?>?;\n    if (pigeonVar_replyList == null) {\n      throw _createConnectionError(pigeonVar_channelName);\n    } else if (pigeonVar_replyList.length > 1) {\n      throw PlatformException(\n        code: pigeonVar_replyList[0]! as String,\n        message: pigeonVar_replyList[1] as String?,\n        details: pigeonVar_replyList[2],\n      );\n    } else if (pigeonVar_replyList[0] == null) {\n      throw PlatformException(\n        code: 'null-error',\n        message: 'Host platform returned null value for non-null return value.',\n      );\n    } else {\n      return (pigeonVar_replyList[0] as bool?)!;\n    }\n  }\n\n  Future<void> setSilentJumpAppEnabled(bool enabled) async {\n    final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.setSilentJumpAppEnabled$pigeonVar_messageChannelSuffix';\n    final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(\n      pigeonVar_channelName,\n      pigeonChannelCodec,\n      binaryMessenger: pigeonVar_binaryMessenger,\n    );\n    final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[enabled]);\n    final List<Object?>? pigeonVar_replyList =\n        await pigeonVar_sendFuture as List<Object?>?;\n    if (pigeonVar_replyList == null) {\n      throw _createConnectionError(pigeonVar_channelName);\n    } else if (pigeonVar_replyList.length > 1) {\n      throw PlatformException(\n        code: pigeonVar_replyList[0]! as String,\n        message: pigeonVar_replyList[1] as String?,\n        details: pigeonVar_replyList[2],\n      );\n    } else {\n      return;\n    }\n  }\n}\n\nclass NativeCommon {\n  /// Constructor for [NativeCommon].  The [binaryMessenger] named argument is\n  /// available for dependency injection.  If it is left null, the default\n  /// BinaryMessenger will be used which routes to the host platform.\n  NativeCommon({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''})\n      : pigeonVar_binaryMessenger = binaryMessenger,\n        pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';\n  final BinaryMessenger? pigeonVar_binaryMessenger;\n\n  static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec();\n\n  final String pigeonVar_messageChannelSuffix;\n\n  Future<bool> startActivityFromUri(String intentUri) async {\n    final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.NativeCommon.startActivityFromUri$pigeonVar_messageChannelSuffix';\n    final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(\n      pigeonVar_channelName,\n      pigeonChannelCodec,\n      binaryMessenger: pigeonVar_binaryMessenger,\n    );\n    final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[intentUri]);\n    final List<Object?>? pigeonVar_replyList =\n        await pigeonVar_sendFuture as List<Object?>?;\n    if (pigeonVar_replyList == null) {\n      throw _createConnectionError(pigeonVar_channelName);\n    } else if (pigeonVar_replyList.length > 1) {\n      throw PlatformException(\n        code: pigeonVar_replyList[0]! as String,\n        message: pigeonVar_replyList[1] as String?,\n        details: pigeonVar_replyList[2],\n      );\n    } else if (pigeonVar_replyList[0] == null) {\n      throw PlatformException(\n        code: 'null-error',\n        message: 'Host platform returned null value for non-null return value.',\n      );\n    } else {\n      return (pigeonVar_replyList[0] as bool?)!;\n    }\n  }\n\n  Future<int> getDeviceSdkInt() async {\n    final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.NativeCommon.getDeviceSdkInt$pigeonVar_messageChannelSuffix';\n    final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(\n      pigeonVar_channelName,\n      pigeonChannelCodec,\n      binaryMessenger: pigeonVar_binaryMessenger,\n    );\n    final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);\n    final List<Object?>? pigeonVar_replyList =\n        await pigeonVar_sendFuture as List<Object?>?;\n    if (pigeonVar_replyList == null) {\n      throw _createConnectionError(pigeonVar_channelName);\n    } else if (pigeonVar_replyList.length > 1) {\n      throw PlatformException(\n        code: pigeonVar_replyList[0]! as String,\n        message: pigeonVar_replyList[1] as String?,\n        details: pigeonVar_replyList[2],\n      );\n    } else if (pigeonVar_replyList[0] == null) {\n      throw PlatformException(\n        code: 'null-error',\n        message: 'Host platform returned null value for non-null return value.',\n      );\n    } else {\n      return (pigeonVar_replyList[0] as int?)!;\n    }\n  }\n\n  Future<String> getDeviceCPUABI() async {\n    final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.NativeCommon.getDeviceCPUABI$pigeonVar_messageChannelSuffix';\n    final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(\n      pigeonVar_channelName,\n      pigeonChannelCodec,\n      binaryMessenger: pigeonVar_binaryMessenger,\n    );\n    final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);\n    final List<Object?>? pigeonVar_replyList =\n        await pigeonVar_sendFuture as List<Object?>?;\n    if (pigeonVar_replyList == null) {\n      throw _createConnectionError(pigeonVar_channelName);\n    } else if (pigeonVar_replyList.length > 1) {\n      throw PlatformException(\n        code: pigeonVar_replyList[0]! as String,\n        message: pigeonVar_replyList[1] as String?,\n        details: pigeonVar_replyList[2],\n      );\n    } else if (pigeonVar_replyList[0] == null) {\n      throw PlatformException(\n        code: 'null-error',\n        message: 'Host platform returned null value for non-null return value.',\n      );\n    } else {\n      return (pigeonVar_replyList[0] as String?)!;\n    }\n  }\n\n  Future<String> getVersionName() async {\n    final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.NativeCommon.getVersionName$pigeonVar_messageChannelSuffix';\n    final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(\n      pigeonVar_channelName,\n      pigeonChannelCodec,\n      binaryMessenger: pigeonVar_binaryMessenger,\n    );\n    final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);\n    final List<Object?>? pigeonVar_replyList =\n        await pigeonVar_sendFuture as List<Object?>?;\n    if (pigeonVar_replyList == null) {\n      throw _createConnectionError(pigeonVar_channelName);\n    } else if (pigeonVar_replyList.length > 1) {\n      throw PlatformException(\n        code: pigeonVar_replyList[0]! as String,\n        message: pigeonVar_replyList[1] as String?,\n        details: pigeonVar_replyList[2],\n      );\n    } else if (pigeonVar_replyList[0] == null) {\n      throw PlatformException(\n        code: 'null-error',\n        message: 'Host platform returned null value for non-null return value.',\n      );\n    } else {\n      return (pigeonVar_replyList[0] as String?)!;\n    }\n  }\n\n  Future<int> getVersionCode() async {\n    final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.NativeCommon.getVersionCode$pigeonVar_messageChannelSuffix';\n    final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(\n      pigeonVar_channelName,\n      pigeonChannelCodec,\n      binaryMessenger: pigeonVar_binaryMessenger,\n    );\n    final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);\n    final List<Object?>? pigeonVar_replyList =\n        await pigeonVar_sendFuture as List<Object?>?;\n    if (pigeonVar_replyList == null) {\n      throw _createConnectionError(pigeonVar_channelName);\n    } else if (pigeonVar_replyList.length > 1) {\n      throw PlatformException(\n        code: pigeonVar_replyList[0]! as String,\n        message: pigeonVar_replyList[1] as String?,\n        details: pigeonVar_replyList[2],\n      );\n    } else if (pigeonVar_replyList[0] == null) {\n      throw PlatformException(\n        code: 'null-error',\n        message: 'Host platform returned null value for non-null return value.',\n      );\n    } else {\n      return (pigeonVar_replyList[0] as int?)!;\n    }\n  }\n\n  Future<void> toast(String msg) async {\n    final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.NativeCommon.toast$pigeonVar_messageChannelSuffix';\n    final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(\n      pigeonVar_channelName,\n      pigeonChannelCodec,\n      binaryMessenger: pigeonVar_binaryMessenger,\n    );\n    final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[msg]);\n    final List<Object?>? pigeonVar_replyList =\n        await pigeonVar_sendFuture as List<Object?>?;\n    if (pigeonVar_replyList == null) {\n      throw _createConnectionError(pigeonVar_channelName);\n    } else if (pigeonVar_replyList.length > 1) {\n      throw PlatformException(\n        code: pigeonVar_replyList[0]! as String,\n        message: pigeonVar_replyList[1] as String?,\n        details: pigeonVar_replyList[2],\n      );\n    } else {\n      return;\n    }\n  }\n\n  Future<void> longToast(String msg) async {\n    final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.NativeCommon.longToast$pigeonVar_messageChannelSuffix';\n    final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(\n      pigeonVar_channelName,\n      pigeonChannelCodec,\n      binaryMessenger: pigeonVar_binaryMessenger,\n    );\n    final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[msg]);\n    final List<Object?>? pigeonVar_replyList =\n        await pigeonVar_sendFuture as List<Object?>?;\n    if (pigeonVar_replyList == null) {\n      throw _createConnectionError(pigeonVar_channelName);\n    } else if (pigeonVar_replyList.length > 1) {\n      throw PlatformException(\n        code: pigeonVar_replyList[0]! as String,\n        message: pigeonVar_replyList[1] as String?,\n        details: pigeonVar_replyList[2],\n      );\n    } else {\n      return;\n    }\n  }\n}\n\nclass Android {\n  /// Constructor for [Android].  The [binaryMessenger] named argument is\n  /// available for dependency injection.  If it is left null, the default\n  /// BinaryMessenger will be used which routes to the host platform.\n  Android({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''})\n      : pigeonVar_binaryMessenger = binaryMessenger,\n        pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';\n  final BinaryMessenger? pigeonVar_binaryMessenger;\n\n  static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec();\n\n  final String pigeonVar_messageChannelSuffix;\n\n  Future<void> addShortcut() async {\n    final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.Android.addShortcut$pigeonVar_messageChannelSuffix';\n    final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(\n      pigeonVar_channelName,\n      pigeonChannelCodec,\n      binaryMessenger: pigeonVar_binaryMessenger,\n    );\n    final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);\n    final List<Object?>? pigeonVar_replyList =\n        await pigeonVar_sendFuture as List<Object?>?;\n    if (pigeonVar_replyList == null) {\n      throw _createConnectionError(pigeonVar_channelName);\n    } else if (pigeonVar_replyList.length > 1) {\n      throw PlatformException(\n        code: pigeonVar_replyList[0]! as String,\n        message: pigeonVar_replyList[1] as String?,\n        details: pigeonVar_replyList[2],\n      );\n    } else {\n      return;\n    }\n  }\n\n  Future<void> startService() async {\n    final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.Android.startService$pigeonVar_messageChannelSuffix';\n    final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(\n      pigeonVar_channelName,\n      pigeonChannelCodec,\n      binaryMessenger: pigeonVar_binaryMessenger,\n    );\n    final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);\n    final List<Object?>? pigeonVar_replyList =\n        await pigeonVar_sendFuture as List<Object?>?;\n    if (pigeonVar_replyList == null) {\n      throw _createConnectionError(pigeonVar_channelName);\n    } else if (pigeonVar_replyList.length > 1) {\n      throw PlatformException(\n        code: pigeonVar_replyList[0]! as String,\n        message: pigeonVar_replyList[1] as String?,\n        details: pigeonVar_replyList[2],\n      );\n    } else {\n      return;\n    }\n  }\n\n  Future<void> setAdminPwd(String pwd) async {\n    final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.Android.setAdminPwd$pigeonVar_messageChannelSuffix';\n    final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(\n      pigeonVar_channelName,\n      pigeonChannelCodec,\n      binaryMessenger: pigeonVar_binaryMessenger,\n    );\n    final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[pwd]);\n    final List<Object?>? pigeonVar_replyList =\n        await pigeonVar_sendFuture as List<Object?>?;\n    if (pigeonVar_replyList == null) {\n      throw _createConnectionError(pigeonVar_channelName);\n    } else if (pigeonVar_replyList.length > 1) {\n      throw PlatformException(\n        code: pigeonVar_replyList[0]! as String,\n        message: pigeonVar_replyList[1] as String?,\n        details: pigeonVar_replyList[2],\n      );\n    } else {\n      return;\n    }\n  }\n\n  Future<int> getOpenListHttpPort() async {\n    final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.Android.getOpenListHttpPort$pigeonVar_messageChannelSuffix';\n    final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(\n      pigeonVar_channelName,\n      pigeonChannelCodec,\n      binaryMessenger: pigeonVar_binaryMessenger,\n    );\n    final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);\n    final List<Object?>? pigeonVar_replyList =\n        await pigeonVar_sendFuture as List<Object?>?;\n    if (pigeonVar_replyList == null) {\n      throw _createConnectionError(pigeonVar_channelName);\n    } else if (pigeonVar_replyList.length > 1) {\n      throw PlatformException(\n        code: pigeonVar_replyList[0]! as String,\n        message: pigeonVar_replyList[1] as String?,\n        details: pigeonVar_replyList[2],\n      );\n    } else if (pigeonVar_replyList[0] == null) {\n      throw PlatformException(\n        code: 'null-error',\n        message: 'Host platform returned null value for non-null return value.',\n      );\n    } else {\n      return (pigeonVar_replyList[0] as int?)!;\n    }\n  }\n\n  Future<bool> isRunning() async {\n    final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.Android.isRunning$pigeonVar_messageChannelSuffix';\n    final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(\n      pigeonVar_channelName,\n      pigeonChannelCodec,\n      binaryMessenger: pigeonVar_binaryMessenger,\n    );\n    final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);\n    final List<Object?>? pigeonVar_replyList =\n        await pigeonVar_sendFuture as List<Object?>?;\n    if (pigeonVar_replyList == null) {\n      throw _createConnectionError(pigeonVar_channelName);\n    } else if (pigeonVar_replyList.length > 1) {\n      throw PlatformException(\n        code: pigeonVar_replyList[0]! as String,\n        message: pigeonVar_replyList[1] as String?,\n        details: pigeonVar_replyList[2],\n      );\n    } else if (pigeonVar_replyList[0] == null) {\n      throw PlatformException(\n        code: 'null-error',\n        message: 'Host platform returned null value for non-null return value.',\n      );\n    } else {\n      return (pigeonVar_replyList[0] as bool?)!;\n    }\n  }\n\n  Future<String> getOpenListVersion() async {\n    final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.Android.getOpenListVersion$pigeonVar_messageChannelSuffix';\n    final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(\n      pigeonVar_channelName,\n      pigeonChannelCodec,\n      binaryMessenger: pigeonVar_binaryMessenger,\n    );\n    final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);\n    final List<Object?>? pigeonVar_replyList =\n        await pigeonVar_sendFuture as List<Object?>?;\n    if (pigeonVar_replyList == null) {\n      throw _createConnectionError(pigeonVar_channelName);\n    } else if (pigeonVar_replyList.length > 1) {\n      throw PlatformException(\n        code: pigeonVar_replyList[0]! as String,\n        message: pigeonVar_replyList[1] as String?,\n        details: pigeonVar_replyList[2],\n      );\n    } else if (pigeonVar_replyList[0] == null) {\n      throw PlatformException(\n        code: 'null-error',\n        message: 'Host platform returned null value for non-null return value.',\n      );\n    } else {\n      return (pigeonVar_replyList[0] as String?)!;\n    }\n  }\n}\n\nabstract class Event {\n  static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec();\n\n  void onServiceStatusChanged(bool isRunning);\n\n  void onServerLog(int level, String time, String log);\n\n  static void setUp(Event? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) {\n    messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';\n    {\n      final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(\n          'dev.flutter.pigeon.openlist_mobile.Event.onServiceStatusChanged$messageChannelSuffix', pigeonChannelCodec,\n          binaryMessenger: binaryMessenger);\n      if (api == null) {\n        pigeonVar_channel.setMessageHandler(null);\n      } else {\n        pigeonVar_channel.setMessageHandler((Object? message) async {\n          assert(message != null,\n          'Argument for dev.flutter.pigeon.openlist_mobile.Event.onServiceStatusChanged was null.');\n          final List<Object?> args = (message as List<Object?>?)!;\n          final bool? arg_isRunning = (args[0] as bool?);\n          assert(arg_isRunning != null,\n              'Argument for dev.flutter.pigeon.openlist_mobile.Event.onServiceStatusChanged was null, expected non-null bool.');\n          try {\n            api.onServiceStatusChanged(arg_isRunning!);\n            return wrapResponse(empty: true);\n          } on PlatformException catch (e) {\n            return wrapResponse(error: e);\n          }          catch (e) {\n            return wrapResponse(error: PlatformException(code: 'error', message: e.toString()));\n          }\n        });\n      }\n    }\n    {\n      final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(\n          'dev.flutter.pigeon.openlist_mobile.Event.onServerLog$messageChannelSuffix', pigeonChannelCodec,\n          binaryMessenger: binaryMessenger);\n      if (api == null) {\n        pigeonVar_channel.setMessageHandler(null);\n      } else {\n        pigeonVar_channel.setMessageHandler((Object? message) async {\n          assert(message != null,\n          'Argument for dev.flutter.pigeon.openlist_mobile.Event.onServerLog was null.');\n          final List<Object?> args = (message as List<Object?>?)!;\n          final int? arg_level = (args[0] as int?);\n          assert(arg_level != null,\n              'Argument for dev.flutter.pigeon.openlist_mobile.Event.onServerLog was null, expected non-null int.');\n          final String? arg_time = (args[1] as String?);\n          assert(arg_time != null,\n              'Argument for dev.flutter.pigeon.openlist_mobile.Event.onServerLog was null, expected non-null String.');\n          final String? arg_log = (args[2] as String?);\n          assert(arg_log != null,\n              'Argument for dev.flutter.pigeon.openlist_mobile.Event.onServerLog was null, expected non-null String.');\n          try {\n            api.onServerLog(arg_level!, arg_time!, arg_log!);\n            return wrapResponse(empty: true);\n          } on PlatformException catch (e) {\n            return wrapResponse(error: e);\n          }          catch (e) {\n            return wrapResponse(error: PlatformException(code: 'error', message: e.toString()));\n          }\n        });\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/l10n/intl_en.arb",
    "content": "{\n  \"@@locale\": \"en\",\n  \"appName\": \"OpenList\",\n  \"desktopShortcut\": \"Desktop shortcut\",\n  \"setAdminPassword\": \"Set Admin password\",\n  \"moreOptions\": \"More options\",\n  \"checkForUpdates\": \"Check for updates\",\n  \"currentIsLatestVersion\": \"Current is latest version\",\n  \"confirm\": \"OK\",\n  \"cancel\": \"Cancel\",\n  \"releasePage\": \"Release Page\",\n  \"downloadApk\": \"Download APK\",\n  \"about\": \"About\",\n  \"general\": \"General\",\n  \"autoCheckForUpdates\": \"Auto check for updates\",\n  \"autoCheckForUpdatesDesc\": \"Check for updates when app starts\",\n  \"wakeLock\": \"Wake lock\",\n  \"wakeLockDesc\": \"Prevent CPU from sleeping when screen is off. (May cause app killed in background on some devices)\",\n  \"bootAutoStartService\": \"Boot auto-start service\",\n  \"bootAutoStartServiceDesc\": \"Automatically start OpenList service after boot. (Please make sure to grant auto-start permission)\",\n  \"webPage\": \"Web Page\",\n  \"settings\": \"Settings\",\n  \"jumpToOtherApp\": \"Jump to other app？\",\n  \"selectAppToOpen\": \"Select app to open\",\n  \"goTo\": \"GO\",\n  \"downloadThisFile\": \"Download this file？\",\n  \"download\": \"download\",\n  \"copiedToClipboard\": \"Copied to clipboard\",\n  \"importantSettings\": \"Important settings\",\n  \"uiSettings\": \"UI\",\n  \"grantManagerStoragePermission\": \"Grant [Manage external storage] permission\",\n  \"grantStoragePermissionDesc\": \"Mounting local storage is a must, otherwise no permission to read and write files\",\n  \"grantStoragePermission\": \"Grant [external storage] permission\",\n  \"grantNotificationPermission\": \"Grant [Notification] permission\",\n  \"grantNotificationPermissionDesc\": \"Used for foreground service keep alive\",\n  \"autoStartWebPage\": \"Set web page as startup page\",\n  \"autoStartWebPageDesc\": \"Default page when opening main interface\",\n  \"dataDirectory\": \"data Directory\",\n  \"setDefaultDirectory\": \"Set as default directory?\",\n  \"silentJumpApp\": \"Silent jump app\",\n  \"silentJumpAppDesc\": \"Jump to other app without prompt\",\n  \"newVersionFound\": \"New Version Found\",\n  \"directDownloadApk\": \"Direct Download APK\",\n  \"initializingNotificationManager\": \"Initializing notification manager\",\n  \"downloadManager\": \"Download\",\n  \"downloadManagerWithCount\": \"Download ({count})\",\n  \"@downloadManagerWithCount\": {\n    \"placeholders\": {\n      \"count\": {\n        \"type\": \"int\"\n      }\n    }\n  },\n  \"modifyAdminPassword\": \"Modify Admin Password\",\n  \"directDownload\": \"Direct Download\",\n  \"browserDownload\": \"Browser Download\",\n  \"loadDownloadFilesFailed\": \"Failed to load download files\",\n  \"noActiveDownloads\": \"No active downloads\",\n  \"downloadFailed\": \"Download failed\",\n  \"startTime\": \"Start time\",\n  \"noCompletedDownloads\": \"No completed downloads\",\n  \"completedTime\": \"Completed time\",\n  \"size\": \"Size\",\n  \"openFile\": \"Open file\",\n  \"deleteRecord\": \"Delete record\",\n  \"deleteFile\": \"Delete file\",\n  \"confirmClear\": \"Confirm Clear\",\n  \"confirmClearAllFiles\": \"Are you sure you want to clear all download files? This action cannot be undone.\",\n  \"cleared\": \"Download directory cleared\",\n  \"clearFailed\": \"Clear failed\",\n  \"cancelDownload\": \"Cancel Download\",\n  \"confirmCancelDownload\": \"Are you sure you want to cancel downloading \\\"{filename}\\\"?\",\n  \"@confirmCancelDownload\": {\n    \"placeholders\": {\n      \"filename\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"continueDownload\": \"Continue Download\",\n  \"confirmDeleteRecord\": \"Are you sure you want to delete the download record of \\\"{filename}\\\"?\",\n  \"@confirmDeleteRecord\": {\n    \"placeholders\": {\n      \"filename\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"delete\": \"Delete\",\n  \"shareFile\": \"Share file\",\n  \"shareFeatureNotImplemented\": \"Share feature not implemented yet\",\n  \"fileInfo\": \"File info\",\n  \"fileName\": \"File name\",\n  \"modifiedTime\": \"Modified time\",\n  \"filePath\": \"Path\",\n  \"confirmDelete\": \"Confirm Delete\",\n  \"confirmDeleteFile\": \"Are you sure you want to delete file \\\"{filename}\\\"? This action cannot be undone.\",\n  \"@confirmDeleteFile\": {\n    \"placeholders\": {\n      \"filename\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"fileDeleted\": \"File deleted\",\n  \"deleteFailed\": \"Delete failed\",\n  \"clear\": \"Clear\",\n  \"noAppToOpenFile\": \"No app found to open this file\",\n  \"viewLocation\": \"View Location\",\n  \"fileNotFound\": \"File not found or has been deleted\",\n  \"noPermissionToOpenFile\": \"No permission to open this file\",\n  \"openFileFailed\": \"Failed to open file: {error}\",\n  \"@openFileFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"fileLocation\": \"File Location\",\n  \"fileSavedTo\": \"File saved to:\",\n  \"fileLocationTip\": \"You can use a file manager to find this file, or try installing the appropriate app to open it.\",\n  \"downloadDirectory\": \"Download Directory\",\n  \"openDirectory\": \"Open Directory\",\n  \"clearRecords\": \"Clear Records\",\n  \"clearAll\": \"Clear All\",\n  \"downloadRecordsCleared\": \"Download records cleared\",\n  \"inProgress\": \"In Progress\",\n  \"completed\": \"Completed\",\n  \"refresh\": \"Refresh\",\n  \"pending\": \"Pending\",\n  \"downloading\": \"Downloading\",\n  \"failed\": \"Failed\",\n  \"cancelled\": \"Cancelled\",\n  \"cannotGetDownloadDirectory\": \"Cannot get download directory\",\n  \"startDownload\": \"Start download: {filename}\",\n  \"@startDownload\": {\n    \"placeholders\": {\n      \"filename\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"downloadProgress\": \"Download progress: {progress}%\",\n  \"@downloadProgress\": {\n    \"placeholders\": {\n      \"progress\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"downloadComplete\": \"Download complete: {filename}\",\n  \"@downloadComplete\": {\n    \"placeholders\": {\n      \"filename\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"open\": \"Open\",\n  \"downloadCancelled\": \"Download cancelled: {url}\",\n  \"@downloadCancelled\": {\n    \"placeholders\": {\n      \"url\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"downloadFailedWithError\": \"Download failed: {filename}\",\n  \"@downloadFailedWithError\": {\n    \"placeholders\": {\n      \"filename\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"userCancelledDownload\": \"User cancelled download\",\n  \"cannotGetBaseDownloadDirectory\": \"Cannot get base download directory\",\n  \"createOpenListDownloadDirectory\": \"Create OpenList download directory: {path}\",\n  \"@createOpenListDownloadDirectory\": {\n    \"placeholders\": {\n      \"path\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"createOpenListDirectoryFailed\": \"Failed to create OpenList directory: {error}\",\n  \"@createOpenListDirectoryFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"openListDownloadDirectory\": \"OpenList download directory: {path}\",\n  \"@openListDownloadDirectory\": {\n    \"placeholders\": {\n      \"path\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"getDownloadDirectoryFailed\": \"Failed to get download directory: {error}\",\n  \"@getDownloadDirectoryFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"parseFilenameFailed\": \"Failed to parse filename: {error}\",\n  \"@parseFilenameFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"needInstallPermission\": \"Install Permission Required\",\n  \"needInstallPermissionDesc\": \"To install APK files, install permission is required. Please enable it manually in settings.\",\n  \"goToSettings\": \"Go to Settings\",\n  \"needInstallPermissionToInstallApk\": \"Install permission is required to install APK files\",\n  \"checkInstallPermissionFailed\": \"Failed to check install permission: {error}\",\n  \"@checkInstallPermissionFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"tryToOpenFile\": \"Trying to open file: {path}\",\n  \"@tryToOpenFile\": {\n    \"placeholders\": {\n      \"path\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"openFileResult\": \"Open file result: {type} - {message}\",\n  \"@openFileResult\": {\n    \"placeholders\": {\n      \"type\": {\n        \"type\": \"String\"\n      },\n      \"message\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"cannotInstallApkFile\": \"Cannot install APK file, you may need to enable \\\"Install unknown apps\\\" in settings\",\n  \"noPermissionToInstallApk\": \"No permission to install APK file, please enable install permission in settings\",\n  \"openFileException\": \"Open file exception: {error}\",\n  \"@openFileException\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"getDownloadFileListFailed\": \"Failed to get download file list: {error}\",\n  \"@getDownloadFileListFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"downloadDirectoryCleared\": \"Download directory cleared\",\n  \"clearDownloadDirectoryFailed\": \"Failed to clear download directory: {error}\",\n  \"@clearDownloadDirectoryFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"fileDeletedLog\": \"File deleted: {filename}\",\n  \"@fileDeletedLog\": {\n    \"placeholders\": {\n      \"filename\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"deleteFileFailedLog\": \"Failed to delete file: {error}\",\n  \"@deleteFileFailedLog\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"preparingDownload\": \"Preparing download...\",\n  \"downloadCancelledStatus\": \"Download cancelled\",\n  \"notificationManagerInitialized\": \"Notification manager initialized successfully\",\n  \"notificationManagerInitFailed\": \"Failed to initialize notification manager: {error}\",\n  \"@notificationManagerInitFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"notificationClicked\": \"Notification clicked: {payload}\",\n  \"@notificationClicked\": {\n    \"placeholders\": {\n      \"payload\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"currentDownloadingFiles\": \"Currently {count} files are downloading\",\n  \"@currentDownloadingFiles\": {\n    \"placeholders\": {\n      \"count\": {\n        \"type\": \"int\"\n      }\n    }\n  },\n  \"downloadProgressDesc\": \"Show file download progress\",\n  \"viewDownloads\": \"View Downloads\",\n  \"showDownloadProgressNotificationFailed\": \"Failed to show download progress notification: {error}\",\n  \"@showDownloadProgressNotificationFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"downloadCompleteNotificationTitle\": \"{filename} download completed\",\n  \"@downloadCompleteNotificationTitle\": {\n    \"placeholders\": {\n      \"filename\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"clickToJumpToDownloadManager\": \"Click to jump to download manager\",\n  \"downloadCompleteTitle\": \"Download Complete\",\n  \"multipleFilesCompleted\": \"{count} files completed, click to jump to download manager\",\n  \"@multipleFilesCompleted\": {\n    \"placeholders\": {\n      \"count\": {\n        \"type\": \"int\"\n      }\n    }\n  },\n  \"downloadCompleteChannel\": \"Download Complete\",\n  \"downloadCompleteChannelDesc\": \"File download complete notification\",\n  \"openDownloadManager\": \"Open Download Manager\",\n  \"showDownloadCompleteNotificationFailed\": \"Failed to show download complete notification: {error}\",\n  \"@showDownloadCompleteNotificationFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"showSingleFileCompleteNotificationFailed\": \"Failed to show single file complete notification: {error}\",\n  \"@showSingleFileCompleteNotificationFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"cancelDownloadNotificationFailed\": \"Failed to cancel download notification: {error}\",\n  \"@cancelDownloadNotificationFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"cancelAllNotificationsFailed\": \"Failed to cancel all notifications: {error}\",\n  \"@cancelAllNotificationsFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"showInFileManager\": \"Show in file manager\",\n  \"fileSize\": \"Size: {size}\",\n  \"@fileSize\": {\n    \"placeholders\": {\n      \"size\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"fileTime\": \"Time: {time}\",\n  \"@fileTime\": {\n    \"placeholders\": {\n      \"time\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"ok\": \"OK\",\n  \"openFileManager\": \"Open File Manager\",\n  \"fileManagerOpened\": \"File manager opened\",\n  \"openFileManagerFailed\": \"Failed to open file manager: {error}\",\n  \"@openFileManagerFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"downloadDirectoryOpened\": \"Download directory opened\",\n  \"openDownloadDirectoryFailed\": \"Failed to open download directory: {error}\",\n  \"@openDownloadDirectoryFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"downloadDirectoryPathUnknown\": \"Download directory path unknown\",\n  \"cannotGetDownloadDirectoryError\": \"Cannot get download directory\",\n  \"startDownloadFile\": \"Start download: {filename}\",\n  \"@startDownloadFile\": {\n    \"placeholders\": {\n      \"filename\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"downloadCompleteFile\": \"Download complete: {filename}\",\n  \"@downloadCompleteFile\": {\n    \"placeholders\": {\n      \"filename\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"downloadFailedFile\": \"Download failed: {filename}\",\n  \"@downloadFailedFile\": {\n    \"placeholders\": {\n      \"filename\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"userCancelledDownloadError\": \"User cancelled download\",\n  \"cannotInstallApkNeedPermission\": \"Cannot install APK file, you may need to enable \\\"Install unknown apps\\\" in settings\",\n  \"noPermissionToInstallApkFile\": \"No permission to install APK file, please enable install permission in settings\",\n  \"preparingDownloadStatus\": \"Preparing download...\",\n  \"downloadCancelledText\": \"Download cancelled\",\n  \"language\": \"Language\",\n  \"languageSettings\": \"Language Settings\",\n  \"languageSettingsDesc\": \"Select app display language\",\n  \"followSystem\": \"Follow System\",\n  \"simplifiedChinese\": \"简体中文\",\n  \"english\": \"English\",\n  \"troubleshooting\": \"Troubleshooting\",\n  \"troubleshootingDesc\": \"Common issues and solutions\",\n  \"databaseNotSavedIssue\": \"Database Not Saved Issue\",\n  \"databaseNotSavedIssueDesc\": \"If you don't manually close OpenList, the database may not be saved to the corresponding db file. If you encounter this issue, please manually close the app to resolve it. (The switch is located in the main program menu on the OpenList interface, as well as in the notification bar)\",\n  \"autoStartIssue\": \"Auto-Start Information\",\n  \"autoStartIssueDesc\": \"When enabling auto-start, it's recommended to disable battery optimization for the app. Currently, after enabling auto-start, the service will automatically start in the background after system reboot, but may not show a notification in the notification bar. Rest assured, the service is running normally. You can check the service status through the quick settings tile in the notification shade, or return to the main interface to check the service toggle to confirm if the service has started.\",\n  \"currentlyDownloading\": \"Downloading\",\n  \"downloadProgressChannel\": \"Download Progress\",\n  \"confirmDownload\": \"Confirm Download\",\n  \"confirmDownloadMessage\": \"Do you want to download this file?\",\n  \"downloadingImage\": \"Downloading image...\",\n  \"laterInstall\": \"Install Later\",\n  \"installNow\": \"Install Now\",\n  \"directDownloadMethod\": \"Direct Download\",\n  \"directDownloadMethodDesc\": \"Use in-app downloader\",\n  \"browserDownloadMethod\": \"Browser Download\",\n  \"browserDownloadMethodDesc\": \"Use system browser\",\n  \"shareLink\": \"Share Link\",\n  \"shareLinkDesc\": \"Share download link\",\n  \"view\": \"View\",\n  \"downloadFunctionTest\": \"Download Function Test\",\n  \"testDirectDownloadFunction\": \"Test direct download function\",\n  \"testDownloadJsonFile\": \"Test download JSON file\",\n  \"testDownloadPngImage\": \"Test download PNG image\",\n  \"testDownloadLargeFile\": \"Test download large file (1MB)\",\n  \"viewDownloadFiles\": \"View download files\",\n  \"checkDownloadManagerForFiles\": \"Please check download manager via bottom navigation bar to view download files\",\n  \"viewDownloadDirectory\": \"View download directory\",\n  \"getDownloadPathFailed\": \"Failed to get\",\n  \"openDownloadTestPage\": \"Do you want to open download test page?\",\n  \"description\": \"Description:\",\n  \"downloadInstructions\": \"• Files will be downloaded to system download directory\\\\n• Download progress will show notifications\\\\n• You can choose to open file after download completes\\\\n• If filename exists, a number will be added automatically\\\\n• Please check download manager via bottom navigation bar to view files\",\n  \"selectDownloadMethod\": \"Select download method\",\n  \"batchDownloadComplete\": \"Batch download complete\",\n  \"imageDownloadSuccess\": \"Image download success\",\n  \"checkImageInDownloadFolder\": \"Please check image in download folder\",\n  \"findApkInDownloadFolder\": \"Please find APK file in download folder to install\",\n  \"downloadingFileProgress\": \"Downloading file {current}/{total}\",\n  \"@downloadingFileProgress\": {\n    \"placeholders\": {\n      \"current\": {\n        \"type\": \"int\"\n      },\n      \"total\": {\n        \"type\": \"int\"\n      }\n    }\n  },\n  \"fileDownloadFailed\": \"File {index} download failed\",\n  \"@fileDownloadFailed\": {\n    \"placeholders\": {\n      \"index\": {\n        \"type\": \"int\"\n      }\n    }\n  },\n  \"apkDownloadCompleteMessage\": \"APK file download completed, do you want to install?\",\n  \"openlist\": \"OpenList\",\n  \"openlistMobile\": \"OpenList Mobile\",\n  \"openSourceLicenses\": \"Open Source Licenses\",\n  \"viewThirdPartyLicenses\": \"View third-party licenses\",\n  \"editOpenListConfig\": \"Edit OpenList Config\",\n  \"save\": \"Save\",\n  \"saved\": \"Saved\",\n  \"edit\": \"Edit\",\n  \"preview\": \"Preview\",\n  \"fileNotFoundWillCreateOnSave\": \"File not found. Will create on save.\",\n  \"loadFailed\": \"Load failed: {error}\",\n  \"@loadFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"saveFailed\": \"Save failed: {error}\",\n  \"@saveFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"invalidJsonFormat\": \"Invalid JSON format at line {line}: {error}\",\n  \"@invalidJsonFormat\": {\n    \"placeholders\": {\n      \"line\": {\n        \"type\": \"int\"\n      },\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"filePermissionDenied\": \"File permission denied. Please check app permissions.\",\n  \"configSavedRestartRequired\": \"Config saved. Please restart OpenList service to take effect.\",\n  \"confirmSaveConfigTitle\": \"Confirm Save\",\n  \"confirmSaveConfigMessage\": \"Modifying configuration may cause service unavailable. Are you sure to save?\",\n  \"saveAndRestart\": \"Save and Restart\",\n  \"saveOnly\": \"Save Only\",\n  \"restoreBackup\": \"Restore Backup\",\n  \"backupRestored\": \"Backup restored successfully\",\n  \"noBackupFound\": \"No backup file found\",\n  \"restoreBackupFailed\": \"Restore backup failed: {error}\",\n  \"@restoreBackupFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"restartingService\": \"Restarting OpenList service...\",\n  \"serviceRestartSuccess\": \"Service restarted successfully\",\n  \"serviceRestartFailed\": \"Failed to restart service. Please restart manually.\",\n  \"serviceRestartOnlyAndroid\": \"Service restart is only supported on Android\"\n}"
  },
  {
    "path": "lib/l10n/intl_zh.arb",
    "content": "{\n  \"@@locale\": \"zh\",\n  \"appName\": \"OpenList\",\n  \"desktopShortcut\": \"桌面快捷方式\",\n  \"setAdminPassword\": \"设置Admin密码\",\n  \"moreOptions\": \"更多选项\",\n  \"checkForUpdates\": \"检查更新\",\n  \"currentIsLatestVersion\": \"已经是最新版本\",\n  \"confirm\": \"确认\",\n  \"cancel\": \"取消\",\n  \"releasePage\": \"发布页面\",\n  \"downloadApk\": \"下载APK\",\n  \"about\": \"关于\",\n  \"general\": \"通用\",\n  \"autoCheckForUpdates\": \"自动检查更新\",\n  \"autoCheckForUpdatesDesc\": \"启动时自动检查更新\",\n  \"wakeLock\": \"唤醒锁\",\n  \"wakeLockDesc\": \"开启防止锁屏后CPU休眠，保持进程在后台运行。（部分系统可能导致杀后台）\",\n  \"bootAutoStartService\": \"开机自启动服务\",\n  \"bootAutoStartServiceDesc\": \"在开机后自动启动OpenList服务。（请确保授予自启动权限）\",\n  \"webPage\": \"网页\",\n  \"settings\": \"设置\",\n  \"selectAppToOpen\": \"选择应用打开\",\n  \"goTo\": \"前往\",\n  \"downloadThisFile\": \"下载此文件吗？\",\n  \"download\": \"下载\",\n  \"copiedToClipboard\": \"已复制到剪贴板\",\n  \"importantSettings\": \"重要\",\n  \"uiSettings\": \"界面\",\n  \"grantManagerStoragePermission\": \"申请【所有文件访问权限】\",\n  \"grantStoragePermissionDesc\": \"挂载本地存储时必须授予，否则无权限读写文件\",\n  \"grantStoragePermission\": \"申请【读写外置存储权限】\",\n  \"grantNotificationPermission\": \"申请【通知权限】\",\n  \"grantNotificationPermissionDesc\": \"用于前台服务保活\",\n  \"autoStartWebPage\": \"将网页设置为打开首页\",\n  \"jumpToOtherApp\": \"跳转到其他APP ？\",\n  \"autoStartWebPageDesc\": \"打开主界面时的首页\",\n  \"dataDirectory\": \"data 文件夹路径\",\n  \"setDefaultDirectory\": \"是否设为初始目录？\",\n  \"silentJumpApp\": \"静默跳转APP\",\n  \"silentJumpAppDesc\": \"跳转APP时，不弹出提示框\",\n  \"newVersionFound\": \"发现新版本\",\n  \"directDownloadApk\": \"直接下载APK\",\n  \"initializingNotificationManager\": \"初始化通知管理器\",\n  \"downloadManager\": \"下载管理\",\n  \"downloadManagerWithCount\": \"下载管理({count})\",\n  \"@downloadManagerWithCount\": {\n    \"placeholders\": {\n      \"count\": {\n        \"type\": \"int\"\n      }\n    }\n  },\n  \"modifyAdminPassword\": \"修改Admin密码\",\n  \"directDownload\": \"直接下载\",\n  \"browserDownload\": \"浏览器下载\",\n  \"loadDownloadFilesFailed\": \"加载下载文件失败\",\n  \"noActiveDownloads\": \"暂无进行中的下载\",\n  \"downloadFailed\": \"下载失败\",\n  \"startTime\": \"开始时间\",\n  \"noCompletedDownloads\": \"暂无已完成的下载\",\n  \"completedTime\": \"完成时间\",\n  \"size\": \"大小\",\n  \"openFile\": \"打开文件\",\n  \"deleteRecord\": \"删除记录\",\n  \"deleteFile\": \"删除文件\",\n  \"confirmClear\": \"确认清空\",\n  \"confirmClearAllFiles\": \"确定要清空所有下载文件吗？此操作不可撤销。\",\n  \"cleared\": \"已清空下载目录\",\n  \"clearFailed\": \"清空失败\",\n  \"cancelDownload\": \"取消下载\",\n  \"confirmCancelDownload\": \"确定要取消下载 \\\"{filename}\\\" 吗？\",\n  \"@confirmCancelDownload\": {\n    \"placeholders\": {\n      \"filename\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"continueDownload\": \"继续下载\",\n  \"confirmDeleteRecord\": \"确定要删除 \\\"{filename}\\\" 的下载记录吗？\",\n  \"@confirmDeleteRecord\": {\n    \"placeholders\": {\n      \"filename\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"delete\": \"删除\",\n  \"shareFile\": \"分享文件\",\n  \"shareFeatureNotImplemented\": \"分享功能待实现\",\n  \"fileInfo\": \"文件信息\",\n  \"fileName\": \"文件名\",\n  \"modifiedTime\": \"修改时间\",\n  \"filePath\": \"路径\",\n  \"confirmDelete\": \"确认删除\",\n  \"confirmDeleteFile\": \"确定要删除文件 \\\"{filename}\\\" 吗？此操作不可撤销。\",\n  \"@confirmDeleteFile\": {\n    \"placeholders\": {\n      \"filename\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"fileDeleted\": \"文件已删除\",\n  \"deleteFailed\": \"删除失败\",\n  \"clear\": \"清空\",\n  \"noAppToOpenFile\": \"没有找到可以打开此文件的应用\",\n  \"viewLocation\": \"查看位置\",\n  \"fileNotFound\": \"文件不存在或已被删除\",\n  \"noPermissionToOpenFile\": \"没有权限打开此文件\",\n  \"openFileFailed\": \"打开文件失败: {error}\",\n  \"@openFileFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"fileLocation\": \"文件位置\",\n  \"fileSavedTo\": \"文件已保存到:\",\n  \"fileLocationTip\": \"您可以使用文件管理器找到此文件，或者尝试安装相应的应用来打开它。\",\n  \"downloadDirectory\": \"下载目录\",\n  \"openDirectory\": \"打开目录\",\n  \"clearRecords\": \"清空记录\",\n  \"clearAll\": \"清空所有\",\n  \"downloadRecordsCleared\": \"已清空下载记录\",\n  \"inProgress\": \"进行中\",\n  \"completed\": \"已完成\",\n  \"refresh\": \"刷新\",\n  \"pending\": \"等待中\",\n  \"downloading\": \"下载中\",\n  \"failed\": \"失败\",\n  \"cancelled\": \"已取消\",\n  \"cannotGetDownloadDirectory\": \"无法获取下载目录\",\n  \"startDownload\": \"开始下载: {filename}\",\n  \"@startDownload\": {\n    \"placeholders\": {\n      \"filename\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"downloadProgress\": \"下载进度: {progress}%\",\n  \"@downloadProgress\": {\n    \"placeholders\": {\n      \"progress\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"downloadComplete\": \"下载完成: {filename}\",\n  \"@downloadComplete\": {\n    \"placeholders\": {\n      \"filename\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"open\": \"打开\",\n  \"downloadCancelled\": \"下载已取消: {url}\",\n  \"@downloadCancelled\": {\n    \"placeholders\": {\n      \"url\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"downloadFailedWithError\": \"下载失败: {filename}\",\n  \"@downloadFailedWithError\": {\n    \"placeholders\": {\n      \"filename\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"userCancelledDownload\": \"用户取消下载\",\n  \"cannotGetBaseDownloadDirectory\": \"无法获取基础下载目录\",\n  \"createOpenListDownloadDirectory\": \"创建OpenList下载目录: {path}\",\n  \"@createOpenListDownloadDirectory\": {\n    \"placeholders\": {\n      \"path\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"createOpenListDirectoryFailed\": \"创建OpenList目录失败: {error}\",\n  \"@createOpenListDirectoryFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"openListDownloadDirectory\": \"OpenList下载目录: {path}\",\n  \"@openListDownloadDirectory\": {\n    \"placeholders\": {\n      \"path\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"getDownloadDirectoryFailed\": \"获取下载目录失败: {error}\",\n  \"@getDownloadDirectoryFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"parseFilenameFailed\": \"解析文件名失败: {error}\",\n  \"@parseFilenameFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"needInstallPermission\": \"需要安装权限\",\n  \"needInstallPermissionDesc\": \"为了安装 APK 文件，需要授予安装权限。请在设置中手动开启。\",\n  \"goToSettings\": \"去设置\",\n  \"needInstallPermissionToInstallApk\": \"需要安装权限才能安装 APK 文件\",\n  \"checkInstallPermissionFailed\": \"检查安装权限失败: {error}\",\n  \"@checkInstallPermissionFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"tryToOpenFile\": \"尝试打开文件: {path}\",\n  \"@tryToOpenFile\": {\n    \"placeholders\": {\n      \"path\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"openFileResult\": \"打开文件结果: {type} - {message}\",\n  \"@openFileResult\": {\n    \"placeholders\": {\n      \"type\": {\n        \"type\": \"String\"\n      },\n      \"message\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"cannotInstallApkFile\": \"无法安装 APK 文件，可能需要在设置中开启\\\"允许安装未知来源应用\\\"\",\n  \"noPermissionToInstallApk\": \"没有权限安装 APK 文件，请在设置中开启安装权限\",\n  \"openFileException\": \"打开文件异常: {error}\",\n  \"@openFileException\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"getDownloadFileListFailed\": \"获取下载文件列表失败: {error}\",\n  \"@getDownloadFileListFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"downloadDirectoryCleared\": \"已清理下载目录\",\n  \"clearDownloadDirectoryFailed\": \"清理下载目录失败: {error}\",\n  \"@clearDownloadDirectoryFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"fileDeletedLog\": \"已删除文件: {filename}\",\n  \"@fileDeletedLog\": {\n    \"placeholders\": {\n      \"filename\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"deleteFileFailedLog\": \"删除文件失败: {error}\",\n  \"@deleteFileFailedLog\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"preparingDownload\": \"准备下载...\",\n  \"downloadCancelledStatus\": \"下载已取消\",\n  \"notificationManagerInitialized\": \"通知管理器初始化成功\",\n  \"notificationManagerInitFailed\": \"通知管理器初始化失败: {error}\",\n  \"@notificationManagerInitFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"notificationClicked\": \"通知被点击: {payload}\",\n  \"@notificationClicked\": {\n    \"placeholders\": {\n      \"payload\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"currentDownloadingFiles\": \"当前有 {count} 个文件在下载\",\n  \"@currentDownloadingFiles\": {\n    \"placeholders\": {\n      \"count\": {\n        \"type\": \"int\"\n      }\n    }\n  },\n  \"downloadProgressDesc\": \"显示文件下载进度\",\n  \"viewDownloads\": \"查看下载\",\n  \"showDownloadProgressNotificationFailed\": \"显示下载进度通知失败: {error}\",\n  \"@showDownloadProgressNotificationFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"downloadCompleteNotificationTitle\": \"{filename} 下载完毕\",\n  \"@downloadCompleteNotificationTitle\": {\n    \"placeholders\": {\n      \"filename\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"clickToJumpToDownloadManager\": \"点击跳转到下载管理\",\n  \"downloadCompleteTitle\": \"下载完成\",\n  \"multipleFilesCompleted\": \"{count} 个文件已完成，点击跳转到下载管理\",\n  \"@multipleFilesCompleted\": {\n    \"placeholders\": {\n      \"count\": {\n        \"type\": \"int\"\n      }\n    }\n  },\n  \"downloadCompleteChannel\": \"下载完成\",\n  \"downloadCompleteChannelDesc\": \"文件下载完成通知\",\n  \"openDownloadManager\": \"打开下载管理\",\n  \"showDownloadCompleteNotificationFailed\": \"显示下载完成通知失败: {error}\",\n  \"@showDownloadCompleteNotificationFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"showSingleFileCompleteNotificationFailed\": \"显示单个文件下载完成通知失败: {error}\",\n  \"@showSingleFileCompleteNotificationFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"cancelDownloadNotificationFailed\": \"取消下载通知失败: {error}\",\n  \"@cancelDownloadNotificationFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"cancelAllNotificationsFailed\": \"取消所有通知失败: {error}\",\n  \"@cancelAllNotificationsFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"showInFileManager\": \"在文件管理器中显示\",\n  \"fileSize\": \"大小: {size}\",\n  \"@fileSize\": {\n    \"placeholders\": {\n      \"size\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"fileTime\": \"时间: {time}\",\n  \"@fileTime\": {\n    \"placeholders\": {\n      \"time\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"ok\": \"确定\",\n  \"openFileManager\": \"打开文件管理器\",\n  \"fileManagerOpened\": \"已打开文件管理器\",\n  \"openFileManagerFailed\": \"打开文件管理器失败: {error}\",\n  \"@openFileManagerFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"downloadDirectoryOpened\": \"已打开下载目录\",\n  \"openDownloadDirectoryFailed\": \"打开下载目录失败: {error}\",\n  \"@openDownloadDirectoryFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"downloadDirectoryPathUnknown\": \"下载目录路径未知\",\n  \"cannotGetDownloadDirectoryError\": \"无法获取下载目录\",\n  \"startDownloadFile\": \"开始下载: {filename}\",\n  \"@startDownloadFile\": {\n    \"placeholders\": {\n      \"filename\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"downloadCompleteFile\": \"下载完成: {filename}\",\n  \"@downloadCompleteFile\": {\n    \"placeholders\": {\n      \"filename\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"downloadFailedFile\": \"下载失败: {filename}\",\n  \"@downloadFailedFile\": {\n    \"placeholders\": {\n      \"filename\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"userCancelledDownloadError\": \"用户取消下载\",\n  \"cannotInstallApkNeedPermission\": \"无法安装 APK 文件，可能需要在设置中开启\\\"允许安装未知来源应用\\\"\",\n  \"noPermissionToInstallApkFile\": \"没有权限安装 APK 文件，请在设置中开启安装权限\",\n  \"preparingDownloadStatus\": \"准备下载...\",\n  \"downloadCancelledText\": \"下载已取消\",\n  \"language\": \"语言\",\n  \"languageSettings\": \"语言设置\",\n  \"languageSettingsDesc\": \"选择应用显示语言\",\n  \"followSystem\": \"跟随系统\",\n  \"simplifiedChinese\": \"简体中文\",\n  \"english\": \"English\",\n  \"troubleshooting\": \"疑难解答\",\n  \"troubleshootingDesc\": \"常见问题与解决方案\",\n  \"databaseNotSavedIssue\": \"数据库未保存问题\",\n  \"databaseNotSavedIssueDesc\": \"如不手动关闭OpenList，则数据库可能不会被保存到对应的db文件中，如遇到此问题，请手动关闭以解决此问题。（开关位于主程序菜单OpenList界面，以及通知栏的通知上）\",\n  \"autoStartIssue\": \"自启动相关说明\",\n  \"autoStartIssueDesc\": \"设置自启动时建议把app的电池优化一并关闭，当前在开启自启动后，系统重启时服务会自动在后台启动，但可能不会在通知栏弹出通知。请放心，服务已正常运行，您可以在通知栏快捷开关查看服务状态，或回到主界面查看服务开关确认服务是否已启动。\",\n  \"currentlyDownloading\": \"正在下载\",\n  \"downloadProgressChannel\": \"下载进度\",\n  \"confirmDownload\": \"确认下载\",\n  \"confirmDownloadMessage\": \"是否要下载这个文件？\",\n  \"downloadingImage\": \"正在下载图片...\",\n  \"laterInstall\": \"稍后安装\",\n  \"installNow\": \"立即安装\",\n  \"directDownloadMethod\": \"直接下载\",\n  \"directDownloadMethodDesc\": \"使用应用内下载器\",\n  \"browserDownloadMethod\": \"浏览器下载\",\n  \"browserDownloadMethodDesc\": \"使用系统浏览器\",\n  \"shareLink\": \"分享链接\",\n  \"shareLinkDesc\": \"分享下载链接\",\n  \"view\": \"查看\",\n  \"downloadFunctionTest\": \"下载功能测试\",\n  \"testDirectDownloadFunction\": \"测试直接下载功能\",\n  \"testDownloadJsonFile\": \"测试下载JSON文件\",\n  \"testDownloadPngImage\": \"测试下载PNG图片\",\n  \"testDownloadLargeFile\": \"测试下载大文件(1MB)\",\n  \"viewDownloadFiles\": \"查看下载文件\",\n  \"checkDownloadManagerForFiles\": \"请通过底部导航栏的\\\"下载管理\\\"查看下载文件\",\n  \"viewDownloadDirectory\": \"查看下载目录\",\n  \"getDownloadPathFailed\": \"获取失败\",\n  \"openDownloadTestPage\": \"是否要打开下载测试页面？\",\n  \"description\": \"说明：\",\n  \"downloadInstructions\": \"• 文件将下载到系统下载目录\\\\n• 下载过程会显示进度通知\\\\n• 下载完成后可以选择打开文件\\\\n• 如果文件名重复会自动添加序号\\\\n• 请通过底部导航栏的\\\\\\\"下载管理\\\\\\\"查看下载文件\",\n  \"selectDownloadMethod\": \"选择下载方式\",\n  \"batchDownloadComplete\": \"批量下载完成\",\n  \"imageDownloadSuccess\": \"图片下载成功\",\n  \"checkImageInDownloadFolder\": \"请在下载目录查看图片\",\n  \"findApkInDownloadFolder\": \"请在下载目录找到APK文件进行安装\",\n  \"downloadingFileProgress\": \"正在下载第 {current}/{total} 个文件\",\n  \"@downloadingFileProgress\": {\n    \"placeholders\": {\n      \"current\": {\n        \"type\": \"int\"\n      },\n      \"total\": {\n        \"type\": \"int\"\n      }\n    }\n  },\n  \"fileDownloadFailed\": \"第 {index} 个文件下载失败\",\n  \"@fileDownloadFailed\": {\n    \"placeholders\": {\n      \"index\": {\n        \"type\": \"int\"\n      }\n    }\n  },\n  \"apkDownloadCompleteMessage\": \"APK文件已下载完成，是否要安装？\",\n  \"openlist\": \"OpenList\",\n  \"openlistMobile\": \"OpenList Mobile\",\n  \"openSourceLicenses\": \"开源许可证\",\n  \"viewThirdPartyLicenses\": \"查看第三方许可证\",\n  \"editOpenListConfig\": \"修改OpenList配置文件\",\n  \"save\": \"保存\",\n  \"saved\": \"已保存\",\n  \"edit\": \"编辑\",\n  \"preview\": \"预览\",\n  \"fileNotFoundWillCreateOnSave\": \"文件不存在,保存时将创建\",\n  \"loadFailed\": \"加载失败:{error}\",\n  \"@loadFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"saveFailed\": \"保存失败:{error}\",\n  \"@saveFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"invalidJsonFormat\": \"JSON格式错误,第{line}行:{error}\",\n  \"@invalidJsonFormat\": {\n    \"placeholders\": {\n      \"line\": {\n        \"type\": \"int\"\n      },\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"filePermissionDenied\": \"文件权限被拒绝,请检查应用权限\",\n  \"configSavedRestartRequired\": \"配置已保存,请重启OpenList服务以生效\",\n  \"confirmSaveConfigTitle\": \"确认保存\",\n  \"confirmSaveConfigMessage\": \"修改配置可能导致服务不可用,确定保存吗?\",\n  \"saveAndRestart\": \"保存并重启\",\n  \"saveOnly\": \"仅保存\",\n  \"restoreBackup\": \"恢复备份\",\n  \"backupRestored\": \"备份已恢复\",\n  \"noBackupFound\": \"未找到备份文件\",\n  \"restoreBackupFailed\": \"恢复备份失败:{error}\",\n  \"@restoreBackupFailed\": {\n    \"placeholders\": {\n      \"error\": {\n        \"type\": \"String\"\n      }\n    }\n  },\n  \"restartingService\": \"正在重启OpenList服务...\",\n  \"serviceRestartSuccess\": \"服务重启成功\",\n  \"serviceRestartFailed\": \"服务重启失败,请手动重启\",\n  \"serviceRestartOnlyAndroid\": \"服务重启仅支持Android系统\"\n}"
  },
  {
    "path": "lib/main.dart",
    "content": "import 'package:openlist_mobile/generated/l10n.dart';\nimport 'package:openlist_mobile/pages/openlist/openlist.dart';\nimport 'package:openlist_mobile/pages/app_update_dialog.dart';\nimport 'package:openlist_mobile/pages/settings/settings.dart';\nimport 'package:openlist_mobile/pages/web/web.dart';\nimport 'package:openlist_mobile/pages/download_manager_page.dart';\nimport 'package:openlist_mobile/utils/download_manager.dart';\nimport 'package:openlist_mobile/utils/notification_manager.dart';\nimport 'package:openlist_mobile/utils/service_manager.dart';\nimport 'package:openlist_mobile/utils/language_controller.dart';\nimport 'package:fade_indexed_stack/fade_indexed_stack.dart';\nimport 'package:flutter/foundation.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_inappwebview/flutter_inappwebview.dart';\nimport 'package:flutter_localizations/flutter_localizations.dart';\nimport 'package:flutter_svg/svg.dart';\nimport 'package:get/get.dart';\n\nimport 'contant/native_bridge.dart';\n\nvoid main() async {\n  WidgetsFlutterBinding.ensureInitialized();\n  \n  // Initialize language controller\n  Get.put(LanguageController());\n  \n  // Initialize notification manager\n  await NotificationManager.initialize();\n  \n  // Initialize service manager (supports both Android and iOS)\n  await ServiceManager.instance.initialize();\n  \n  // For iOS: Ensure service is started on first launch\n  if (defaultTargetPlatform == TargetPlatform.iOS) {\n    try {\n      // Check if service is running\n      final isRunning = await ServiceManager.instance.checkServiceStatus();\n      if (!isRunning) {\n        // Start service automatically on iOS\n        await ServiceManager.instance.startService();\n      }\n    } catch (e) {\n      debugPrint('Failed to start iOS service on launch: $e');\n    }\n  }\n  \n  // Android WebView debugging\n  if (!kIsWeb &&\n      kDebugMode &&\n      defaultTargetPlatform == TargetPlatform.android) {\n    await InAppWebViewController.setWebContentsDebuggingEnabled(kDebugMode);\n  }\n\n  runApp(const MyApp());\n}\n\nclass MyApp extends StatelessWidget {\n  const MyApp({super.key});\n\n  // This widget is the root of your application.\n  @override\n  Widget build(BuildContext context) {\n    return GetBuilder<LanguageController>(\n      builder: (languageController) {\n        // 如果语言控制器设置为跟随系统，则使用null让系统自动选择\n        // 否则使用指定的locale\n        Locale? appLocale = languageController.locale;\n        \n        return GetMaterialApp(\n          title: 'OpenList',\n          themeMode: ThemeMode.system,\n          theme: ThemeData(\n            useMaterial3: true,\n            colorSchemeSeed: Colors.blueGrey,\n            inputDecorationTheme: const InputDecorationTheme(\n              border: OutlineInputBorder(),\n            ),\n          ),\n          darkTheme: ThemeData(\n            useMaterial3: true,\n            brightness: Brightness.dark,\n            colorSchemeSeed: Colors.blueGrey,\n            /* dark theme settings */\n          ),\n          locale: appLocale,\n          fallbackLocale: const Locale('en'),\n          supportedLocales: S.delegate.supportedLocales,\n          localizationsDelegates: const [\n            S.delegate,\n            GlobalMaterialLocalizations.delegate,\n            GlobalWidgetsLocalizations.delegate,\n            GlobalCupertinoLocalizations.delegate,\n          ],\n          home: const MyHomePage(title: \"\"),\n        );\n      },\n    );\n  }\n}\n\nclass MyHomePage extends StatelessWidget {\n  const MyHomePage({super.key, required this.title});\n\n  final String title;\n  static const webPageIndex = 0;\n\n  @override\n  Widget build(BuildContext context) {\n    final controller = Get.put(_MainController());\n\n    return Scaffold(\n        body: Obx(\n          () => FadeIndexedStack(\n            lazy: true,\n            index: controller.selectedIndex.value,\n            children: [\n              WebScreen(key: webGlobalKey),\n              const OpenListScreen(),\n              const DownloadManagerPage(),\n              const SettingsScreen()\n            ],\n          ),\n        ),\n        bottomNavigationBar: Obx(() => NavigationBar(\n                destinations: [\n                  NavigationDestination(\n                    icon: const Icon(Icons.preview),\n                    label: S.current.webPage,\n                  ),\n                  NavigationDestination(\n                    icon: SvgPicture.asset(\n                      \"assets/openlist.svg\",\n                      color: Theme.of(context).hintColor,\n                      width: 32,\n                      height: 32,\n                    ),\n                    label: S.current.appName,\n                  ),\n                  NavigationDestination(\n                    icon: const Icon(Icons.arrow_downward),\n                    label: _getDownloadLabel(),\n                  ),\n                  NavigationDestination(\n                    icon: const Icon(Icons.settings),\n                    label: S.current.settings,\n                  ),\n                ],\n                selectedIndex: controller.selectedIndex.value,\n                onDestinationSelected: (int index) {\n                  // Web\n                  if (controller.selectedIndex.value == webPageIndex &&\n                      controller.selectedIndex.value == webPageIndex) {\n                    webGlobalKey.currentState?.onClickNavigationBar();\n                  }\n\n                  controller.setPageIndex(index);\n                })));\n  }\n\n  String _getDownloadLabel() {\n    int activeCount = DownloadManager.activeTasks.length;\n    if (activeCount > 0) {\n      return S.current.downloadManagerWithCount(activeCount);\n    } else {\n      return S.current.downloadManager;\n    }\n  }\n}\n\nclass _MainController extends GetxController {\n  final selectedIndex = 1.obs;\n\n  setPageIndex(int index) {\n    selectedIndex.value = index;\n  }\n\n  @override\n  void onInit() async {\n    final webPage = await NativeBridge.appConfig.isAutoOpenWebPageEnabled();\n    if (webPage) {\n      setPageIndex(MyHomePage.webPageIndex);\n    }\n\n    WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {\n      if (await NativeBridge.appConfig.isAutoCheckUpdateEnabled()) {\n        AppUpdateDialog.checkUpdateAndShowDialog(Get.context!, null);\n      }\n    });\n\n    super.onInit();\n  }\n}"
  },
  {
    "path": "lib/pages/app_update_dialog.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'dart:io' show Platform;\nimport 'package:flutter_smooth_markdown/flutter_smooth_markdown.dart';\n\nimport '../generated/l10n.dart';\nimport '../utils/update_checker.dart';\nimport '../utils/app_store_update.dart';\nimport '../utils/intent_utils.dart';\nimport '../utils/download_manager.dart';\n\nclass AppUpdateDialog extends StatelessWidget {\n  final String content;\n  final String apkUrl;\n  final String htmlUrl;\n  final String version;\n\n  const AppUpdateDialog(\n      {super.key,\n      required this.content,\n      required this.apkUrl,\n      required this.version,\n      required this.htmlUrl});\n\n  static checkUpdateAndShowDialog(\n      BuildContext context, ValueChanged<bool>? checkFinished) async {\n    if (Platform.isIOS) {\n      try {\n        final hasNewVersion = await AppStoreUpdate.checkAndShowUpdate();\n        checkFinished?.call(hasNewVersion);\n      } catch (_) {\n        checkFinished?.call(false);\n      }\n      return;\n    }\n\n    final checker = UpdateChecker(owner: \"openlistteam\", repo: \"OpenList-Mobile\");\n    await checker.downloadData();\n    final hasNewVersion = await checker.hasNewVersion();\n    \n    checkFinished?.call(hasNewVersion);\n    \n    if (hasNewVersion) {\n      showDialog(\n        context: context,\n        barrierDismissible: false,\n        barrierColor: Colors.black.withOpacity(0.5),\n        builder: (context) {\n          return AppUpdateDialog(\n            content: checker.getUpdateContent(),\n            apkUrl: checker.getApkDownloadUrl(),\n            htmlUrl: checker.getHtmlUrl(),\n            version: checker.getTag(),\n          );\n        },\n      );\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final theme = Theme.of(context);\n    final hasValidApkUrl = apkUrl.trim().isNotEmpty &&\n        Uri.tryParse(apkUrl) != null;\n    \n    return AlertDialog(\n      title: Row(\n        children: [\n          Icon(\n            Icons.system_update,\n            color: theme.colorScheme.primary,\n            size: 28,\n          ),\n          const SizedBox(width: 12),\n          Expanded(\n            child: Text(S.of(context).newVersionFound),\n          ),\n        ],\n      ),\n      content: SingleChildScrollView(\n        child: Column(\n          mainAxisSize: MainAxisSize.min,\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: [\n            Container(\n              padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),\n              decoration: BoxDecoration(\n                color: theme.colorScheme.primaryContainer,\n                borderRadius: BorderRadius.circular(20),\n              ),\n              child: Text(\n                version,\n                style: TextStyle(\n                  fontWeight: FontWeight.bold,\n                  color: theme.colorScheme.onPrimaryContainer,\n                ),\n              ),\n            ),\n            const SizedBox(height: 16),\n            Card(\n              margin: EdgeInsets.zero,\n              child: Padding(\n                padding: const EdgeInsets.all(12),\n                child: SmoothMarkdown(\n                  data: content,\n                  onTapLink: (url) {\n                    IntentUtils.getUrlIntent(url).launchChooser(url);\n                  },\n                ),\n              ),\n            ),\n\n            const SizedBox(height: 16),\n            \n            Text(\n              S.of(context).selectDownloadMethod,\n              style: theme.textTheme.titleSmall?.copyWith(\n                fontWeight: FontWeight.bold,\n              ),\n            ),\n            const SizedBox(height: 8),\n            \n            if (Platform.isAndroid && hasValidApkUrl) ...[\n              Card(\n                margin: EdgeInsets.zero,\n                child: ListTile(\n                  leading: Icon(\n                    Icons.download,\n                    color: theme.colorScheme.primary,\n                  ),\n                  title: Text(S.of(context).directDownloadApk),\n                  subtitle: Text(S.of(context).directDownloadMethodDesc),\n                  trailing: const Icon(Icons.chevron_right),\n                  onTap: () async {\n                    Navigator.pop(context);\n                    DownloadManager.downloadFileInBackground(\n                      url: apkUrl,\n                      filename: 'OpenList_$version.apk',\n                    );\n                  },\n                ),\n              ),\n              const SizedBox(height: 8),\n\n              Card(\n                margin: EdgeInsets.zero,\n                child: ListTile(\n                  leading: Icon(\n                    Icons.open_in_browser,\n                    color: theme.colorScheme.secondary,\n                  ),\n                  title: Text(S.of(context).downloadApk),\n                  subtitle: Text(S.of(context).browserDownloadMethodDesc),\n                  trailing: const Icon(Icons.chevron_right),\n                  onTap: () {\n                    Navigator.pop(context);\n                    IntentUtils.getUrlIntent(apkUrl)\n                        .launchChooser(S.of(context).downloadApk);\n                  },\n                ),\n              ),\n              const SizedBox(height: 8),\n            ],\n\n            Card(\n              margin: EdgeInsets.zero,\n              child: ListTile(\n                leading: Icon(\n                  Icons.article_outlined,\n                  color: theme.colorScheme.tertiary,\n                ),\n                title: Text(S.of(context).releasePage),\n                trailing: const Icon(Icons.chevron_right),\n                onTap: () {\n                  Navigator.pop(context);\n                  IntentUtils.getUrlIntent(htmlUrl)\n                      .launchChooser(S.of(context).releasePage);\n                },\n              ),\n            ),\n          ],\n        ),\n      ),\n      actions: <Widget>[\n        TextButton(\n          child: Text(S.of(context).cancel),\n          onPressed: () {\n            Navigator.pop(context);\n          },\n        ),\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "lib/pages/download_manager_page.dart",
    "content": "import 'dart:io';\nimport 'package:flutter/material.dart';\nimport 'package:get/get.dart';\nimport 'package:open_filex/open_filex.dart';\nimport 'package:open_file_manager/open_file_manager.dart';\nimport '../utils/download_manager.dart';\nimport '../generated/l10n.dart';\n\n/// 下载文件管理页面\nclass DownloadManagerPage extends StatefulWidget {\n  const DownloadManagerPage({Key? key}) : super(key: key);\n\n  @override\n  State<DownloadManagerPage> createState() => _DownloadManagerPageState();\n}\n\nclass _DownloadManagerPageState extends State<DownloadManagerPage>\n    with TickerProviderStateMixin {\n  List<FileSystemEntity> _downloadedFiles = [];\n  bool _isLoading = true;\n  String? _downloadPath;\n  late TabController _tabController;\n\n  @override\n  void initState() {\n    super.initState();\n    _tabController = TabController(length: 2, vsync: this);\n    _loadDownloadedFiles();\n    \n    // 定期刷新活跃任务状态\n    _startPeriodicRefresh();\n  }\n\n  @override\n  void dispose() {\n    _tabController.dispose();\n    super.dispose();\n  }\n\n  void _startPeriodicRefresh() {\n    // 每秒刷新一次活跃任务状态\n    Stream.periodic(const Duration(seconds: 1)).listen((_) {\n      if (mounted && _tabController.index == 0) {\n        setState(() {});\n      }\n    });\n  }\n\n  Future<void> _loadDownloadedFiles() async {\n    setState(() {\n      _isLoading = true;\n    });\n\n    try {\n      _downloadedFiles = await DownloadManager.getDownloadedFiles();\n      _downloadPath = await DownloadManager.getDownloadDirectoryPath();\n    } catch (e) {\n      print('${S.current.loadDownloadFilesFailed}: $e');\n    }\n\n    setState(() {\n      _isLoading = false;\n    });\n  }\n\n  String _formatFileSize(int bytes) {\n    if (bytes < 1024) return '${bytes}B';\n    if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)}KB';\n    if (bytes < 1024 * 1024 * 1024) return '${(bytes / (1024 * 1024)).toStringAsFixed(1)}MB';\n    return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(1)}GB';\n  }\n\n  String _formatDateTime(DateTime dateTime) {\n    return '${dateTime.year}-${dateTime.month.toString().padLeft(2, '0')}-${dateTime.day.toString().padLeft(2, '0')} '\n           '${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}';\n  }\n\n  IconData _getFileIcon(String filename) {\n    String extension = filename.split('.').last.toLowerCase();\n    switch (extension) {\n      case 'pdf':\n        return Icons.picture_as_pdf;\n      case 'jpg':\n      case 'jpeg':\n      case 'png':\n      case 'gif':\n        return Icons.image;\n      case 'mp4':\n      case 'avi':\n      case 'mkv':\n        return Icons.video_file;\n      case 'mp3':\n      case 'wav':\n        return Icons.audio_file;\n      case 'zip':\n      case 'rar':\n      case '7z':\n        return Icons.archive;\n      case 'doc':\n      case 'docx':\n        return Icons.description;\n      case 'xls':\n      case 'xlsx':\n        return Icons.table_chart;\n      case 'apk':\n        return Icons.android;\n      default:\n        return Icons.insert_drive_file;\n    }\n  }\n\n  Color _getStatusColor(DownloadStatus status) {\n    switch (status) {\n      case DownloadStatus.pending:\n        return Colors.orange;\n      case DownloadStatus.downloading:\n        return Colors.blue;\n      case DownloadStatus.completed:\n        return Colors.green;\n      case DownloadStatus.failed:\n        return Colors.red;\n      case DownloadStatus.cancelled:\n        return Colors.grey;\n    }\n  }\n\n  Widget _buildActiveTasksTab() {\n    List<DownloadTask> activeTasks = DownloadManager.activeTasks;\n    \n    if (activeTasks.isEmpty) {\n      return Center(\n        child: Column(\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: [\n            const Icon(\n              Icons.download_outlined,\n              size: 64,\n              color: Colors.grey,\n            ),\n            const SizedBox(height: 16),\n            Text(\n              S.of(context).noActiveDownloads,\n              style: const TextStyle(\n                fontSize: 16,\n                color: Colors.grey,\n              ),\n            ),\n          ],\n        ),\n      );\n    }\n\n    return ListView.builder(\n      itemCount: activeTasks.length,\n      itemBuilder: (context, index) {\n        DownloadTask task = activeTasks[index];\n        \n        return Card(\n          margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),\n          child: Padding(\n            padding: const EdgeInsets.all(16),\n            child: Column(\n              crossAxisAlignment: CrossAxisAlignment.start,\n              children: [\n                Row(\n                  children: [\n                    Icon(\n                      _getFileIcon(task.filename),\n                      size: 32,\n                      color: _getStatusColor(task.status),\n                    ),\n                    const SizedBox(width: 12),\n                    Expanded(\n                      child: Column(\n                        crossAxisAlignment: CrossAxisAlignment.start,\n                        children: [\n                          Text(\n                            task.filename,\n                            style: const TextStyle(\n                              fontSize: 16,\n                              fontWeight: FontWeight.w500,\n                            ),\n                            maxLines: 1,\n                            overflow: TextOverflow.ellipsis,\n                          ),\n                          const SizedBox(height: 4),\n                          Text(\n                            task.statusText,\n                            style: TextStyle(\n                              fontSize: 14,\n                              color: _getStatusColor(task.status),\n                            ),\n                          ),\n                        ],\n                      ),\n                    ),\n                    if (task.status == DownloadStatus.downloading)\n                      IconButton(\n                        icon: const Icon(Icons.cancel, color: Colors.red),\n                        onPressed: () {\n                          _confirmCancelDownload(task);\n                        },\n                      ),\n                  ],\n                ),\n                const SizedBox(height: 12),\n                if (task.status == DownloadStatus.downloading) ...[\n                  LinearProgressIndicator(\n                    value: task.progress,\n                    backgroundColor: Colors.grey[300],\n                    valueColor: AlwaysStoppedAnimation<Color>(\n                      _getStatusColor(task.status),\n                    ),\n                  ),\n                  const SizedBox(height: 8),\n                  Row(\n                    mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                    children: [\n                      Text(\n                        task.progressText,\n                        style: const TextStyle(fontSize: 12, color: Colors.grey),\n                      ),\n                      Text(\n                        '${(task.progress * 100).toStringAsFixed(1)}%',\n                        style: const TextStyle(fontSize: 12, color: Colors.grey),\n                      ),\n                    ],\n                  ),\n                ] else if (task.status == DownloadStatus.failed) ...[\n                  Container(\n                    padding: const EdgeInsets.all(8),\n                    decoration: BoxDecoration(\n                      color: Colors.red[50],\n                      borderRadius: BorderRadius.circular(4),\n                    ),\n                    child: Row(\n                      children: [\n                        const Icon(Icons.error, color: Colors.red, size: 16),\n                        const SizedBox(width: 8),\n                        Expanded(\n                          child: Text(\n                            task.errorMessage ?? S.of(context).downloadFailed,\n                            style: const TextStyle(\n                              fontSize: 12,\n                              color: Colors.red,\n                            ),\n                            maxLines: 2,\n                            overflow: TextOverflow.ellipsis,\n                          ),\n                        ),\n                      ],\n                    ),\n                  ),\n                ],\n                const SizedBox(height: 8),\n                Text(\n                  '${S.of(context).startTime}: ${_formatDateTime(task.startTime)}',\n                  style: const TextStyle(fontSize: 12, color: Colors.grey),\n                ),\n              ],\n            ),\n          ),\n        );\n      },\n    );\n  }\n\n  Widget _buildCompletedTasksTab() {\n    List<DownloadTask> completedTasks = DownloadManager.completedTasks;\n    \n    if (_isLoading) {\n      return const Center(child: CircularProgressIndicator());\n    }\n\n    if (completedTasks.isEmpty && _downloadedFiles.isEmpty) {\n      return Center(\n        child: Column(\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: [\n            const Icon(\n              Icons.download_done,\n              size: 64,\n              color: Colors.grey,\n            ),\n            const SizedBox(height: 16),\n            Text(\n              S.of(context).noCompletedDownloads,\n              style: const TextStyle(\n                fontSize: 16,\n                color: Colors.grey,\n              ),\n            ),\n          ],\n        ),\n      );\n    }\n\n    return RefreshIndicator(\n      onRefresh: _loadDownloadedFiles,\n      child: ListView(\n        children: [\n          // 显示任务记录中的已完成下载\n          ...completedTasks.map((task) => Card(\n            margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),\n            child: ListTile(\n              leading: Icon(\n                _getFileIcon(task.filename),\n                size: 32,\n                color: _getStatusColor(task.status),\n              ),\n              title: Text(\n                task.filename,\n                maxLines: 1,\n                overflow: TextOverflow.ellipsis,\n              ),\n              subtitle: Column(\n                crossAxisAlignment: CrossAxisAlignment.start,\n                children: [\n                  Text(\n                    task.statusText,\n                    style: TextStyle(color: _getStatusColor(task.status)),\n                  ),\n                  if (task.endTime != null)\n                    Text('${S.of(context).completedTime}: ${_formatDateTime(task.endTime!)}'),\n                  if (task.totalBytes > 0)\n                    Text('${S.of(context).size}: ${_formatFileSize(task.totalBytes)}'),\n                ],\n              ),\n              trailing: PopupMenuButton<String>(\n                onSelected: (value) {\n                  switch (value) {\n                    case 'open':\n                      if (task.status == DownloadStatus.completed) {\n                        _openFile(task.filePath);\n                      }\n                      break;\n                    case 'open_folder':\n                      if (task.status == DownloadStatus.completed) {\n                        _openFileManager(task.filePath);\n                      }\n                      break;\n                    case 'delete_record':\n                      _confirmDeleteTaskRecord(task);\n                      break;\n                    case 'delete_file':\n                      if (task.status == DownloadStatus.completed) {\n                        _confirmDeleteFile(task.filename);\n                      }\n                      break;\n                  }\n                },\n                itemBuilder: (context) => [\n                  if (task.status == DownloadStatus.completed)\n                    PopupMenuItem(\n                      value: 'open',\n                      child: Row(\n                        children: [\n                          const Icon(Icons.open_in_new),\n                          const SizedBox(width: 8),\n                          Text(S.of(context).openFile),\n                        ],\n                      ),\n                    ),\n                  if (task.status == DownloadStatus.completed)\n                    PopupMenuItem(\n                      value: 'open_folder',\n                      child: Row(\n                        children: [\n                          const Icon(Icons.folder_open),\n                          const SizedBox(width: 8),\n                          Text(S.of(context).showInFileManager),\n                        ],\n                      ),\n                    ),\n                  PopupMenuItem(\n                    value: 'delete_record',\n                    child: Row(\n                      children: [\n                        const Icon(Icons.delete_outline),\n                        const SizedBox(width: 8),\n                        Text(S.of(context).deleteRecord),\n                      ],\n                    ),\n                  ),\n                  if (task.status == DownloadStatus.completed)\n                    PopupMenuItem(\n                      value: 'delete_file',\n                      child: Row(\n                        children: [\n                          const Icon(Icons.delete, color: Colors.red),\n                          const SizedBox(width: 8),\n                          Text(S.of(context).deleteFile, style: const TextStyle(color: Colors.red)),\n                        ],\n                      ),\n                    ),\n                ],\n              ),\n            ),\n          )),\n          \n          // 显示文件系统中的其他下载文件\n          ..._downloadedFiles.where((file) {\n            String filename = file.path.split('/').last;\n            // 过滤掉已经在任务记录的文件\n            return !completedTasks.any((task) => task.filename == filename);\n          }).map((file) {\n            String filename = file.path.split('/').last;\n            FileStat stat = file.statSync();\n            \n            return Card(\n              margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),\n              child: ListTile(\n                leading: Icon(\n                  _getFileIcon(filename),\n                  size: 32,\n                  color: Theme.of(context).primaryColor,\n                ),\n                title: Text(\n                  filename,\n                  maxLines: 1,\n                  overflow: TextOverflow.ellipsis,\n                ),\n                subtitle: Column(\n                  crossAxisAlignment: CrossAxisAlignment.start,\n                  children: [\n                    Text('${S.of(context).size}: ${_formatFileSize(stat.size)}'),\n                    Text('${S.of(context).modifiedTime}: ${_formatDateTime(stat.modified)}'),\n                  ],\n                ),\n                trailing: const Icon(Icons.more_vert),\n                onTap: () => _showFileOptions(file),\n              ),\n            );\n          }),\n        ],\n      ),\n    );\n  }\n\n  void _confirmCancelDownload(DownloadTask task) {\n    showDialog(\n      context: context,\n      builder: (context) => AlertDialog(\n        title: Text(S.of(context).cancelDownload),\n        content: Text(S.of(context).confirmCancelDownload(task.filename)),\n        actions: [\n          TextButton(\n            onPressed: () => Navigator.pop(context),\n            child: Text(S.of(context).continueDownload),\n          ),\n          TextButton(\n            onPressed: () {\n              Navigator.pop(context);\n              DownloadManager.cancelDownload(task.id);\n              setState(() {});\n            },\n            child: Text(S.of(context).cancelDownload, style: const TextStyle(color: Colors.red)),\n          ),\n        ],\n      ),\n    );\n  }\n\n  void _confirmDeleteTaskRecord(DownloadTask task) {\n    showDialog(\n      context: context,\n      builder: (context) => AlertDialog(\n        title: Text(S.of(context).deleteRecord),\n        content: Text(S.of(context).confirmDeleteRecord(task.filename)),\n        actions: [\n          TextButton(\n            onPressed: () => Navigator.pop(context),\n            child: Text(S.of(context).cancel),\n          ),\n          TextButton(\n            onPressed: () {\n              Navigator.pop(context);\n              DownloadManager.removeTask(task.id);\n              setState(() {});\n            },\n            child: Text(S.of(context).delete, style: const TextStyle(color: Colors.red)),\n          ),\n        ],\n      ),\n    );\n  }\n\n  void _showFileOptions(FileSystemEntity file) {\n    String filename = file.path.split('/').last;\n    \n    showModalBottomSheet(\n      context: context,\n      builder: (context) => Container(\n        padding: const EdgeInsets.all(16),\n        child: Column(\n          mainAxisSize: MainAxisSize.min,\n          children: [\n            Text(\n              filename,\n              style: Theme.of(context).textTheme.titleMedium,\n              maxLines: 2,\n              overflow: TextOverflow.ellipsis,\n            ),\n            const SizedBox(height: 16),\n            ListTile(\n              leading: const Icon(Icons.open_in_new),\n              title: Text(S.of(context).openFile),\n              onTap: () async {\n                Navigator.pop(context);\n                await _openFile(file.path);\n              },\n            ),\n            ListTile(\n              leading: const Icon(Icons.folder_open),\n              title: Text(S.of(context).showInFileManager),\n              onTap: () {\n                Navigator.pop(context);\n                _openFileManager(file.path);\n              },\n            ),\n            ListTile(\n              leading: const Icon(Icons.share),\n              title: Text(S.of(context).shareFile),\n              onTap: () {\n                Navigator.pop(context);\n                // 这里可以添加分享功能\n                Get.showSnackbar(GetSnackBar(\n                  message: S.of(context).shareFeatureNotImplemented,\n                  duration: const Duration(seconds: 2),\n                ));\n              },\n            ),\n            ListTile(\n              leading: const Icon(Icons.info),\n              title: Text(S.of(context).fileInfo),\n              onTap: () {\n                Navigator.pop(context);\n                _showFileInfo(file);\n              },\n            ),\n            ListTile(\n              leading: const Icon(Icons.delete, color: Colors.red),\n              title: Text(S.of(context).deleteFile, style: const TextStyle(color: Colors.red)),\n              onTap: () {\n                Navigator.pop(context);\n                _confirmDeleteFile(filename);\n              },\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n\n  void _showFileInfo(FileSystemEntity file) {\n    String filename = file.path.split('/').last;\n    FileStat stat = file.statSync();\n    \n    showDialog(\n      context: context,\n      builder: (context) => AlertDialog(\n        title: Text(S.of(context).fileInfo),\n        content: Column(\n          mainAxisSize: MainAxisSize.min,\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: [\n            Text('${S.of(context).fileName}: $filename'),\n            const SizedBox(height: 8),\n            Text('${S.of(context).size}: ${_formatFileSize(stat.size)}'),\n            const SizedBox(height: 8),\n            Text('${S.of(context).modifiedTime}: ${_formatDateTime(stat.modified)}'),\n            const SizedBox(height: 8),\n            Text('${S.of(context).filePath}: ${file.path}'),\n          ],\n        ),\n        actions: [\n          TextButton(\n            onPressed: () => Navigator.pop(context),\n            child: Text(S.of(context).ok),\n          ),\n        ],\n      ),\n    );\n  }\n\n  void _confirmDeleteFile(String filename) {\n    showDialog(\n      context: context,\n      builder: (context) => AlertDialog(\n        title: Text(S.of(context).confirmDelete),\n        content: Text(S.of(context).confirmDeleteFile(filename)),\n        actions: [\n          TextButton(\n            onPressed: () => Navigator.pop(context),\n            child: Text(S.of(context).cancel),\n          ),\n          TextButton(\n            onPressed: () async {\n              Navigator.pop(context);\n              bool success = await DownloadManager.deleteFile(filename);\n              if (success) {\n                Get.showSnackbar(GetSnackBar(\n                  message: S.of(context).fileDeleted,\n                  duration: const Duration(seconds: 2),\n                ));\n                _loadDownloadedFiles(); // 刷新列表\n              } else {\n                Get.showSnackbar(GetSnackBar(\n                  message: S.of(context).deleteFailed,\n                  duration: const Duration(seconds: 2),\n                ));\n              }\n            },\n            child: Text(S.of(context).delete, style: const TextStyle(color: Colors.red)),\n          ),\n        ],\n      ),\n    );\n  }\n\n  void _confirmClearAll() {\n    showDialog(\n      context: context,\n      builder: (context) => AlertDialog(\n        title: Text(S.of(context).confirmClear),\n        content: Text(S.of(context).confirmClearAllFiles),\n        actions: [\n          TextButton(\n            onPressed: () => Navigator.pop(context),\n            child: Text(S.of(context).cancel),\n          ),\n          TextButton(\n            onPressed: () async {\n              Navigator.pop(context);\n              bool success = await DownloadManager.clearDownloadDirectory();\n              if (success) {\n                DownloadManager.clearCompletedTasks();\n                Get.showSnackbar(GetSnackBar(\n                  message: S.of(context).cleared,\n                  duration: const Duration(seconds: 2),\n                ));\n                _loadDownloadedFiles(); // 刷新列表\n                setState(() {});\n              } else {\n                Get.showSnackbar(GetSnackBar(\n                  message: S.of(context).clearFailed,\n                  duration: const Duration(seconds: 2),\n                ));\n              }\n            },\n            child: Text(S.of(context).clear, style: const TextStyle(color: Colors.red)),\n          ),\n        ],\n      ),\n    );\n  }\n\n  /// 打开文件\n  Future<void> _openFile(String filePath) async {\n    try {\n      final result = await OpenFilex.open(filePath);\n      \n      switch (result.type) {\n        case ResultType.done:\n          // 文件成功打开，不需要额外提示\n          break;\n        case ResultType.noAppToOpen:\n          Get.showSnackbar(GetSnackBar(\n            message: S.of(context).noAppToOpenFile,\n            duration: const Duration(seconds: 3),\n            mainButton: TextButton(\n              onPressed: () {\n                _showFileLocation(filePath);\n              },\n              child: Text(S.of(context).viewLocation),\n            ),\n          ));\n          break;\n        case ResultType.fileNotFound:\n          Get.showSnackbar(GetSnackBar(\n            message: S.of(context).fileNotFound,\n            duration: const Duration(seconds: 3),\n          ));\n          break;\n        case ResultType.permissionDenied:\n          Get.showSnackbar(GetSnackBar(\n            message: S.of(context).noPermissionToOpenFile,\n            duration: const Duration(seconds: 3),\n          ));\n          break;\n        case ResultType.error:\n          Get.showSnackbar(GetSnackBar(\n            message: S.of(context).openFileFailed(result.message ?? ''),\n            duration: const Duration(seconds: 3),\n            mainButton: TextButton(\n              onPressed: () {\n                _showFileLocation(filePath);\n              },\n              child: Text(S.of(context).viewLocation),\n            ),\n          ));\n          break;\n      }\n    } catch (e) {\n      Get.showSnackbar(GetSnackBar(\n        message: S.of(context).openFileFailed(e.toString()),\n        duration: const Duration(seconds: 3),\n        mainButton: TextButton(\n          onPressed: () {\n            _showFileLocation(filePath);\n          },\n          child: Text(S.of(context).viewLocation),\n        ),\n      ));\n    }\n  }\n\n  /// 打开文件管理器并跳转到指定文件位置\n  Future<void> _openFileManager(String filePath) async {\n    try {\n      // 获取文件所在目录\n      String directoryPath = filePath.substring(0, filePath.lastIndexOf('/'));\n      \n      // 尝试打开文件管理器并定位到文件\n      await openFileManager(\n        androidConfig: AndroidConfig(\n          folderType: AndroidFolderType.other,\n          folderPath: directoryPath,\n        ),\n        iosConfig: IosConfig(\n          folderPath: directoryPath,\n        ),\n      );\n      \n      Get.showSnackbar(GetSnackBar(\n        message: S.of(context).fileManagerOpened,\n        duration: const Duration(seconds: 2),\n      ));\n    } catch (e) {\n      print('打开文件管理器失败: $e');\n      Get.showSnackbar(GetSnackBar(\n        message: S.of(context).openFileManagerFailed(e.toString()),\n        duration: const Duration(seconds: 3),\n      ));\n    }\n  }\n\n  /// 打开下载目录\n  Future<void> _openDownloadDirectory() async {\n    if (_downloadPath != null) {\n      try {\n        await openFileManager(\n          androidConfig: AndroidConfig(\n            folderType: AndroidFolderType.other,\n            folderPath: _downloadPath!,\n          ),\n          iosConfig: IosConfig(\n            folderPath: _downloadPath!,\n          ),\n        );\n        Get.showSnackbar(GetSnackBar(\n          message: S.of(context).downloadDirectoryOpened,\n          duration: const Duration(seconds: 2),\n        ));\n      } catch (e) {\n        print('打开下载目录失败: $e');\n        Get.showSnackbar(GetSnackBar(\n          message: S.of(context).openDownloadDirectoryFailed(e.toString()),\n          duration: const Duration(seconds: 3),\n        ));\n      }\n    } else {\n      Get.showSnackbar(GetSnackBar(\n        message: S.of(context).downloadDirectoryPathUnknown,\n        duration: const Duration(seconds: 2),\n      ));\n    }\n  }\n\n  /// 显示文件位置信息\n  void _showFileLocation(String filePath) {\n    Get.dialog(\n      AlertDialog(\n        title: Text(S.of(context).fileLocation),\n        content: Column(\n          mainAxisSize: MainAxisSize.min,\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: [\n            Text(S.of(context).fileSavedTo),\n            const SizedBox(height: 8),\n            SelectableText(\n              filePath,\n              style: const TextStyle(fontSize: 12),\n            ),\n            const SizedBox(height: 16),\n            Text(\n              S.of(context).fileLocationTip,\n              style: const TextStyle(fontSize: 14, color: Colors.grey),\n            ),\n          ],\n        ),\n        actions: [\n          TextButton(\n            onPressed: () => Get.back(),\n            child: Text(S.of(context).cancel),\n          ),\n          TextButton(\n            onPressed: () {\n              Get.back();\n              _openFileManager(filePath);\n            },\n            child: Text(S.of(context).openFileManager),\n          ),\n        ],\n      ),\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        title: Text(S.of(context).downloadManager),\n        bottom: TabBar(\n          controller: _tabController,\n          tabs: [\n            Tab(\n              icon: const Icon(Icons.downloading),\n              text: '${S.of(context).inProgress} (${DownloadManager.activeTasks.length})',\n            ),\n            Tab(\n              icon: const Icon(Icons.download_done),\n              text: '${S.of(context).completed} (${DownloadManager.completedTasks.length + _downloadedFiles.length})',\n            ),\n          ],\n        ),\n        actions: [\n          IconButton(\n            icon: const Icon(Icons.refresh),\n            onPressed: () {\n              _loadDownloadedFiles();\n              setState(() {});\n            },\n          ),\n          PopupMenuButton<String>(\n            onSelected: (value) {\n              switch (value) {\n                case 'clear_records':\n                  DownloadManager.clearCompletedTasks();\n                  setState(() {});\n                  Get.showSnackbar(GetSnackBar(\n                    message: S.of(context).downloadRecordsCleared,\n                    duration: const Duration(seconds: 2),\n                  ));\n                  break;\n                case 'clear_all':\n                  _confirmClearAll();\n                  break;\n                case 'open_folder':\n                  _openDownloadDirectory();\n                  break;\n              }\n            },\n            itemBuilder: (context) => [\n              PopupMenuItem(\n                value: 'open_folder',\n                child: Row(\n                  children: [\n                    const Icon(Icons.folder_open),\n                    const SizedBox(width: 8),\n                    Text(S.of(context).openDirectory),\n                  ],\n                ),\n              ),\n              PopupMenuItem(\n                value: 'clear_records',\n                child: Row(\n                  children: [\n                    const Icon(Icons.clear_all),\n                    const SizedBox(width: 8),\n                    Text(S.of(context).clearRecords),\n                  ],\n                ),\n              ),\n              PopupMenuItem(\n                value: 'clear_all',\n                child: Row(\n                  children: [\n                    const Icon(Icons.delete_forever, color: Colors.red),\n                    const SizedBox(width: 8),\n                    Text(S.of(context).clearAll, style: const TextStyle(color: Colors.red)),\n                  ],\n                ),\n              ),\n            ],\n          ),\n        ],\n      ),\n      body: TabBarView(\n        controller: _tabController,\n        children: [\n          _buildActiveTasksTab(),\n          _buildCompletedTasksTab(),\n        ],\n      ),\n    );\n  }\n}"
  },
  {
    "path": "lib/pages/openlist/about_dialog.dart",
    "content": "import 'dart:ffi';\n\nimport 'package:openlist_mobile/contant/native_bridge.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_svg/svg.dart';\nimport 'package:get/get.dart';\n\nimport '../../generated/l10n.dart';\nimport '../../generated_api.dart';\nimport '../../utils/intent_utils.dart';\n\nclass AppAboutDialog extends StatefulWidget {\n  const AppAboutDialog({super.key});\n\n  @override\n  State<AppAboutDialog> createState() {\n    return _AppAboutDialogState();\n  }\n}\n\nclass _AppAboutDialogState extends State<AppAboutDialog> {\n  String _openlistVersion = \"\";\n  String _version = \"\";\n  int _versionCode = 0;\n\n  Future<Void?> updateVer() async {\n    _openlistVersion = await Android().getOpenListVersion();\n    _version = await NativeBridge.common.getVersionName();\n    _versionCode = await NativeBridge.common.getVersionCode();\n    return null;\n  }\n\n  @override\n  void initState() {\n    updateVer().then((value) => setState(() {}));\n\n    super.initState();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final theme = Theme.of(context);\n    final openlistUrl =\n        \"https://github.com/OpenListTeam/OpenList/releases/tag/$_openlistVersion\";\n    final appUrl =\n        \"https://github.com/OpenListTeam/OpenList-Mobile/releases/tag/$_version\";\n    \n    return Dialog(\n      child: SingleChildScrollView(\n        child: Padding(\n          padding: const EdgeInsets.all(24),\n          child: Column(\n            mainAxisSize: MainAxisSize.min,\n            crossAxisAlignment: CrossAxisAlignment.center,\n            children: [\n              SvgPicture.asset(\n                \"assets/openlist.svg\",\n                width: 72,\n                height: 72,\n              ),\n              const SizedBox(height: 16),\n              Text(\n                S.of(context).appName,\n                style: theme.textTheme.headlineSmall?.copyWith(\n                  fontWeight: FontWeight.bold,\n                ),\n              ),\n              const SizedBox(height: 4),\n              Text(\n                '$_version ($_versionCode)',\n                style: theme.textTheme.bodyMedium?.copyWith(\n                  color: theme.colorScheme.onSurfaceVariant,\n                ),\n              ),\n              const SizedBox(height: 24),\n              Align(\n                alignment: Alignment.centerLeft,\n                child: Text(\n                  S.of(context).about,\n                  style: theme.textTheme.titleSmall?.copyWith(\n                    fontWeight: FontWeight.bold,\n                  ),\n                ),\n              ),\n              const SizedBox(height: 8),\n              Card(\n                margin: EdgeInsets.zero,\n                child: ListTile(\n                  leading: Icon(\n                    Icons.folder_open,\n                    color: theme.colorScheme.primary,\n                  ),\n                  title: Text(S.of(context).openlist),\n                  subtitle: Text(_openlistVersion.isNotEmpty \n                      ? _openlistVersion\n                      : S.of(context).about),\n                  trailing: const Icon(Icons.open_in_new, size: 20),\n                  onTap: () {\n                    IntentUtils.getUrlIntent(openlistUrl).launchChooser(S.of(context).openlist);\n                  },\n                  onLongPress: () {\n                    Clipboard.setData(ClipboardData(text: openlistUrl));\n                    Get.showSnackbar(GetSnackBar(\n                      message: S.of(context).copiedToClipboard,\n                      duration: const Duration(seconds: 1),\n                    ));\n                  },\n                ),\n              ),\n              const SizedBox(height: 8),\n              Card(\n                margin: EdgeInsets.zero,\n                child: ListTile(\n                  leading: Icon(\n                    Icons.phone_android,\n                    color: theme.colorScheme.secondary,\n                  ),\n                  title: Text(S.of(context).openlistMobile),\n                  subtitle: Text(_version),\n                  trailing: const Icon(Icons.open_in_new, size: 20),\n                  onTap: () {\n                    IntentUtils.getUrlIntent(appUrl).launchChooser(S.of(context).openlistMobile);\n                  },\n                  onLongPress: () {\n                    Clipboard.setData(ClipboardData(text: appUrl));\n                    Get.showSnackbar(GetSnackBar(\n                      message: S.of(context).copiedToClipboard,\n                      duration: const Duration(seconds: 1),\n                    ));\n                  },\n                ),\n              ),\n              const SizedBox(height: 8),\n              Card(\n                margin: EdgeInsets.zero,\n                child: ListTile(\n                  leading: Icon(\n                    Icons.description_outlined,\n                    color: theme.colorScheme.tertiary,\n                  ),\n                  title: Text(S.of(context).openSourceLicenses),\n                  subtitle: Text(S.of(context).viewThirdPartyLicenses),\n                  trailing: const Icon(Icons.chevron_right),\n                  onTap: () {\n                    showLicensePage(\n                      context: context,\n                      applicationName: S.of(context).appName,\n                      applicationVersion: '$_version ($_versionCode)',\n                      applicationIcon: Padding(\n                        padding: const EdgeInsets.all(8.0),\n                        child: SvgPicture.asset(\n                          \"assets/openlist.svg\",\n                          width: 48,\n                          height: 48,\n                        ),\n                      ),\n                    );\n                  },\n                ),\n              ),\n              const SizedBox(height: 24),\n              SizedBox(\n                width: double.infinity,\n                child: FilledButton.tonal(\n                  onPressed: () => Navigator.pop(context),\n                  child: Text(S.of(context).ok),\n                ),\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/pages/openlist/config_editor_page.dart",
    "content": "import 'dart:io';\nimport 'dart:async';\nimport 'dart:convert';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_highlight/flutter_highlight.dart';\nimport 'package:flutter_highlight/themes/monokai-sublime.dart';\nimport 'package:flutter_highlight/themes/github.dart';\nimport 'package:get/get.dart';\nimport '../../contant/native_bridge.dart';\nimport '../../generated/l10n.dart';\nimport '../../utils/service_manager.dart';\n\n/// Config.json Editor with validation, backup, and real-time syntax checking\nclass ConfigEditorPage extends StatefulWidget {\n  const ConfigEditorPage({Key? key}) : super(key: key);\n\n  @override\n  State<ConfigEditorPage> createState() => _ConfigEditorPageState();\n}\n\nclass _ConfigEditorPageState extends State<ConfigEditorPage> {\n  final TextEditingController _controller = TextEditingController();\n  String _filePath = '';\n  String _backupFilePath = '';\n  bool _isLoading = true;\n  bool _isPreview = false;\n  String? _errorMessage;\n  String? _jsonErrorMessage;\n  int? _jsonErrorLine;\n  Timer? _debounceTimer;\n\n  @override\n  void initState() {\n    super.initState();\n    _loadConfigFile();\n    // Real-time JSON validation\n    _controller.addListener(_validateJson);\n  }\n\n  /// Real-time JSON syntax validation with debounce\n  void _validateJson() {\n    if (_isPreview) return; // Skip validation in preview mode\n    \n    // Cancel previous timer to implement debounce\n    _debounceTimer?.cancel();\n    \n    // Create new timer with 300ms delay\n    _debounceTimer = Timer(const Duration(milliseconds: 300), () {\n      final text = _controller.text.trim();\n      if (text.isEmpty) {\n        if (mounted) {\n          setState(() {\n            _jsonErrorMessage = null;\n            _jsonErrorLine = null;\n          });\n        }\n        return;\n      }\n\n      try {\n        jsonDecode(text);\n        if (mounted) {\n          setState(() {\n            _jsonErrorMessage = null;\n            _jsonErrorLine = null;\n          });\n        }\n      } on FormatException catch (e) {\n        if (mounted) {\n          // Extract line number from error message\n          final match = RegExp(r'line (\\d+)').firstMatch(e.message);\n          setState(() {\n            _jsonErrorMessage = e.message;\n            _jsonErrorLine = match != null ? int.tryParse(match.group(1) ?? '') : null;\n          });\n        }\n      }\n    });\n  }\n\n  /// Load config file with permission checking\n  Future<void> _loadConfigFile() async {\n    try {\n      setState(() {\n        _isLoading = true;\n        _errorMessage = null;\n      });\n\n      final dataDir = await NativeBridge.appConfig.getDataDir();\n      _filePath = '$dataDir/config.json';\n      _backupFilePath = '$dataDir/config.json.backup';\n      final file = File(_filePath);\n      \n      if (await file.exists()) {\n        _controller.text = await file.readAsString();\n      } else {\n        _controller.text = '{\\n  \\n}';\n        if (mounted) {\n          setState(() {\n            _errorMessage = S.of(context).fileNotFoundWillCreateOnSave;\n          });\n        }\n      }\n    } on FileSystemException catch (e) {\n      if (mounted) {\n        setState(() {\n          _errorMessage = e.osError?.errorCode == 13 \n            ? S.of(context).filePermissionDenied \n            : S.of(context).loadFailed(e.message);\n        });\n      }\n    } catch (e) {\n      if (mounted) {\n        setState(() {\n          _errorMessage = S.of(context).loadFailed(e.toString());\n        });\n      }\n    } finally {\n      if (mounted) {\n        setState(() => _isLoading = false);\n      }\n    }\n  }\n\n  /// Restore from backup file\n  Future<void> _restoreBackup() async {\n    try {\n      final backupFile = File(_backupFilePath);\n      if (!await backupFile.exists()) {\n        if (mounted) {\n          Get.showSnackbar(GetSnackBar(\n            message: S.of(context).noBackupFound,\n            duration: const Duration(seconds: 2),\n          ));\n        }\n        return;\n      }\n\n      final backupContent = await backupFile.readAsString();\n      setState(() {\n        _controller.text = backupContent;\n      });\n\n      if (mounted) {\n        Get.showSnackbar(GetSnackBar(\n          message: S.of(context).backupRestored,\n          duration: const Duration(seconds: 2),\n        ));\n      }\n    } catch (e) {\n      if (mounted) {\n        Get.showSnackbar(GetSnackBar(\n          message: S.of(context).restoreBackupFailed(e.toString()),\n          duration: const Duration(seconds: 2),\n        ));\n      }\n    }\n  }\n\n  /// Show confirmation dialog before saving\n  /// Provides three options: Cancel, Save Only, Save and Restart\n  Future<void> _showSaveConfirmation() async {\n    final result = await showDialog<String>(\n      context: context,\n      builder: (context) => AlertDialog(\n        title: Text(S.of(context).confirmSaveConfigTitle),\n        content: Text(S.of(context).confirmSaveConfigMessage),\n        actions: [\n          TextButton(\n            onPressed: () => Navigator.of(context).pop('cancel'),\n            child: Text(S.of(context).cancel),\n          ),\n          TextButton(\n            onPressed: () => Navigator.of(context).pop('save'),\n            child: Text(S.of(context).saveOnly),\n          ),\n          FilledButton(\n            onPressed: () => Navigator.of(context).pop('save_restart'),\n            child: Text(S.of(context).saveAndRestart),\n          ),\n        ],\n      ),\n    );\n\n    if (result == 'save' || result == 'save_restart') {\n      final saveSuccess = await _saveConfigFile();\n      \n      // Restart service if requested and save was successful\n      if (saveSuccess && result == 'save_restart') {\n        await _restartOpenListService();\n      }\n    }\n  }\n\n  /// Restart OpenList service after config changes\n  /// Calls ServiceManager.instance.restartService() to stop and start the service\n  /// Only works on Android platform\n  Future<void> _restartOpenListService() async {\n    if (!Platform.isAndroid) {\n      if (mounted) {\n        Get.showSnackbar(GetSnackBar(\n          message: S.of(context).serviceRestartOnlyAndroid,\n          duration: const Duration(seconds: 2),\n        ));\n      }\n      return;\n    }\n\n    try {\n      // Show loading indicator\n      if (mounted) {\n        Get.showSnackbar(GetSnackBar(\n          message: S.of(context).restartingService,\n          duration: const Duration(seconds: 2),\n          showProgressIndicator: true,\n        ));\n      }\n\n      // Restart service via ServiceManager\n      final success = await ServiceManager.instance.restartService();\n      \n      if (mounted) {\n        if (success) {\n          Get.showSnackbar(GetSnackBar(\n            message: S.of(context).serviceRestartSuccess,\n            duration: const Duration(seconds: 3),\n          ));\n        } else {\n          Get.showSnackbar(GetSnackBar(\n            message: S.of(context).serviceRestartFailed,\n            duration: const Duration(seconds: 3),\n            backgroundColor: Colors.orange,\n          ));\n        }\n      }\n    } catch (e) {\n      if (mounted) {\n        Get.showSnackbar(GetSnackBar(\n          message: S.of(context).saveFailed(e.toString()),\n          duration: const Duration(seconds: 3),\n          backgroundColor: Colors.red,\n        ));\n      }\n    }\n  }\n\n  /// Save config file with JSON validation and backup mechanism\n  /// Returns true if save was successful, false otherwise\n  Future<bool> _saveConfigFile() async {\n    final text = _controller.text.trim();\n    \n    // Validate JSON format before saving\n    try {\n      jsonDecode(text);\n    } on FormatException catch (e) {\n      if (mounted) {\n        final match = RegExp(r'line (\\d+)').firstMatch(e.message);\n        final line = match != null ? int.tryParse(match.group(1) ?? '') : null;\n        Get.showSnackbar(GetSnackBar(\n          message: line != null \n            ? S.of(context).invalidJsonFormat(line, e.message)\n            : S.of(context).saveFailed(e.message),\n          duration: const Duration(seconds: 3),\n          backgroundColor: Colors.red,\n        ));\n      }\n      return false;\n    }\n\n    File? backupFile;\n    try {\n      final file = File(_filePath);\n      \n      // Create backup before saving\n      if (await file.exists()) {\n        backupFile = File(_backupFilePath);\n        await file.copy(_backupFilePath);\n      }\n\n      // Ensure parent directory exists\n      await file.parent.create(recursive: true);\n      \n      // Write new config\n      await file.writeAsString(text);\n      \n      if (mounted) {\n        Get.showSnackbar(GetSnackBar(\n          message: S.of(context).saved,\n          duration: const Duration(seconds: 2),\n        ));\n      }\n      \n      return true;\n    } on FileSystemException catch (e) {\n      // Restore backup on failure\n      if (backupFile != null && await backupFile.exists()) {\n        try {\n          await backupFile.copy(_filePath);\n        } catch (_) {}\n      }\n      \n      if (mounted) {\n        final errorMsg = e.osError?.errorCode == 13 \n          ? S.of(context).filePermissionDenied \n          : S.of(context).saveFailed(e.message);\n        Get.showSnackbar(GetSnackBar(\n          message: errorMsg,\n          duration: const Duration(seconds: 3),\n          backgroundColor: Colors.red,\n        ));\n      }\n      return false;\n    } catch (e) {\n      // Restore backup on failure\n      if (backupFile != null && await backupFile.exists()) {\n        try {\n          await backupFile.copy(_filePath);\n        } catch (_) {}\n      }\n      \n      if (mounted) {\n        Get.showSnackbar(GetSnackBar(\n          message: S.of(context).saveFailed(e.toString()),\n          duration: const Duration(seconds: 3),\n          backgroundColor: Colors.red,\n        ));\n      }\n      return false;\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final isDark = Theme.of(context).brightness == Brightness.dark;\n    \n    return Scaffold(\n      appBar: AppBar(\n        title: const Text('config.json'),\n        actions: [\n          // Restore backup button\n          IconButton(\n            icon: const Icon(Icons.restore),\n            onPressed: _restoreBackup,\n            tooltip: S.of(context).restoreBackup,\n          ),\n          // Toggle preview/edit mode\n          IconButton(\n            icon: Icon(_isPreview ? Icons.edit : Icons.visibility),\n            onPressed: () => setState(() => _isPreview = !_isPreview),\n            tooltip: _isPreview ? S.of(context).edit : S.of(context).preview,\n          ),\n          // Reload file\n          IconButton(\n            icon: const Icon(Icons.refresh),\n            onPressed: _loadConfigFile,\n            tooltip: S.of(context).refresh,\n          ),\n          // Save with confirmation\n          IconButton(\n            icon: const Icon(Icons.save),\n            onPressed: _showSaveConfirmation,\n            tooltip: S.of(context).save,\n          ),\n        ],\n      ),\n      body: _isLoading\n          ? const Center(child: CircularProgressIndicator())\n          : Column(\n              crossAxisAlignment: CrossAxisAlignment.stretch,\n              children: [\n                // Warning message banner\n                if (_errorMessage != null)\n                  Container(\n                    color: Colors.orange.withOpacity(0.2),\n                    padding: const EdgeInsets.all(8),\n                    child: Row(\n                      children: [\n                        const Icon(Icons.warning, color: Colors.orange, size: 20),\n                        const SizedBox(width: 8),\n                        Expanded(\n                          child: Text(_errorMessage!, \n                            style: const TextStyle(color: Colors.orange, fontSize: 12)),\n                        ),\n                      ],\n                    ),\n                  ),\n                \n                // File path display\n                Container(\n                  color: Theme.of(context).colorScheme.surfaceContainerHighest,\n                  padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),\n                  child: Text(_filePath, \n                    style: Theme.of(context).textTheme.bodySmall),\n                ),\n                \n                // Editor or preview\n                Expanded(\n                  child: _isPreview\n                      ? SingleChildScrollView(\n                          padding: const EdgeInsets.all(16),\n                          child: HighlightView(\n                            _controller.text,\n                            language: 'json',\n                            theme: isDark ? monokaiSublimeTheme : githubTheme,\n                            textStyle: const TextStyle(\n                              fontFamily: 'monospace',\n                              fontSize: 14,\n                            ),\n                          ),\n                        )\n                      : TextField(\n                          controller: _controller,\n                          maxLines: null,\n                          expands: true,\n                          style: const TextStyle(\n                            fontFamily: 'monospace',\n                            fontSize: 14,\n                          ),\n                          decoration: const InputDecoration(\n                            border: InputBorder.none,\n                            contentPadding: EdgeInsets.all(16),\n                          ),\n                        ),\n                ),\n                \n                // JSON syntax error display at bottom\n                if (_jsonErrorMessage != null && !_isPreview)\n                  Container(\n                    color: Colors.red.withOpacity(0.1),\n                    padding: const EdgeInsets.all(12),\n                    child: Row(\n                      crossAxisAlignment: CrossAxisAlignment.start,\n                      children: [\n                        const Icon(Icons.error, color: Colors.red, size: 20),\n                        const SizedBox(width: 8),\n                        Expanded(\n                          child: Column(\n                            crossAxisAlignment: CrossAxisAlignment.start,\n                            mainAxisSize: MainAxisSize.min,\n                            children: [\n                              if (_jsonErrorLine != null)\n                                Text(\n                                  'Line $_jsonErrorLine',\n                                  style: const TextStyle(\n                                    color: Colors.red,\n                                    fontWeight: FontWeight.bold,\n                                    fontSize: 12,\n                                  ),\n                                ),\n                              Text(\n                                _jsonErrorMessage!,\n                                style: const TextStyle(\n                                  color: Colors.red,\n                                  fontSize: 11,\n                                ),\n                              ),\n                            ],\n                          ),\n                        ),\n                      ],\n                    ),\n                  ),\n              ],\n            ),\n    );\n  }\n\n  @override\n  void dispose() {\n    _debounceTimer?.cancel();\n    _controller.dispose();\n    super.dispose();\n  }\n}\n"
  },
  {
    "path": "lib/pages/openlist/log_level_view.dart",
    "content": "import 'package:flutter/cupertino.dart';\n\nimport '../../contant/log_level.dart';\n\nclass LogLevelView extends StatefulWidget {\n  final int level;\n\n  const LogLevelView({super.key, required this.level});\n\n  @override\n  State<LogLevelView> createState() => _LogLevelViewState();\n}\n\nclass _LogLevelViewState extends State<LogLevelView> {\n  @override\n  Widget build(BuildContext context) {\n    final s = LogLevel.toStr(widget.level);\n    final c = LogLevel.toColor(widget.level);\n    return Text(s, style: TextStyle(color: c));\n  }\n}\n"
  },
  {
    "path": "lib/pages/openlist/log_list_view.dart",
    "content": "import 'package:openlist_mobile/pages/openlist/log_level_view.dart';\nimport 'package:flutter/material.dart';\n\nclass Log {\n  final int level;\n  final String time;\n  final String content;\n\n  Log(this.level, this.time, this.content);\n}\n\nclass LogListView extends StatefulWidget {\n  const LogListView({Key? key, required this.logs, this.controller}) : super(key: key);\n\n  final List<Log> logs;\n  final ScrollController? controller;\n\n  @override\n  State<LogListView> createState() => _LogListViewState();\n}\n\nclass _LogListViewState extends State<LogListView> {\n  @override\n  Widget build(BuildContext context) {\n    return ListView.builder(\n      itemCount: widget.logs.length,\n      controller: widget.controller,\n      itemBuilder: (context, index) {\n        final log = widget.logs[index];\n        return ListTile(\n          dense: true,\n          title: SelectableText(log.content),\n          subtitle: SelectableText(log.time),\n          leading: LogLevelView(level: log.level),\n        );\n      },\n    );\n  }\n}\n"
  },
  {
    "path": "lib/pages/openlist/openlist.dart",
    "content": "import 'dart:io';\nimport 'package:openlist_mobile/generated_api.dart';\nimport 'package:openlist_mobile/pages/openlist/about_dialog.dart';\nimport 'package:openlist_mobile/pages/openlist/pwd_edit_dialog.dart';\nimport 'package:openlist_mobile/pages/openlist/config_editor_page.dart';\nimport 'package:openlist_mobile/pages/app_update_dialog.dart';\nimport 'package:openlist_mobile/widgets/switch_floating_action_button.dart';\nimport 'package:openlist_mobile/utils/service_manager.dart';\nimport 'package:flutter/material.dart';\nimport 'package:get/get.dart';\n\nimport '../../generated/l10n.dart';\nimport 'log_list_view.dart';\n\nclass OpenListScreen extends StatelessWidget {\n  const OpenListScreen({Key? key}) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    final ui = Get.put(OpenListController());\n\n    return Scaffold(\n        appBar: AppBar(\n            backgroundColor: Theme.of(context).colorScheme.primaryContainer,\n            title: Obx(() => Text(\"OpenList - ${ui.openlistVersion.value}\")),\n            actions: [\n              IconButton(\n                tooltip: S.current.setAdminPassword,\n                onPressed: () {\n                  showDialog(\n                      context: context,\n                      builder: (context) => PwdEditDialog(onConfirm: (pwd) async {\n                            try {\n                              await Android().setAdminPwd(pwd);\n                              Get.showSnackbar(GetSnackBar(\n                                  title: S.current.setAdminPassword,\n                                  message: pwd,\n                                  duration: const Duration(seconds: 1)));\n                            } catch (e) {\n                              Get.showSnackbar(GetSnackBar(\n                                  title: S.current.setAdminPassword,\n                                  message: 'Error: $e',\n                                  duration: const Duration(seconds: 2)));\n                            }\n                          }));\n                },\n                icon: const Icon(Icons.password),\n              ),\n              IconButton(\n                tooltip: S.of(context).editOpenListConfig,\n                onPressed: () {\n                  Get.to(() => const ConfigEditorPage());\n                },\n                icon: const Icon(Icons.edit_note),\n              ),\n              // Desktop shortcut is only available on Android\n              if (Platform.isAndroid)\n                IconButton(\n                  tooltip: S.of(context).desktopShortcut,\n                  onPressed: () async  {\n                    try {\n                      await Android().addShortcut();\n                      Get.showSnackbar(GetSnackBar(\n                          message: S.of(context).desktopShortcut,\n                          duration: const Duration(seconds: 1)));\n                    } catch (e) {\n                      Get.showSnackbar(GetSnackBar(\n                          message: 'Error: $e',\n                          duration: const Duration(seconds: 2)));\n                    }\n                  },\n                  icon: const Icon(Icons.add_home),\n                ),\n              PopupMenuButton(\n                tooltip: S.of(context).moreOptions,\n                itemBuilder: (context) {\n                  return [\n                    PopupMenuItem(\n                      value: 1,\n                      onTap: () async {\n                        AppUpdateDialog.checkUpdateAndShowDialog(context, (b) {\n                          if (!b) {\n                            Get.showSnackbar(GetSnackBar(\n                                message: S.of(context).currentIsLatestVersion,\n                                duration: const Duration(seconds: 2)));\n                          }\n                        });\n                      },\n                      child: Text(S.of(context).checkForUpdates),\n                    ),\n                    PopupMenuItem(\n                      value: 2,\n                      onTap: () {\n                        showDialog(context: context, builder: ((context){\n                          return const AppAboutDialog();\n                        }));\n                      },\n                      child: Text(S.of(context).about),\n                    ),\n                  ];\n                },\n                icon: const Icon(Icons.more_vert),\n              )\n            ]),\n        floatingActionButton: Obx(\n          () => SwitchFloatingButton(\n              isSwitch: ui.isSwitch.value,\n              onSwitchChange: (s) async {\n                ui.clearLog();\n                if (s) {\n                  // 启动服务\n                  await ServiceManager.instance.startService();\n                } else {\n                  // 停止服务\n                  await ServiceManager.instance.stopService();\n                }\n              }),\n        ),\n        body: Obx(() => LogListView(logs: ui.logs, controller: ui.scrollController)));\n  }\n}\n\nclass MyEventReceiver extends Event {\n  Function(Log log) logCb;\n  Function(bool isRunning) statusCb;\n\n  MyEventReceiver(this.statusCb, this.logCb);\n\n  @override\n  void onServiceStatusChanged(bool isRunning) {\n    statusCb(isRunning);\n  }\n\n  @override\n  void onServerLog(int level, String time, String log) {\n    logCb(Log(level, time, log));\n  }\n}\n\nclass OpenListController extends GetxController {\n  final ScrollController scrollController = ScrollController();\n  var isSwitch = false.obs;\n  var openlistVersion = \"\".obs;\n\n  var logs = <Log>[].obs;\n\n  void clearLog() {\n    logs.clear();\n  }\n\n  void addLog(Log log) {\n    logs.add(log);\n    if (scrollController.hasClients) {\n      scrollController.jumpTo(scrollController.position.maxScrollExtent);\n    }\n  }\n\n  @override\n  void onInit() {\n    // 设置日志接收器，但状态变化只通过ServiceManager处理\n    Event.setUp(MyEventReceiver(\n        (isRunning) {\n          // 不在这里更新状态，避免冲突\n          print('Event receiver status: $isRunning');\n        }, \n        (log) => addLog(log)));\n    \n    Android().getOpenListVersion().then((value) => openlistVersion.value = value);\n    \n    // 获取初始状态\n    ServiceManager.instance.checkServiceStatus().then((isRunning) async {\n      isSwitch.value = isRunning;\n      if (Platform.isIOS && !isRunning) {\n        await ServiceManager.instance.startService();\n      }\n    });\n\n    // 只监听ServiceManager的状态变化\n    ServiceManager.instance.serviceStatusStream.listen((isRunning) {\n      print('ServiceManager status changed: $isRunning');\n      isSwitch.value = isRunning;\n    });\n\n    super.onInit();\n  }\n}"
  },
  {
    "path": "lib/pages/openlist/pwd_edit_dialog.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:get/get.dart';\nimport '../../generated/l10n.dart';\n\nclass PwdEditDialog extends StatefulWidget {\n  final ValueChanged<String> onConfirm;\n\n  const PwdEditDialog({super.key, required this.onConfirm});\n\n  @override\n  State<PwdEditDialog> createState() {\n    return _PwdEditDialogState();\n  }\n}\n\nclass _PwdEditDialogState extends State<PwdEditDialog>\n    with SingleTickerProviderStateMixin {\n  final TextEditingController pwdController = TextEditingController();\n\n  @override\n  void dispose() {\n    pwdController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return AlertDialog(\n      title: Text(S.of(context).modifyAdminPassword),\n      content: Column(\n        mainAxisSize: MainAxisSize.min,\n        children: [\n          TextField(\n            controller: pwdController,\n            decoration: const InputDecoration(\n              labelText: \"admin密码\",\n            ),\n          ),\n        ],\n      ),\n      actions: [\n        TextButton(\n          onPressed: () {Get.back();},\n          child: Text(S.of(context).cancel),\n        ),\n        FilledButton(\n          onPressed: () {\n            Get.back();\n            widget.onConfirm(pwdController.text);\n          },\n          child: Text(S.of(context).confirm),\n        ),\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "lib/pages/settings/preference_widgets.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass DividerPreference extends StatelessWidget {\n  const DividerPreference({super.key, required this.title});\n\n  final String title;\n\n  @override\n  Widget build(BuildContext context) {\n    return Column(children: [\n      const Divider(\n        height: 1,\n      ),\n      Container(\n        alignment: Alignment.center,\n        padding: const EdgeInsets.fromLTRB(16, 8, 16, 8),\n        child: Text(\n          title,\n          style: Theme.of(context).textTheme.titleMedium?.copyWith(color: Theme.of(context).primaryColor),\n        ),\n      ),\n    ]);\n  }\n}\n\nclass BasicPreference extends StatelessWidget {\n  final String title;\n  final String subtitle;\n  final Widget? leading;\n  final Widget? trailing;\n  final GestureTapCallback? onTap;\n\n  const BasicPreference({\n    super.key,\n    required this.title,\n    required this.subtitle,\n    this.onTap,\n    this.leading,\n    this.trailing,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return ListTile(\n      title: Text(title),\n      subtitle: Text(subtitle),\n      leading: leading,\n      trailing: trailing,\n      onTap: onTap,\n    );\n  }\n}\n\nclass SwitchPreference extends StatelessWidget {\n  const SwitchPreference({\n    super.key,\n    required this.title,\n    required this.subtitle,\n    this.icon,\n    required this.value,\n    required this.onChanged,\n  });\n\n  final String title;\n  final String subtitle;\n  final Widget? icon;\n  final bool value;\n  final ValueChanged<bool> onChanged;\n\n  @override\n  Widget build(BuildContext context) {\n    return BasicPreference(\n      title: title,\n      subtitle: subtitle,\n      leading: icon,\n      trailing: Switch(value: value, onChanged: onChanged),\n      onTap: () {\n        onChanged(!value);\n      },\n    );\n  }\n}\n"
  },
  {
    "path": "lib/pages/settings/settings.dart",
    "content": "import 'dart:io';\nimport 'package:openlist_mobile/contant/native_bridge.dart';\nimport 'package:openlist_mobile/generated_api.dart';\nimport 'package:openlist_mobile/pages/settings/preference_widgets.dart';\nimport 'package:openlist_mobile/pages/settings/troubleshooting_page.dart';\nimport 'package:openlist_mobile/utils/language_controller.dart';\nimport 'package:file_picker/file_picker.dart';\nimport 'package:flutter/material.dart';\nimport 'package:get/get.dart';\nimport 'package:permission_handler/permission_handler.dart';\n\nimport '../../generated/l10n.dart';\n\nclass SettingsScreen extends StatefulWidget {\n  const SettingsScreen({Key? key}) : super(key: key);\n\n  @override\n  State<SettingsScreen> createState() {\n    return _SettingsScreenState();\n  }\n}\n\nclass _SettingsScreenState extends State<SettingsScreen> {\n  late AppLifecycleListener _lifecycleListener;\n\n  @override\n  void initState() {\n    _lifecycleListener = AppLifecycleListener(\n      onResume: () async {\n        final controller = Get.put(_SettingsController());\n        controller.updateData();\n      },\n    );\n    super.initState();\n  }\n\n  @override\n  void dispose() {\n    _lifecycleListener.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final controller = Get.put(_SettingsController());\n    return Scaffold(\n        body: Obx(\n      () => ListView(\n        children: [\n          // SizedBox(height: MediaQuery.of(context).padding.top),\n          // Android-specific permission requests\n          if (Platform.isAndroid) ...[\n          Visibility(\n            visible: !controller._managerStorageGranted.value ||\n                !controller._notificationGranted.value ||\n                !controller._storageGranted.value,\n            child: DividerPreference(title: S.of(context).importantSettings),\n          ),\n          Visibility(\n            visible: !controller._managerStorageGranted.value,\n            child: BasicPreference(\n              title: S.of(context).grantManagerStoragePermission,\n              subtitle: S.of(context).grantStoragePermissionDesc,\n              onTap: () {\n                Permission.manageExternalStorage.request();\n              },\n            ),\n          ),\n          Visibility(\n              visible: !controller._storageGranted.value,\n              child: BasicPreference(\n                title: S.of(context).grantStoragePermission,\n                subtitle: S.of(context).grantStoragePermissionDesc,\n                onTap: () {\n                  Permission.storage.request();\n                },\n              )),\n\n          Visibility(\n              visible: !controller._notificationGranted.value,\n              child: BasicPreference(\n                title: S.of(context).grantNotificationPermission,\n                subtitle: S.of(context).grantNotificationPermissionDesc,\n                onTap: () {\n                  Permission.notification.request();\n                },\n              )),\n          ], // End of Android-specific permissions\n\n          DividerPreference(title: S.of(context).general),\n\n          // Language Settings\n          BasicPreference(\n            title: S.of(context).language,\n            subtitle: _getLanguageDisplayName(),\n            leading: const Icon(Icons.language),\n            onTap: () {\n              _showLanguageSelectionDialog(context);\n            },\n          ),\n\n          SwitchPreference(\n            title: S.of(context).autoCheckForUpdates,\n            subtitle: S.of(context).autoCheckForUpdatesDesc,\n            icon: const Icon(Icons.system_update),\n            value: controller.autoUpdate,\n            onChanged: (value) {\n              controller.autoUpdate = value;\n            },\n          ),\n          SwitchPreference(\n            title: S.of(context).wakeLock,\n            subtitle: S.of(context).wakeLockDesc,\n            icon: const Icon(Icons.screen_lock_portrait),\n            value: controller.wakeLock,\n            onChanged: (value) {\n              controller.wakeLock = value;\n            },\n          ),\n          SwitchPreference(\n            title: S.of(context).bootAutoStartService,\n            subtitle: S.of(context).bootAutoStartServiceDesc,\n            icon: const Icon(Icons.power_settings_new),\n            value: controller.startAtBoot,\n            onChanged: (value) {\n              controller.startAtBoot = value;\n            },\n          ),\n          // AutoStartWebPage\n          SwitchPreference(\n            title: S.of(context).autoStartWebPage,\n            subtitle: S.of(context).autoStartWebPageDesc,\n            icon: const Icon(Icons.open_in_browser),\n            value: controller._autoStartWebPage.value,\n            onChanged: (value) {\n              controller.autoStartWebPage = value;\n            },\n          ),\n\n          // Data directory setting - only for Android\n          if (Platform.isAndroid)\n            BasicPreference(\n              title: S.of(context).dataDirectory,\n              subtitle: controller._dataDir.value,\n              leading: const Icon(Icons.folder),\n              onTap: () async {\n                final path = await FilePicker.platform.getDirectoryPath();\n\n                if (path == null) {\n                  Get.showSnackbar(GetSnackBar(\n                      message: S.current.setDefaultDirectory,\n                      duration: const Duration(seconds: 3),\n                      mainButton: TextButton(\n                        onPressed: () {\n                          controller.setDataDir(\"\");\n                          Get.back();\n                        },\n                        child: Text(S.current.confirm),\n                      )));\n                } else {\n                  controller.setDataDir(path);\n                }\n              },\n            ),\n          DividerPreference(title: S.of(context).uiSettings),\n          SwitchPreference(\n              icon: const Icon(Icons.pan_tool_alt_outlined),\n              title: S.of(context).silentJumpApp,\n              subtitle: S.of(context).silentJumpAppDesc,\n              value: controller._silentJumpApp.value,\n              onChanged: (value) {\n                controller.silentJumpApp = value;\n              }),\n          \n          BasicPreference(\n            title: S.of(context).troubleshooting,\n            subtitle: S.of(context).troubleshootingDesc,\n            leading: const Icon(Icons.help_outline),\n            onTap: () {\n              Navigator.push(\n                context,\n                MaterialPageRoute(\n                  builder: (context) => const TroubleshootingPage(),\n                ),\n              );\n            },\n          ),\n        ],\n      ),\n    ));\n  }\n\n  String _getLanguageDisplayName() {\n    final languageController = Get.find<LanguageController>();\n    final currentOption = languageController.currentLanguageOption;\n    \n    switch (currentOption.name) {\n      case 'followSystem':\n        return S.of(context).followSystem;\n      case 'simplifiedChinese':\n        return S.of(context).simplifiedChinese;\n      case 'english':\n        return S.of(context).english;\n      default:\n        return currentOption.name;\n    }\n  }\n\n  void _showLanguageSelectionDialog(BuildContext context) {\n    showDialog(\n      context: context,\n      builder: (BuildContext context) {\n        return AlertDialog(\n          title: Text(S.of(context).languageSettings),\n          content: SingleChildScrollView(\n            child: LanguageSelector(\n              onLanguageChanged: () {\n                Navigator.of(context).pop();\n                setState(() {}); // 刷新界面以显示新的语言设置\n              },\n            ),\n          ),\n          actions: [\n            TextButton(\n              onPressed: () {\n                Navigator.of(context).pop();\n              },\n              child: Text(S.of(context).cancel),\n            ),\n          ],\n        );\n      },\n    );\n  }\n}\n\nclass _SettingsController extends GetxController {\n  final _dataDir = \"\".obs;\n  final _autoUpdate = true.obs;\n  final _managerStorageGranted = true.obs;\n  final _notificationGranted = true.obs;\n  final _storageGranted = true.obs;\n\n  setDataDir(String value) async {\n    NativeBridge.appConfig.setDataDir(value);\n    _dataDir.value = await NativeBridge.appConfig.getDataDir();\n  }\n\n  get dataDir => _dataDir.value;\n\n  set autoUpdate(value) => {\n        _autoUpdate.value = value,\n        NativeBridge.appConfig.setAutoCheckUpdateEnabled(value)\n      };\n\n  get autoUpdate => _autoUpdate.value;\n\n  final _wakeLock = true.obs;\n\n  set wakeLock(value) => {\n        _wakeLock.value = value,\n        NativeBridge.appConfig.setWakeLockEnabled(value)\n      };\n\n  get wakeLock => _wakeLock.value;\n\n  final _autoStart = true.obs;\n\n  set startAtBoot(value) => {\n        _autoStart.value = value,\n        NativeBridge.appConfig.setStartAtBootEnabled(value)\n      };\n\n  get startAtBoot => _autoStart.value;\n\n  final _autoStartWebPage = false.obs;\n\n  set autoStartWebPage(value) => {\n        _autoStartWebPage.value = value,\n        NativeBridge.appConfig.setAutoOpenWebPageEnabled(value)\n      };\n\n  get autoStartWebPage => _autoStartWebPage.value;\n\n  final _silentJumpApp = false.obs;\n\n  get silentJumpApp => _silentJumpApp.value;\n\n  set silentJumpApp(value) => {\n        _silentJumpApp.value = value,\n        NativeBridge.appConfig.setSilentJumpAppEnabled(value)\n      };\n\n  @override\n  void onInit() async {\n    updateData();\n\n    super.onInit();\n  }\n\n  void updateData() async {\n    final cfg = AppConfig();\n    cfg.isAutoCheckUpdateEnabled().then((value) => autoUpdate = value);\n    cfg.isWakeLockEnabled().then((value) => wakeLock = value);\n    cfg.isStartAtBootEnabled().then((value) => startAtBoot = value);\n    cfg.isAutoOpenWebPageEnabled().then((value) => autoStartWebPage = value);\n    cfg.isSilentJumpAppEnabled().then((value) => silentJumpApp = value);\n\n    _dataDir.value = await cfg.getDataDir();\n\n    final sdk = await NativeBridge.common.getDeviceSdkInt();\n    // A11\n    if (sdk >= 30) {\n      _managerStorageGranted.value =\n          await Permission.manageExternalStorage.isGranted;\n    } else {\n      _managerStorageGranted.value = true;\n      _storageGranted.value = await Permission.storage.isGranted;\n    }\n\n    // A12\n    if (sdk >= 32) {\n      _notificationGranted.value = await Permission.notification.isGranted;\n    } else {\n      _notificationGranted.value = true;\n    }\n  }\n}"
  },
  {
    "path": "lib/pages/settings/troubleshooting_page.dart",
    "content": "import 'package:flutter/material.dart';\nimport '../../generated/l10n.dart';\n\nclass TroubleshootingPage extends StatelessWidget {\n  const TroubleshootingPage({Key? key}) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        title: Text(S.of(context).troubleshooting),\n      ),\n      body: ListView(\n        padding: const EdgeInsets.all(16.0),\n        children: [\n          _buildIssueCard(\n            context,\n            icon: Icons.power_settings_new,\n            title: S.of(context).autoStartIssue,\n            description: S.of(context).autoStartIssueDesc,\n          ),\n          _buildIssueCard(\n            context,\n            icon: Icons.storage,\n            title: S.of(context).databaseNotSavedIssue,\n            description: S.of(context).databaseNotSavedIssueDesc,\n          ),\n        ],\n      ),\n    );\n  }\n\n  Widget _buildIssueCard(\n    BuildContext context, {\n    required IconData icon,\n    required String title,\n    required String description,\n  }) {\n    return Card(\n      margin: const EdgeInsets.only(bottom: 16.0),\n      child: Padding(\n        padding: const EdgeInsets.all(16.0),\n        child: Column(\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: [\n            Row(\n              children: [\n                Icon(\n                  icon,\n                  size: 28,\n                  color: Theme.of(context).colorScheme.primary,\n                ),\n                const SizedBox(width: 12),\n                Expanded(\n                  child: Text(\n                    title,\n                    style: Theme.of(context).textTheme.titleMedium?.copyWith(\n                          fontWeight: FontWeight.bold,\n                        ),\n                  ),\n                ),\n              ],\n            ),\n            const SizedBox(height: 12),\n            Text(\n              description,\n              style: Theme.of(context).textTheme.bodyMedium,\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/pages/web/web.dart",
    "content": "import 'dart:developer';\nimport 'dart:io' show Platform;\n\nimport 'package:openlist_mobile/contant/native_bridge.dart';\nimport 'package:openlist_mobile/generated_api.dart';\nimport 'package:openlist_mobile/utils/intent_utils.dart';\nimport 'package:openlist_mobile/utils/download_manager.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_inappwebview/flutter_inappwebview.dart';\nimport 'package:get/get.dart';\n\nimport '../../generated/l10n.dart';\n\nGlobalKey<WebScreenState> webGlobalKey = GlobalKey();\n\nclass WebScreen extends StatefulWidget {\n  const WebScreen({Key? key}) : super(key: key);\n\n  @override\n  State<StatefulWidget> createState() {\n    return WebScreenState();\n  }\n}\n\nclass WebScreenState extends State<WebScreen> with WidgetsBindingObserver {\n  InAppWebViewController? _webViewController;\n  InAppWebViewSettings settings = InAppWebViewSettings(\n    allowsInlineMediaPlayback: true,\n    allowBackgroundAudioPlaying: true,\n    iframeAllowFullscreen: true,\n    javaScriptEnabled: true,\n    mediaPlaybackRequiresUserGesture: false,\n    useShouldOverrideUrlLoading: true,\n    // iOS specific: Enable page caching and state preservation\n    cacheEnabled: true,\n    sharedCookiesEnabled: true,\n    limitsNavigationsToAppBoundDomains: Platform.isIOS,\n    // Enable disk and memory cache for better state preservation\n    cacheMode: CacheMode.LOAD_DEFAULT,\n    // Prevent WebView from being suspended in background\n    allowsBackForwardNavigationGestures: true,\n    // iOS: Suppress rendering until content is loaded\n    suppressesIncrementalRendering: false,\n  );\n\n  double _progress = 0;\n  String _url = \"http://localhost:5244\";\n  bool _canGoBack = false;\n  bool _isLoading = false;\n\n  static const Set<String> _inAppSafeSchemes = {\n    \"about\",\n    \"data\",\n    \"file\",\n  };\n\n  bool _isLoopbackHost(String host) {\n    final normalized = host.toLowerCase();\n    return normalized == \"localhost\" ||\n        normalized == \"127.0.0.1\" ||\n        normalized == \"::1\";\n  }\n\n  bool _isAllowedInAppNavigation(Uri uri) {\n    final scheme = uri.scheme.toLowerCase();\n\n    if (_inAppSafeSchemes.contains(scheme)) {\n      return true;\n    }\n\n    if (scheme == \"http\" || scheme == \"https\") {\n      if (!Platform.isIOS) {\n        return true;\n      }\n      return _isLoopbackHost(uri.host);\n    }\n\n    return false;\n  }\n\n  Future<void> _openExternalUri(String uriString) async {\n    final silentMode = await NativeBridge.appConfig.isSilentJumpAppEnabled();\n    if (silentMode) {\n      NativeCommon().startActivityFromUri(uriString);\n      return;\n    }\n\n    if (!mounted) return;\n\n    Get.showSnackbar(GetSnackBar(\n        message: S.current.jumpToOtherApp,\n        duration: const Duration(seconds: 5),\n        mainButton: TextButton(\n          onPressed: () {\n            NativeCommon().startActivityFromUri(uriString);\n          },\n          child: Text(S.current.goTo),\n        )));\n  }\n\n  onClickNavigationBar() {\n    log(\"onClickNavigationBar\");\n    _webViewController?.reload();\n  }\n\n  @override\n  void initState() {\n    super.initState();\n    // Register lifecycle observer to handle app state changes\n    WidgetsBinding.instance.addObserver(this);\n    \n    // Get OpenList HTTP port\n    Android()\n        .getOpenListHttpPort()\n        .then((port) {\n          setState(() {\n            _url = \"http://localhost:$port\";\n          });\n          log(\"OpenList URL set to: $_url\");\n        })\n        .catchError((error) {\n          log(\"Failed to get OpenList port: $error\");\n        });\n\n    // Wait a bit for service to be ready before loading\n    Future.delayed(const Duration(milliseconds: 500), () {\n      if (mounted && _webViewController == null) {\n        // Will be initialized when WebView is created\n        log(\"WebView will initialize with URL: $_url\");\n      }\n    });\n  }\n\n  @override\n  void dispose() {\n    // Remove lifecycle observer when widget is disposed\n    WidgetsBinding.instance.removeObserver(this);\n    _webViewController?.dispose();\n    super.dispose();\n  }\n\n  @override\n  void didChangeAppLifecycleState(AppLifecycleState state) {\n    super.didChangeAppLifecycleState(state);\n    log(\"App lifecycle state changed: $state\");\n    \n    switch (state) {\n      case AppLifecycleState.resumed:\n        // App returned to foreground, WebView should be active\n        log(\"App resumed, WebView is active\");\n        _webViewController?.resume();\n        break;\n      case AppLifecycleState.paused:\n        // App entered background, ensure WebView state is preserved\n        log(\"App paused, WebView entering background\");\n        // Note: Do not pause WebView to keep background tasks running\n        // The UIBackgroundModes in Info.plist allows WebKit processes to continue\n        break;\n      case AppLifecycleState.inactive:\n        // App transitioning states (e.g., incoming call, app switcher)\n        log(\"App inactive\");\n        break;\n      case AppLifecycleState.detached:\n        // App is detached from UI\n        log(\"App detached\");\n        break;\n      case AppLifecycleState.hidden:\n        // App is hidden\n        log(\"App hidden\");\n        break;\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return PopScope(\n        canPop: !_canGoBack,\n        onPopInvoked: (didPop) async {\n          log(\"onPopInvoked $didPop\");\n          if (didPop) return;\n          _webViewController?.goBack();\n        },\n        child: Scaffold(\n          body: Column(children: <Widget>[\n            SizedBox(height: MediaQuery.of(context).padding.top),\n            LinearProgressIndicator(\n              value: _progress,\n              backgroundColor: Colors.grey[200],\n              valueColor: const AlwaysStoppedAnimation<Color>(Colors.blue),\n            ),\n            Expanded(\n              child: InAppWebView(\n                initialSettings: settings,\n                initialUrlRequest: URLRequest(url: WebUri(_url)),\n                onWebViewCreated: (InAppWebViewController controller) {\n                  _webViewController = controller;\n                  log(\"WebView created, loading URL: $_url\");\n                },\n                onLoadStart: (InAppWebViewController controller, Uri? url) {\n                  log(\"onLoadStart $url\");\n                  setState(() {\n                    _progress = 0;\n                    _isLoading = true;\n                  });\n                },\n                shouldOverrideUrlLoading: (controller, navigationAction) async {\n                  log(\"shouldOverrideUrlLoading ${navigationAction.request.url}\");\n\n                  final uri = navigationAction.request.url;\n                  if (uri == null) {\n                    return NavigationActionPolicy.CANCEL;\n                  }\n\n                  if (_isAllowedInAppNavigation(uri)) {\n                    return NavigationActionPolicy.ALLOW;\n                  }\n\n                  final scheme = uri.scheme.toLowerCase();\n                  if (Platform.isIOS && scheme == \"javascript\") {\n                    log(\"Blocked javascript navigation on iOS: ${uri.toString()}\");\n                    return NavigationActionPolicy.CANCEL;\n                  }\n\n                  await _openExternalUri(uri.toString());\n                  return NavigationActionPolicy.CANCEL;\n                },\n                onReceivedError: (controller, request, error) async {\n                  log(\"WebView error: ${error.description}\");\n                  \n                  // Check if OpenList service is running\n                  try {\n                    if (!await Android().isRunning()) {\n                      log(\"Service not running, attempting to start...\");\n                      await Android().startService();\n\n                      // Wait for service to start and retry\n                      for (int i = 0; i < 3; i++) {\n                        await Future.delayed(const Duration(milliseconds: 500));\n                        if (await Android().isRunning()) {\n                          log(\"Service started, reloading WebView\");\n                          _webViewController?.reload();\n                          break;\n                        }\n                      }\n                    }\n                  } catch (e) {\n                    log(\"Failed to handle WebView error: $e\");\n                  }\n                },\n                onDownloadStartRequest: (controller, url) async {\n                  Get.showSnackbar(GetSnackBar(\n                    title: S.of(context).downloadThisFile,\n                    message: url.suggestedFilename ??\n                        url.contentDisposition ??\n                        url.toString(),\n                    duration: const Duration(seconds: 5),\n                    mainButton: Column(children: [\n                      TextButton(\n                        onPressed: () async {\n                          Get.closeCurrentSnackbar();\n                          // 使用内置下载管理器后台下载\n                          DownloadManager.downloadFileInBackground(\n                            url: url.url.toString(),\n                            filename: url.suggestedFilename,\n                          );\n                        },\n                        child: Text(S.of(context).directDownload),\n                      ),\n                      TextButton(\n                        onPressed: () {\n                          IntentUtils.getUrlIntent(url.url.toString())\n                              .launchChooser(S.of(context).selectAppToOpen);\n                        },\n                        child: Text(S.of(context).selectAppToOpen),\n                      ),\n                      TextButton(\n                        onPressed: () {\n                          IntentUtils.getUrlIntent(url.url.toString()).launch();\n                        },\n                        child: Text(S.of(context).browserDownload),\n                      ),\n                    ]),\n                    onTap: (_) {\n                      Clipboard.setData(\n                          ClipboardData(text: url.url.toString()));\n                      Get.closeCurrentSnackbar();\n                      Get.showSnackbar(GetSnackBar(\n                        message: S.of(context).copiedToClipboard,\n                        duration: const Duration(seconds: 1),\n                      ));\n                    },\n                  ));\n                },\n                onLoadStop:\n                    (InAppWebViewController controller, Uri? url) async {\n                  log(\"onLoadStop $url\");\n                  setState(() {\n                    _progress = 0;\n                    _isLoading = false;\n                  });\n                },\n                onProgressChanged:\n                    (InAppWebViewController controller, int progress) {\n                  setState(() {\n                    _progress = progress / 100;\n                    if (_progress == 1) _progress = 0;\n                  });\n                  controller.canGoBack().then((value) => setState(() {\n                        _canGoBack = value;\n                      }));\n                },\n                onUpdateVisitedHistory: (InAppWebViewController controller,\n                    WebUri? url, bool? isReload) {\n                  _url = url.toString();\n                },\n              ),\n            ),\n          ]),\n        ));\n  }\n}\n"
  },
  {
    "path": "lib/utils/app_store_update.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter/services.dart';\n\nclass AppStoreUpdate {\n  static const MethodChannel _channel = MethodChannel('openlist/app_store_update');\n\n  static Future<bool> checkAndShowUpdate() async {\n    if (!Platform.isIOS) {\n      return false;\n    }\n\n    final result = await _channel.invokeMethod<bool>('checkAndShowUpdate');\n    return result ?? false;\n  }\n}\n"
  },
  {
    "path": "lib/utils/download_examples.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:get/get.dart';\nimport 'download_manager.dart';\nimport '../generated/l10n.dart';\n\n/// 下载功能使用示例\nclass DownloadExamples {\n  \n  /// 示例1: 简单文件下载\n  static Future<void> downloadSimpleFile() async {\n    await DownloadManager.downloadFileInBackground(\n      url: 'https://example.com/document.pdf',\n      filename: 'my_document.pdf',\n    );\n  }\n\n  /// 示例2: 带进度的下载\n  static Future<void> downloadWithProgress() async {\n    await DownloadManager.downloadFileWithProgress(\n      url: 'https://example.com/large_file.zip',\n      filename: 'large_file.zip',\n    );\n  }\n\n  /// 示例3: 自定义进度回调\n  static Future<void> downloadWithCustomProgress() async {\n    await DownloadManager.downloadFileWithProgress(\n      url: 'https://example.com/video.mp4',\n      filename: 'video.mp4',\n    );\n  }\n\n  /// 示例4: 批量下载\n  static Future<void> downloadMultipleFiles(List<String> urls) async {\n    for (int i = 0; i < urls.length; i++) {\n      String url = urls[i];\n      String filename = 'file_${i + 1}_${DateTime.now().millisecondsSinceEpoch}';\n      \n      Get.showSnackbar(GetSnackBar(\n        message: S.current.downloadingFileProgress(i + 1, urls.length),\n        duration: Duration(seconds: 2),\n      ));\n      \n      bool success = await DownloadManager.downloadFileInBackground(\n        url: url,\n        filename: filename,\n      );\n      \n      if (!success) {\n        Get.showSnackbar(GetSnackBar(\n          message: S.current.fileDownloadFailed(i + 1),\n          duration: Duration(seconds: 3),\n        ));\n        break;\n      }\n    }\n    \n    Get.showSnackbar(GetSnackBar(\n      message: S.current.batchDownloadComplete,\n      duration: Duration(seconds: 3),\n    ));\n  }\n\n  /// 示例5: 下载并显示自定义对话框\n  static Future<void> downloadWithCustomDialog(BuildContext context) async {\n    // 显示确认对话框\n    bool? shouldDownload = await showDialog<bool>(\n      context: context,\n      builder: (context) => AlertDialog(\n        title: Text(S.current.confirmDownload),\n        content: Text(S.current.confirmDownloadMessage),\n        actions: [\n          TextButton(\n            onPressed: () => Navigator.of(context).pop(false),\n            child: Text(S.current.cancel),\n          ),\n          TextButton(\n            onPressed: () => Navigator.of(context).pop(true),\n            child: Text(S.current.download),\n          ),\n        ],\n      ),\n    );\n\n    if (shouldDownload == true) {\n      await DownloadManager.downloadFileWithProgress(\n        url: 'https://example.com/important_file.pdf',\n        filename: 'important_file.pdf',\n      );\n    }\n  }\n\n  /// 示例6: 下载图片并显示预览\n  static Future<void> downloadImageWithPreview(String imageUrl) async {\n    // 先显示加载提示\n    Get.dialog(\n      AlertDialog(\n        content: Column(\n          mainAxisSize: MainAxisSize.min,\n          children: [\n            CircularProgressIndicator(),\n            SizedBox(height: 16),\n            Text(S.current.downloadingImage),\n          ],\n        ),\n      ),\n      barrierDismissible: false,\n    );\n\n    bool success = await DownloadManager.downloadFileInBackground(\n      url: imageUrl,\n      filename: 'image_${DateTime.now().millisecondsSinceEpoch}.jpg',\n    );\n\n    Get.back(); // 关闭加载对话框\n\n    if (success) {\n      Get.showSnackbar(GetSnackBar(\n        message: S.current.imageDownloadSuccess,\n        duration: Duration(seconds: 3),\n        mainButton: TextButton(\n          onPressed: () {\n            // 可以在这里添加打开图片的逻辑\n            Get.showSnackbar(GetSnackBar(\n              message: S.current.checkImageInDownloadFolder,\n              duration: Duration(seconds: 2),\n            ));\n          },\n          child: Text(S.current.view),\n        ),\n      ));\n    }\n  }\n\n  /// 示例7: 下载APK并提示安装\n  static Future<void> downloadApkAndInstall(String apkUrl, String version) async {\n    bool success = await DownloadManager.downloadFileWithProgress(\n      url: apkUrl,\n      filename: 'app_update_v$version.apk',\n    );\n\n    if (success) {\n      Get.dialog(\n        AlertDialog(\n          title: Text(S.current.downloadCompleteTitle),\n          content: Text(S.current.apkDownloadCompleteMessage),\n          actions: [\n            TextButton(\n              onPressed: () => Get.back(),\n              child: Text(S.current.laterInstall),\n            ),\n            TextButton(\n              onPressed: () {\n                Get.back();\n                // 这里可以添加安装APK的逻辑\n                Get.showSnackbar(GetSnackBar(\n                  message: S.current.findApkInDownloadFolder,\n                  duration: Duration(seconds: 5),\n                ));\n              },\n              child: Text(S.current.installNow),\n            ),\n          ],\n        ),\n      );\n    }\n  }\n}\n\n/// 下载工具类 - 提供一些便捷方法\nclass DownloadUtils {\n  \n  /// 检查URL是否为下载链接\n  static bool isDownloadUrl(String url) {\n    final downloadExtensions = [\n      '.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx',\n      '.zip', '.rar', '.7z', '.tar', '.gz',\n      '.mp3', '.mp4', '.avi', '.mkv', '.mov',\n      '.jpg', '.jpeg', '.png', '.gif', '.bmp',\n      '.apk', '.exe', '.dmg', '.deb', '.rpm'\n    ];\n    \n    String lowerUrl = url.toLowerCase();\n    return downloadExtensions.any((ext) => lowerUrl.contains(ext));\n  }\n\n  /// 从URL获取文件扩展名\n  static String getFileExtension(String url) {\n    try {\n      Uri uri = Uri.parse(url);\n      String path = uri.path;\n      if (path.contains('.')) {\n        return path.split('.').last.toLowerCase();\n      }\n    } catch (e) {\n      print('获取文件扩展名失败: $e');\n    }\n    return '';\n  }\n\n  /// 格式化文件大小\n  static String formatFileSize(int bytes) {\n    if (bytes < 1024) return '${bytes}B';\n    if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)}KB';\n    if (bytes < 1024 * 1024 * 1024) return '${(bytes / (1024 * 1024)).toStringAsFixed(1)}MB';\n    return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(1)}GB';\n  }\n\n  /// 显示下载选择对话框\n  static void showDownloadOptions(BuildContext context, String url, {String? filename}) {\n    showModalBottomSheet(\n      context: context,\n      builder: (context) => Container(\n        padding: EdgeInsets.all(16),\n        child: Column(\n          mainAxisSize: MainAxisSize.min,\n          children: [\n            Text(\n              S.current.selectDownloadMethod,\n              style: Theme.of(context).textTheme.titleLarge,\n            ),\n            SizedBox(height: 16),\n            ListTile(\n              leading: Icon(Icons.download),\n              title: Text(S.current.directDownloadMethod),\n              subtitle: Text(S.current.directDownloadMethodDesc),\n              onTap: () {\n                Navigator.pop(context);\n                DownloadManager.downloadFileWithProgress(\n                  url: url,\n                  filename: filename,\n                );\n              },\n            ),\n            ListTile(\n              leading: Icon(Icons.open_in_browser),\n              title: Text(S.current.browserDownloadMethod),\n              subtitle: Text(S.current.browserDownloadMethodDesc),\n              onTap: () {\n                Navigator.pop(context);\n                // 这里可以调用原有的Intent方式\n              },\n            ),\n            ListTile(\n              leading: Icon(Icons.share),\n              title: Text(S.current.shareLink),\n              subtitle: Text(S.current.shareLinkDesc),\n              onTap: () {\n                Navigator.pop(context);\n                // 这里可以添加分享功能\n              },\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}"
  },
  {
    "path": "lib/utils/download_manager.dart",
    "content": "import 'dart:io';\nimport 'dart:developer';\nimport 'package:dio/dio.dart';\nimport 'package:path_provider/path_provider.dart';\nimport 'package:permission_handler/permission_handler.dart';\nimport 'package:get/get.dart' as getx;\nimport 'package:flutter/material.dart';\nimport 'package:open_filex/open_filex.dart';\nimport 'notification_manager.dart';\nimport '../generated/l10n.dart';\n\n/// 下载任务状态\nenum DownloadStatus {\n  pending,    // 等待中\n  downloading, // 下载中\n  completed,   // 已完成\n  failed,      // 失败\n  cancelled,   // 已取消\n}\n\n/// 下载任务\nclass DownloadTask {\n  final String id;\n  final String url;\n  final String filename;\n  final String filePath;\n  DownloadStatus status;\n  double progress;\n  int receivedBytes;\n  int totalBytes;\n  String? errorMessage;\n  DateTime startTime;\n  DateTime? endTime;\n  CancelToken? cancelToken;\n\n  DownloadTask({\n    required this.id,\n    required this.url,\n    required this.filename,\n    required this.filePath,\n    this.status = DownloadStatus.pending,\n    this.progress = 0.0,\n    this.receivedBytes = 0,\n    this.totalBytes = 0,\n    this.errorMessage,\n    DateTime? startTime,\n    this.endTime,\n    this.cancelToken,\n  }) : startTime = startTime ?? DateTime.now();\n\n  String get statusText {\n    switch (status) {\n      case DownloadStatus.pending:\n        return S.current.pending;\n      case DownloadStatus.downloading:\n        return S.current.downloading;\n      case DownloadStatus.completed:\n        return S.current.completed;\n      case DownloadStatus.failed:\n        return S.current.failed;\n      case DownloadStatus.cancelled:\n        return S.current.cancelled;\n    }\n  }\n\n  String get progressText {\n    if (totalBytes > 0) {\n      return '${_formatBytes(receivedBytes)} / ${_formatBytes(totalBytes)}';\n    }\n    return _formatBytes(receivedBytes);\n  }\n\n  String _formatBytes(int bytes) {\n    if (bytes < 1024) return '${bytes}B';\n    if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)}KB';\n    if (bytes < 1024 * 1024 * 1024) return '${(bytes / (1024 * 1024)).toStringAsFixed(1)}MB';\n    return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(1)}GB';\n  }\n}\n\nclass DownloadManager {\n  static final Dio _dio = Dio();\n  static final Map<String, DownloadTask> _activeTasks = {};\n  static final List<DownloadTask> _completedTasks = [];\n  \n  /// 获取所有活跃的下载任务\n  static List<DownloadTask> get activeTasks => _activeTasks.values.toList();\n  \n  /// 获取所有已完成的下载任务\n  static List<DownloadTask> get completedTasks => _completedTasks;\n  \n  /// 获取所有下载任务\n  static List<DownloadTask> get allTasks => [..._activeTasks.values, ..._completedTasks];\n\n  /// 带进度条的下载（后台下载，不阻塞UI）\n  static Future<bool> downloadFileWithProgress({\n    required String url,\n    String? filename,\n  }) async {\n    // 初始化通知管理器\n    await NotificationManager.initialize();\n    \n    // 生成任务ID\n    String taskId = DateTime.now().millisecondsSinceEpoch.toString();\n    \n    // 获取下载目录\n    Directory? downloadDir = await _getOpenListDownloadDirectory();\n    if (downloadDir == null) {\n      getx.Get.showSnackbar(getx.GetSnackBar(\n        message: S.current.cannotGetDownloadDirectory,\n        duration: const Duration(seconds: 3),\n      ));\n      return false;\n    }\n\n    // 确定文件名和路径\n    String finalFilename = filename ?? _getFilenameFromUrl(url);\n    String filePath = '${downloadDir.path}/$finalFilename';\n    filePath = _getUniqueFilePath(filePath);\n    finalFilename = filePath.split('/').last;\n\n    // 创建下载任务\n    CancelToken cancelToken = CancelToken();\n    DownloadTask task = DownloadTask(\n      id: taskId,\n      url: url,\n      filename: finalFilename,\n      filePath: filePath,\n      status: DownloadStatus.pending,\n      cancelToken: cancelToken,\n    );\n\n    // 添加到活跃任务列表\n    _activeTasks[taskId] = task;\n\n    // 显示开始下载提示（只显示一次）\n    getx.Get.showSnackbar(getx.GetSnackBar(\n      message: S.current.startDownloadFile(finalFilename),\n      duration: const Duration(seconds: 2),\n      backgroundColor: Colors.green,\n    ));\n\n    try {\n      // 更新任务状态\n      task.status = DownloadStatus.downloading;\n      \n      // 显示初始通知\n      await NotificationManager.showDownloadProgressNotification();\n      \n      // 执行下载\n      await _dio.download(\n        url,\n        filePath,\n        cancelToken: cancelToken,\n        onReceiveProgress: (received, total) {\n          if (task.status == DownloadStatus.cancelled) return;\n          \n          task.receivedBytes = received;\n          task.totalBytes = total;\n          if (total > 0) {\n            task.progress = received / total;\n          }\n          \n          // 更新通知进度\n          NotificationManager.showDownloadProgressNotification();\n          \n          log('下载进度: ${(task.progress * 100).toStringAsFixed(1)}%');\n        },\n      );\n\n      // 下载完成\n      task.status = DownloadStatus.completed;\n      task.endTime = DateTime.now();\n      task.progress = 1.0;\n\n      // 移动到已完成列表\n      _activeTasks.remove(taskId);\n      _completedTasks.insert(0, task); // 插入到开头，最新的在前面\n\n      // 显示单个文件完成通知\n      await NotificationManager.showSingleFileCompleteNotification(task);\n\n      // 显示完成提示\n      getx.Get.showSnackbar(getx.GetSnackBar(\n        message: S.current.downloadCompleteFile(finalFilename),\n        duration: const Duration(seconds: 3),\n        backgroundColor: Colors.blue,\n        mainButton: TextButton(\n          onPressed: () {\n            _openFile(filePath);\n          },\n          child: Text(S.current.open),\n        ),\n      ));\n\n      log('文件下载完成: $filePath');\n      return true;\n\n    } catch (e) {\n      if (e is DioException && e.type == DioExceptionType.cancel) {\n        // 用户取消下载\n        task.status = DownloadStatus.cancelled;\n        task.endTime = DateTime.now();\n        log('下载已取消: $url');\n      } else {\n        // 下载失败\n        task.status = DownloadStatus.failed;\n        task.errorMessage = e.toString();\n        task.endTime = DateTime.now();\n        log('下载失败: $e');\n        \n        getx.Get.showSnackbar(getx.GetSnackBar(\n          message: S.current.downloadFailedFile(finalFilename),\n          duration: const Duration(seconds: 3),\n          backgroundColor: Colors.red,\n        ));\n      }\n\n      // 移动到已完成列表\n      _activeTasks.remove(taskId);\n      _completedTasks.insert(0, task);\n      \n      // 更新通知状态\n      if (_activeTasks.isEmpty) {\n        await NotificationManager.cancelDownloadNotification();\n      } else {\n        await NotificationManager.showDownloadProgressNotification();\n      }\n      \n      return false;\n    }\n  }\n\n  /// 简单的后台下载（推荐使用）\n  static Future<bool> downloadFileInBackground({\n    required String url,\n    String? filename,\n  }) async {\n    return await downloadFileWithProgress(\n      url: url,\n      filename: filename,\n    );\n  }\n\n  /// 取消下载任务\n  static void cancelDownload(String taskId) {\n    DownloadTask? task = _activeTasks[taskId];\n    if (task != null && task.cancelToken != null) {\n      task.cancelToken!.cancel(S.current.userCancelledDownloadError);\n    }\n  }\n\n  /// 清除已完成的下载记录\n  static void clearCompletedTasks() {\n    _completedTasks.clear();\n  }\n\n  /// 删除下载任务记录\n  static void removeTask(String taskId) {\n    _activeTasks.remove(taskId);\n    _completedTasks.removeWhere((task) => task.id == taskId);\n  }\n\n  /// 获取OpenList专用下载目录\n  static Future<Directory?> _getOpenListDownloadDirectory() async {\n    try {\n      Directory? baseDir;\n      \n      if (Platform.isAndroid) {\n        // Android: 优先使用公共下载目录\n        baseDir = Directory('/storage/emulated/0/Download');\n        if (!await baseDir.exists()) {\n          // 如果公共下载目录不存在，使用外部存储目录\n          baseDir = await getExternalStorageDirectory();\n          if (baseDir != null) {\n            baseDir = Directory('${baseDir.path}/Download');\n          }\n        }\n      } else if (Platform.isIOS) {\n        // iOS: 使用应用文档目录下的Downloads文件夹\n        baseDir = await getApplicationDocumentsDirectory();\n        baseDir = Directory('${baseDir.path}/Downloads');\n      } else {\n        // 其他平台（如Windows、macOS、Linux）\n        baseDir = await getDownloadsDirectory();\n      }\n\n      if (baseDir == null) {\n        log('无法获取基础下载目录');\n        return null;\n      }\n\n      // 创建OpenList专用文件夹\n      Directory openListDir = Directory('${baseDir.path}/OpenList');\n      \n      if (!await openListDir.exists()) {\n        try {\n          await openListDir.create(recursive: true);\n          log('创建OpenList下载目录: ${openListDir.path}');\n        } catch (e) {\n          log('创建OpenList目录失败: $e');\n          // 如果创建失败，返回基础目录\n          return baseDir;\n        }\n      }\n\n      log('OpenList下载目录: ${openListDir.path}');\n      return openListDir;\n      \n    } catch (e) {\n      log('获取下载目录失败: $e');\n      return null;\n    }\n  }\n\n  /// 从URL中提取文件名\n  static String _getFilenameFromUrl(String url) {\n    try {\n      Uri uri = Uri.parse(url);\n      String path = uri.path;\n      if (path.isNotEmpty && path.contains('/')) {\n        String filename = path.split('/').last;\n        if (filename.isNotEmpty) {\n          return filename;\n        }\n      }\n    } catch (e) {\n      log('解析文件名失败: $e');\n    }\n    \n    // 如果无法从URL提取文件名，使用时间戳\n    return 'download_${DateTime.now().millisecondsSinceEpoch}';\n  }\n\n  /// 获取唯一的文件路径（避免重名）\n  static String _getUniqueFilePath(String originalPath) {\n    File file = File(originalPath);\n    if (!file.existsSync()) {\n      return originalPath;\n    }\n\n    String directory = file.parent.path;\n    String nameWithoutExtension = file.path.split('/').last.split('.').first;\n    String extension = file.path.contains('.') \n        ? '.${file.path.split('.').last}' \n        : '';\n\n    int counter = 1;\n    String newPath;\n    do {\n      newPath = '$directory/${nameWithoutExtension}_$counter$extension';\n      counter++;\n    } while (File(newPath).existsSync());\n\n    return newPath;\n  }\n\n  /// 检查是否为 APK 文件\n  static bool _isApkFile(String filePath) {\n    return filePath.toLowerCase().endsWith('.apk');\n  }\n\n  /// 检查和请求安装权限\n  static Future<bool> _checkInstallPermission() async {\n    if (!Platform.isAndroid) return true;\n    \n    try {\n      // 检查是否有安装权限\n      bool hasPermission = await Permission.requestInstallPackages.isGranted;\n      \n      if (!hasPermission) {\n        // 请求安装权限\n        PermissionStatus status = await Permission.requestInstallPackages.request();\n        \n        if (status.isGranted) {\n          return true;\n        } else if (status.isPermanentlyDenied) {\n          // 权限被永久拒绝，引导用户到设置页面\n          getx.Get.dialog(\n            AlertDialog(\n              title: Text(S.current.needInstallPermission),\n              content: Text(S.current.needInstallPermissionDesc),\n              actions: [\n                TextButton(\n                  onPressed: () => getx.Get.back(),\n                  child: Text(S.current.cancel),\n                ),\n                TextButton(\n                  onPressed: () {\n                    getx.Get.back();\n                    openAppSettings();\n                  },\n                  child: Text(S.current.goToSettings),\n                ),\n              ],\n            ),\n          );\n          return false;\n        } else {\n          getx.Get.showSnackbar(getx.GetSnackBar(\n            message: S.current.needInstallPermissionToInstallApk,\n            duration: const Duration(seconds: 3),\n          ));\n          return false;\n        }\n      }\n      \n      return true;\n    } catch (e) {\n      log('检查安装权限失败: $e');\n      return true; // 如果检查失败，继续尝试打开\n    }\n  }\n\n  /// 尝试打开文件\n  static Future<void> _openFile(String filePath) async {\n    try {\n      log('尝试打开文件: $filePath');\n      \n      // 如果是 APK 文件，先检查安装权限\n      if (_isApkFile(filePath)) {\n        bool hasPermission = await _checkInstallPermission();\n        if (!hasPermission) {\n          return; // 没有权限，不继续打开\n        }\n      }\n      \n      // 使用 open_filex 插件打开文件\n      final result = await OpenFilex.open(filePath);\n      \n      log('打开文件结果: ${result.type} - ${result.message}');\n      \n      // 根据结果显示相应的提示\n      switch (result.type) {\n        case ResultType.done:\n          // 文件成功打开，不需要额外提示\n          break;\n        case ResultType.noAppToOpen:\n          if (_isApkFile(filePath)) {\n            getx.Get.showSnackbar(getx.GetSnackBar(\n              message: S.current.cannotInstallApkNeedPermission,\n              duration: const Duration(seconds: 5),\n              mainButton: TextButton(\n                onPressed: () {\n                  openAppSettings();\n                },\n                child: Text(S.current.goToSettings),\n              ),\n            ));\n          } else {\n            getx.Get.showSnackbar(getx.GetSnackBar(\n              message: S.current.noAppToOpenFile,\n              duration: const Duration(seconds: 3),\n              mainButton: TextButton(\n                onPressed: () {\n                  _showFileLocation(filePath);\n                },\n                child: Text(S.current.viewLocation),\n              ),\n            ));\n          }\n          break;\n        case ResultType.fileNotFound:\n          getx.Get.showSnackbar(getx.GetSnackBar(\n            message: S.current.fileNotFound,\n            duration: const Duration(seconds: 3),\n          ));\n          break;\n        case ResultType.permissionDenied:\n          if (_isApkFile(filePath)) {\n            getx.Get.showSnackbar(getx.GetSnackBar(\n              message: S.current.noPermissionToInstallApkFile,\n              duration: const Duration(seconds: 5),\n              mainButton: TextButton(\n                onPressed: () {\n                  openAppSettings();\n                },\n                child: Text(S.current.goToSettings),\n              ),\n            ));\n          } else {\n            getx.Get.showSnackbar(getx.GetSnackBar(\n              message: S.current.noPermissionToOpenFile,\n              duration: const Duration(seconds: 3),\n            ));\n          }\n          break;\n        case ResultType.error:\n          getx.Get.showSnackbar(getx.GetSnackBar(\n            message: S.current.openFileFailed(result.message),\n            duration: const Duration(seconds: 3),\n            mainButton: TextButton(\n              onPressed: () {\n                _showFileLocation(filePath);\n              },\n              child: Text(S.current.viewLocation),\n            ),\n          ));\n          break;\n      }\n    } catch (e) {\n      log('打开文件异常: $e');\n      getx.Get.showSnackbar(getx.GetSnackBar(\n        message: S.current.openFileException(e.toString()),\n        duration: const Duration(seconds: 3),\n        mainButton: TextButton(\n          onPressed: () {\n            _showFileLocation(filePath);\n          },\n          child: Text(S.current.viewLocation),\n        ),\n      ));\n    }\n  }\n\n  /// 显示文件位置信息\n  static void _showFileLocation(String filePath) {\n    getx.Get.dialog(\n      AlertDialog(\n        title: Text(S.current.fileLocation),\n        content: Column(\n          mainAxisSize: MainAxisSize.min,\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: [\n            Text(S.current.fileSavedTo),\n            const SizedBox(height: 8),\n            SelectableText(\n              filePath,\n              style: const TextStyle(fontSize: 12),\n            ),\n            const SizedBox(height: 16),\n            Text(\n              S.current.fileLocationTip,\n              style: const TextStyle(fontSize: 14, color: Colors.grey),\n            ),\n          ],\n        ),\n        actions: [\n          TextButton(\n            onPressed: () => getx.Get.back(),\n            child: Text(S.current.ok),\n          ),\n        ],\n      ),\n    );\n  }\n\n  /// 获取OpenList下载目录路径（公共方法）\n  static Future<String?> getDownloadDirectoryPath() async {\n    Directory? dir = await _getOpenListDownloadDirectory();\n    return dir?.path;\n  }\n\n  /// 列出已下载的文件\n  static Future<List<FileSystemEntity>> getDownloadedFiles() async {\n    try {\n      Directory? downloadDir = await _getOpenListDownloadDirectory();\n      if (downloadDir != null && await downloadDir.exists()) {\n        return downloadDir.listSync();\n      }\n    } catch (e) {\n      log('获取下载文件列表失败: $e');\n    }\n    return [];\n  }\n\n  /// 清理下载目录\n  static Future<bool> clearDownloadDirectory() async {\n    try {\n      Directory? downloadDir = await _getOpenListDownloadDirectory();\n      if (downloadDir != null && await downloadDir.exists()) {\n        await downloadDir.delete(recursive: true);\n        log('已清理下载目录');\n        return true;\n      }\n    } catch (e) {\n      log('清理下载目录失败: $e');\n    }\n    return false;\n  }\n\n  /// 删除指定文件\n  static Future<bool> deleteFile(String filename) async {\n    try {\n      Directory? downloadDir = await _getOpenListDownloadDirectory();\n      if (downloadDir != null) {\n        File file = File('${downloadDir.path}/$filename');\n        if (await file.exists()) {\n          await file.delete();\n          log('已删除文件: $filename');\n          return true;\n        }\n      }\n    } catch (e) {\n      log('删除文件失败: $e');\n    }\n    return false;\n  }\n}\n\n/// 下载控制器（保持向后兼容）\nclass DownloadController extends getx.GetxController {\n  double _progress = 0.0;\n  String _statusText = S.current.preparingDownloadStatus;\n  bool _isCancelled = false;\n\n  double get progress => _progress;\n  String get statusText => _statusText;\n  bool get isCancelled => _isCancelled;\n\n  void updateProgress(double progress, int received, int total) {\n    if (_isCancelled) return;\n    \n    _progress = progress;\n    _statusText = '${_formatBytes(received)} / ${_formatBytes(total)}';\n    update();\n  }\n\n  void cancelDownload() {\n    _isCancelled = true;\n    _statusText = S.current.downloadCancelledText;\n    update();\n  }\n\n  String _formatBytes(int bytes) {\n    if (bytes < 1024) return '${bytes}B';\n    if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)}KB';\n    if (bytes < 1024 * 1024 * 1024) return '${(bytes / (1024 * 1024)).toStringAsFixed(1)}MB';\n    return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(1)}GB';\n  }\n}"
  },
  {
    "path": "lib/utils/download_test.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:get/get.dart';\nimport 'download_manager.dart';\nimport '../generated/l10n.dart';\n\n/// 下载功能测试页面\nclass DownloadTestPage extends StatelessWidget {\n  const DownloadTestPage({Key? key}) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        title: Text(S.current.downloadFunctionTest),\n      ),\n      body: Padding(\n        padding: const EdgeInsets.all(16.0),\n        child: Column(\n          crossAxisAlignment: CrossAxisAlignment.stretch,\n          children: [\n            Text(\n              S.current.testDirectDownloadFunction,\n              style: Theme.of(context).textTheme.headlineSmall,\n            ),\n            const SizedBox(height: 20),\n            ElevatedButton(\n              onPressed: () async {\n                // 测试下载一个小文件\n                await DownloadManager.downloadFileWithProgress(\n                  url: 'https://httpbin.org/json',\n                  filename: 'test.json',\n                );\n              },\n              child: Text(S.current.testDownloadJsonFile),\n            ),\n            const SizedBox(height: 10),\n            ElevatedButton(\n              onPressed: () async {\n                // 测试下载图片\n                await DownloadManager.downloadFileWithProgress(\n                  url: 'https://httpbin.org/image/png',\n                  filename: 'test_image.png',\n                );\n              },\n              child: Text(S.current.testDownloadPngImage),\n            ),\n            const SizedBox(height: 10),\n            ElevatedButton(\n              onPressed: () async {\n                // 测试下载较大文件\n                await DownloadManager.downloadFileWithProgress(\n                  url: 'https://httpbin.org/drip?duration=5&numbytes=1024000',\n                  filename: 'large_test.bin',\n                );\n              },\n              child: Text(S.current.testDownloadLargeFile),\n            ),\n            const SizedBox(height: 10),\n            ElevatedButton(\n              onPressed: () {\n                // 提示用户通过底部导航栏查看下载文件\n                Get.showSnackbar(GetSnackBar(\n                  message: S.current.checkDownloadManagerForFiles,\n                  duration: const Duration(seconds: 2),\n                ));\n              },\n              child: Text(S.current.viewDownloadFiles),\n            ),\n            const SizedBox(height: 10),\n            ElevatedButton(\n              onPressed: () async {\n                // 显示下载目录路径\n                String? path = await DownloadManager.getDownloadDirectoryPath();\n                Get.dialog(\n                  AlertDialog(\n                    title: Text(S.current.downloadDirectory),\n                    content: SelectableText(path ?? S.current.getDownloadPathFailed),\n                    actions: [\n                      TextButton(\n                        onPressed: () => Get.back(),\n                        child: Text(S.current.ok),\n                      ),\n                    ],\n                  ),\n                );\n              },\n              child: Text(S.current.viewDownloadDirectory),\n            ),\n            const SizedBox(height: 20),\n            Text(\n              S.current.description,\n              style: Theme.of(context).textTheme.titleMedium,\n            ),\n            Text(\n              S.current.downloadInstructions,\n              style: Theme.of(context).textTheme.bodyMedium,\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n\n/// 在主应用中添加测试入口的辅助方法\nclass DownloadTestHelper {\n  static void showTestDialog(BuildContext context) {\n    Get.dialog(\n      AlertDialog(\n        title: Text(S.current.downloadFunctionTest),\n        content: Text(S.current.openDownloadTestPage),\n        actions: [\n          TextButton(\n            onPressed: () => Get.back(),\n            child: Text(S.current.cancel),\n          ),\n          TextButton(\n            onPressed: () {\n              Get.back();\n              Get.to(() => const DownloadTestPage());\n            },\n            child: Text(S.current.ok),\n          ),\n        ],\n      ),\n    );\n  }\n}"
  },
  {
    "path": "lib/utils/intent_utils.dart",
    "content": "import 'package:android_intent_plus/android_intent.dart';\n\nclass IntentUtils {\n  static AndroidIntent getUrlIntent(String url) {\n    return AndroidIntent(action: \"action_view\", data: url);\n  }\n}\n"
  },
  {
    "path": "lib/utils/language_controller.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:get/get.dart';\nimport 'package:openlist_mobile/utils/language_manager.dart';\nimport 'package:openlist_mobile/generated/l10n.dart';\n\nclass LanguageController extends GetxController {\n  static LanguageController get to => Get.find();\n  \n  final _currentLanguageOption = LanguageManager.supportedLanguages.first.obs;\n  final _locale = Rxn<Locale>();\n  \n  LanguageOption get currentLanguageOption => _currentLanguageOption.value;\n  Locale? get locale => _locale.value;\n  \n  @override\n  void onInit() {\n    super.onInit();\n    _loadSavedLanguage();\n  }\n  \n  // 加载保存的语言设置\n  Future<void> _loadSavedLanguage() async {\n    try {\n      final savedOption = await LanguageManager.instance.getSavedLanguageOption();\n      _currentLanguageOption.value = savedOption;\n      \n      final savedLocale = await LanguageManager.instance.getCurrentLocale();\n      _locale.value = savedLocale;\n    } catch (e) {\n      debugPrint('Failed to load saved language: $e');\n    }\n  }\n  \n  // 切换语言\n  Future<void> changeLanguage(LanguageOption option) async {\n    try {\n      _currentLanguageOption.value = option;\n      \n      // 保存语言设置\n      await LanguageManager.instance.saveLanguageCode(option.code);\n      \n      // 更新locale\n      if (option.locale != null) {\n        _locale.value = option.locale;\n        Get.updateLocale(option.locale!);\n      } else {\n        // 跟随系统语言\n        final systemLocale = Get.deviceLocale ?? const Locale('en');\n        // 检查系统语言是否被支持\n        final supportedSystemLocale = _getSupportedLocale(systemLocale);\n        _locale.value = null; // 保持null表示跟随系统\n        Get.updateLocale(supportedSystemLocale);\n      }\n      \n    } catch (e) {\n      debugPrint('Failed to change language: $e');\n    }\n  }\n  \n  // 获取支持的语言环境\n  Locale _getSupportedLocale(Locale deviceLocale) {\n    // 检查是否直接支持\n    for (final option in LanguageManager.supportedLanguages) {\n      if (option.locale?.languageCode == deviceLocale.languageCode) {\n        return option.locale!;\n      }\n    }\n    \n    // 默认返回英语\n    return const Locale('en');\n  }\n  \n  // 获取当前应该使用的locale（考虑跟随系统的情况）\n  Locale getEffectiveLocale() {\n    if (_locale.value != null) {\n      return _locale.value!;\n    }\n    \n    // 如果是跟随系统，返回系统语言或默认语言\n    final systemLocale = Get.deviceLocale ?? const Locale('en');\n    return _getSupportedLocale(systemLocale);\n  }\n}\n\n// 语言选择器组件\nclass LanguageSelector extends StatelessWidget {\n  final VoidCallback? onLanguageChanged;\n  \n  const LanguageSelector({\n    Key? key,\n    this.onLanguageChanged,\n  }) : super(key: key);\n  \n  @override\n  Widget build(BuildContext context) {\n    return GetBuilder<LanguageController>(\n      builder: (controller) {\n        return Column(\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: [\n            ...LanguageManager.supportedLanguages.map(\n              (option) => RadioListTile<LanguageOption>(\n                title: Text(_getLocalizedLanguageName(option)),\n                value: option,\n                groupValue: controller.currentLanguageOption,\n                onChanged: (LanguageOption? value) async {\n                  if (value != null) {\n                    await controller.changeLanguage(value);\n                    onLanguageChanged?.call();\n                  }\n                },\n              ),\n            ),\n          ],\n        );\n      },\n    );\n  }\n  \n  String _getLocalizedLanguageName(LanguageOption option) {\n    switch (option.name) {\n      case 'followSystem':\n        return S.current.followSystem;\n      case 'simplifiedChinese':\n        return S.current.simplifiedChinese;\n      case 'english':\n        return S.current.english;\n      default:\n        return option.name;\n    }\n  }\n}\n"
  },
  {
    "path": "lib/utils/language_manager.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:shared_preferences/shared_preferences.dart';\n\nclass LanguageManager {\n  static const String _languageKey = 'app_language';\n  static const String _systemLanguageValue = 'system';\n  \n  static LanguageManager? _instance;\n  static LanguageManager get instance => _instance ??= LanguageManager._();\n  \n  LanguageManager._();\n  \n  // 支持的语言\n  static const List<LanguageOption> supportedLanguages = [\n    LanguageOption(\n      code: _systemLanguageValue,\n      name: 'followSystem',\n      locale: null,\n    ),\n    LanguageOption(\n      code: 'zh',\n      name: 'simplifiedChinese',\n      locale: Locale('zh'),\n    ),\n    LanguageOption(\n      code: 'en',\n      name: 'english',\n      locale: Locale('en'),\n    ),\n  ];\n  \n  // 获取保存的语言代码\n  Future<String> getSavedLanguageCode() async {\n    final prefs = await SharedPreferences.getInstance();\n    return prefs.getString(_languageKey) ?? _systemLanguageValue;\n  }\n  \n  // 保存语言代码\n  Future<void> saveLanguageCode(String languageCode) async {\n    final prefs = await SharedPreferences.getInstance();\n    await prefs.setString(_languageKey, languageCode);\n  }\n  \n  // 获取保存的语言选项\n  Future<LanguageOption> getSavedLanguageOption() async {\n    final languageCode = await getSavedLanguageCode();\n    return supportedLanguages.firstWhere(\n      (option) => option.code == languageCode,\n      orElse: () => supportedLanguages.first,\n    );\n  }\n  \n  // 根据语言代码获取Locale\n  Future<Locale?> getLocaleFromCode(String languageCode) async {\n    if (languageCode == _systemLanguageValue) {\n      return null; // 跟随系统\n    }\n    \n    final option = supportedLanguages.firstWhere(\n      (option) => option.code == languageCode,\n      orElse: () => supportedLanguages.first,\n    );\n    \n    return option.locale;\n  }\n  \n  // 获取当前应该使用的Locale\n  Future<Locale?> getCurrentLocale() async {\n    final languageCode = await getSavedLanguageCode();\n    return await getLocaleFromCode(languageCode);\n  }\n  \n  // 清除语言设置（恢复为跟随系统）\n  Future<void> clearLanguageSetting() async {\n    final prefs = await SharedPreferences.getInstance();\n    await prefs.remove(_languageKey);\n  }\n}\n\nclass LanguageOption {\n  final String code;\n  final String name; // 对应本地化键名\n  final Locale? locale;\n  \n  const LanguageOption({\n    required this.code,\n    required this.name,\n    required this.locale,\n  });\n  \n  @override\n  bool operator ==(Object other) {\n    if (identical(this, other)) return true;\n    return other is LanguageOption && other.code == code;\n  }\n  \n  @override\n  int get hashCode => code.hashCode;\n}\n"
  },
  {
    "path": "lib/utils/notification_manager.dart",
    "content": "import 'dart:io';\nimport 'dart:developer';\nimport 'package:flutter_local_notifications/flutter_local_notifications.dart';\nimport 'package:get/get.dart' as getx;\nimport 'download_manager.dart';\nimport '../pages/download_manager_page.dart';\nimport '../generated/l10n.dart';\n\nclass NotificationManager {\n  static final FlutterLocalNotificationsPlugin _notifications = FlutterLocalNotificationsPlugin();\n  static bool _isInitialized = false;\n  static const int _downloadNotificationId = 1000;\n  \n  /// 初始化通知\n  static Future<void> initialize() async {\n    if (_isInitialized) return;\n    \n    try {\n      // Android 初始化设置\n      const AndroidInitializationSettings initializationSettingsAndroid =\n          AndroidInitializationSettings('@mipmap/ic_launcher');\n\n      // iOS 初始化设置\n      const DarwinInitializationSettings initializationSettingsIOS =\n          DarwinInitializationSettings(\n        requestAlertPermission: true,\n        requestBadgePermission: true,\n        requestSoundPermission: true,\n      );\n\n      const InitializationSettings initializationSettings =\n          InitializationSettings(\n        android: initializationSettingsAndroid,\n        iOS: initializationSettingsIOS,\n      );\n\n      await _notifications.initialize(\n        initializationSettings,\n        onDidReceiveNotificationResponse: _onNotificationTapped,\n      );\n\n      // 请求通知权限\n      if (Platform.isAndroid) {\n        await _notifications\n            .resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()\n            ?.requestNotificationsPermission();\n      } else if (Platform.isIOS) {\n        await _notifications\n            .resolvePlatformSpecificImplementation<IOSFlutterLocalNotificationsPlugin>()\n            ?.requestPermissions(\n              alert: true,\n              badge: true,\n              sound: true,\n            );\n      }\n\n      _isInitialized = true;\n      log('通知管理器初始化成功');\n    } catch (e) {\n      log('通知管理器初始化失败: $e');\n    }\n  }\n\n  /// 处理通知点击事件\n  static void _onNotificationTapped(NotificationResponse response) {\n    log('通知被点击: ${response.payload}');\n    \n    // 跳转到下载管理页面\n    if (getx.Get.context != null) {\n      getx.Get.to(() => const DownloadManagerPage());\n    }\n  }\n\n  /// 显示或更新下载进度通知\n  static Future<void> showDownloadProgressNotification() async {\n    if (!_isInitialized) {\n      await initialize();\n    }\n\n    try {\n      List<DownloadTask> activeTasks = DownloadManager.activeTasks\n          .where((task) => task.status == DownloadStatus.downloading)\n          .toList();\n\n      if (activeTasks.isEmpty) {\n        // 没有活跃下载任务，取消通知\n        await _notifications.cancel(_downloadNotificationId);\n        return;\n      }\n\n      String title;\n      String body;\n      int progress = 0;\n      int maxProgress = 100;\n\n      if (activeTasks.length == 1) {\n        // 单个文件下载\n        DownloadTask task = activeTasks.first;\n        title = S.current.currentlyDownloading;\n        body = task.filename;\n        progress = (task.progress * 100).round();\n      } else {\n        // 多个文件下载\n        title = S.current.currentlyDownloading;\n        body = S.current.currentDownloadingFiles(activeTasks.length);\n        \n        // 计算总进度 - 所有文件下载进度的总和\n        double totalProgress = 0;\n        \n        for (DownloadTask task in activeTasks) {\n          totalProgress += task.progress;\n        }\n        \n        // 总进度条为所有文件进度的平均值\n        double avgProgress = totalProgress / activeTasks.length;\n        progress = (avgProgress * 100).round();\n      }\n\n      // Android 通知详情\n      AndroidNotificationDetails androidDetails = AndroidNotificationDetails(\n        'download_channel',\n        S.current.downloadProgressChannel,\n        channelDescription: S.current.downloadProgressDesc,\n        importance: Importance.low,\n        priority: Priority.low,\n        showProgress: true,\n        maxProgress: maxProgress,\n        progress: progress,\n        ongoing: true, // 常驻通知\n        autoCancel: false,\n        playSound: false,\n        enableVibration: false,\n        icon: '@mipmap/ic_launcher',\n        actions: [\n          AndroidNotificationAction(\n            'view_downloads',\n            S.current.viewDownloads,\n            showsUserInterface: true,\n          ),\n        ],\n      );\n\n      // iOS 通知详情\n      const DarwinNotificationDetails iosDetails = DarwinNotificationDetails(\n        presentAlert: false,\n        presentBadge: false,\n        presentSound: false,\n      );\n\n      NotificationDetails notificationDetails = NotificationDetails(\n        android: androidDetails,\n        iOS: iosDetails,\n      );\n\n      await _notifications.show(\n        _downloadNotificationId,\n        title,\n        body,\n        notificationDetails,\n        payload: 'download_progress',\n      );\n\n    } catch (e) {\n      log('显示下载进度通知失败: $e');\n    }\n  }\n\n  /// 显示下载完成通知\n  static Future<void> showDownloadCompleteNotification() async {\n    if (!_isInitialized) {\n      await initialize();\n    }\n\n    try {\n      // 检查是否还有其他下载任务在进行\n      bool hasActiveDownloads = DownloadManager.activeTasks.isNotEmpty;\n\n      if (hasActiveDownloads) {\n        // 还有其他下载在进行，不显示完成通知，继续显示进度通知\n        await showDownloadProgressNotification();\n        return;\n      }\n\n      // 先取消进度通知\n      await _notifications.cancel(_downloadNotificationId);\n\n      // 获取最近完成的任务数量\n      List<DownloadTask> completedTasks = DownloadManager.completedTasks\n          .where((task) => task.status == DownloadStatus.completed)\n          .toList();\n\n      if (completedTasks.isEmpty) return;\n\n      String title;\n      String body;\n\n      if (completedTasks.length == 1) {\n        // 单个文件完成\n        DownloadTask task = completedTasks.first;\n        title = S.current.downloadCompleteNotificationTitle(task.filename);\n        body = S.current.clickToJumpToDownloadManager;\n      } else {\n        // 多个文件完成\n        title = S.current.downloadCompleteTitle;\n        body = S.current.multipleFilesCompleted(completedTasks.length);\n      }\n\n      // Android 通知详情\n      AndroidNotificationDetails androidDetails = AndroidNotificationDetails(\n        'download_complete_channel',\n        S.current.downloadCompleteChannel,\n        channelDescription: S.current.downloadCompleteChannelDesc,\n        importance: Importance.high,\n        priority: Priority.high,\n        autoCancel: true,\n        playSound: true,\n        enableVibration: true,\n        icon: '@mipmap/ic_launcher',\n        actions: [\n          AndroidNotificationAction(\n            'open_downloads',\n            S.current.openDownloadManager,\n            showsUserInterface: true,\n          ),\n        ],\n      );\n\n      // iOS 通知详情\n      const DarwinNotificationDetails iosDetails = DarwinNotificationDetails(\n        presentAlert: true,\n        presentBadge: true,\n        presentSound: true,\n      );\n\n      NotificationDetails notificationDetails = NotificationDetails(\n        android: androidDetails,\n        iOS: iosDetails,\n      );\n\n      await _notifications.show(\n        _downloadNotificationId + 1,\n        title,\n        body,\n        notificationDetails,\n        payload: 'download_complete',\n      );\n\n    } catch (e) {\n      log('显示下载完成通知失败: $e');\n    }\n  }\n\n  /// 显示单个文件下载完成通知\n  static Future<void> showSingleFileCompleteNotification(DownloadTask task) async {\n    if (!_isInitialized) {\n      await initialize();\n    }\n\n    try {\n      // 检查是否还有其他下载任务在进行\n      bool hasActiveDownloads = DownloadManager.activeTasks.isNotEmpty;\n\n      if (hasActiveDownloads) {\n        // 还有其他下载在进行，更新进度通知\n        await showDownloadProgressNotification();\n        return;\n      }\n\n      // 先取消进度通知\n      await _notifications.cancel(_downloadNotificationId);\n\n      String title = S.current.downloadCompleteNotificationTitle(task.filename);\n      String body = S.current.clickToJumpToDownloadManager;\n\n      // Android 通知详情\n      AndroidNotificationDetails androidDetails = AndroidNotificationDetails(\n        'download_complete_channel',\n        S.current.downloadCompleteChannel,\n        channelDescription: S.current.downloadCompleteChannelDesc,\n        importance: Importance.high,\n        priority: Priority.high,\n        autoCancel: true,\n        playSound: true,\n        enableVibration: true,\n        icon: '@mipmap/ic_launcher',\n        actions: [\n          AndroidNotificationAction(\n            'open_downloads',\n            S.current.openDownloadManager,\n            showsUserInterface: true,\n          ),\n        ],\n      );\n\n      // iOS 通知详情\n      const DarwinNotificationDetails iosDetails = DarwinNotificationDetails(\n        presentAlert: true,\n        presentBadge: true,\n        presentSound: true,\n      );\n\n      NotificationDetails notificationDetails = NotificationDetails(\n        android: androidDetails,\n        iOS: iosDetails,\n      );\n\n      await _notifications.show(\n        _downloadNotificationId + 1,\n        title,\n        body,\n        notificationDetails,\n        payload: 'download_complete',\n      );\n\n    } catch (e) {\n      log('显示单个文件下载完成通知失败: $e');\n    }\n  }\n\n  /// 取消下载通知\n  static Future<void> cancelDownloadNotification() async {\n    try {\n      await _notifications.cancel(_downloadNotificationId);\n    } catch (e) {\n      log('取消下载通知失败: $e');\n    }\n  }\n\n  /// 取消所有通知\n  static Future<void> cancelAllNotifications() async {\n    try {\n      await _notifications.cancelAll();\n    } catch (e) {\n      log('取消所有通知失败: $e');\n    }\n  }\n\n  /// 格式化字节大小\n  static String _formatBytes(int bytes) {\n    if (bytes < 1024) return '${bytes}B';\n    if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)}KB';\n    if (bytes < 1024 * 1024 * 1024) return '${(bytes / (1024 * 1024)).toStringAsFixed(1)}MB';\n    return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(1)}GB';\n  }\n}"
  },
  {
    "path": "lib/utils/service_manager.dart",
    "content": "import 'dart:async';\nimport 'dart:io';\nimport 'package:flutter/services.dart';\nimport 'package:flutter/foundation.dart';\nimport 'package:openlist_mobile/generated_api.dart';\n\n/// Service Manager - Manages OpenList backend service lifecycle\nclass ServiceManager {\n  static const String _channelName = 'com.openlist.mobile/service';\n  static const MethodChannel _channel = MethodChannel(_channelName);\n  \n  static ServiceManager? _instance;\n  static ServiceManager get instance => _instance ??= ServiceManager._();\n  \n  ServiceManager._();\n  \n  // Service status stream controller\n  final StreamController<bool> _serviceStatusController = StreamController<bool>.broadcast();\n  \n  /// Service status stream\n  Stream<bool> get serviceStatusStream => _serviceStatusController.stream;\n  \n  bool _isServiceRunning = false;\n  Timer? _statusCheckTimer;\n  \n  /// Current service running status\n  bool get isServiceRunning => _isServiceRunning;\n  \n  /// Initialize service manager\n  Future<void> initialize() async {\n    \n    try {\n      // 设置方法调用处理器\n      _channel.setMethodCallHandler(_handleMethodCall);\n      \n      // 开始定期检查服务状态\n      _startStatusCheck();\n      \n      // 初始检查服务状态\n      await checkServiceStatus();\n      \n      debugPrint('ServiceManager initialized');\n    } catch (e) {\n      debugPrint('Failed to initialize ServiceManager: $e');\n    }\n  }\n  \n  /// 处理来自原生端的方法调用\n  Future<dynamic> _handleMethodCall(MethodCall call) async {\n    debugPrint('ServiceManager received method call: ${call.method}');\n    switch (call.method) {\n      case 'onServiceStatusChanged':\n        final bool isRunning = call.arguments['isRunning'] ?? false;\n        debugPrint('ServiceManager status change notification: $isRunning');\n        _updateServiceStatus(isRunning);\n        break;\n      default:\n        debugPrint('Unknown method call: ${call.method}');\n    }\n  }\n  \n  /// Start OpenList service\n  Future<bool> startService() async {\n    try {\n      if (Platform.isAndroid) {\n        final bool result = await _channel.invokeMethod('startService');\n        debugPrint('Start service result (Android): $result');\n        \n        // Delay status check to give service startup time\n        Timer(const Duration(seconds: 2), () => checkServiceStatus());\n        \n        return result;\n      } else if (Platform.isIOS) {\n        // Use Pigeon API for iOS\n        await Android().startService();\n        debugPrint('Start service called (iOS)');\n        \n        // Delay status check\n        Timer(const Duration(seconds: 2), () => checkServiceStatus());\n        \n        return true;\n      }\n      \n      return false;\n    } catch (e) {\n      debugPrint('Failed to start service: $e');\n      return false;\n    }\n  }\n  \n  /// Stop OpenList service\n  Future<bool> stopService() async {\n    try {\n      if (Platform.isAndroid) {\n        final bool result = await _channel.invokeMethod('stopService');\n        debugPrint('Stop service result (Android): $result');\n        \n        // Update status immediately\n        if (result) {\n          _updateServiceStatus(false);\n        }\n        \n        // Delay status check to confirm service stopped\n        Timer(const Duration(seconds: 1), () => checkServiceStatus());\n        \n        return result;\n      } else if (Platform.isIOS) {\n        // iOS does not need explicit stop - managed by OpenListManager\n        debugPrint('Stop service called (iOS) - managed by system');\n        _updateServiceStatus(false);\n        return true;\n      }\n      \n      return false;\n    } catch (e) {\n      debugPrint('Failed to stop service: $e');\n      return false;\n    }\n  }\n  \n  /// Check service status\n  Future<bool> checkServiceStatus() async {\n    try {\n      bool isRunning = false;\n      \n      if (Platform.isAndroid) {\n        isRunning = await _channel.invokeMethod('isServiceRunning');\n      } else if (Platform.isIOS) {\n        // Use Pigeon API for iOS\n        isRunning = await Android().isRunning();\n      }\n      \n      _updateServiceStatus(isRunning);\n      return isRunning;\n    } catch (e) {\n      debugPrint('Failed to check service status: $e');\n      return false;\n    }\n  }\n  \n  /// Restart service\n  Future<bool> restartService() async {\n    try {\n      await stopService();\n      await Future.delayed(const Duration(seconds: 2));\n      return await startService();\n    } catch (e) {\n      debugPrint('Failed to restart service: $e');\n      return false;\n    }\n  }\n  \n  /// 检查是否在电池优化白名单中\n  Future<bool> isBatteryOptimizationIgnored() async {\n    if (!Platform.isAndroid) return true;\n    \n    try {\n      final bool result = await _channel.invokeMethod('isBatteryOptimizationIgnored');\n      return result;\n    } catch (e) {\n      debugPrint('Failed to check battery optimization status: $e');\n      return false;\n    }\n  }\n  \n  /// 请求忽略电池优化\n  Future<bool> requestIgnoreBatteryOptimization() async {\n    if (!Platform.isAndroid) return true;\n    \n    try {\n      final bool result = await _channel.invokeMethod('requestIgnoreBatteryOptimization');\n      return result;\n    } catch (e) {\n      debugPrint('Failed to request battery optimization exemption: $e');\n      return false;\n    }\n  }\n  \n  /// 打开电池优化设置\n  Future<bool> openBatteryOptimizationSettings() async {\n    if (!Platform.isAndroid) return false;\n    \n    try {\n      final bool result = await _channel.invokeMethod('openBatteryOptimizationSettings');\n      return result;\n    } catch (e) {\n      debugPrint('Failed to open battery optimization settings: $e');\n      return false;\n    }\n  }\n  \n  /// 打开自启动设置\n  Future<bool> openAutoStartSettings() async {\n    if (!Platform.isAndroid) return false;\n    \n    try {\n      final bool result = await _channel.invokeMethod('openAutoStartSettings');\n      return result;\n    } catch (e) {\n      debugPrint('Failed to open auto start settings: $e');\n      return false;\n    }\n  }\n  \n  /// 获取服务地址\n  Future<String> getServiceAddress() async {\n    if (!Platform.isAndroid) return '';\n    \n    try {\n      final String address = await _channel.invokeMethod('getServiceAddress');\n      return address;\n    } catch (e) {\n      debugPrint('Failed to get service address: $e');\n      return '';\n    }\n  }\n  \n  /// 开始定期检查服务状态\n  void _startStatusCheck() {\n    _statusCheckTimer?.cancel();\n    _statusCheckTimer = Timer.periodic(const Duration(seconds: 30), (timer) {\n      checkServiceStatus();\n    });\n  }\n  \n  /// 停止状态检查\n  void _stopStatusCheck() {\n    _statusCheckTimer?.cancel();\n    _statusCheckTimer = null;\n  }\n  \n  /// 更新服务状态\n  void _updateServiceStatus(bool isRunning) {\n    if (_isServiceRunning != isRunning) {\n      _isServiceRunning = isRunning;\n      _serviceStatusController.add(isRunning);\n      debugPrint('Service status changed: $isRunning');\n    }\n  }\n  \n  /// 释放资源\n  void dispose() {\n    _stopStatusCheck();\n    _serviceStatusController.close();\n  }\n}"
  },
  {
    "path": "lib/utils/update_checker.dart",
    "content": "import 'dart:convert';\nimport 'dart:core';\nimport 'dart:developer';\nimport 'dart:io';\n\nimport 'package:openlist_mobile/contant/native_bridge.dart';\n\nclass UpdateChecker {\n  String owner;\n  String repo;\n\n  Map<String, dynamic>? _data;\n\n  UpdateChecker({required this.owner, required this.repo});\n\n  String _versionName = \"\";\n  String _systemABI = \"\";\n\n  downloadData() async {\n    _data = await _getLatestRelease(owner, repo);\n    _versionName = await NativeBridge.common.getVersionName();\n    _systemABI = await NativeBridge.common.getDeviceCPUABI();\n  }\n\n  Map<String, dynamic> get data {\n    if (_data == null) {\n      throw Exception('Data not downloaded');\n    }\n    return _data!;\n  }\n\n  static Future<Map<String, dynamic>> _getLatestRelease(\n      String owner, String repo) async {\n    HttpClient client = HttpClient();\n    final req = await client.getUrl(\n        Uri.parse('https://api.github.com/repos/$owner/$repo/releases/latest'));\n    final response = await req.close();\n\n    if (response.statusCode == HttpStatus.ok) {\n      final body = await response.transform(utf8.decoder).join();\n      return json.decode(body);\n    } else {\n      throw Exception(\n          'Failed to get latest release, status code: ${response.statusCode}');\n    }\n  }\n\n  String getTag() {\n    return data['tag_name'];\n  }\n\n  Future<bool> hasNewVersion() async {\n    final latestVersion = getTag();\n    final currentVersion = _versionName;\n\n    log('latestVersion: $latestVersion, currentVersion: $currentVersion');\n    // return true;\n    return _extractNumbers(latestVersion) > _extractNumbers(currentVersion);\n  }\n\n  String getApkDownloadUrl() {\n    final assets = data['assets'];\n    for (var asset in assets) {\n      if (asset['name'].contains(_systemABI)) {\n        return asset['browser_download_url'];\n      }\n    }\n\n    throw Exception('Failed to get apk download url');\n  }\n\n  String getUpdateContent() {\n    return data['body'];\n  }\n\n  String getHtmlUrl() {\n    return data['html_url'];\n  }\n\n  // 1.24.011609 to Int\n  static int _extractNumbers(String input) {\n    final s = input.replaceAll(RegExp(r'[^0-9]'), '');\n    return int.parse(s);\n  }\n}\n"
  },
  {
    "path": "lib/widgets/switch_floating_action_button.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass SwitchFloatingButton extends StatefulWidget {\n  final bool isSwitch;\n  final ValueChanged<bool> onSwitchChange;\n\n  const SwitchFloatingButton(\n      {super.key, required this.isSwitch, required this.onSwitchChange});\n\n  @override\n  State<SwitchFloatingButton> createState() => _SwitchFloatingButtonState();\n}\n\nclass _SwitchFloatingButtonState extends State<SwitchFloatingButton>\n    with SingleTickerProviderStateMixin {\n  late final AnimationController _controller;\n  late final Animation<double> _animation;\n\n  @override\n  void initState() {\n    super.initState();\n    _controller = AnimationController(\n      vsync: this,\n      duration: const Duration(milliseconds: 200),\n    );\n\n    _animation = Tween(begin: 0.0, end: 1.0).animate(_controller);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final icon = widget.isSwitch\n        ? const Icon(Icons.stop, size: 48)\n        : const Icon(Icons.send, size: 32);\n\n    return FloatingActionButton(\n      onPressed: () {\n        if (widget.isSwitch && _controller.isCompleted) {\n          _controller.reverse(from: 0.5);\n        } else {\n          _controller.forward(from: 0.5);\n        }\n        widget.onSwitchChange(!widget.isSwitch);\n      },\n      backgroundColor: widget.isSwitch\n          ? Theme.of(context).colorScheme.inversePrimary\n          : Theme.of(context).colorScheme.primaryContainer,\n      elevation: 8.0,\n      shape: const CircleBorder(),\n      child: RotationTransition(\n        turns: _animation,\n        child: icon,\n      ),\n    );\n  }\n\n  @override\n  void dispose() {\n    super.dispose();\n    _controller.dispose();\n  }\n}\n"
  },
  {
    "path": "openlist-lib/openlistlib/common.go",
    "content": "package openlistlib\n\nimport \"net\"\n\nfunc GetOutboundIP() (net.IP, error) {\n\tconn, err := net.Dial(\"udp\", \"8.8.8.8:80\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer conn.Close()\n\n\tlocalAddr := conn.LocalAddr().(*net.UDPAddr)\n\treturn localAddr.IP, nil\n}\n\nfunc GetOutboundIPString() string {\n\tnetIp, err := GetOutboundIP()\n\tif err != nil {\n\t\treturn \"localhost\"\n\t}\n\n\treturn netIp.String()\n}\n\n"
  },
  {
    "path": "openlist-lib/openlistlib/internal/log.go",
    "content": "package internal\n\nimport log \"github.com/sirupsen/logrus\"\n\ntype MyFormatter struct {\n\tlog.Formatter\n\tOnLog func(entry *log.Entry)\n}\n\nfunc (f *MyFormatter) Format(entry *log.Entry) ([]byte, error) {\n\tf.OnLog(entry)\n\treturn nil, nil\n}\n"
  },
  {
    "path": "openlist-lib/openlistlib/server.go",
    "content": "package openlistlib\n\nimport (\n\t\"errors\"\n\t\"time\"\n\n\t\"github.com/OpenListTeam/OpenList/v4/internal/bootstrap\"\n\t\"github.com/OpenListTeam/OpenList/v4/internal/db\"\n\t\"github.com/OpenListTeam/OpenList/v4/openlistlib/internal\"\n\t\"github.com/OpenListTeam/OpenList/v4/pkg/utils\"\n\tlog \"github.com/sirupsen/logrus\"\n)\n\ntype LogCallback interface {\n\tOnLog(level int16, time int64, message string)\n}\n\ntype Event interface {\n\tOnStartError(t string, err string)\n\tOnShutdown(t string)\n\tOnProcessExit(code int)\n}\n\nvar startFailedHookUuid = \"\"\nvar shutdownHookUuid = \"\"\nvar logFormatter *internal.MyFormatter\n\nfunc Init(event Event, cb LogCallback) error {\n\tif startFailedHookUuid != \"\" {\n\t\tbootstrap.RemoveEndpointStartFailedHook(startFailedHookUuid)\n\t\tstartFailedHookUuid = \"\"\n\t}\n\tif shutdownHookUuid != \"\" {\n\t\tbootstrap.RemoveEndpointShutdownHook(shutdownHookUuid)\n\t\tshutdownHookUuid = \"\"\n\t}\n\tbootstrap.Init()\n\tstartFailedHookUuid = bootstrap.RegisterEndpointStartFailedHook(event.OnStartError)\n\tshutdownHookUuid = bootstrap.RegisterEndpointShutdownHook(event.OnShutdown)\n\tlogFormatter = &internal.MyFormatter{\n\t\tOnLog: func(entry *log.Entry) {\n\t\t\tcb.OnLog(int16(entry.Level), entry.Time.UnixMilli(), entry.Message)\n\t\t},\n\t}\n\tif utils.Log == nil {\n\t\treturn errors.New(\"utils.log is nil\")\n\t} else {\n\t\tutils.Log.SetFormatter(logFormatter)\n\t\tutils.Log.ExitFunc = event.OnProcessExit\n\t}\n\treturn nil\n}\n\nfunc IsRunning(t string) bool {\n\treturn bootstrap.IsRunning(t)\n}\n\n// Start starts the server\nfunc Start() {\n\tbootstrap.Start()\n}\n\n// Shutdown timeout 毫秒\nfunc Shutdown(timeout int64) (err error) {\n\ttimeoutDuration := time.Duration(timeout) * time.Millisecond\n\tbootstrap.Shutdown(timeoutDuration)\n\n\t// Force database sync before shutdown\n\tForceDBSync()\n\t//bootstrap.Release()\n\treturn nil\n}\n\n// ForceDBSync forces SQLite WAL checkpoint to sync data to main database file\nfunc ForceDBSync() error {\n\tlog.Info(\"Forcing database sync (WAL checkpoint)...\")\n\n\t// Get the database instance and execute WAL checkpoint\n\tgormDB := db.GetDb()\n\tif gormDB != nil {\n\t\tsqlDB, err := gormDB.DB()\n\t\tif err != nil {\n\t\t\tlog.Errorf(\"Failed to get database connection: %v\", err)\n\t\t\treturn err\n\t\t}\n\n\t\t// Execute WAL checkpoint with TRUNCATE mode to force sync and remove WAL files\n\t\t_, err = sqlDB.Exec(\"PRAGMA wal_checkpoint(TRUNCATE)\")\n\t\tif err != nil {\n\t\t\tlog.Errorf(\"Failed to execute WAL checkpoint: %v\", err)\n\t\t\treturn err\n\t\t}\n\n\t\t// Also execute synchronous commit to ensure data is written to disk\n\t\t_, err = sqlDB.Exec(\"PRAGMA synchronous=FULL\")\n\t\tif err != nil {\n\t\t\tlog.Warnf(\"Failed to set synchronous mode: %v\", err)\n\t\t}\n\n\t\tlog.Info(\"Database sync completed successfully\")\n\t} else {\n\t\tlog.Warn(\"Database instance is nil, skipping sync\")\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "openlist-lib/openlistlib/settings.go",
    "content": "package openlistlib\n\nimport (\n\t\"github.com/OpenListTeam/OpenList/v4/cmd\"\n\t\"github.com/OpenListTeam/OpenList/v4/cmd/flags\"\n\t\"github.com/OpenListTeam/OpenList/v4/internal/op\"\n\t\"github.com/OpenListTeam/OpenList/v4/pkg/utils\"\n)\n\nfunc SetConfigData(path string) {\n\tflags.DataDir = path\n}\n\nfunc SetConfigLogStd(b bool) {\n\tflags.LogStd = b\n}\n\nfunc SetConfigDebug(b bool) {\n\tflags.Debug = b\n}\n\nfunc SetConfigNoPrefix(b bool) {\n\tflags.NoPrefix = b\n}\n\nfunc SetAdminPassword(pwd string) {\n\tadmin, err := op.GetAdmin()\n\tif err != nil {\n\t\tutils.Log.Errorf(\"failed get admin user: %+v\", err)\n\t\treturn\n\t}\n\tadmin.SetPassword(pwd)\n\tif err := op.UpdateUser(admin); err != nil {\n\t\tutils.Log.Errorf(\"failed update admin user: %+v\", err)\n\t\treturn\n\t}\n\tutils.Log.Infof(\"admin user has been updated:\")\n\tutils.Log.Infof(\"username: %s\", admin.Username)\n\tutils.Log.Infof(\"password: %s\", pwd)\n\tcmd.DelAdminCacheOnline()\n}\n"
  },
  {
    "path": "openlist-lib/scripts/clear.sh",
    "content": "#!/bin/bash\n\nmkdir /tmp/openlist\nrm -rf /tmp/openlist/*\ncp -r ../scripts /tmp/openlist\ncp -r ../openlistlib /tmp/openlist\n\nrm -rf ../*\n\ncp -r /tmp/openlist/* ../"
  },
  {
    "path": "openlist-lib/scripts/fix_ios_dependencies.sh",
    "content": "#!/bin/bash\n\necho \"Fixing iOS incompatible dependencies by patching rclone source...\"\n\n# Find the module root\nif [ -f go.mod ]; then\n    MODULE_ROOT=\".\"\nelif [ -f ../go.mod ]; then\n    MODULE_ROOT=\"..\"\n    cd ..\nelse\n    echo \"Error: Cannot find go.mod\"\n    exit 1\nfi\n\necho \"Working in module root: $(pwd)\"\n\n# Check for rclone dependency\necho \"Checking for rclone dependency...\"\nRCLONE_VERSION=$(go list -m all | grep \"github.com/rclone/rclone\" | awk '{print $2}' || echo \"\")\n\nif [ -z \"$RCLONE_VERSION\" ]; then\n    echo \"No rclone dependency found, skipping patch\"\n    exit 0\nfi\n\necho \"Found rclone version: $RCLONE_VERSION\"\n\n# Function to create minimal local rclone (backup method)\ncreate_minimal_rclone() {\n    echo \"Using backup method: creating minimal local rclone module...\"\n    \n    LOCAL_RCLONE_DIR=\"./local_rclone\"\n    rm -rf \"$LOCAL_RCLONE_DIR\" 2>/dev/null || true\n    \n    # Create the directory structure\n    mkdir -p \"$LOCAL_RCLONE_DIR/lib/buildinfo\"\n    \n    # Create a minimal go.mod for the local rclone\n    cat > \"$LOCAL_RCLONE_DIR/go.mod\" << 'GOMOD_EOF'\nmodule github.com/rclone/rclone\n\ngo 1.19\nGOMOD_EOF\n    \n    # Create the patched osversion.go file\n    cat > \"$LOCAL_RCLONE_DIR/lib/buildinfo/osversion.go\" << 'OSVERSION_EOF'\n//go:build !windows\n\npackage buildinfo\n\n// GetOSVersion returns OS version, kernel and bitness\nfunc GetOSVersion() (osVersion, osKernel string) {\n\treturn\n}\nOSVERSION_EOF\n    \n    # Add replace directive to use local copy\n    echo \"Adding replace directive to use local rclone copy...\"\n    go mod edit -replace github.com/rclone/rclone=\"./local_rclone\"\n    \n    return 0\n}\n\n# Try primary method first\necho \"Attempting primary method: patching existing rclone...\"\n\n# Download dependencies to get the source\necho \"Downloading dependencies...\"\ngo mod download\n\n# Find the rclone module path\nRCLONE_PATH=$(go list -m -f '{{.Dir}}' github.com/rclone/rclone 2>/dev/null)\n\nif [ -z \"$RCLONE_PATH\" ]; then\n    echo \"Could not find rclone module path, trying alternative method...\"\n    # Try to find it in GOPATH/pkg/mod\n    GOPATH_MOD=$(go env GOPATH)/pkg/mod\n    RCLONE_PATH=$(find \"$GOPATH_MOD\" -name \"rclone@$RCLONE_VERSION\" -type d 2>/dev/null | head -1)\nfi\n\nif [ -z \"$RCLONE_PATH\" ]; then\n    echo \"Could not locate rclone source directory, using backup method...\"\n    create_minimal_rclone\nelse\n    echo \"Found rclone source at: $RCLONE_PATH\"\n    \n    # Path to the problematic file\n    OSVERSION_FILE=\"$RCLONE_PATH/lib/buildinfo/osversion.go\"\n    \n    if [ ! -f \"$OSVERSION_FILE\" ]; then\n        echo \"osversion.go file not found, using backup method...\"\n        create_minimal_rclone\n    else\n        echo \"Found osversion.go at: $OSVERSION_FILE\"\n        \n        # Try to patch the existing file\n        LOCAL_RCLONE_DIR=\"./local_rclone\"\n        rm -rf \"$LOCAL_RCLONE_DIR\" 2>/dev/null || true\n        mkdir -p \"$LOCAL_RCLONE_DIR\"\n        \n        echo \"Copying rclone source to local directory...\"\n        if cp -r \"$RCLONE_PATH\"/* \"$LOCAL_RCLONE_DIR/\" 2>/dev/null; then\n            # Fix permissions on the copied files\n            echo \"Fixing permissions on copied files...\"\n            find \"$LOCAL_RCLONE_DIR\" -type f -exec chmod u+w {} \\; 2>/dev/null || true\n            find \"$LOCAL_RCLONE_DIR\" -type d -exec chmod u+w {} \\; 2>/dev/null || true\n            \n            # Update the file path to local copy\n            OSVERSION_FILE=\"$LOCAL_RCLONE_DIR/lib/buildinfo/osversion.go\"\n            \n            # Try to patch the file\n            if [ -f \"$OSVERSION_FILE\" ] && [ -w \"$OSVERSION_FILE\" ]; then\n                echo \"Patching osversion.go for iOS compatibility...\"\n                \n                # Create the patch content\n                cat > \"$OSVERSION_FILE\" << 'PATCH_EOF'\n//go:build !windows\n\npackage buildinfo\n\n// GetOSVersion returns OS version, kernel and bitness\nfunc GetOSVersion() (osVersion, osKernel string) {\n\treturn\n}\nPATCH_EOF\n                \n                # Add replace directive to use local copy\n                echo \"Adding replace directive to use local rclone copy...\"\n                go mod edit -replace github.com/rclone/rclone=\"./local_rclone\"\n            else\n                echo \"Cannot write to copied file, using backup method...\"\n                create_minimal_rclone\n            fi\n        else\n            echo \"Failed to copy rclone source, using backup method...\"\n            create_minimal_rclone\n        fi\n    fi\nfi\n\n# Verify the patch\necho \"Verifying patch...\"\nFINAL_OSVERSION_FILE=\"./local_rclone/lib/buildinfo/osversion.go\"\n\nif [ -f \"$FINAL_OSVERSION_FILE\" ]; then\n    echo \"✅ Patched osversion.go found\"\n    echo \"Content:\"\n    cat \"$FINAL_OSVERSION_FILE\"\n    echo \"\"\n    \n    # Check if the patch was applied correctly\n    if grep -q \"host.PlatformInformation\\|host.KernelVersion\\|host.KernelArch\" \"$FINAL_OSVERSION_FILE\"; then\n        echo \"❌ WARNING: File still contains problematic host function calls!\"\n        echo \"Patch may not have been applied correctly.\"\n        exit 1\n    else\n        echo \"✅ Patch applied successfully - no problematic host function calls found\"\n    fi\nelse\n    echo \"❌ Failed to create patched osversion.go\"\n    exit 1\nfi\n\n# Clean and rebuild to apply changes\necho \"Cleaning and rebuilding module with patched rclone...\"\ngo mod tidy\ngo mod download\n\necho \"✅ iOS dependency fix completed by patching rclone source\"\necho \"The problematic gopsutil calls in rclone have been disabled for iOS builds\""
  },
  {
    "path": "openlist-lib/scripts/gobind.sh",
    "content": "#!/bin/bash\n\n# Build version information\nbuiltAt=\"${OPENLIST_BUILT_AT:-$(date +'%F %T %z')}\"\ngitAuthor=\"${OPENLIST_GIT_AUTHOR:-The OpenList Projects Contributors <noreply@openlist.team>}\"\ngitCommit=\"${OPENLIST_GIT_COMMIT:-$(git log --pretty=format:'%h' -1 2>/dev/null || echo 'unknown')}\"\nversion=\"${OPENLIST_VERSION:-dev}\"\nwebVersion=\"${OPENLIST_WEB_VERSION:-rolling}\"\n\necho \"Building with version info:\"\necho \"  Version: $version\"\necho \"  WebVersion: $webVersion\"\necho \"  GitCommit: $gitCommit\"\necho \"  BuiltAt: $builtAt\"\n\n# Construct ldflags\nldflags=\"-s -w\"\nldflags=\"$ldflags -X 'github.com/OpenListTeam/OpenList/v4/internal/conf.BuiltAt=$builtAt'\"\nldflags=\"$ldflags -X 'github.com/OpenListTeam/OpenList/v4/internal/conf.GitAuthor=$gitAuthor'\"\nldflags=\"$ldflags -X 'github.com/OpenListTeam/OpenList/v4/internal/conf.GitCommit=$gitCommit'\"\nldflags=\"$ldflags -X 'github.com/OpenListTeam/OpenList/v4/internal/conf.Version=$version'\"\nldflags=\"$ldflags -X 'github.com/OpenListTeam/OpenList/v4/internal/conf.WebVersion=$webVersion'\"\n\n# First check if we're in the right place\necho \"Starting Android build from: $(pwd)\"\n\n# For Android, we need to find the bindable package directory, not just go.mod\n# The original approach was correct - look for openlistlib directory\nif [ -d ../openlistlib ]; then\n    echo \"Found openlistlib directory, using that for Android build\"\n    cd ../openlistlib || exit\nelse\n    echo \"Searching for bindable package directory...\"\n    cd ../ || exit\n    \n    # Look for directories that might contain bindable packages\n    if [ -d openlistlib ]; then\n        echo \"Found openlistlib in current directory\"\n        cd openlistlib || exit\n    elif [ -d cmd/openlistlib ]; then\n        echo \"Found openlistlib in cmd directory\"\n        cd cmd/openlistlib || exit\n    else\n        echo \"Error: Cannot find openlistlib directory for Android binding\"\n        echo \"Current directory: $(pwd)\"\n        echo \"Directory contents:\"\n        ls -la\n        echo \"Looking for Go files that might be bindable...\"\n        find . -name \"*.go\" -type f | head -10\n        exit 1\n    fi\nfi\n\necho \"Current directory: $(pwd)\"\necho \"Building OpenList for Android...\"\n\n# Check if this directory has Go files suitable for binding\nif ! ls *.go >/dev/null 2>&1; then\n    echo \"Warning: No Go files found in current directory\"\n    echo \"Directory contents:\"\n    ls -la\nfi\n\nif [ \"$1\" == \"debug\" ]; then\n  gomobile bind -ldflags \"$ldflags\" -v -androidapi 19 -target=\"android/arm64\"\nelse\n  gomobile bind -ldflags \"$ldflags\" -v -androidapi 19\nfi\n\necho \"Moving aar and jar files to android/app/libs\"\nmkdir -p ../../android/app/libs\nmv -f ./*.aar ../../android/app/libs\nmv -f ./*.jar ../../android/app/libs\n"
  },
  {
    "path": "openlist-lib/scripts/gobind_ios.sh",
    "content": "#!/bin/bash\n\n# Build version information\nbuiltAt=\"${OPENLIST_BUILT_AT:-$(date +'%F %T %z')}\"\ngitAuthor=\"${OPENLIST_GIT_AUTHOR:-The OpenList Projects Contributors <noreply@openlist.team>}\"\ngitCommit=\"${OPENLIST_GIT_COMMIT:-$(git log --pretty=format:'%h' -1 2>/dev/null || echo 'unknown')}\"\nversion=\"${OPENLIST_VERSION:-dev}\"\nwebVersion=\"${OPENLIST_WEB_VERSION:-rolling}\"\n\necho \"Building with version info:\"\necho \"  Version: $version\"\necho \"  WebVersion: $webVersion\"\necho \"  GitCommit: $gitCommit\"\necho \"  BuiltAt: $builtAt\"\n\n# Construct ldflags\nldflags=\"-s -w\"\nldflags=\"$ldflags -X 'github.com/OpenListTeam/OpenList/v4/internal/conf.BuiltAt=$builtAt'\"\nldflags=\"$ldflags -X 'github.com/OpenListTeam/OpenList/v4/internal/conf.GitAuthor=$gitAuthor'\"\nldflags=\"$ldflags -X 'github.com/OpenListTeam/OpenList/v4/internal/conf.GitCommit=$gitCommit'\"\nldflags=\"$ldflags -X 'github.com/OpenListTeam/OpenList/v4/internal/conf.Version=$version'\"\nldflags=\"$ldflags -X 'github.com/OpenListTeam/OpenList/v4/internal/conf.WebVersion=$webVersion'\"\n\necho \"Starting iOS build from: $(pwd)\"\n\n# Find openlistlib directory\nif [ -d ../openlistlib ]; then\n    cd ../openlistlib || exit\nelif [ -d openlistlib ]; then\n    cd openlistlib || exit\nelse\n    echo \"Error: Cannot find openlistlib directory\"\n    exit 1\nfi\n\necho \"Current directory: $(pwd)\"\n\n# Check if gomobile is available\nif ! command -v gomobile &> /dev/null; then\n    echo \"Error: gomobile not found. Please run init_gomobile.sh first.\"\n    exit 1\nfi\n\necho \"Go version: $(go version)\"\n\n# Work from module root if go.mod exists in parent\nif [ -f ../go.mod ]; then\n    echo \"Found go.mod in parent directory\"\n    cd ..\n    \n    # Fix iOS incompatible dependencies\n    echo \"Fixing iOS incompatible dependencies...\"\n    chmod +x scripts/fix_ios_dependencies.sh\n    ./scripts/fix_ios_dependencies.sh\n    \n    # Update mobile packages\n    echo \"Updating mobile packages...\"\n    go get -u golang.org/x/mobile/...\n    go install golang.org/x/mobile/cmd/gobind@latest\n    go install golang.org/x/mobile/cmd/gomobile@latest\n    \n    # Reinitialize gomobile\n    echo \"Reinitializing gomobile...\"\n    gomobile clean || true\n    gomobile init\n    \n    # Set CGO environment for iOS\n    export CGO_ENABLED=1\n    \n    # Build for iOS with iOS-specific build tags to exclude incompatible packages\n    echo \"Starting iOS build from module root...\"\n    echo \"CGO_ENABLED: $CGO_ENABLED\"\n    \n    # Use build tags to exclude problematic packages on iOS\n    echo \"Attempting gomobile bind with iOS tags...\"\n    gomobile bind -ldflags \"$ldflags\" -v -target=\"ios\" -tags=\"ios,mobile\" ./openlistlib 2>&1 | tee ios_build.log\n    \n    # Check the exit status\n    if [ $? -ne 0 ]; then\n        echo \"Error: gomobile bind failed\"\n        echo \"=== Build log ===\"\n        cat ios_build.log 2>/dev/null || echo \"No build log available\"\n        echo \"=== End build log ===\"\n        \n        # Try to get more specific error information\n        echo \"Checking for specific issues...\"\n        \n        # Check if it's a dependency issue\n        if grep -q \"cannot find package\\|no Go files\\|build constraints exclude all Go files\" ios_build.log; then\n            echo \"Detected dependency or build constraint issues\"\n            \n            # Try with minimal tags\n            echo \"Retrying with minimal build tags...\"\n            gomobile bind -ldflags \"$ldflags\" -v -target=\"ios\" ./openlistlib 2>&1 | tee ios_build_minimal.log\n            \n            if [ $? -ne 0 ]; then\n                echo \"Minimal build also failed:\"\n                cat ios_build_minimal.log 2>/dev/null || echo \"No minimal build log available\"\n                exit 1\n            fi\n        else\n            echo \"Unknown build error, exiting\"\n            exit 1\n        fi\n    fi\n    \n    echo \"Listing generated files in current directory:\"\n    ls -la *.xcframework 2>/dev/null || echo \"No .xcframework files found in current directory\"\n    \n    # Also check if any frameworks were generated with different patterns\n    ls -la Openlistlib.xcframework 2>/dev/null || echo \"Openlistlib.xcframework not found\"\n    ls -la openlistlib.xcframework 2>/dev/null || echo \"openlistlib.xcframework not found\"\n    \n    # Find the Flutter project root by looking for pubspec.yaml\n    echo \"Locating Flutter project root...\"\n    FLUTTER_ROOT=\"\"\n    CURRENT_DIR=$(pwd)\n    \n    # Check various possible locations for pubspec.yaml\n    if [ -f \"pubspec.yaml\" ]; then\n        FLUTTER_ROOT=\".\"\n        echo \"Found Flutter project root at current directory\"\n    elif [ -f \"../pubspec.yaml\" ]; then\n        FLUTTER_ROOT=\"../\"\n        echo \"Found Flutter project root at ../\"\n    elif [ -f \"../../pubspec.yaml\" ]; then\n        FLUTTER_ROOT=\"../../\"\n        echo \"Found Flutter project root at ../../\"\n    elif [ -f \"../../../pubspec.yaml\" ]; then\n        FLUTTER_ROOT=\"../../../\"\n        echo \"Found Flutter project root at ../../../\"\n    else\n        # Try to find it by searching upwards\n        SEARCH_DIR=\"$CURRENT_DIR\"\n        for i in {1..5}; do\n            SEARCH_DIR=\"$(dirname \"$SEARCH_DIR\")\"\n            if [ -f \"$SEARCH_DIR/pubspec.yaml\" ]; then\n                FLUTTER_ROOT=$(realpath --relative-to=\"$CURRENT_DIR\" \"$SEARCH_DIR\")\n                echo \"Found Flutter project root at: $FLUTTER_ROOT\"\n                break\n            fi\n        done\n        \n        if [ -z \"$FLUTTER_ROOT\" ]; then\n            echo \"Warning: Cannot find Flutter project root (pubspec.yaml), using default relative path\"\n            FLUTTER_ROOT=\"../../\"\n        fi\n    fi\n    \n    echo \"Using Flutter project root: $FLUTTER_ROOT\"\n    echo \"Creating iOS Frameworks directory at: ${FLUTTER_ROOT}ios/Frameworks\"\n    mkdir -p \"${FLUTTER_ROOT}ios/Frameworks\"\n    \n    # Check if xcframework files exist before moving\n    if ls *.xcframework 1> /dev/null 2>&1; then\n        echo \"Moving xcframework files to Flutter iOS Frameworks directory...\"\n        \n        # Ensure the target directory exists\n        mkdir -p \"${FLUTTER_ROOT}ios/Frameworks\"\n        \n        # Copy files to Flutter project\n        for framework in *.xcframework; do\n            echo \"Copying $framework to ${FLUTTER_ROOT}ios/Frameworks/\"\n            cp -rf \"$framework\" \"${FLUTTER_ROOT}ios/Frameworks/\"\n        done\n        \n        echo \"✅ iOS framework build completed successfully\"\n        echo \"Files in Flutter iOS Frameworks directory:\"\n        ls -lah \"${FLUTTER_ROOT}ios/Frameworks/\"\n        \n        # Verify the files are in the expected location\n        EXPECTED_PATH=\"${FLUTTER_ROOT}ios/Frameworks\"\n        ABSOLUTE_EXPECTED_PATH=$(cd \"$EXPECTED_PATH\" && pwd)\n        if [ -d \"$EXPECTED_PATH\" ] && [ \"$(ls -A \"$EXPECTED_PATH\")\" ]; then\n            echo \"✅ Framework files successfully placed in: $ABSOLUTE_EXPECTED_PATH\"\n            \n            # List all frameworks found\n            echo \"\"\n            echo \"=== Frameworks Ready ===\"\n            find \"$EXPECTED_PATH\" -name \"*.xcframework\" -type d -exec basename {} \\;\n        else\n            echo \"❌ Warning: Framework files may not be in the expected location\"\n        fi\n    else\n        echo \"Warning: No .xcframework files were generated\"\n        echo \"Checking if files were generated with different names...\"\n        ls -la *.framework 2>/dev/null || echo \"No .framework files found either\"\n        ls -la openlistlib* 2>/dev/null || echo \"No openlistlib files found\"\n        exit 1\n    fi\nelse\n    echo \"Error: No go.mod found in parent directory\"\n    exit 1\nfi"
  },
  {
    "path": "openlist-lib/scripts/init_gomobile.sh",
    "content": "#!/bin/bash\n\necho \"Installing gomobile and dependencies...\"\n\n# Install gomobile command\necho \"Installing gomobile command...\"\ngo install golang.org/x/mobile/cmd/gomobile@latest || {\n    echo \"Failed to install gomobile\"\n    exit 1\n}\n\n# Install gobind command (needed for iOS)\necho \"Installing gobind command...\"\ngo install golang.org/x/mobile/cmd/gobind@latest || {\n    echo \"Failed to install gobind\"\n    exit 1\n}\n\n# Install bind packages\necho \"Installing bind packages...\"\ngo get golang.org/x/mobile/bind@latest || {\n    echo \"Failed to install golang.org/x/mobile/bind\"\n    exit 1\n}\n\ngo get golang.org/x/mobile/bind/objc@latest || {\n    echo \"Failed to install golang.org/x/mobile/bind/objc\"\n    exit 1\n}\n\n# Initialize gomobile\necho \"Initializing gomobile...\"\ngomobile init || {\n    echo \"Failed to initialize gomobile\"\n    exit 1\n}\n\necho \"Gomobile initialization completed successfully\"\n\n# Verify installation\necho \"Verifying installation...\"\necho \"gomobile version: $(gomobile version 2>/dev/null || echo 'version command failed')\"\necho \"gobind available: $(command -v gobind >/dev/null && echo 'yes' || echo 'no')\""
  },
  {
    "path": "openlist-lib/scripts/init_openlist.sh",
    "content": "#!/bin/bash\n\nGIT_REPO=\"https://github.com/OpenListTeam/OpenList.git\"\nTAG_NAME=$(git -c 'versionsort.suffix=-' ls-remote --exit-code --refs --sort='version:refname' --tags $GIT_REPO | tail -n 1 | cut -d'/' -f3)\n\necho \"OpenList - ${TAG_NAME}\"\nrm -rf ./src\nunset GIT_WORK_TREE\ngit clone --branch \"$TAG_NAME\" https://github.com/OpenListTeam/OpenList.git ./src\nrm -rf ./src/.git\n\necho \"Checking cloned source structure:\"\nls -la ./src/\n\n# Move the entire OpenList source to the parent directory\necho \"Moving OpenList source files...\"\nmv -f ./src/* ../\nrm -rf ./src\n\necho \"Checking moved files in parent directory:\"\nls -la ../\n\n# Check if we have the main go.mod in the right place\nif [ -f ../go.mod ]; then\n    echo \"Found go.mod in parent directory\"\n    cd ../\n    go mod edit -replace github.com/djherbis/times@v1.6.0=github.com/jing332/times@latest\n    echo \"OpenList source initialization completed\"\nelse\n    echo \"Error: go.mod not found after moving files\"\n    echo \"Contents of parent directory:\"\n    ls -la ../\n    exit 1\nfi\n"
  },
  {
    "path": "openlist-lib/scripts/init_web.sh",
    "content": "#!/bin/bash\n\necho \"Initializing Web assets...\"\n\nmkdir -p dist\n\n# Function to fetch release info with retries and proxy fallback\nfetch_release_info() {\n    local attempt=1\n    local max_attempts=3\n    local api_url=\"https://api.github.com/repos/OpenListTeam/OpenList-Frontend/releases/latest\"\n    local proxy_url=\"https://ghproxy.lvedong.eu.org/https://api.github.com/repos/OpenListTeam/OpenList-Frontend/releases/latest\"\n    \n    # First try direct API\n    echo \"Trying direct GitHub API...\"\n    while [ $attempt -le $max_attempts ]; do\n        echo \"Direct API attempt $attempt/$max_attempts...\"\n        \n        RELEASE_INFO=$(curl -fsSL --max-time 10 \\\n            -H \"Accept: application/vnd.github.v3+json\" \\\n            \"$api_url\" 2>/dev/null)\n        \n        local curl_exit_code=$?\n        \n        if [ $curl_exit_code -eq 0 ] && [ -n \"$RELEASE_INFO\" ]; then\n            echo \"Successfully fetched release info via direct API on attempt $attempt\"\n            return 0\n        else\n            echo \"Direct API attempt $attempt failed (exit code: $curl_exit_code)\"\n            if [ $attempt -lt $max_attempts ]; then\n                echo \"Waiting 3 seconds before retry...\"\n                sleep 3\n            fi\n        fi\n        \n        attempt=$((attempt + 1))\n    done\n    \n    echo \"Direct API failed after $max_attempts attempts, trying proxy...\"\n    \n    # Try proxy API\n    attempt=1\n    while [ $attempt -le $max_attempts ]; do\n        echo \"Proxy API attempt $attempt/$max_attempts...\"\n        \n        RELEASE_INFO=$(curl -fsSL --max-time 15 \\\n            -H \"Accept: application/vnd.github.v3+json\" \\\n            \"$proxy_url\" 2>/dev/null)\n        \n        local curl_exit_code=$?\n        \n        if [ $curl_exit_code -eq 0 ] && [ -n \"$RELEASE_INFO\" ]; then\n            echo \"Successfully fetched release info via proxy on attempt $attempt\"\n            return 0\n        else\n            echo \"Proxy attempt $attempt failed (exit code: $curl_exit_code)\"\n            if [ $attempt -lt $max_attempts ]; then\n                echo \"Waiting 5 seconds before retry...\"\n                sleep 5\n            fi\n        fi\n        \n        attempt=$((attempt + 1))\n    done\n    \n    echo \"Failed to fetch release info via both direct API and proxy\"\n    return 1\n}\n\n# Get release info from GitHub API with retries\necho \"Fetching latest release information...\"\nif ! fetch_release_info; then\n    echo \"Error: Failed to fetch release info from GitHub API\"\n    exit 1\nfi\n\n# Parse download URL with better pattern matching\necho \"Parsing download URL...\"\nif command -v jq >/dev/null 2>&1; then\n    DOWNLOAD_URL=$(echo \"$RELEASE_INFO\" | jq -r '.assets[] | select(.browser_download_url | test(\"openlist-frontend-dist.*\\\\.tar\\\\.gz$\") and (test(\"openlist-frontend-dist-lite\") | not)) | .browser_download_url')\nelse\n    # Fallback without jq - look for versioned filename\n    DOWNLOAD_URL=$(echo \"$RELEASE_INFO\" | grep -o '\"browser_download_url\":\"[^\"]*openlist-frontend-dist-v[^\"]*\\.tar\\.gz\"' | grep -v 'lite' | head -1 | sed 's/.*\"browser_download_url\":\"\\([^\"]*\\)\".*/\\1/')\n    \n    # If versioned pattern doesn't work, try general pattern\n    if [ -z \"$DOWNLOAD_URL\" ]; then\n        DOWNLOAD_URL=$(echo \"$RELEASE_INFO\" | grep -o '\"browser_download_url\":\"[^\"]*openlist-frontend-dist[^\"]*\\.tar\\.gz\"' | grep -v 'lite' | head -1 | sed 's/.*\"browser_download_url\":\"\\([^\"]*\\)\".*/\\1/')\n    fi\nfi\n\nif [ -z \"$DOWNLOAD_URL\" ] || [ \"$DOWNLOAD_URL\" = \"null\" ]; then\n    echo \"Error: Could not determine download URL\"\n    exit 1\nfi\n\necho \"Download URL: $DOWNLOAD_URL\"\n\n# Function to download file with retries and proxy fallback\ndownload_file() {\n    local url=\"$1\"\n    local output=\"$2\"\n    local attempt=1\n    local max_attempts=3\n    \n    # First try direct download\n    echo \"Trying direct download...\"\n    while [ $attempt -le $max_attempts ]; do\n        echo \"Direct download attempt $attempt/$max_attempts...\"\n        \n        # Try download\n        if curl -fsSL --max-time 30 -o \"$output\" \"$url\"; then\n            echo \"Direct download successful on attempt $attempt\"\n            return 0\n        else\n            local curl_exit_code=$?\n            echo \"Direct download attempt $attempt failed (exit code: $curl_exit_code)\"\n            \n            # Remove partial file if it exists\n            rm -f \"$output\"\n            \n            if [ $attempt -lt $max_attempts ]; then\n                echo \"Waiting 3 seconds before retry...\"\n                sleep 3\n            fi\n        fi\n        \n        attempt=$((attempt + 1))\n    done\n    \n    # Try proxy download if direct failed\n    echo \"Direct download failed, trying proxy download...\"\n    local proxy_url=\"https://ghproxy.lvedong.eu.org/$url\"\n    attempt=1\n    \n    while [ $attempt -le $max_attempts ]; do\n        echo \"Proxy download attempt $attempt/$max_attempts...\"\n        \n        # Try proxy download\n        if curl -fsSL --max-time 45 -o \"$output\" \"$proxy_url\"; then\n            echo \"Proxy download successful on attempt $attempt\"\n            return 0\n        else\n            local curl_exit_code=$?\n            echo \"Proxy download attempt $attempt failed (exit code: $curl_exit_code)\"\n            \n            # Remove partial file if it exists\n            rm -f \"$output\"\n            \n            if [ $attempt -lt $max_attempts ]; then\n                echo \"Waiting 5 seconds before retry...\"\n                sleep 5\n            fi\n        fi\n        \n        attempt=$((attempt + 1))\n    done\n    \n    echo \"Failed to download via both direct and proxy methods\"\n    return 1\n}\n\n# Download the file with retries\necho \"Downloading web assets...\"\nif ! download_file \"$DOWNLOAD_URL\" \"dist.tar.gz\"; then\n    echo \"Error: Failed to download web assets after multiple attempts\"\n    exit 1\nfi\n\n# Extract and install\necho \"Extracting web assets...\"\ntar -zxf dist.tar.gz -C dist || {\n    echo \"Error: Failed to extract archive\"\n    exit 1\n}\n\necho \"Installing web assets...\"\nrm -rf ../public/dist\nmv -f dist ../public\nrm -f dist.tar.gz\n\necho \"Web assets initialization completed\""
  },
  {
    "path": "openlist-lib/scripts/init_web_ios.sh",
    "content": "#!/bin/bash\n\necho \"Initializing Web assets for iOS build...\"\n\n# Create dist directory\nmkdir -p dist\n\n# Use a more reliable approach for iOS with retry mechanism\n# First, try to get the latest release info with better error handling\necho \"Fetching latest release information...\"\n\n# Function to fetch release info with retries and proxy fallback\nfetch_release_info() {\n    local attempt=1\n    local max_attempts=3\n    local api_url=\"https://api.github.com/repos/OpenListTeam/OpenList-Frontend/releases/latest\"\n    local proxy_url=\"https://ghproxy.lvedong.eu.org/https://api.github.com/repos/OpenListTeam/OpenList-Frontend/releases/latest\"\n    \n    # First try direct API\n    echo \"Trying direct GitHub API...\"\n    while [ $attempt -le $max_attempts ]; do\n        echo \"Direct API attempt $attempt/$max_attempts...\"\n        \n        RELEASE_INFO=$(curl -fsSL --max-time 10 \\\n            -H \"Accept: application/vnd.github.v3+json\" \\\n            -H \"User-Agent: OpenList-iOS-Builder\" \\\n            \"$api_url\" 2>/dev/null)\n        \n        local curl_exit_code=$?\n        \n        if [ $curl_exit_code -eq 0 ] && [ -n \"$RELEASE_INFO\" ]; then\n            echo \"Successfully fetched release info via direct API on attempt $attempt\"\n            return 0\n        else\n            echo \"Direct API attempt $attempt failed (exit code: $curl_exit_code)\"\n            if [ $attempt -lt $max_attempts ]; then\n                echo \"Waiting 3 seconds before retry...\"\n                sleep 3\n            fi\n        fi\n        \n        attempt=$((attempt + 1))\n    done\n    \n    echo \"Direct API failed after $max_attempts attempts, trying proxy...\"\n    \n    # Try proxy API\n    attempt=1\n    while [ $attempt -le $max_attempts ]; do\n        echo \"Proxy API attempt $attempt/$max_attempts...\"\n        \n        RELEASE_INFO=$(curl -fsSL --max-time 15 \\\n            -H \"Accept: application/vnd.github.v3+json\" \\\n            -H \"User-Agent: OpenList-iOS-Builder\" \\\n            \"$proxy_url\" 2>/dev/null)\n        \n        local curl_exit_code=$?\n        \n        if [ $curl_exit_code -eq 0 ] && [ -n \"$RELEASE_INFO\" ]; then\n            echo \"Successfully fetched release info via proxy on attempt $attempt\"\n            return 0\n        else\n            echo \"Proxy attempt $attempt failed (exit code: $curl_exit_code)\"\n            if [ $attempt -lt $max_attempts ]; then\n                echo \"Waiting 5 seconds before retry...\"\n                sleep 5\n            fi\n        fi\n        \n        attempt=$((attempt + 1))\n    done\n    \n    echo \"Failed to fetch release info via both direct API and proxy\"\n    return 1\n}\n\n# Try to fetch release info with retries\nif ! fetch_release_info; then\n    echo \"Cannot proceed without API access\"\n    exit 1\nelse\n    echo \"Successfully fetched release info, parsing download URL...\"\n    \n    # Check if jq is available\n    if command -v jq >/dev/null 2>&1; then\n        echo \"Using jq to parse JSON...\"\n        DOWNLOAD_URL=$(echo \"$RELEASE_INFO\" | jq -r '.assets[] | select(.browser_download_url | test(\"openlist-frontend-dist.*\\\\.tar\\\\.gz$\") and (test(\"openlist-frontend-dist-lite\") | not)) | .browser_download_url')\n        echo \"jq found URL: $DOWNLOAD_URL\"\n    else\n        echo \"jq not available, using grep/sed to parse JSON...\"\n        # More robust fallback parsing without jq\n        # Look for openlist-frontend-dist-v*.tar.gz but not lite version\n        DOWNLOAD_URL=$(echo \"$RELEASE_INFO\" | grep -o '\"browser_download_url\":\"[^\"]*openlist-frontend-dist-v[^\"]*\\.tar\\.gz\"' | grep -v 'lite' | head -1 | sed 's/.*\"browser_download_url\":\"\\([^\"]*\\)\".*/\\1/')\n        echo \"grep/sed found URL: $DOWNLOAD_URL\"\n        \n        # If the above doesn't work, try a more general pattern\n        if [ -z \"$DOWNLOAD_URL\" ] || [ \"$DOWNLOAD_URL\" = \"null\" ]; then\n            echo \"Trying more general pattern...\"\n            DOWNLOAD_URL=$(echo \"$RELEASE_INFO\" | grep -o '\"browser_download_url\":\"[^\"]*openlist-frontend-dist[^\"]*\\.tar\\.gz\"' | grep -v 'lite' | head -1 | sed 's/.*\"browser_download_url\":\"\\([^\"]*\\)\".*/\\1/')\n            echo \"General pattern found URL: $DOWNLOAD_URL\"\n        fi\n    fi\nfi\n\n# Validate download URL\nif [ -z \"$DOWNLOAD_URL\" ] || [ \"$DOWNLOAD_URL\" = \"null\" ]; then\n    echo \"Error: Could not determine download URL from API response\"\n    echo \"API response preview:\"\n    echo \"$RELEASE_INFO\" | head -20\n    exit 1\nfi\n\necho \"Download URL: $DOWNLOAD_URL\"\n\n# Function to download file with retries and proxy fallback\ndownload_file() {\n    local url=\"$1\"\n    local output=\"$2\"\n    local attempt=1\n    local max_attempts=3\n    \n    # First try direct download\n    echo \"Trying direct download...\"\n    while [ $attempt -le $max_attempts ]; do\n        echo \"Direct download attempt $attempt/$max_attempts...\"\n        \n        # Try download\n        if curl -fsSL --max-time 30 -o \"$output\" \"$url\"; then\n            echo \"Direct download successful on attempt $attempt\"\n            return 0\n        else\n            local curl_exit_code=$?\n            echo \"Direct download attempt $attempt failed (exit code: $curl_exit_code)\"\n            \n            # Remove partial file if it exists\n            rm -f \"$output\"\n            \n            if [ $attempt -lt $max_attempts ]; then\n                echo \"Waiting 3 seconds before retry...\"\n                sleep 3\n            fi\n        fi\n        \n        attempt=$((attempt + 1))\n    done\n    \n    # Try proxy download if direct failed\n    echo \"Direct download failed, trying proxy download...\"\n    local proxy_url=\"https://ghproxy.lvedong.eu.org/$url\"\n    attempt=1\n    \n    while [ $attempt -le $max_attempts ]; do\n        echo \"Proxy download attempt $attempt/$max_attempts...\"\n        \n        # Try proxy download\n        if curl -fsSL --max-time 45 -o \"$output\" \"$proxy_url\"; then\n            echo \"Proxy download successful on attempt $attempt\"\n            return 0\n        else\n            local curl_exit_code=$?\n            echo \"Proxy download attempt $attempt failed (exit code: $curl_exit_code)\"\n            \n            # Remove partial file if it exists\n            rm -f \"$output\"\n            \n            if [ $attempt -lt $max_attempts ]; then\n                echo \"Waiting 5 seconds before retry...\"\n                sleep 5\n            fi\n        fi\n        \n        attempt=$((attempt + 1))\n    done\n    \n    echo \"Failed to download via both direct and proxy methods\"\n    return 1\n}\n\n# Download the file with retries\necho \"Downloading web assets...\"\nif ! download_file \"$DOWNLOAD_URL\" \"dist.tar.gz\"; then\n    echo \"Error: Failed to download web assets after multiple attempts\"\n    exit 1\nfi\n\n# Verify the downloaded file\nif [ ! -f dist.tar.gz ] || [ ! -s dist.tar.gz ]; then\n    echo \"Error: Downloaded file is empty or missing\"\n    exit 1\nfi\n\necho \"Downloaded file size: $(ls -lh dist.tar.gz | awk '{print $5}')\"\n\n# Extract the archive\necho \"Extracting web assets...\"\nif tar -zxf dist.tar.gz -C dist 2>/dev/null; then\n    echo \"Extraction successful\"\nelse\n    echo \"Error: Failed to extract archive\"\n    echo \"File info:\"\n    file dist.tar.gz 2>/dev/null || echo \"file command not available\"\n    ls -la dist.tar.gz\n    exit 1\nfi\n\n# Move to final location\necho \"Installing web assets...\"\nrm -rf ../public/dist\nif [ -d dist ]; then\n    mv dist ../public/\n    echo \"Web assets installed successfully\"\nelse\n    echo \"Error: Extracted directory not found\"\n    exit 1\nfi\n\n# Cleanup\nrm -f dist.tar.gz\n\necho \"Web assets initialization completed\""
  },
  {
    "path": "openlist_version",
    "content": "v4.2.1\n"
  },
  {
    "path": "pigeon_config.yaml",
    "content": "# Pigeon configuration for OpenList Mobile\n# Generate platform-specific API code from Dart definitions\n\ninput: pigeons/pigeon.dart\ndart_out: lib/generated_api.dart\n\njava_out: android/app/src/main/java/com/openlist/pigeon/GeneratedApi.java\njava_package: com.openlist.pigeon\n\nswift_out: ios/Runner/GeneratedPluginRegistrant.swift\n"
  },
  {
    "path": "pigeons/pigeon.dart",
    "content": "import 'package:pigeon/pigeon.dart';\n\n@HostApi()\nabstract class AppConfig {\n  bool isWakeLockEnabled();\n\n  void setWakeLockEnabled(bool enabled);\n\n  bool isStartAtBootEnabled();\n\n  void setStartAtBootEnabled(bool enabled);\n\n  bool isAutoCheckUpdateEnabled();\n\n  void setAutoCheckUpdateEnabled(bool enabled);\n\n  bool isAutoOpenWebPageEnabled();\n\n  void setAutoOpenWebPageEnabled(bool enabled);\n\n  String getDataDir();\n\n  void setDataDir(String dir);\n\n  bool isSilentJumpAppEnabled();\n\n  void setSilentJumpAppEnabled(bool enabled);\n}\n\n@HostApi()\nabstract class NativeCommon {\n  bool startActivityFromUri(String intentUri);\n\n  int getDeviceSdkInt();\n\n  String getDeviceCPUABI();\n\n  String getVersionName();\n\n  int getVersionCode();\n\n  void toast(String msg);\n\n  void longToast(String msg);\n}\n\n@HostApi()\nabstract class Android {\n  void addShortcut();\n\n  void startService();\n\n  void setAdminPwd(String pwd);\n\n  int getOpenListHttpPort();\n\n  bool isRunning();\n\n  String getOpenListVersion();\n}\n\n@FlutterApi()\nabstract class Event {\n  void onServiceStatusChanged(bool isRunning);\n\n  void onServerLog(\n    int level,\n    String time,\n    String log,\n  );\n}\n"
  },
  {
    "path": "pigeons/run.cmd",
    "content": "flutter pub run pigeon --input pigeons/pigeon.dart^\n --dart_out lib/generated_api.dart^\n --java_out android/app/src/main/java/com/openlist/pigeon/GeneratedApi.java^\n --java_package \"com.openlist.pigeon\"\n`"
  },
  {
    "path": "pubspec.yaml",
    "content": "name: openlist_mobile\ndescription: \"OpenList for Android and iOS\"\n# The following line prevents the package from being accidentally published to\n# pub.dev using `flutter pub publish`. This is preferred for private packages.\npublish_to: 'none' # Remove this line if you wish to publish to pub.dev\n\n# The following defines the version and build number for your application.\n# A version number is three numbers separated by dots, like 1.2.43\n# followed by an optional build number separated by a +.\n# Both the version and the builder number may be overridden in flutter\n# build by specifying --build-name and --build-number, respectively.\n# In Android, build-name is used as versionName while build-number used as versionCode.\n# Read more about Android versioning at https://developer.android.com/studio/publish/versioning\n# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.\n# Read more about iOS versioning at\n# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html\n# In Windows, build-name is used as the major, minor, and patch parts\n# of the product and file versions while build-number is used as the build suffix.\nversion: 4.2.1+1\n\nenvironment:\n  sdk: '>=3.2.4 <4.0.0'\n\n# Dependencies specify other packages that your package needs in order to work.\n# To automatically upgrade your package dependencies to the latest versions\n# consider running `flutter pub upgrade --major-versions`. Alternatively,\n# dependencies can be manually updated by changing the version numbers below to\n# the latest version available on pub.dev. To see which dependencies have newer\n# versions available, run `flutter pub outdated`.\ndependencies:\n  flutter:\n    sdk: flutter\n\n  flutter_localizations:\n    sdk: flutter\n  intl: ^0.20.2\n\n  # The following adds the Cupertino Icons font to your application.\n  # Use with the CupertinoIcons class for iOS style icons.\n  cupertino_icons: ^1.0.2\n  get: ^4.6.6\n  pigeon: ^26.0.0\n  flutter_inappwebview: ^6.0.0\n  android_intent_plus: ^5.3.0\n  flutter_svg: ^2.0.9\n  permission_handler: ^12.0.1\n  lazy_load_indexed_stack: ^1.1.0\n  animations: ^2.0.11\n  file_picker: ^10.2.0\n  fade_indexed_stack: ^0.2.2\n  dio: ^5.4.0\n  path_provider: ^2.1.2\n  open_filex: ^4.3.4\n  flutter_local_notifications: ^19.3.1\n  open_file_manager: ^2.0.1\n  shared_preferences: ^2.3.2\n  flutter_highlight: ^0.7.0\n  flutter_smooth_markdown: ^0.1.5\n\n\ndev_dependencies:\n  flutter_test:\n    sdk: flutter\n\n  # The \"flutter_lints\" package below contains a set of recommended lints to\n  # encourage good coding practices. The lint set provided by the package is\n  # activated in the `analysis_options.yaml` file located at the root of your\n  # package. See that file for information about deactivating specific lint\n  # rules and activating additional ones.\n  flutter_lints: ^6.0.0\n  intl_utils: ^2.8.7\n\n\n# For information on the generic Dart part of this file, see the\n# following page: https://dart.dev/tools/pub/pubspec\n\n# The following section is specific to Flutter packages.\nflutter:\n\n  # The following line ensures that the Material Icons font is\n  # included with your application, so that you can use the icons in\n  # the material Icons class.\n  uses-material-design: true\n\n\n  # To add assets to your application, add an assets section, like this:\n  assets:\n    - assets/openlist.svg\n  #   - images/a_dot_burr.jpeg\n  #   - images/a_dot_ham.jpeg\n\n  # An image asset can refer to one or more resolution-specific \"variants\", see\n  # https://flutter.dev/assets-and-images/#resolution-aware\n\n  # For details regarding adding assets from package dependencies, see\n  # https://flutter.dev/assets-and-images/#from-packages\n\n  # To add custom fonts to your application, add a fonts section here,\n  # in this \"flutter\" section. Each entry in this list should have a\n  # \"family\" key with the font family name, and a \"fonts\" key with a\n  # list giving the asset and other descriptors for the font. For\n  # example:\n  # fonts:\n  #   - family: Schyler\n  #     fonts:\n  #       - asset: fonts/Schyler-Regular.ttf\n  #       - asset: fonts/Schyler-Italic.ttf\n  #         style: italic\n  #   - family: Trajan Pro\n  #     fonts:\n  #       - asset: fonts/TrajanPro.ttf\n  #       - asset: fonts/TrajanPro_Bold.ttf\n  #         weight: 700\n  #\n  # For details regarding fonts from package dependencies,\n  # see https://flutter.dev/custom-fonts/#from-packages\nflutter_intl:\n  enabled: true\n  main_locale: zh"
  },
  {
    "path": "test/widget_test.dart",
    "content": "// This is a basic Flutter widget test.\n//\n// To perform an interaction with a widget in your test, use the WidgetTester\n// utility in the flutter_test package. For example, you can send tap and scroll\n// gestures. You can also use WidgetTester to find child widgets in the widget\n// tree, read text, and verify that the values of widget properties are correct.\n\nimport 'package:flutter/material.dart';\nimport 'package:flutter_test/flutter_test.dart';\n\nimport 'package:openlist_mobile/main.dart';\n\nvoid main() {\n  testWidgets('Counter increments smoke test', (WidgetTester tester) async {\n    // Build our app and trigger a frame.\n    await tester.pumpWidget(const MyApp());\n\n    // Verify that our counter starts at 0.\n    expect(find.text('0'), findsOneWidget);\n    expect(find.text('1'), findsNothing);\n\n    // Tap the '+' icon and trigger a frame.\n    await tester.tap(find.byIcon(Icons.add));\n    await tester.pump();\n\n    // Verify that our counter has incremented.\n    expect(find.text('0'), findsNothing);\n    expect(find.text('1'), findsOneWidget);\n  });\n}\n"
  }
]