Showing preview only (1,469K chars total). Download the full file or copy to clipboard to get everything.
Repository: klxiaoniu/QQVersionList
Branch: master
Commit: fe1f952ce4f6
Files: 225
Total size: 1.2 MB
Directory structure:
gitextract_9y815yus/
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug-报告.md
│ │ └── 功能请求.md
│ ├── dependabot.yml
│ ├── pull_request_template.md
│ └── workflows/
│ ├── codeql.yml
│ ├── crowdin.yml
│ └── push_ci.yml
├── .gitignore
├── DataListShared.md
├── LICENSE
├── README.md
├── ReadmeAssets/
│ ├── Get-it-on-JiuQi-NotifCenter-WeChatMiniProgram.md
│ └── Get-it-on-Obtainium.md
├── SECURITY.md
├── UserAgreement.md
├── app/
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── proguard-rules.pro
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── xiaoniu/
│ │ └── qqversionlist/
│ │ └── ExampleInstrumentedTest.kt
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── xiaoniu/
│ │ │ └── qqversionlist/
│ │ │ ├── QverbowApplication.kt
│ │ │ ├── data/
│ │ │ │ ├── LocalAppStackResult.kt
│ │ │ │ ├── LocalAppStackRule.kt
│ │ │ │ ├── QQVersionBean.kt
│ │ │ │ ├── QverbowRelease.kt
│ │ │ │ ├── QverbowReleaseAssets.kt
│ │ │ │ ├── TIMVersionBean.kt
│ │ │ │ └── WeixinVersionBean.kt
│ │ │ ├── service/
│ │ │ │ └── FirebaseMessagingService.kt
│ │ │ ├── ui/
│ │ │ │ ├── ExpUrlListAdapter.kt
│ │ │ │ ├── LocalAppDetailsActivity.kt
│ │ │ │ ├── LocalAppDetailsActivityViewModel.kt
│ │ │ │ ├── LocalQQAdapter.kt
│ │ │ │ ├── LocalTIMAdapter.kt
│ │ │ │ ├── LocalWeixinAdapter.kt
│ │ │ │ ├── MainActivity.kt
│ │ │ │ ├── MainActivityViewModel.kt
│ │ │ │ ├── OSSLicensesActivity.kt
│ │ │ │ ├── OSSLicensesMenuActivity.kt
│ │ │ │ ├── QQVersionAdapter.kt
│ │ │ │ ├── QQVersionListFragment.kt
│ │ │ │ ├── ShiplyAdvancedConfigFragment.kt
│ │ │ │ ├── TIMVersionAdapter.kt
│ │ │ │ ├── TIMVersionListFragment.kt
│ │ │ │ ├── VersionListPagerAdapter.kt
│ │ │ │ ├── WeixinVersionAdapter.kt
│ │ │ │ ├── WeixinVersionListFragment.kt
│ │ │ │ ├── components/
│ │ │ │ │ └── cell/
│ │ │ │ │ ├── CellBottomClick.kt
│ │ │ │ │ ├── CellBottomSwitch.kt
│ │ │ │ │ ├── CellMiddleClick.kt
│ │ │ │ │ ├── CellMiddleSwitch.kt
│ │ │ │ │ ├── CellSingleClick.kt
│ │ │ │ │ ├── CellSingleSwitch.kt
│ │ │ │ │ ├── CellTopClick.kt
│ │ │ │ │ └── CellTopSwitch.kt
│ │ │ │ └── theme/
│ │ │ │ ├── Color.kt
│ │ │ │ ├── Theme.kt
│ │ │ │ └── Type.kt
│ │ │ └── util/
│ │ │ ├── ClipboardUtil.kt
│ │ │ ├── DataStoreUtil.kt
│ │ │ ├── DexResolver.kt
│ │ │ ├── EnumDexContainer.kt
│ │ │ ├── Extensions.kt
│ │ │ ├── FileUtil.kt
│ │ │ ├── GitHubRestApiUtil.kt
│ │ │ ├── InfoUtil.kt
│ │ │ ├── KeyStoreUtil.kt
│ │ │ ├── LogUtil.kt
│ │ │ ├── OSSLicensesObject.kt
│ │ │ ├── ShiplyUtil.kt
│ │ │ ├── StringUtil.kt
│ │ │ ├── VersionUtil.kt
│ │ │ ├── ZhipuSDKUtil.kt
│ │ │ └── boundo_LICENSE
│ │ └── res/
│ │ ├── drawable/
│ │ │ ├── accessibility_new_20px.xml
│ │ │ ├── accessibility_new_24px.xml
│ │ │ ├── ai_generate_2.xml
│ │ │ ├── alert_line.xml
│ │ │ ├── apps_line.xml
│ │ │ ├── arrow_down_s_line.xml
│ │ │ ├── arrow_left_line.xml
│ │ │ ├── arrow_right_s_line.xml
│ │ │ ├── arrow_up_s_line.xml
│ │ │ ├── beacon_official.xml
│ │ │ ├── bottom_sheet_drag_handle.xml
│ │ │ ├── braces_line.xml
│ │ │ ├── bubble_chart_line.xml
│ │ │ ├── bugly_official.xml
│ │ │ ├── built_with_material_licensed_under_agpl_v3.xml
│ │ │ ├── check_circle.xml
│ │ │ ├── clipboard_line.xml
│ │ │ ├── compose.xml
│ │ │ ├── download_line.xml
│ │ │ ├── error_warning_fill.xml
│ │ │ ├── file_copy_line.xml
│ │ │ ├── file_text_line.xml
│ │ │ ├── flashlight_line.xml
│ │ │ ├── flask_line.xml
│ │ │ ├── flutter_line.xml
│ │ │ ├── git_commit_line.xml
│ │ │ ├── git_repository_line.xml
│ │ │ ├── github_line.xml
│ │ │ ├── gitlab_line.xml
│ │ │ ├── hippy_official.xml
│ │ │ ├── ic_launcher_background.xml
│ │ │ ├── ic_launcher_foreground.xml
│ │ │ ├── ic_launcher_foreground_splash.xml
│ │ │ ├── ic_launcher_special_foreground.xml
│ │ │ ├── info_card_line.xml
│ │ │ ├── information_line.xml
│ │ │ ├── key_line.xml
│ │ │ ├── kuikly_official.xml
│ │ │ ├── lightbulb_fill.xml
│ │ │ ├── link.xml
│ │ │ ├── mini_program_line.xml
│ │ │ ├── open_source_line.xml
│ │ │ ├── oteam_official.xml
│ │ │ ├── palette_line.xml
│ │ │ ├── phone_find_line.xml
│ │ │ ├── qqnt_logo_unofficial.xml
│ │ │ ├── qqnt_logo_unofficial_fix.xml
│ │ │ ├── question_line.xml
│ │ │ ├── qv_logo_notification.xml
│ │ │ ├── reactjs_line.xml
│ │ │ ├── refresh_line.xml
│ │ │ ├── rightly_official.xml
│ │ │ ├── save_line.xml
│ │ │ ├── scan_line.xml
│ │ │ ├── scan_shortcut_icon_foreground.xml
│ │ │ ├── search_line.xml
│ │ │ ├── settings_line.xml
│ │ │ ├── shape_cell_bottom.xml
│ │ │ ├── shape_cell_middle.xml
│ │ │ ├── shape_cell_single.xml
│ │ │ ├── shape_cell_top.xml
│ │ │ ├── share_line.xml
│ │ │ ├── shield_keyhole_line.xml
│ │ │ ├── shiply_official.xml
│ │ │ ├── skip_forward_line.xml
│ │ │ ├── sparkling_line.xml
│ │ │ ├── stack_line.xml
│ │ │ ├── stop_fill.xml
│ │ │ ├── stop_line.xml
│ │ │ ├── tencent_logo.xml
│ │ │ ├── tools_line.xml
│ │ │ └── ue_icon_2023_black.xml
│ │ ├── layout/
│ │ │ ├── activity_local_app_details.xml
│ │ │ ├── activity_main.xml
│ │ │ ├── applications_config_back_button.xml
│ │ │ ├── bottomsheet_shiply_advanced_config.xml
│ │ │ ├── cell_bottom_click.xml
│ │ │ ├── cell_bottom_switch.xml
│ │ │ ├── cell_middle_click.xml
│ │ │ ├── cell_middle_switch.xml
│ │ │ ├── cell_single_click.xml
│ │ │ ├── cell_single_switch.xml
│ │ │ ├── cell_top_click.xml
│ │ │ ├── cell_top_switch.xml
│ │ │ ├── dialog_about.xml
│ │ │ ├── dialog_changes_llm_inference.xml
│ │ │ ├── dialog_exp_back.xml
│ │ │ ├── dialog_experimental_features.xml
│ │ │ ├── dialog_firebase_first_info.xml
│ │ │ ├── dialog_format_define.xml
│ │ │ ├── dialog_guess.xml
│ │ │ ├── dialog_hash.xml
│ │ │ ├── dialog_loading.xml
│ │ │ ├── dialog_local_qq_tim_info.xml
│ │ │ ├── dialog_personalization.xml
│ │ │ ├── dialog_private_token_setting.xml
│ │ │ ├── dialog_setting.xml
│ │ │ ├── dialog_shiply.xml
│ │ │ ├── dialog_tencent_app_store.xml
│ │ │ ├── exp_link_next_button.xml
│ │ │ ├── item_exp_back_url_card.xml
│ │ │ ├── item_qq_version.xml
│ │ │ ├── item_qq_version_detail.xml
│ │ │ ├── item_tim_version.xml
│ │ │ ├── item_tim_version_detail.xml
│ │ │ ├── item_weixin_version.xml
│ │ │ ├── item_weixin_version_detail.xml
│ │ │ ├── local_qq.xml
│ │ │ ├── local_tim.xml
│ │ │ ├── local_weixin.xml
│ │ │ ├── recycle_qq_version.xml
│ │ │ ├── recycle_tim_version.xml
│ │ │ ├── success_button.xml
│ │ │ ├── update_qvt_button.xml
│ │ │ └── user_agreement.xml
│ │ ├── layout-v26/
│ │ │ ├── dialog_changes_llm_inference.xml
│ │ │ └── update_qvt_button.xml
│ │ ├── mipmap-anydpi-v26/
│ │ │ ├── ic_launcher.xml
│ │ │ ├── ic_launcher_round.xml
│ │ │ ├── scan_shortcut_icon.xml
│ │ │ └── scan_shortcut_icon_round.xml
│ │ ├── values/
│ │ │ ├── arrays.xml
│ │ │ ├── attrs.xml
│ │ │ ├── colors.xml
│ │ │ ├── ic_launcher_background.xml
│ │ │ ├── scan_shortcut_icon_background.xml
│ │ │ ├── strings.xml
│ │ │ └── themes.xml
│ │ ├── values-ar-rSA/
│ │ │ └── strings.xml
│ │ ├── values-en-rUS/
│ │ │ └── strings.xml
│ │ ├── values-es-rES/
│ │ │ └── strings.xml
│ │ ├── values-fr-rFR/
│ │ │ └── strings.xml
│ │ ├── values-ja-rJP/
│ │ │ └── strings.xml
│ │ ├── values-ko-rKR/
│ │ │ └── strings.xml
│ │ ├── values-night/
│ │ │ └── themes.xml
│ │ ├── values-ru-rRU/
│ │ │ └── strings.xml
│ │ ├── values-zh-rCN/
│ │ │ └── strings.xml
│ │ ├── values-zh-rHK/
│ │ │ └── strings.xml
│ │ ├── xml/
│ │ │ ├── backup_rules.xml
│ │ │ └── data_extraction_rules.xml
│ │ └── xml-v25/
│ │ └── shortcuts.xml
│ └── test/
│ └── java/
│ └── com/
│ └── xiaoniu/
│ └── qqversionlist/
│ └── ExampleUnitTest.kt
├── build.gradle.kts
├── crowdin.yml
├── gradle/
│ ├── libs.versions.toml
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
└── settings.gradle.kts
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/ISSUE_TEMPLATE/bug-报告.md
================================================
---
name: Bug 报告
about: 创建报告以帮助我们改进
title: ''
labels: bug
assignees: ''
---
**描述错误**
对错误的清晰而简洁的描述。
**重现步骤**
重现行为的步骤:
1. 转到“……”
2. 点击“……”
3. 向下滚动到“……”
4. 看到错误
**期望行为**
对您期望发生的事情的清晰而简洁的描述。
**截图**
如果适用,请添加截图以帮助解释您的问题。
**QQ 版本列表实用工具(请填写以下信息):**
- 版本:(例如 v1.2.2-Release)
**智能手机(请填写以下信息):**
- 设备:(例如 Google Pixel 8 Pro)
- 操作系统:(例如 Android 14)
**其他上下文**
在这里添加有关问题的任何其他上下文。
================================================
FILE: .github/ISSUE_TEMPLATE/功能请求.md
================================================
---
name: 功能请求
about: 为此项目提出建议
title: ''
labels: ''
assignees: ''
---
**您的功能请求是否与问题有关?请描述。**
对问题的清晰而简洁的描述。例如:我总是感到沮丧,当……
**描述您希望发生的解决方案**
对您希望发生的事情的清晰而简洁的描述。
**描述您考虑过的替代方案**
对您考虑过的任何替代解决方案或功能的清晰而简洁的描述。
**其他上下文**
在这里添加有关功能请求的任何其他上下文或截图。
================================================
FILE: .github/dependabot.yml
================================================
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: gradle
directory: /
schedule:
interval: monthly
ignore:
# 智谱 AI 开放平台 Java SDK 版本号不是语义版本号 SemVer,会被 Maven 或 GitHub Dependabot 误判,因此需要手动忽略。
- dependency-name: cn.bigmodel.openapi:oapi-java-sdk
- dependency-name: androidx.core:core-splashscreen
groups:
maven-dependencies:
patterns:
- "*"
- package-ecosystem: github-actions
directory: /
schedule:
interval: daily
groups:
action-dependencies:
patterns:
- "*"
================================================
FILE: .github/pull_request_template.md
================================================
## 这个 PR 解决了什么问题?
> 请补充以下信息
### 需求背景
……
### 更新日志
#### 修复
- ……
#### 新增
- ……
#### 优化
- ……
#### 其它更改
- ……
#### 上游更改
- ……
## 自检清单
> 请确认下列所有选项并打勾
- [ ] 此 PR 已实现我的所有预期更改,可以被合并。
- [ ] 我确认此 PR 全部代码仅由本人(或联合作者)编写,代码所有权归本人(或联合作者)所有。
- [ ] 此 PR 更新日志已提供(或无须提供)。
- [ ] Readme 文档无须补充(或已补充)。
> 请确认下列选项并打勾。如不认可选项内容或存在需备注内容,请在选项旁随附补充说明。
- [ ] 我认可并确认我贡献的代码将以 [GNU Affero General Public License Version 3](https://github.com/klxiaoniu/QQVersionList/blob/master/LICENSE) 开源许可进行发行。
- (单项选择)若后续项目决定更改开源许可:
- [ ] 请通知我并等待我的决定
- [ ] 无需通知,我授权项目所有者自行决定我的贡献的开源许可变更。
- [ ] 其他,请补充说明:_______
================================================
FILE: .github/workflows/codeql.yml
================================================
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
schedule:
- cron: '30 1 * * 0'
jobs:
analyze:
name: Analyze
# Runner size impacts CodeQL analysis time. To learn more, please see:
# - https://gh.io/recommended-hardware-resources-for-running-codeql
# - https://gh.io/supported-runners-and-hardware-resources
# - https://gh.io/using-larger-runners
# Consider using larger runners for possible analysis time improvements.
# runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'java-kotlin' ]
# CodeQL supports [ 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' ]
# Use only 'java-kotlin' to analyze code written in Java, Kotlin or both
# Use only 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
if: ${{ github.event_name != 'pull_request' && (github.repository == 'klxiaoniu/QQVersionList' || github.repository == 'ArcticFoxPro/QQVersionList') }}
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Setup local.properties
run: echo "" > local.properties
# 向 app 目录插入 google-services.json,文件内容来自 Secret
- name: Insert google-services.json
run: |
cat <<EOF > app/google-services.json
${{ secrets.GOOGLE_SERVICES }}
EOF
shell: bash
- name: Set up JDK
uses: actions/setup-java@v5
with:
java-version: '21'
distribution: 'temurin'
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
# If this step fails, then you should remove it and run the build manually (see below)
# - name: Autobuild
# uses: github/codeql-action/autobuild@v3
# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
- run: |
echo "Run, Build Application using script"
./gradlew build
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
with:
category: "/language:${{matrix.language}}"
================================================
FILE: .github/workflows/crowdin.yml
================================================
name: Crowdin Action
on:
push:
branches: [ master ]
schedule:
- cron: '0 0 * * *'
jobs:
synchronize-with-crowdin:
runs-on: ubuntu-latest
if: github.repository == 'klxiaoniu/QQVersionList'
steps:
- name: Checkout
uses: actions/checkout@v6
- name: crowdin action
uses: crowdin/github-action@v2
with:
upload_sources: true
upload_translations: true
download_translations: true
localization_branch_name: i18n_crowdin_translations
create_pull_request: true
pull_request_title: 'New Crowdin Translations'
pull_request_body: 'New Crowdin translations by [Crowdin GitHub Action](https://github.com/crowdin/github-action)'
pull_request_labels: '本地化(I18n)'
pull_request_assignees: 'crowdin-bot'
pull_request_base_branch_name: 'master'
auto_approve_imported: true
env:
# A classic GitHub Personal Access Token with the 'repo' scope selected (the user should have write access to the repository).
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# A numeric ID, found at https://crowdin.com/project/<projectName>/tools/api
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
# Visit https://crowdin.com/settings#api-key to create this token
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
================================================
FILE: .github/workflows/push_ci.yml
================================================
name: Android CI
on:
push:
branches: [ "master" ]
paths-ignore:
- '**.md'
- '**.txt'
- '.github/**'
- '.idea/**'
- '!.github/workflows/**'
jobs:
build:
runs-on: ubuntu-latest
if: ${{ !startsWith(github.event.head_commit.message, '[skip ci]') && success() && github.repository == 'klxiaoniu/QQVersionList' }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup local.properties
run: echo "" > local.properties
# 向 app 目录插入 google-services.json,文件内容来自 Secret
- name: Insert google-services.json
run: |
cat <<EOF > app/google-services.json
${{ secrets.GOOGLE_SERVICES }}
EOF
shell: bash
- name: set up JDK 21
uses: actions/setup-java@v5
with:
java-version: '21'
distribution: 'temurin'
cache: gradle
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: |
echo ${{ secrets.SIGN_KEYSTORE_BASE64 }} | base64 -d > keystore.jks
./gradlew build
env:
KEYSTORE_PATH: "../keystore.jks"
KEYSTORE_PASSWORD: ${{ secrets.SIGN_KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.SIGN_ALIAS }}
KEY_PASSWORD: ${{ secrets.SIGN_KEY_PASSWORD }}
- name: Upload release
uses: actions/upload-artifact@v7.0.1
with:
path: ${{github.workspace}}/app/build/outputs/apk/release/*.apk
name: QQVersionList-release-${{ github.event.head_commit.id }}
retention-days: 90
- name: Upload mappings
uses: actions/upload-artifact@v7.0.1
with:
path: ${{github.workspace}}/app/build/outputs/mapping/release/mapping.txt
name: QQVersionList-release-${{ github.event.head_commit.id }}-mapping
retention-days: 90
================================================
FILE: .gitignore
================================================
*.iml
.gradle
/local.properties
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
/app/release
/.idea
/app/debug
app/google-services.json
================================================
FILE: DataListShared.md
================================================
# QQ 版本列表实用工具个人信息第三方共享清单
1. 本清单仅反映您在全部功能模式下,我们对您的信息与第三方共享的情况。
2. 为了完成相应的功能,我们会接入来自第三方的应用开发工具包(SDK)来获得技术支持,比如实现云端消息推送和帮助我们统计了解产品用户分布和数据分析,我们可能会向此类合作伙伴提供以下个人信息。我们在不同时期可能会选择使用其中部分合作方或变更合作方,故清单内的合作伙伴可能会进行调整。
## OkHttp3
- **第三方主体名称**:Square
- **使用场景描述**:网络请求
- **共享的个人信息类型**:无
- **使用的系统权限**:网络权限
- **第三方隐私政策或用户协议或官网链接**:https://square.github.io/okhttp/
## Google Analytics
- **第三方主体名称**:Google LLC
- **使用场景描述**:统计分析,被 Firebase Cloud Messaging 所绑定
- **共享的个人信息类型**:移动设备标识符、基于网络地址的地理位置信息、Firebase 安装 ID
- **使用的系统权限**:网络权限
- **第三方隐私政策或用户协议或官网链接**:https://www.google.com/analytics/terms/
## Firebase Cloud Messaging
- **第三方主体名称**:Google LLC
- **使用场景描述**:推送消息通知
- **共享的个人信息类型**:Firebase 安装 ID
- **使用的系统权限**:通知权限、网络权限
- **第三方隐私政策或用户协议或官网链接**:https://firebase.google.com/support/privacy
## Java API for GitHub
- **第三方主体名称**:Kohsuke Kawaguchi
- **使用场景描述**:请求 GitHub REST API
- **共享的个人信息类型**:GitHub 个人访问令牌
- **使用的系统权限**:网络权限
- **第三方隐私政策或用户协议或官网链接**:https://github.com/hub4j/github-api
## 智谱 AI 开放平台大模型接口 Java SDK
- **第三方主体名称**:北京智谱华章科技有限公司
- **使用场景描述**:智谱 AI 开放平台大模型接口调用
- **共享的个人信息类型**:智谱 AI 开放平台 Token
- **使用的系统权限**:网络权限
- **第三方隐私政策或用户协议或官网链接**:https://bigmodel.cn/dev/howuse/privacypolicy
================================================
FILE: LICENSE
================================================
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.
================================================
FILE: README.md
================================================
# QQ 版本列表实用工具 for Android

<div align="center">
[](https://sourcery.ai)
[注意事项](#注意事项) | [简介](#简介) | [如何使用](#如何使用) | [获取更新](#获取更新) | [常见问题](#常见问题) | [其它](#其它)
[贡献成员](#贡献成员) | [开源相关](#开源相关) | [商业服务鸣谢](#商业服务鸣谢) | [星标趋势](#星标趋势) | [孪生项目](#孪生项目)
</div>
<details>
<summary>关于近期多家国内 Android OEM 系统报毒 QQ 版本列表实用工具的声明</summary>
## 关于近期多家国内 Android OEM 系统报毒 QQ 版本列表实用工具的声明
近期我们发现多家国内 Android OEM 系统(包括但不限于小米 MIUI/HyperOS、荣耀 MagicOS 等)将 QQ 版本列表实用工具列为病毒,特在此发表声明:
### 1. 产品声明与保证
QQ 版本列表实用工具严格遵守中华人民共和国相关法律法规。我们确认,该应用程序的所有代码及其更新版本均未包含任何恶意代码或病毒性质的内容。
### 2. 开源许可证声明
QQ 版本列表实用工具的源代码依据经过开源促进会([Open Source Initiative](https://opensource.org))认证的 GNU Affero General Public License Version 3(AGPL v3)协议开放源代码,用户可以依照协议条款自由查看、审计和使用源代码。
### 3. 法律责任
若任何个人或组织因上述误报行为遭受损害,QQ 版本列表实用工具保留依法追究法律责任的权利。
特此声明!
附 VirusTotal 对 QQ 版本列表实用工具 v1.2.2-3b425fc 的扫描结果:https://www.virustotal.com/gui/file/865f30709812664937192d55c64568f7c8df3406f0dda3faf5534e64eef756c2
</details>
<span id="注意事项"></span>
## 注意事项:使用前须知
> [!WARNING]
> 请确保您在使用前充分审慎阅读了[用户协议](https://github.com/klxiaoniu/QQVersionList/blob/master/UserAgreement.md)。鉴于 QQ 测试版可能存在不可预知的稳定性问题,您在下载及使用该测试版本之前,必须明确并确保自身具备足够的风险识别和承受能力。根据相关条款,您使用本软件时应当已了解并同意,因下载或使用 QQ 测试版而可能产生的任何直接或间接损失、损害以及其他不利后果,均由您自行承担全部责任。
> [!WARNING]
> QQ 版本列表实用工具为[自由软件](https://www.gnu.org/philosophy/free-sw.html),其第一方软件本体及源代码仅可在 GitHub 或相关官方平台上免费并自由获取。根据[用户协议](https://github.com/klxiaoniu/QQVersionList/blob/master/UserAgreement.md),如果您从未经 GitHub 或相关官方平台获取本应用,QQ 版本列表实用工具无法保证该应用能够正常使用,并对因此给您造成的损失不予负责。
> [!WARNING]
> QQ 版本列表实用工具提供的所有服务及内容均旨在促进合法的学习交流活动,严禁用户将其用于任何非法、违规或侵犯他人权益的目的。敬请所有用户严格遵守相关法律法规,在使用本应用的过程中秉持合法、正当与诚信原则,切勿涉足任何违法用途。如有违反,相关法律责任将由行为人自负,同时,本应用亦保留采取一切必要措施的权利,包括但不限于暂停或终止服务,并追究其法律责任。
> [!WARNING]
> QQ 版本列表实用工具不面向中国大陆境内公众用户提供服务,因此不会在中国大陆境内进行移动互联网应用程序备案。根据[中华人民共和国《工业和信息化部关于开展移动互联网应用程序备案工作的通知》(工信部信管〔2023〕105号)](https://www.gov.cn/zhengce/zhengceku/202308/content_6897341.htm),应用程序分发平台不得为未履行备案手续的应用程序提供分发服务。请任何未经 QQ 版本列表实用工具授权而私自上架 QQ 版本列表实用工具的在中华人民共和国境内运营的各软件下载站或应用程序分发平台立即全面下架 QQ 版本列表实用工具。因私自上架 QQ 版本列表实用工具而触犯相关法律法规或产生任何直接或间接损失、损害以及其他不利后果的,均由您自行承担全部责任。
## 简介
QQ 版本列表实用工具 for Android 是一个使用 Material 3 组件库构建,可用于查看 Android QQ 与 Android TIM 版本列表的 Android 软件。QQ 版本列表实用工具用户可以通过本应用了解到 Android QQ 与 Android TIM 版本更新的最新信息。
<span id="如何使用"></span>
## 如何使用?
### 版本列表
在进入 QQ 版本列表实用工具时,您首先会看到一系列显示“x.y.z”“xxx MB”的卡片,这些卡片组成的列表即为 Android QQ 的版本列表[^1],卡片列表展示了已经或即将发布的 Android QQ 版本。
[^1]: Android QQ 版本信息来源:https://im.qq.com/rainbow/androidQQVersionList
从右向左滑动版本列表,可切换到 TIM 版本列表[^2]和微信版本列表[^3]。
[^2]: Android TIM 版本信息来源:https://im.qq.com/rainbow/TIMDownload
[^3]: Android 微信版本信息来源:https://weixin.qq.com/updates
通过点击卡片右侧箭头按钮,可展开卡片以查阅到更为详尽的信息。
默认情况下,长按卡片文字会弹出展示卡片原始 JSON 字符串的对话框,长按对话框文字可选择复制字符串内容。可在设置中关闭此功能。
### 应用包分析
在 QQ 版本列表实用工具首页,长按顶部“本机 QQ/TIM”卡片即可查看本机 QQ/TIM 的应用包分析。
也可将 QQ/TIM APK 文件通过系统分享至 QQ 版本列表实用工具以查看该 QQ/TIM APK 的应用包分析。
### 实验性功能
> [!IMPORTANT]
> QQ 版本列表实用工具可能以软件实验形式提供一些尚不稳定的服务,此类服务会明确标注“实验性”(或其的其它语言形式)。您使用此类服务即代表您已明确并确保自身具备足够的风险识别和承受能力。因使用此类实验性服务而可能产生的任何直接或间接损失、损害以及其它不利后果,QQ 版本列表实用工具不承担责任。
在 QQ 版本列表实用工具界面,点击底部锥形瓶按钮即可进入实验性功能对话框。
#### 从腾讯服务器配置拉取微信最新测试版下载直链
Android 微信测试版相关信息配置在[腾讯服务器配置文件](https://dldir1v6.qq.com/weixin/android/weixin_android_alpha_config.json)内。可使用 QQ 版本列表实用工具提供的“从腾讯服务器配置拉取微信最新测试版下载直链”功能尝试获取微信最新测试版下载直链。
> [!WARNING]
> 此功能并非每次请求都能成功获取到 Android 微信测试版下载直链,当无法获取下载直链时可能存在的情况是微信还未发布测试版或测试版已撤包。QQ 版本列表实用工具不对此功能及其任何后果作出任何可靠性保证。请明确并确保自身具备足够的风险识别和承受能力。
#### 从微信输入法测试通道获取微信输入法最新测试版下载直链
Android 微信输入法测试版下载直链可由[微信输入法公网测试通道](https://z.weixin.qq.com/android/download?channel=latest)重定向获取。可使用 QQ 版本列表实用工具提供的“从微信输入法测试通道获取微信输入法最新测试版下载直链”功能尝试获取微信输入法最新测试版下载直链。
#### 腾讯应用宝更新获取(实验性)
QQ、TIM、微信、企业微信、微信输入法使用腾讯应用宝([腾讯应用开放平台](https://app.open.qq.com/))分发软件最新安装包。可使用 QQ 版本列表实用工具提供的腾讯应用宝更新获取(实验性)获取 QQ、TIM、微信、企业微信、微信输入法最新腾讯应用宝上架版本安装包下载直链。
在 QQ 版本列表实用工具界面,点击底部锥形瓶按钮即可看到“腾讯应用宝更新获取(实验性)”选项,点击即可进入“腾讯应用宝更新获取(实验性)”对话框。之后按提示进行操作即可。
#### TDS 腾讯端服务 Shiply 容器与发布平台更新获取(实验性)
腾讯 QQ 使用 [TDS 腾讯端服务 Shiply 容器与发布平台](https://shiply.tds.qq.com/),根据 QQ 号(uin)及其终端信息分发小范围灰度测试安装包。当您所使用的 Android QQ 收到官方升级弹窗提醒时,可使用 QQ 版本列表实用工具提供的 TDS 腾讯端服务 Shiply 容器与发布平台更新获取(实验性)尝试获取升级安装包下载直链。
在 QQ 版本列表实用工具界面,点击底部锥形瓶按钮即可看到“TDS 腾讯端服务 Shiply 容器与发布平台更新获取(实验性)”选项,点击即可进入“Shiply 平台更新获取(实验性)”对话框。
对话框含有两个输入框,分别是“uin”和“版本”。请在“uin”输入框填写收到官方升级弹窗提醒的 QQ 号,在“版本”输入框填写收到官方升级弹窗提醒时正在使用的 QQ 版本。
在“进阶配置”中可配置“appid(非必填)”等可选参数。
填写完毕后,点击“开始”,QQ 版本列表实用工具将会尝试根据填写参数构造内容并以此向 Shiply 平台发送 POST 请求。
如果返回内容包含 Android 应用安装包下载直链,QQ 版本列表实用工具将会自动对 Android 应用安装包下载直链进行识别和置顶推荐,点击推荐卡片可进行进一步操作(如下载、分享等),长按推荐卡片即可快捷复制直链。
> [!WARNING]
> 此功能并非每次请求都能成功获取到 Android QQ 应用安装包下载直链,无法获取 Android QQ 应用安装包下载直链则表示填写的参数不在 Shiply 平台认可与分发范围内。QQ 版本列表实用工具不对此功能及其任何后果作出任何可靠性保证。请明确并确保自身具备足够的风险识别和承受能力。
### 扫版(维护模式)
> [!WARNING]
> 由于 Android QQ 和 Android TIM 的较新版本越来越倾向于使用包含特征码的下载直链,使得通过枚举法获取到 Android QQ 和 Android TIM 的下载直链变得异常困难。基于此,QQ 版本列表实用工具的扫版功能进入维护模式。
>
> #### 扫版进入维护模式意味着什么?
>
> - 现行相关功能仍可继续使用;
> - 不再对相关功能进行积极开发,也不再接受相关新功能请求;
> - QQ 版本列表实用工具不再对相关功能及其任何后果作出任何可靠性保证。使用时请明确并确保自身具备足够的风险识别和承受能力。
在 Android QQ - 首页侧滑菜单 - 设置 - 关于QQ与帮助 中可得知,Android QQ 的版本号通常为 `x.y.z.nnnnn`。其中 `x.y.z` 在这里被称为“主版本号”,而 `nnnnn` 被称为“小版本号”。
在 QQ 版本列表实用工具界面,点击右下角放大镜图标浮动按钮即可进入“扫版 Extended”对话框。
对话框含有三个输入框,分别是“主版本号”、“扫版类型”和“小版本号”。“主版本号”已经预填入了版本列表显示的最新版本号,也可自行修改。
- 若选择扫正式版,无需填写小版本号,软件将尝试访问以下链接:
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>_64.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>_64_HB.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>_64_HB1.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>_64_HB2.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>_64_HB3.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>_64_BBPJ.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>_HB_64.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>_HB1_64.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>_HB2_64.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>_HB3_64.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>_BBPJ_64.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>_64_HD.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>_64_HD1.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>_64_HD2.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>_64_HD3.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>_HD_64.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>_HD1_64.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>_HD2_64.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>_HD3_64.apk`
- 若选择扫测试版,则需要填写起始小版本号:
- 默认情况下,软件将尝试访问 `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>.<小版本号>_64.apk` ,若当次访问未果,默认情况下将按照设置逻辑自动递增小版本号后再次尝试访问,直到访问成功为止。
- 在设置中打开扩展测试版扫版格式后,软件将尝试访问以下链接:
<details>
<summary>点击展开</summary>
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>.<小版本号>_64.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>.<小版本号>_64_HB.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>.<小版本号>_64_HB1.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>.<小版本号>_64_HB2.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>.<小版本号>_64_HB3.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>.<小版本号>_HB_64.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>.<小版本号>_HB1_64.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>.<小版本号>_HB2_64.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>.<小版本号>_HB3_64.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>.<小版本号>_64_HD.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>.<小版本号>_64_HD1.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>.<小版本号>_64_HD2.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>.<小版本号>_64_HD3.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>.<小版本号>_HD_64.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>.<小版本号>_HD1_64.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>.<小版本号>_HD2_64.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>.<小版本号>_HD3_64.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>.<小版本号>_64_HD1HB.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>.<小版本号>_HD1HB_64.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>.<小版本号>_test.apk`
</details>
若当次访问未果,默认情况下将按照设置逻辑自动递增小版本号后再次尝试访问,直到访问成功为止。
- 设置自定义扫版后缀后,可以扫描以下直链格式:
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号><自定义后缀>.apk`
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_<主版本号>.<小版本号><自定义后缀>.apk`
- 若选择 TIM 扫版,对应的直链为:
- `https://downv6.qq.com/qqweb/QQ_1/android_apk/TIM_<主版本号>.<小版本号><自定义后缀>.apk`
填入相应输入框内容后,软件将尝试访问上述链接。若当次访问未果,默认情况下将自动递增小版本号后再次尝试访问,直到访问成功为止。
> [!TIP]
> QQ 版本列表实用工具实验性支持了 Android 微信的扫版。若选择微信扫版,对话框将变更为四个输入框,分别是“主版本号”、“扫版类型”、“真实版本号”和“十六进制代码”,对应的直链为:
>
> - `http://dldir1v6.qq.com/weixin/android/weixin<主版本号>android<真实版本号>_<十六进制代码>_arm64.apk`
> - `http://dldir1v6.qq.com/weixin/android/weixin<主版本号>android<真实版本号>_<十六进制代码>_arm64_1.apk`
>
> 填入相应输入框内容后,软件将尝试访问上述链接。若当次访问未果,默认情况下将自动递增十六进制代码后再次尝试访问,直到访问成功为止。
访问成功后,软件会弹出成功对话框,对话框下方提供了一系列动作按钮,依次是“分享”、“下载”、“停止”、“跳过”和“复制”。
> [!WARNING]
> 微信扫版为 QQ 版本列表实用工具附带的实验性功能,可能存在不可预知的稳定性问题。请明确并确保自身具备足够的风险识别和承受能力。
#### Firebase 服务(实验性)
借助 Google Firebase 云消息传递,QQ 版本列表实用工具 1.4.1 版本实现了检测到版本列表更新后向用户推送系统通知的功能。
在 QQ 版本列表实用工具界面,点击底部锥形瓶按钮即可看到“初始化 Firebase 服务”选项,点击后即可初始化 Firebase 服务。
初始化 Firebase 服务后,将立即在本地由 Firebase SDK 生成注册令牌,并将标识符和配置数据上传到 Firebase 服务器。此操作一经启用即无法撤销。
初始化 Firebase 服务后,点击设置即可看到“版本列表更新时推送系统通知(通过 Firebase 云消息传递)”开关。打开后,QQ 版本列表实用工具首先将申请系统通知权限,之后将与 Google 服务器进行通信,上述步骤完成后即可订阅版本列表更新通知。
> [!IMPORTANT]
> Firebase 服务依赖于设备 Google Play 服务,设备缺失 Google Play 服务时将无法使用 Firebase 服务。[^4]
[^4]: Firebase Android SDK 对 Google Play 服务的依赖:https://firebase.google.cn/docs/android/android-play-services
> [!IMPORTANT]
> 订阅版本列表更新通知需和 Google 服务器进行通信,请确保您的设备可以正常连接到 Google 服务器。
> [!WARNING]
> 在中国大陆发行的 Android 设备可能存在无法接收 Firebase 云消息传递的情况。
> [!WARNING]
> “通过 Firebase 云消息传递的版本列表更新提醒”为实验性功能,QQ 版本列表实用工具不对此功能的及时性和有效性做出任何可靠性保证。
<span id="获取更新"></span>
## 获取更新
<a href='https://github.com/klxiaoniu/QQVersionList/releases'><img src='https://raw.githubusercontent.com/klxiaoniu/QQVersionList/master/ReadmeAssets/GitHub-Badge.png' width="300" alt="Get it on GitHub"></a>
<a href='https://github.com/klxiaoniu/QQVersionList/blob/master/ReadmeAssets/Get-it-on-Obtainium.md'><img src='https://raw.githubusercontent.com/ImranR98/Obtainium/main/assets/graphics/badge_obtainium.png' width="300" alt="Obtanium"></a>
## 常见问题
### QQ 版本列表实用工具能否实现“检测到新测试版本下载直链自动提醒您”的功能?
QQ 版本列表实用工具不能实现“检测到新测试版本下载直链自动提醒您”的功能,因为这需要自有服务器,并且需要自有服务器去长时间请求腾讯服务器,存在法律和技术风险。
### 设置 - 扫版直链格式设置 里的“其它”是什么?
#### “使用 QQ 8.9.58 测试版直链格式”
腾讯 QQ 团队曾在且目前仅在 QQ 8.9.58 测试 QQNT 技术架构时使用了 `https://downv6.qq.com/qqweb/QQ_1/android_apk/qq_<主版本号>.<小版本号>_64.apk` 直链格式。鉴于此,QQ 版本列表实用工具添加了支持此类非标准但实际存在的直链的选项。
勾选“使用 QQ 8.9.58 测试版直链格式”后,“正式版”“测试版”扫版格式将变更为 `https://downv6.qq.com/qqweb/QQ_1/android_apk/qq_<主版本号><自定义后缀>.apk` 或 `https://downv6.qq.com/qqweb/QQ_1/android_apk/qq_<主版本号>.<小版本号><自定义后缀>.apk`。
#### “扫版类型添加 QQ 9.0.8.14600 空格直链格式”
2023 年 12 月 22 日,腾讯 QQ 官方团队在上传 Android QQ 9.0.8.14600 版本时,不慎将常规链接格式 `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android_9.0.8.14600.64_apk` 错误配置为包含 URL 编码空格形式的链接地址 `https://downv6.qq.com/qqweb/QQ_1/android_apk/Android%209.0.8.14600%2064.apk` 。 鉴于此,QQ 版本列表实用工具增设了“空格扫版”扫版模式,该功能在原有的“测试版”扫版模式上将版本号中的 `.` 字符替换为 URL 编码的空格符 `%20`,以适应并支持此类非标准但实际存在的直链。
勾选“扫版类型添加 QQ 9.0.8.14600 空格直链格式”后,QQ 版本列表实用工具将会在扫版类型下拉菜单添加“空格扫版”选项,选中即可使用上述格式。
### 为什么默认添加了 QQ 测试版扫版小版本号必须为 5 的倍数这项限制?
基于对 Android QQ 长期以来的版本号发布规律进行深入观察和分析的结果,我们发现 Android QQ 小版本号更新通常遵循每增加一个有效版本即递增 5 的倍数这一特定模式。为了贴近这一潜在实际规范并确保 QQ 版本列表实用工具的快捷性,QQ 版本列表实用工具依据最佳实践原则,默认设置小版本号和扫版必须为 5 的倍数的限制规则。此限制并非强制,用户可随时进入设置解除此限制。
### 版本列表中已经有新的版本号了,为什么我使用枚举扫版却获取不到下载链接?
即使版本列表已出现了新的版本号,也并不意味着 QQ 团队已经完成了新版本(含测试版)安装包在腾讯公网服务器的部署和发布。一种可能的情况是,QQ 团队正在进行新版本的内部测试阶段或小范围灰度推送阶段,因而尚未对外提供广泛公网下载渠道。
### 为什么不提供 Android 微信的版本列表更新日志?
目前还没有找到来自官方的可靠且请求次数少而信息密度大的 Android 微信版本列表更新日志数据源,因此 QQ 版本列表实用工具无法提供 Android 微信的版本列表更新日志,烦请前往浏览器查看微信官网提供的更新日志。如果您发现了可靠的 Android 微信版本列表更新日志数据源,欢迎提出 Issue(s) 或提交 PR。
## 其它
欢迎[帮助我们完成本地化翻译](https://crowdin.com/project/qqversionstool)!提交翻译则代表您同意您的译文将跟随 QQ 版本列表实用工具项目采用 [GNU Affero General Public License Version 3](/LICENSE) 开源许可。
[](https://crowdin.com/project/qqversionstool)
> [!IMPORTANT]
> QQ 版本列表实用工具始终坚守法律底线,秉持尊重与保护所有用户及第三方合法权益的原则。我们深切认识到任何可能存在的权益侵犯行为都会对权益方造成潜在影响,对此,我们表示由衷歉意,并承诺,一旦接到权益方的权益受到侵犯的通知,我们将立即依法启动核查程序,并在确认侵权事实后,迅速采取有效措施,以最大程度地消除不良影响,恢复并保障权益方的合法权益。敬请相关权益方在发现 QQ 版本列表实用工具存在任何侵权内容时,及时与我们取得联系,我们将竭诚为权益方提供必要的协助与支持。
> [!NOTE]
> “腾讯”“QQ”“腾讯 QQ”“腾讯 TIM”“微信”“WeChat”“Weixin”“腾讯微信”“企业微信”“WeCom”“微信输入法”“WeType”“应用宝”“腾讯应用宝”“腾讯企点”等是深圳市腾讯计算机系统有限公司和/或其关联公司的商标。本项目对“腾讯”“QQ”“腾讯 QQ”“腾讯 TIM”“微信”“WeChat”“Weixin”“腾讯微信”“企业微信”“WeCom”“微信输入法”“WeType”“应用宝”“腾讯应用宝”“腾讯企点”等的使用旨在注明和指向对应主体,并非表示对“腾讯”“QQ”“腾讯 QQ”“腾讯 TIM”“微信”“WeChat”“Weixin”“腾讯微信”“企业微信”“WeCom”“微信输入法”“WeType”“应用宝”“腾讯应用宝”“腾讯企点”等商标的注册和拥有。
>
> Android™ 是 Google LLC 的商标。
>
> Unreal® 及其徽标是 Epic Games, Inc. 在美国及其他国家或地区的商标或注册商标。
## 贡献成员
<a href="https://github.com/klxiaoniu/QQVersionList/graphs/contributors">
<img src="https://contrib.rocks/image?repo=klxiaoniu/QQVersionList" alt="贡献成员"/>
</a>
## 开源相关
QQ 版本列表实用工具采用 [GNU Affero General Public License Version 3](https://github.com/klxiaoniu/QQVersionList/blob/master/LICENSE) 开源许可。
QQ 版本列表实用工具的诞生离不开以下开源项目,感谢以下开源项目的作者和贡献者:
- [Material Components for Android(Android Open Source Project)](https://github.com/material-components/material-components-android/),Licensed under [Apache License Version 2.0](https://github.com/material-components/material-components-android/blob/master/LICENSE)
- [Android Jetpack(Android Open Source Project)](https://developer.android.com/jetpack),Licensed under [Apache License Version 2.0](https://github.com/androidx/androidx/blob/androidx-main/LICENSE.txt)
- [Remix Icon(Remix Design)](https://remixicon.com/),Licensed under [Apache License Version 2.0](https://remixicon.com/license)
- [OkHttp(Square)](https://square.github.io/okhttp/),Licensed under [Apache License Version 2.0](https://github.com/square/okhttp/blob/master/LICENSE.txt)
- [Kotlin(JetBrains)](https://kotlinlang.org/),Licensed under [Apache License Version 2.0](https://github.com/JetBrains/kotlin/blob/master/license%2FREADME.md)
- [Gson(Google)](https://github.com/google/gson/),Licensed under [Apache License Version 2.0](https://github.com/google/gson/blob/master/LICENSE)
- [Coil](https://coil-kt.github.io/coil/),Licensed under [Apache License Version 2.0](https://github.com/coil-kt/coil/blob/main/LICENSE.txt)
- [Eclipse Temurin™](https://adoptium.net/temurin/),Licensed under GNU General Public License, version 2 with the Classpath Exception
- [Oracle JDK](https://www.oracle.com/java/technologies/downloads/),Licensed under [Oracle No-Fee Terms and Conditions](https://www.java.com/freeuselicense)
- [JetBrains Runtime](https://github.com/JetBrains/JetBrainsRuntime),Licensed under [GNU General Public License Version 2](https://github.com/JetBrains/JetBrainsRuntime/blob/main/LICENSE)
- [Kotlin Serialization](https://github.com/Kotlin/kotlinx.serialization),Licensed under [Apache License Version 2.0](https://github.com/Kotlin/kotlinx.serialization/blob/master/LICENSE.txt)
- [Get QQ Update Link(owo233)](https://github.com/callng/GQUL),Licensed under [The Unlicense](https://github.com/callng/GQUL/blob/master/LICENSE)
- [Paris(Airbnb)](https://github.com/airbnb/paris),Licensed under [Apache License Version 2.0](https://github.com/airbnb/paris/blob/master/LICENSE)
- [Apache Maven™](https://maven.apache.org/),Licensed under [Apache License Version 2.0](https://github.com/apache/maven/blob/master/LICENSE)
- [Gradle](https://gradle.org/),Licensed under [Apache License Version 2.0](https://github.com/gradle/gradle/blob/master/LICENSE)
- [Material Symbols / Material Icons(Google)](https://fonts.google.com/icons),Licensed under [Apache License Version 2.0](https://github.com/google/material-design-icons/blob/master/LICENSE)
- [Obtainium(Imran)](https://github.com/ImranR98/Obtainium),Licensed under [GNU General Public License Version 3](https://github.com/ImranR98/Obtainium/blob/main/LICENSE.md)
- [Secrets Gradle Plugin for Android(Google)](https://github.com/google/secrets-gradle-plugin),Licensed under [Apache License Version 2.0](https://github.com/google/secrets-gradle-plugin/blob/main/LICENSE)
- [Firebase Android Open Source Development(Google)](https://firebase.google.com/),Licensed under [Apache License Version 2.0](https://github.com/firebase/firebase-android-sdk/blob/main/LICENSE)
- [AndroidFastScroll(张海)](https://github.com/zhanghai/AndroidFastScroll),Licensed under [Apache License Version 2.0](https://github.com/zhanghai/AndroidFastScroll/blob/master/LICENSE)
- [Kotlin Coroutines on Android](https://github.com/Kotlin/kotlinx.coroutines),Licensed under [Apache License Version 2.0](https://github.com/Kotlin/kotlinx.coroutines/blob/master/LICENSE.txt)
- [Google Play services Plugins](https://github.com/google/play-services-plugins),Licensed under [Apache License Version 2.0](https://github.com/google/play-services-plugins/blob/main/LICENSE)
- [Apache Commons™](https://commons.apache.org/),Licensed under [Apache License Version 2.0](https://www.apache.org/licenses/LICENSE-2.0)
- [腾讯 Kona 国密套件](https://github.com/Tencent/TencentKonaSMSuite),Licensed under [GNU General Public License, version 2 with the Classpath Exception](https://github.com/Tencent/TencentKonaSMSuite/blob/master/LICENSE.txt)
- [斑朵 Boundo(Cliff Liu)](https://github.com/cliuff/boundo),Licensed under [Apache License Version 2.0](https://github.com/cliuff/boundo/blob/master/LICENSE)
- [Smali/Baksmali(Google fork of JesusFreke's)](https://github.com/google/smali)
- [智谱 AI 开放平台大模型接口 Java SDK](https://github.com/MetaGLM/zhipuai-sdk-java-v4),Licensed under [MIT License](https://github.com/MetaGLM/zhipuai-sdk-java-v4/blob/main/LICENSE)
- [Jackson Core & Databind(FasterXML)](https://github.com/FasterXML/jackson),Licensed under Apache License Version 2.0
- [Java API for GitHub(Kohsuke Kawaguchi)](https://github.com/hub4j/github-api),Licensed under [MIT License](https://github.com/hub4j/github-api/blob/main/LICENSE.txt)
- [jsoup: Java HTML Parser](https://jsoup.org/),Licensed under [MIT License](https://jsoup.org/license)
- [Pangu.java](https://github.com/vinta/pangu.java),Licensed under [MIT License](https://github.com/vinta/pangu.java/blob/master/LICENSE)
- [AboutLibraries(Mike Penz)](https://github.com/mikepenz/AboutLibraries),Licensed under [Apache License Version 2.0](https://github.com/mikepenz/AboutLibraries/blob/develop/LICENSE)
## 商业服务鸣谢
- 感谢 [Crowdin](https://crowdin.com/) 为本开源项目提供免费的[开源项目计划](https://crowdin.com/page/open-source-project-setup-request)。[Crowdin](https://crowdin.com/) 是面向团队和企业的 AI 驱动本地化软件。使用 600+ 个应用和集成自动翻译您的内容。
## 星标趋势
[](https://starchart.cc/klxiaoniu/QQVersionList)
## 孪生项目
[QQ 版本列表 Vigor for WeChat MiniProgram](https://github.com/ArcticFoxPro/QQVersionListTool-WeChatMiniProgram),Licensed under [木兰公共许可证, 第2版](https://github.com/ArcticFoxPro/QQVersionListTool-WeChatMiniProgram/blob/main/LICENSE)
[](https://github.com/ArcticFoxPro/QQVersionListTool-WeChatMiniProgram)
================================================
FILE: ReadmeAssets/Get-it-on-JiuQi-NotifCenter-WeChatMiniProgram.md
================================================
> [!warning]
> 2024 年 10 月 17 日,九七公司「通知中心」微信小程序改名为「通知侠」重新上线,但因内容安全为由去掉了 GitHub Releases 更新订阅。故此页面内容不再可用。
---
> [!important]
> 2024 年 8 月 2 日,[九七公司「通知中心」微信小程序被微信公众平台以“无实质服务内容引导跳转”暂停服务](https://mp.weixin.qq.com/s/ITU6H_UjA804srGeE2-FOA)。此页面内容可能在一段时间内不可用。
# 获取更新——基于九七公司「通知中心」微信小程序方案
您可以使用广州九七网络科技有限公司「通知中心」微信小程序来接收 QQ 版本列表实用工具的未来更新。「通知中心」微信小程序可以让您在微信平台上一站式收到三方软件发布新版本的通知。
「通知中心」微信小程序含有微信广告平台提供的广告。
## 快速订阅
复制下方链接,在微信任意会话窗口(如文件传输助手)中发送后点击打开:
```
#小程序://通知中心/ZVsvPVMyrGbTwkf
```
之后点击订阅,并按照提示完成后续步骤即可。
## 手动订阅
1. 微信搜一搜「通知中心」,在搜索结果中找到小程序认证主体为广州九七网络科技有限公司的「通知中心」小程序,点击进入。
2. 在小程序首页找到 软件开发-GitHub Release,输入:
```
https://github.com/klxiaoniu/QQVersionList/
```
3. 点击添加按钮,并按照提示完成后续步骤即可。
================================================
FILE: ReadmeAssets/Get-it-on-Obtainium.md
================================================
# 获取更新——基于 [Obtainium](https://github.com/ImranR98/Obtainium) 方案
您可以使用开源软件 [Obtainium](https://github.com/ImranR98/Obtainium) 来接收 QQ 版本列表实用工具的未来更新。Obtainium 可以让您从一个 APP 内直接从其他 APP 的发布源获取安装包和更新应用程序,并在发布新版本时收到通知。
## 一键导入
若您的 Android 设备已安装 Obtainium,点击下方图片即可快速导入 QQ 版本列表实用工具的 Obtainium 配置。
<a href='http://apps.obtainium.imranr.dev/redirect.html?r=obtainium://app/%7B%22id%22:%22com.xiaoniu.qqversionlist%22,%22url%22:%22https://github.com/klxiaoniu/QQVersionList%22,%22author%22:%22%E5%BF%AB%E4%B9%90%E5%B0%8F%E7%89%9B%E3%80%81%E6%9C%89%E9%B2%AB%E9%9B%AA%E7%8B%90%22,%22name%22:%22QQ%20%E7%89%88%E6%9C%AC%E5%88%97%E8%A1%A8%E5%AE%9E%E7%94%A8%E5%B7%A5%E5%85%B7%22,%22preferredApkIndex%22:0,%22additionalSettings%22:%22%7B%5C%22includePrereleases%5C%22:false,%5C%22appName%5C%22:%5C%22QQ%20%E7%89%88%E6%9C%AC%E5%88%97%E8%A1%A8%E5%AE%9E%E7%94%A8%E5%B7%A5%E5%85%B7%5C%22,%5C%22versionExtractionRegEx%5C%22:%5C%22(?%3C=v)(.*)%5C%22,%5C%22matchGroupToUse%5C%22:%5C%22$0-Release%5C%22%7D%22%7D'><img src='https://raw.githubusercontent.com/ImranR98/Obtainium/main/assets/graphics/badge_obtainium.png' width="300" alt="Obtanium"></a>
## 手动导入
下载安装并打开 Obtainium,点击底部导航栏“添加应用”,在“来源 URL”中输入:
```
https://github.com/klxiaoniu/QQVersionList/
```
然后在下方“提取版本号的正则表达式”内填入:
```
(?<=v)(.*)
```
“从上述匹配结果中引用的捕获组”填入:
```
$0-Release
```
填入完成后,点击“添加”按钮即可。
================================================
FILE: SECURITY.md
================================================
## 上报漏洞 Reporting a Vulnerability
如果您发现了关于此项目的任何漏洞,请不要公开,请在[此处](https://github.com/klxiaoniu/QQVersionList/security)向我们私下报告漏洞。
If you discover any vulnerabilities regarding this project, please do not make them public and report them to us privately at [here](https://github.com/klxiaoniu/QQVersionList/security).
================================================
FILE: UserAgreement.md
================================================
# QQ 版本列表实用工具用户协议
> 此用户协议于 2024 年 10 月 28 日修订,并于 2024 年 10 月 29 日实施。
## 导言
欢迎您使用 QQ 版本列表实用工具。
为使用 QQ 版本列表实用工具及服务,您应当阅读并遵守《QQ 版本列表实用工具用户协议》(以下简称“本协议”)。请您务必审慎阅读,充分理解各条款内容,特别是免除或者限制责任的条款,以及开通或使用某项服务的单独协议,并选择接受或不接受。限制、免责条款可能以加粗形式提示您注意。
除非您已阅读并接受本协议所有条款,否则您无权获取和使用 QQ 版本列表实用工具及相关服务。您的任何使用本应用行为即视为您已阅读并同意上述协议的约束。如果您未满 18 周岁,请在法定监护人的陪同下阅读本协议及其它上述协议,并特别注意未成年人使用条款。
## 一、协议的范围
本协议是您与 QQ 版本列表实用工具之间关于您下载,安装,使用,复制 QQ 版本列表实用工具,以及使用 QQ 版本列表实用工具相关服务所订立的协议。
## 二、关于 QQ 版本列表实用工具提供的服务
QQ 版本列表实用工具提供的服务是指提供 Android QQ 和 Android TIM 版本列表的查看和对腾讯服务器提供的 Android QQ 及其它可能存在的腾讯软件产品的下载链接的枚举法扫描。QQ 版本列表实用工具用户可以通过本应用及时获取到 Android QQ 及其它可能存在的腾讯软件产品的版本更新的最新信息。
QQ 版本列表实用工具不面向中国大陆境内公众用户提供服务。请确认您并非中国大陆公民。
此外,QQ 版本列表实用工具可能以软件实验形式提供一些尚不稳定的服务,您在使用此类服务时,必须明确并确保自身具备足够的风险识别和承受能力。
## 三、软件的获取
您可以直接从 GitHub 平台获取 QQ 版本列表实用工具本体及其附带开源许可和知识产权相关文本的源代码。获取应用后,您需要按照该应用提示的步骤正确使用。
如果您从未经 GitHub 或相关官方平台获取本应用,QQ 版本列表实用工具无法保证该应用能够正常使用,并对因此给您造成的损失不予负责。
如果您不再需要使用 QQ 版本列表实用工具,可以不再打开应用使用或卸载应用。
## 四、软件的更新
为了改善用户体验,完善服务内容,QQ 版本列表实用工具将不断努力开发新的服务,并为您不时提供软件更新(这些更新可能会采取软件替换,修改,功能强化,版本升级等形式)。
为了保证 QQ 版本列表实用工具及服务的安全性和功能的一致性,QQ 版本列表实用工具有权不向您特别通知而对软件进行更新,或者对软件的部分功能效果进行改变或限制。
QQ 版本列表实用工具新版本发布后,旧版本的软件可能无法使用,QQ 版本列表实用工具不保证旧版 QQ 版本列表实用工具及相应的服务继续可用,请您随时核对并获取最新版本。
## 五、用户个人信息保护
保护用户个人信息是 QQ 版本列表实用工具的一项基本原则,QQ 版本列表实用工具本身不会主动获取和存储您的个人信息。
但在您使用过程中,可能会因网络通信的必要性而被软件本体或腾讯服务器自动获取您的 IP 地址等网络相关信息,我们将严格遵守法律法规的规定,并采取适当措施保护您的网络信息安全。
若您启用 QQ 版本列表实用工具中的 Firebase 服务时,Firebase SDK 将立即在本地生成注册令牌,并上传您的移动设备标识符、基于网络地址的地理位置信息、Firebase 安装 ID。此操作一经启用即无法撤销。
QQ 版本列表实用工具可能以软件实验形式提供一些尚不稳定的服务,用户在使用此类实验性服务时可能需要用户手动填写个人信息。QQ 版本列表实用工具承诺不会获取和存储您填写的任何个人信息。
QQ 版本列表实用工具非常重视对未成年人个人信息的保护。若您是 18 周岁(不含)以下的未成年人,在使用 QQ 版本列表实用工具的服务前,应事先取得您的法定监护人的书面同意。
## 六、主权力义务条款
您理解并同意,为了向您提供有效的服务,您在此许可 QQ 版本列表实用工具利用您移动通讯终端设备的处理器和宽带等资源。QQ 版本列表实用工具使用过程中可能产生的数据流量的费用,您需自行向运营商了解相关资费信息。
QQ 版本列表实用工具可能以软件实验形式提供一些尚不稳定的服务,此类服务会明确标注“实验性”(或其的其它语言形式)。您使用此类服务即代表您已明确并确保自身具备足够的风险识别和承受能力。
## 七、用户行为规范
您充分了解并同意使用本应用服务时须遵守法律法规,不得利用 QQ 版本列表实用工具从事违法违规行为,包括但不限于:
1. 发布、传送、传播、储存危害国家安全统一、破坏社会稳定、违反公序良俗、侮辱、诽谤、淫秽、暴力以及任何违反国家法律法规的内容;
2. 发布、传送、传播、储存侵害他人知识产权、商业秘密等合法权利的内容;
3. 恶意虚构事实、隐瞒真相以误导、欺骗他人;
4. 发布、传送、传播广告信息及垃圾信息;
5. 将 QQ 版本列表实用工具用于任何未经许可的商业目的,包括但不限于以营利为目的推广、销售产品或服务;
6. 其它法律法规禁止的行为。
如果用户违反了本条约定,相关国家机关、机构、公司或个人等实体可能会对用户提起诉讼、罚款或采取其它制裁措施,并要求 QQ 版本列表实用工具给予协助。造成损害的,用户应依法予以赔偿,QQ 版本列表实用工具不承担任何责任。
如果 QQ 版本列表实用工具发现或收到他人举报您发布的信息违反本条约定,QQ 版本列表实用工具有权进行独立判断并采取技术手段予以删除、屏蔽或断开链接。同时,QQ 版本列表实用工具有权视用户行为性质采取包括但不限于暂停或终止服务,限制、冻结或终止 QQ 版本列表实用工具使用,追究法律责任等措施。
用户违反本条约定,导致任何第三方损害的,用户应当独立承担责任;QQ 版本列表实用工具因此遭受损失的,用户也应当一并赔偿。
## 八、软件使用规范
除非法律允许或 QQ 版本列表实用工具的书面许可,您使用 QQ 版本列表实用工具过程中不得删除 QQ 版本列表实用工具及其副本上关于知识产权和开放源代码许可的信息。
## 九、免责条款
您充分了解并同意:
QQ 版本列表实用工具仅为用户提供 Android QQ 和 Android TIM 版本信息展示和上述产品及其它可能存在的腾讯软件产品的官方版本获取服务,所有下载链接均来源于腾讯官方服务器。对于链接的有效性、可用性以及任何由腾讯官方服务器产生或导致的任何问题,QQ 版本列表实用工具不承担责任。
鉴于 QQ、TIM 和其它任意腾讯软件产品的测试版本可能存在不可预知的稳定性问题,您在下载及使用任何腾讯软件产品的任何测试版本即代表您已明确并确保自身具备足够的风险识别和承受能力。您明确并同意其使用任何腾讯软件产品的任何测试版本所存在的风险将完全由其本人和/或其法定责任主体承担,QQ 版本列表实用工具对此不承担任何责任。
QQ 版本列表实用工具可能以软件实验形式提供一些尚不稳定的服务,此类服务会明确标注“实验性”(或其的其它语言形式)。您使用此类服务即代表您已明确并确保自身具备足够的风险识别和承受能力。因使用此类实验性服务而可能产生的任何直接或间接损失、损害以及其它不利后果,QQ 版本列表实用工具不承担责任。
除 QQ 版本列表实用工具注明之用户协议外,其它因不当使用本应用而导致的任何意外、疏忽、合约毁坏、诽谤、版权或其它知识产权侵犯及其所造成的任何损失,QQ 版本列表实用工具概不负责,亦不承担任何法律责任。
对于因不可抗力或因黑客攻击、通讯线路中断等 QQ 版本列表实用工具不能控制的原因造成的网络服务中断或其它缺陷,导致用户不能正常使用本应用,QQ 版本列表实用工具不承担任何责任,但将尽力减少因此给用户造成的损失或影响。
本协议未涉及的问题请参见中华人民共和国相关适用法律法规,当本协议与中华人民共和国相关适用法律法规冲突时,以中华人民共和国相关适用法律法规为准。本条所述之免责条款并不影响用户根据适用法律享有的法定权利。
## 十、其它
您使用 QQ 版本列表实用工具即视为您已阅读并同意接受 QQ 版本列表实用工具协议的约束。QQ 版本列表实用工具有权在必要时修改本协议条款。如果您不接受修改后的条款,应当停止使用 QQ 版本列表实用工具。
QQ 版本列表实用工具有权随时修改本协议,并将在应用内显著位置弹窗通知用户。修订后的协议自公布之日起在一定期限后生效,请用户定期查看相关公告,继续使用本应用即视为同意接受修订后的协议。
QQ 版本列表实用工具基于 GNU Affero General Public License Version 3 许可开放源代码。
本应用与深圳市腾讯计算机系统有限公司和/或其关联公司无关。
“腾讯”“QQ”“腾讯 QQ”“腾讯 TIM”“微信”“WeChat”“Weixin”“腾讯微信”“企业微信”“WeCom”“微信输入法”“WeType”“应用宝”“腾讯应用宝”等是深圳市腾讯计算机系统有限公司和/或其关联公司的商标。本项目对“腾讯”“QQ”“腾讯 QQ”“腾讯 TIM”“微信”“WeChat”“Weixin”“腾讯微信”“企业微信”“WeCom”“微信输入法”“WeType”“应用宝”“腾讯应用宝”等的使用旨在注明和指向对应主体,并非表示对“腾讯”“QQ”“腾讯 QQ”“腾讯 TIM”“微信”“WeChat”“Weixin”“腾讯微信”“企业微信”“WeCom”“微信输入法”“WeType”“应用宝”“腾讯应用宝”等商标的注册和拥有。
Android™ 是 Google LLC 的商标。
GITHUB® 是 GitHub, Inc. 在美国和其他国家或地区的商标或注册商标。
Unreal® 及其徽标是 Epic Games, Inc. 在美国及其它国家或地区的商标或注册商标。
本协议的签订地是中华人民共和国福建省福州市。
本协议适用中华人民共和国相关适用法律法规。如果双方发生纠纷,应友好协商解决;如协商不成,您同意将纠纷提交给签订地具有管辖权的法院提起诉讼。
本协议中的某些条款因故无法适用,则本协议的其它条款继续适用且无法适用的条款将会被修改,以便其能够依法适用。
本协议是您和 QQ 版本列表实用工具之间关于本产品相关事项的最终的、完整的、排他的协议,且取代和合并之前当事人关于此类事项(包括任何形式的最终用户许可、开放源代码许可、服务条款和隐私政策)的讨论和协议。
每部分的标题只为阅读之便而无任何法律或合同义务。
本协议可能存在多种语言版本。各语言版本之间如有不一致之处,以中文版本为准。
除非我们书面同意,您不得转让本协议所规定的权利义务。任何违反上述规定企图转让的行为均无效。在法律许可的范围内,QQ 版本列表实用工具对本协议享有最终解释权。
QQ 版本列表实用工具
2024 年 10 月 28 日
================================================
FILE: app/.gitignore
================================================
/build
================================================
FILE: app/build.gradle.kts
================================================
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
// SPDX-License-Identifier: AGPL-3.0-or-later
/*
Qverbow Util
Copyright (C) 2023 klxiaoniu
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
id("com.google.android.libraries.mapsplatform.secrets-gradle-plugin")
id("com.google.gms.google-services")
kotlin("plugin.serialization") version "2.2.10"
alias(libs.plugins.compose.compiler)
alias(libs.plugins.aboutlibraries)
}
private fun gitCommitHash(project: Project): String {
return project.providers.exec {
commandLine("git rev-parse --verify --short HEAD".split(" "))
}.standardOutput.asText.get().trim()
}
private fun gitCommitCount(project: Project): Int {
return project.providers.exec {
commandLine("git rev-list HEAD --count".split(" "))
}.standardOutput.asText.get().trim().toInt()
}
val gitCommitCount = gitCommitCount(project)
val gitCommitHash = gitCommitHash(project)
android {
namespace = "com.xiaoniu.qqversionlist"
compileSdk = 36
defaultConfig {
applicationId = "com.xiaoniu.qqversionlist"
minSdk = 24
targetSdk = 36
versionCode = gitCommitCount
versionName = "1.5.3-$gitCommitHash"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
arguments += listOf("-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON")
}
}
}
signingConfigs {
System.getenv("KEYSTORE_PATH")?.let {
create("release") {
storeFile = file(it)
storePassword = System.getenv("KEYSTORE_PASSWORD")
keyAlias = System.getenv("KEY_ALIAS")
keyPassword = System.getenv("KEY_PASSWORD")
}
}
}
buildTypes {
release {
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
)
signingConfig =
signingConfigs.findByName("release") ?: signingConfigs.findByName("debug")
signingConfig?.enableV2Signing = true
signingConfig?.enableV3Signing = true
signingConfig?.enableV4Signing = true
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
buildFeatures {
viewBinding = true
buildConfig = true
compose = true
}
}
kotlin {
compilerOptions {
jvmTarget = JvmTarget.JVM_21
}
}
aboutLibraries {
license.mapLicensesToSpdx = false
}
dependencies {
implementation(libs.aboutlibraries.core)
implementation(libs.androidx.activity)
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.activity.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.biometric)
implementation(libs.androidx.browser)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.constraintlayout)
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.core.splashscreen)
implementation(libs.androidx.datastore.preferences)
implementation(libs.androidx.fragment.ktx)
implementation(libs.androidx.lifecycle.livedata.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.lifecycle.viewmodel.compose)
implementation(libs.androidx.lifecycle.viewmodel.ktx)
implementation(libs.androidx.material3)
implementation(libs.androidx.material3.window.sizeclass)
implementation(libs.androidx.recyclerview)
implementation(libs.androidx.runtime.livedata)
implementation(libs.androidx.ui)
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.viewpager2)
implementation(libs.coil)
implementation(libs.coil.network.okhttp)
implementation(libs.commons.compress)
implementation(libs.commons.io)
implementation(libs.firebase.analytics)
implementation(libs.firebase.messaging)
implementation(platform(libs.firebase.bom))
implementation(libs.github.api)
implementation(libs.gson)
implementation(libs.jsoup)
implementation(libs.kona.crypto)
implementation(libs.kona.provider)
implementation(libs.kotlinx.coroutines.android)
implementation(libs.kotlinx.serialization.json)
implementation(libs.library)
implementation(libs.material)
implementation(libs.maven.artifact)
implementation(libs.oapi.java.sdk)
implementation(libs.okhttp)
implementation(libs.pangu)
implementation(libs.paris)
implementation(libs.smali.dexlib2)
testImplementation(libs.junit)
androidTestImplementation(platform(libs.androidx.compose.bom))
androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.ui.test.junit4)
debugImplementation(libs.androidx.ui.tooling)
debugImplementation(libs.androidx.ui.test.manifest)
}
================================================
FILE: app/proguard-rules.pro
================================================
# Qverbow Util
# Copyright (C) 2023 klxiaoniu
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
-keep class com.xiaoniu.qqversionlist.data.** { *; }
-keep class com.tencent.kona.crypto.provider.* { *; }
-keep class com.fasterxml.jackson.core.type.TypeReference { *; }
-keep class com.zhipu.oapi.service.v4.api.* { *; }
-keep class com.zhipu.oapi.service.v4.model.* { *; }
-keep class io.reactivex.Single { *; }
-keep class org.kohsuke.github.* { *; }
-dontwarn com.sun.tools.javac.processing.JavacFiler
-dontwarn com.sun.tools.javac.processing.JavacProcessingEnvironment
-dontwarn com.sun.tools.javac.util.Context
-dontwarn com.sun.tools.javac.util.Options
-dontwarn java.beans.ConstructorProperties
-dontwarn java.beans.Transient
-dontwarn javax.lang.model.SourceVersion
-dontwarn javax.lang.model.element.AnnotationMirror
-dontwarn javax.lang.model.element.Element
-dontwarn javax.lang.model.element.ExecutableElement
-dontwarn javax.tools.Diagnostic$Kind
-dontwarn javax.tools.JavaFileManager$Location
-dontwarn javax.tools.JavaFileManager
-dontwarn javax.tools.StandardLocation
-dontwarn lombok.**
-dontwarn org.apache.tools.ant.BuildException
-dontwarn org.apache.tools.ant.Location
-dontwarn org.apache.tools.ant.Project
-dontwarn org.apache.tools.ant.Task
-dontwarn org.apache.tools.ant.types.FileSet
-dontwarn org.apache.tools.ant.types.Path
-dontwarn org.apache.tools.ant.types.Reference
-dontwarn org.apache.tools.ant.types.ResourceCollection
-dontwarn org.eclipse.**
-dontwarn org.osgi.framework.Bundle
-dontwarn org.osgi.framework.BundleContext
-dontwarn com.infradna.tool.bridge_method_injector.BridgeMethodsAdded
-dontwarn com.infradna.tool.bridge_method_injector.WithBridgeMethods
-dontwarn edu.umd.cs.findbugs.annotations.NonNull
-dontwarn edu.umd.cs.findbugs.annotations.SuppressFBWarnings
-dontwarn okhttp3.internal.annotations.EverythingIsNonNull
-dontwarn edu.umd.cs.findbugs.annotations.CheckForNull
================================================
FILE: app/src/androidTest/java/com/xiaoniu/qqversionlist/ExampleInstrumentedTest.kt
================================================
package com.xiaoniu.qqversionlist
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert.*
import org.junit.Test
import org.junit.runner.RunWith
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.xiaoniu.qqversionlist", appContext.packageName)
}
}
================================================
FILE: app/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<queries>
<package android:name="com.tencent.mobileqq" />
<package android:name="com.tencent.tim" />
<package android:name="com.tencent.mm" />
</queries>
<application
android:name=".QverbowApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:enableOnBackInvokedCallback="true"
android:fullBackupContent="@xml/backup_rules"
android:hardwareAccelerated="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:largeHeap="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.QQVersionList"
android:usesCleartextTraffic="true"
tools:targetApi="35">
<activity
android:name=".ui.OSSLicensesActivity"
android:exported="false"
android:label="@string/openSourceLicenseTitle"
android:theme="@style/Theme.QQVersionList" />
<activity
android:name=".ui.OSSLicensesMenuActivity"
android:exported="false"
android:label="@string/openSourceLicenseTitle"
android:theme="@style/Theme.QQVersionList" />
<activity
android:name=".ui.MainActivity"
android:configChanges="orientation|screenSize|screenLayout"
android:exported="true"
android:windowSoftInputMode="adjustNothing">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" />
</activity>
<activity
android:name=".ui.LocalAppDetailsActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:mimeType="application/vnd.android.package-archive" />
<data android:scheme="file" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/vnd.android.package-archive" />
</intent-filter>
</activity>
<!--
Set custom default icon. This is used when no icon is set for incoming notification messages.
See README(https://goo.gl/l4GJaQ) for more.
-->
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/qv_logo_notification" />
<meta-data
android:name="firebase_messaging_auto_init_enabled"
android:value="false" />
<meta-data
android:name="firebase_analytics_collection_enabled"
android:value="false" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="@string/rainbow_notification_channel_id" />
<service
android:name=".service.FirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>
</manifest>
================================================
FILE: app/src/main/java/com/xiaoniu/qqversionlist/QverbowApplication.kt
================================================
// SPDX-License-Identifier: AGPL-3.0-or-later
/*
Qverbow Util
Copyright (C) 2023 klxiaoniu
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.xiaoniu.qqversionlist
import android.app.Application
import com.google.android.material.color.DynamicColors
import com.tencent.kona.crypto.KonaCryptoProvider
import java.security.Security
class QverbowApplication : Application() {
companion object {
lateinit var instance: QverbowApplication
const val SHIPLY_DEFAULT_APPID = "537230561"
const val SHIPLY_DEFAULT_SDK_VERSION = "1.3.36-RC03"
const val SHIPLY_APPID_QQ = "4cd6974be1"
const val SHIPLY_APPID_TIM = "ad6b501b0e"
const val SHIPLY_SIGN_ID_QQ = "0ccc46ca-154c-4c6b-8b0b-4d8537ffcbcc"
const val SHIPLY_SIGN_ID_TIM = "33641818-aee7-445a-82d4-b7d0bce3a85a"
const val ANDROID_QQ_PACKAGE_NAME = "com.tencent.mobileqq"
const val ANDROID_TIM_PACKAGE_NAME = "com.tencent.tim"
const val ANDROID_WECHAT_PACKAGE_NAME = "com.tencent.mm"
const val ANDROID_WECOM_PACKAGE_NAME = "com.tencent.wework"
const val ANDROID_WETYPE_PACKAGE_NAME = "com.tencent.wetype"
const val ANDROID_QIDIAN_PACKAGE_NAME = "com.tencent.qidian"
const val EARLIEST_ACCESSIBILITY_QQ_VERSION = false
const val EARLIEST_ACCESSIBILITY_TIM_VERSION = false
const val EARLIEST_QQNT_FRAMEWORK_QQ_VERSION_STABLE = "8.9.63"
const val EARLIEST_QQNT_FRAMEWORK_TIM_VERSION_STABLE = "4.0.0"
const val EARLIEST_UNREAL_ENGINE_QQ_VERSION_STABLE = "8.8.55"
const val EARLIEST_KUIKLY_FRAMEWORK_QQ_VERSION_STABLE = "8.9.50"
const val EARLIEST_KUIKLY_FRAMEWORK_TIM_VERSION_STABLE = "4.0.0"
const val ZHIPU_TOKEN = "ZhipuAIMaaSPlatformToken"
const val GITHUB_TOKEN = "GitHubPersonalAccessToken"
}
override fun onCreate() {
// Android 12+ 动态颜色
DynamicColors.applyToActivitiesIfAvailable(this)
instance = this
super.onCreate()
Security.addProvider(KonaCryptoProvider()) // 腾讯 Kona 国密套件需要在应用启动时添加 Provider
}
}
================================================
FILE: app/src/main/java/com/xiaoniu/qqversionlist/data/LocalAppStackResult.kt
================================================
// SPDX-License-Identifier: AGPL-3.0-or-later
/*
Qverbow Util
Copyright (C) 2023 klxiaoniu
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.xiaoniu.qqversionlist.data
data class LocalAppStackResult(
val id: String,
val dex: String
)
================================================
FILE: app/src/main/java/com/xiaoniu/qqversionlist/data/LocalAppStackRule.kt
================================================
// SPDX-License-Identifier: AGPL-3.0-or-later
/*
Qverbow Util
Copyright (C) 2023 klxiaoniu
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.xiaoniu.qqversionlist.data
data class LocalAppStackRule(
val id: String,
val dex: Array<String>,
val type: String,
val url: String? = null,
val desc: String? = null
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as LocalAppStackRule
if (id != other.id) return false
if (!dex.contentEquals(other.dex)) return false
if (type != other.type) return false
if (url != other.url) return false
if (desc != other.desc) return false
return true
}
override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + dex.contentHashCode()
result = 31 * result + type.hashCode()
result = 31 * result + (url?.hashCode() ?: 0)
result = 31 * result + (desc?.hashCode() ?: 0)
return result
}
}
================================================
FILE: app/src/main/java/com/xiaoniu/qqversionlist/data/QQVersionBean.kt
================================================
// SPDX-License-Identifier: AGPL-3.0-or-later
/*
Qverbow Util
Copyright (C) 2023 klxiaoniu
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.xiaoniu.qqversionlist.data
import kotlinx.serialization.Serializable
/**
* @param versions QQ 版本代码
* @param versionNumber QQ 版本号
* @param size QQ 版本额定大小
* @param featureTitle QQ 版本特性标题
* @param imgs QQ 应用宣传图片列表
* @param summary QQ 版本特性描述列表
* @param jsonString 该 QQ 版本 JSON 字符串详情
* @param displayType 卡片展示类型,0 为收起态,1 为展开态
* @param displayInstall 展示是否安装到本机的标签
* @param isQQNTFramework 该版本是否基于 QQNT 技术架构
* @param isUnrealEngine 该版本是否存在 Unreal Engine Lib
* @param isKuiklyInside 该版本是否存在 TDS 腾讯端服务 Kukily 开发框架
*/
@Serializable
data class QQVersionBean(
val versions: String,
val versionNumber: String,
val size: String,
val featureTitle: String,
val imgs: List<String>,
val summary: List<String>,
var jsonString: String = "",
var displayType: Int = 0, // 0为收起
var displayInstall: Boolean = false, // false 为不展示
var isAccessibility: Boolean = false,
var isQQNTFramework: Boolean = false,
var isUnrealEngine: Boolean = false,
var isKuiklyInside: Boolean = false
)
================================================
FILE: app/src/main/java/com/xiaoniu/qqversionlist/data/QverbowRelease.kt
================================================
// SPDX-License-Identifier: AGPL-3.0-or-later
/*
Qverbow Util
Copyright (C) 2023 klxiaoniu
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.xiaoniu.qqversionlist.data
import java.util.Date
data class QverbowRelease(
val tagName: String,
val name: String?,
val body: String?,
val createdAt: Date,
val htmlUrl: String,
val zipballUrl: String?,
val tarballUrl: String?,
val isDraft: Boolean,
val isPrerelease: Boolean,
val assets: List<QverbowReleaseAssets>?
)
================================================
FILE: app/src/main/java/com/xiaoniu/qqversionlist/data/QverbowReleaseAssets.kt
================================================
// SPDX-License-Identifier: AGPL-3.0-or-later
/*
Qverbow Util
Copyright (C) 2023 klxiaoniu
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.xiaoniu.qqversionlist.data
class QverbowReleaseAssets(
val name: String, val browserDownloadUrl: String, val contentType: String, val size: Long
)
================================================
FILE: app/src/main/java/com/xiaoniu/qqversionlist/data/TIMVersionBean.kt
================================================
// SPDX-License-Identifier: AGPL-3.0-or-later
/*
Qverbow Util
Copyright (C) 2023 klxiaoniu
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.xiaoniu.qqversionlist.data
import kotlinx.serialization.Serializable
/**
* @param version TIM 版本号
* @param datetime TIM 版本发布日期
* @param fix TIM 版本优化描述
* @param feature TIM 版本新功能描述
* @param link TIM 最新官网正式版下载链接
* @param jsonString 该 TIM 版本 JSON 字符串详情
* @param displayType 卡片展示类型,0 为收起态,1 为展开态
* @param displayInstall 展示是否安装到本机的标签
* @param isQQNTFramework 该版本是否基于 QQNT 技术架构
* @param isKuiklyInside 该版本是否存在 TDS 腾讯端服务 Kukily 开发框架
*/
@Serializable
data class TIMVersionBean(
val version: String,
val datetime: String,
val fix: List<String>,
val feature: List<String>,
var link: String = "",
var jsonString: String = "",
var displayType: Int = 0, // 0为收起
var displayInstall: Boolean = false, // false 为不展示
var isAccessibility: Boolean = false,
var isQQNTFramework: Boolean = false,
var isKuiklyInside: Boolean = false
)
================================================
FILE: app/src/main/java/com/xiaoniu/qqversionlist/data/WeixinVersionBean.kt
================================================
// SPDX-License-Identifier: AGPL-3.0-or-later
/*
Qverbow Util
Copyright (C) 2023 klxiaoniu
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.xiaoniu.qqversionlist.data
data class WeixinVersionBean(
val version: String,
val datetime: String,
val isAlpha: Boolean = false,
var displayInstall: Boolean = false, // false 为不展示
var displayType: Int = 0, // 0为收起
var link: String = "",
)
================================================
FILE: app/src/main/java/com/xiaoniu/qqversionlist/service/FirebaseMessagingService.kt
================================================
// SPDX-License-Identifier: AGPL-3.0-or-later
/*
Qverbow Util
Copyright (C) 2023 klxiaoniu
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.xiaoniu.qqversionlist.service
import android.app.NotificationManager
import androidx.core.app.NotificationCompat
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import com.xiaoniu.qqversionlist.R
class FirebaseMessagingService : FirebaseMessagingService() {
override fun onNewToken(token: String) {
super.onNewToken(token)
}
override fun onMessageReceived(remoteMessage: RemoteMessage) {
if (!remoteMessage.data.isNotEmpty()) {
// 创建 Notification
val notificationBuilder = NotificationCompat.Builder(
this, getString(R.string.rainbow_notification_channel_id)
).setSmallIcon(R.drawable.qv_logo_notification)
.setContentTitle(remoteMessage.notification!!.title)
.setContentText(remoteMessage.notification!!.body)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(null)
.setAutoCancel(true)
// 获取 NotificationManager 并发送通知
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
notificationManager.notify(1, notificationBuilder.build())
}
}
}
================================================
FILE: app/src/main/java/com/xiaoniu/qqversionlist/ui/ExpUrlListAdapter.kt
================================================
// SPDX-License-Identifier: AGPL-3.0-or-later
/*
Qverbow Util
Copyright (C) 2023 klxiaoniu
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.xiaoniu.qqversionlist.ui
import android.content.Intent
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.card.MaterialCardView
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.xiaoniu.qqversionlist.R
import com.xiaoniu.qqversionlist.databinding.ExpLinkNextButtonBinding
import com.xiaoniu.qqversionlist.databinding.ItemExpBackUrlCardBinding
import com.xiaoniu.qqversionlist.util.ClipboardUtil.copyText
import com.xiaoniu.qqversionlist.util.FileUtil.downloadFile
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import okhttp3.Request
class ExpUrlListAdapter(private val urlList: List<String>) :
RecyclerView.Adapter<ExpUrlListAdapter.ExpUrlViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ExpUrlViewHolder {
val binding =
ItemExpBackUrlCardBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ExpUrlViewHolder(binding)
}
inner class ExpUrlViewHolder(binding: ItemExpBackUrlCardBinding) :
RecyclerView.ViewHolder(binding.root) {
val expUrlText = binding.expUrlText
var currentUrl: String? = null
init {
val expUrlCard = itemView.findViewById<MaterialCardView>(R.id.exp_url_card)
expUrlCard.setOnClickListener {
currentUrl?.let { url ->
CoroutineScope(Dispatchers.IO).launch {
var appSize = ""
try {
val okHttpClient = OkHttpClient()
val request = Request.Builder().url(url).head().build()
val response = okHttpClient.newCall(request).execute()
appSize = "%.2f".format(
response.header("Content-Length")?.toDoubleOrNull()
?.div(1024 * 1024)
)
} catch (_: Exception) {
} finally {
withContext(Dispatchers.Main) {
val expLinkNextButtonBinding =
ExpLinkNextButtonBinding.inflate(
LayoutInflater.from(itemView.context)
)
expLinkNextButtonBinding.root.parent?.let { parent ->
if (parent is ViewGroup) parent.removeView(
expLinkNextButtonBinding.root
)
}
val expNextMaterialDialog =
MaterialAlertDialogBuilder(itemView.context)
.setTitle(R.string.additionalActions)
.setIcon(R.drawable.flask_line)
.setView(expLinkNextButtonBinding.root).apply {
if (appSize != "" && appSize != "-1" && appSize != "0") setMessage(
"${itemView.context.getString(R.string.downloadLink)}$url\n\n${
itemView.context.getString(
R.string.fileSize
)
}$appSize MB"
)
else setMessage("${itemView.context.getString(R.string.downloadLink)}$url")
}.show()
expLinkNextButtonBinding.apply {
expNextBtnCopy.setOnClickListener {
expNextMaterialDialog.dismiss()
itemView.context.copyText(url)
}
expNextBtnDownload.setOnClickListener {
expNextMaterialDialog.dismiss()
downloadFile(itemView.context, url)
}
expNextBtnShare.setOnClickListener {
expNextMaterialDialog.dismiss()
val shareIntent = Intent(Intent.ACTION_SEND).apply {
type = "text/plain"
putExtra(Intent.EXTRA_TEXT, url)
}
itemView.context.startActivity(
Intent.createChooser(
shareIntent,
itemView.context.getString(R.string.shareTo)
)
)
}
}
}
}
}
}
}
expUrlCard.setOnLongClickListener {
currentUrl?.let { url ->
itemView.context.copyText(url)
}
true
}
}
}
override fun onBindViewHolder(holder: ExpUrlViewHolder, position: Int) {
val currentUrl = urlList[position]
holder.expUrlText.text = currentUrl
holder.currentUrl = currentUrl
}
override fun getItemCount(): Int {
return urlList.size
}
}
================================================
FILE: app/src/main/java/com/xiaoniu/qqversionlist/ui/LocalAppDetailsActivity.kt
================================================
// SPDX-License-Identifier: AGPL-3.0-or-later
/*
Qverbow Util
Copyright (C) 2023 klxiaoniu
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.xiaoniu.qqversionlist.ui
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Build.VERSION.SDK_INT
import android.os.Bundle
import android.provider.OpenableColumns
import android.view.LayoutInflater
import android.widget.TextView
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.core.content.IntentCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isVisible
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import coil3.load
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.progressindicator.LinearProgressIndicator
import com.google.gson.GsonBuilder
import com.google.gson.JsonObject
import com.xiaoniu.qqversionlist.QverbowApplication.Companion.ZHIPU_TOKEN
import com.xiaoniu.qqversionlist.R
import com.xiaoniu.qqversionlist.data.LocalAppStackResult
import com.xiaoniu.qqversionlist.databinding.ActivityLocalAppDetailsBinding
import com.xiaoniu.qqversionlist.databinding.DialogChangesLlmInferenceBinding
import com.xiaoniu.qqversionlist.databinding.DialogLocalQqTimInfoBinding
import com.xiaoniu.qqversionlist.ui.LocalAppDetailsActivityViewModel.Companion.DEX_PRE_RULES
import com.xiaoniu.qqversionlist.ui.LocalAppDetailsActivityViewModel.Companion.RULES_ID_ORDER
import com.xiaoniu.qqversionlist.ui.LocalAppDetailsActivityViewModel.Companion.RULE_TYPE_OPEN_SOURCE_3RD_PARTY
import com.xiaoniu.qqversionlist.ui.LocalAppDetailsActivityViewModel.Companion.RULE_TYPE_OTEAM_TENCENT
import com.xiaoniu.qqversionlist.ui.LocalAppDetailsActivityViewModel.Companion.RULE_TYPE_PRIVATE_TENCENT
import com.xiaoniu.qqversionlist.ui.MainActivity.Companion.JUDGE_UA_TARGET
import com.xiaoniu.qqversionlist.ui.theme.QQVersionListTheme
import com.xiaoniu.qqversionlist.util.ClipboardUtil.copyText
import com.xiaoniu.qqversionlist.util.DataStoreUtil
import com.xiaoniu.qqversionlist.util.InfoUtil.dialogError
import com.xiaoniu.qqversionlist.util.InfoUtil.openUrlWithChromeCustomTab
import com.xiaoniu.qqversionlist.util.InfoUtil.showToast
import com.xiaoniu.qqversionlist.util.KeyStoreUtil
import com.xiaoniu.qqversionlist.util.StringUtil.pangu
import com.xiaoniu.qqversionlist.util.ZhipuSDKUtil.getZhipuWrite
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.apache.commons.io.FileUtils
import java.io.File
import java.net.SocketTimeoutException
import java.nio.file.FileSystems
import java.util.Locale
import kotlin.collections.sortedWith
class LocalAppDetailsActivity : AppCompatActivity() {
lateinit var viewModel: LocalAppDetailsActivityViewModel
lateinit var binding: ActivityLocalAppDetailsBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
// 不加这段代码的话 Google 可能会在系统栏加遮罩
if (SDK_INT >= Build.VERSION_CODES.Q) window.isNavigationBarContrastEnforced = false
setContentView(R.layout.activity_local_app_details)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
if (DataStoreUtil.getIntKV("userAgreement", 0) < JUDGE_UA_TARGET) {
showToast(R.string.haveNotAgreeUA)
finish()
}
binding = ActivityLocalAppDetailsBinding.inflate(layoutInflater)
val localInterChangesLlmGen = DataStoreUtil.getBooleanKV("localInterChangesLlmGen", false)
val viewRoot = binding.root
setContentView(viewRoot)
viewModel = ViewModelProvider(this)[LocalAppDetailsActivityViewModel::class.java]
binding.apply {
topAppBar.setNavigationOnClickListener {
finish()
}
progressLine.apply {
showAnimationBehavior = LinearProgressIndicator.SHOW_NONE
hideAnimationBehavior = LinearProgressIndicator.HIDE_ESCAPE
}
viewModel.apply {
isAIShowing.observe(this@LocalAppDetailsActivity) { isAIShowing ->
if (isAIShowing && localInterChangesLlmGen) {
floatingActionButtonLlm.setOnClickListener {
val dialogChangesLlmInferenceBinding =
DialogChangesLlmInferenceBinding.inflate(
layoutInflater
)
val changeText =
activityDiff.value + "\n\n" + serviceDiff.value + "\n\n" + receiverDiff.value + "\n\n" + providerDiff.value + "\n\n" + permissionDiff.value
val versionChange =
"${appName.value} ${localVersionNameWithInter.value} → ${versionName.value}"
CoroutineScope(
Dispatchers.IO
).launch {
try {
val token = KeyStoreUtil.getStringKVwithKeyStore(ZHIPU_TOKEN)
val tokenIsNullOrEmpty = token.isNullOrEmpty()
if (!tokenIsNullOrEmpty) {
runOnUiThread { viewModel.setChangesBackLLMWorking(true) }
val llmResponse = getZhipuWrite(
getString(
R.string.llmInferenceLocalChangesPrompt,
appName.value,
localVersionNameWithInter.value,
versionName.value,
Locale.getDefault().toString()
),
versionChange + "\n\n" + changeText,
token
)
val gson = GsonBuilder().setPrettyPrinting().create()
val responseObject = gson.fromJson(
llmResponse, JsonObject::class.java
)
runOnUiThread {
if (responseObject.getAsJsonPrimitive("code").asInt == 200) {
val zhipuContent =
responseObject.getAsJsonObject("data").asJsonObject.getAsJsonArray(
"choices"
).asJsonArray.first().asJsonObject.getAsJsonObject(
"message"
).asJsonObject.getAsJsonPrimitive(
"content"
).asString
viewModel.setChangesBackLLMGenText(zhipuContent.pangu())
} else {
val zhipuContent =
responseObject.getAsJsonPrimitive("msg").asString + getString(
R.string.colon
) + responseObject.getAsJsonObject("error").asJsonObject.getAsJsonPrimitive(
"message"
).asString
viewModel.setChangesBackLLMGenText(zhipuContent)
}
viewModel.setChangesBackLLMWorking(false)
}
} else runOnUiThread {
viewModel.setChangesBackLLMWorking(false)
viewModel.setChangesBackLLMGenText(getString(R.string.zhipuTokenIsNull))
}
} catch (_: SocketTimeoutException) {
runOnUiThread {
viewModel.setChangesBackLLMWorking(false)
viewModel.setChangesBackLLMGenText(getString(R.string.timeout))
}
} catch (e: Exception) {
runOnUiThread {
e.printStackTrace()
dialogError(e)
}
}
}
val dialogChangesLlmInference =
MaterialAlertDialogBuilder(this@LocalAppDetailsActivity).setTitle(R.string.llmInferenceLocalChanges)
.setView(dialogChangesLlmInferenceBinding.root)
.setIcon(R.drawable.ai_generate_2).show()
dialogChangesLlmInferenceBinding.versionChangeInfo.text = versionChange
viewModel.isChangesBackLLMWorking.observe(this@LocalAppDetailsActivity,
Observer { isWorking ->
if (DataStoreUtil.getBooleanKV("updateLogLlmGen", false)) {
dialogChangesLlmInferenceBinding.llmGenCard.isVisible =
!isWorking && viewModel.changesBackLLMGenText.value.toString()
.isNotEmpty()
dialogChangesLlmInferenceBinding.progressIndicator.isVisible =
isWorking
dialogChangesLlmInferenceBinding.btnCopy.isVisible =
!isWorking && viewModel.changesBackLLMGenText.value.toString()
.isNotEmpty()
dialogChangesLlmInferenceBinding.btnCopy.setOnClickListener {
copyText(
viewModel.changesBackLLMGenText.value.toString() + "\n\n" + getString(
R.string.genByAITips
)
)
}
} else {
dialogChangesLlmInferenceBinding.llmGenCard.isVisible =
false
dialogChangesLlmInferenceBinding.progressIndicator.isVisible =
false
dialogChangesLlmInferenceBinding.btnCopy.isVisible = false
dialogChangesLlmInferenceBinding.btnCopy.setOnClickListener(
null
)
}
})
viewModel.changesBackLLMGenText.observe(this@LocalAppDetailsActivity,
Observer { text ->
dialogChangesLlmInferenceBinding.llmGenText.text = text
})
dialogChangesLlmInferenceBinding.btnOk.setOnClickListener {
dialogChangesLlmInference.dismiss()
}
}
floatingActionButtonLlm.show()
} else {
floatingActionButtonLlm.setOnClickListener(null)
floatingActionButtonLlm.hide()
}
}
localAppStackResults.observe(this@LocalAppDetailsActivity) { result ->
stackInfoList.setContent {
QQVersionListTheme { LocalAppDetailsStackWindow(result) }
}
}
appIconImage.observe(this@LocalAppDetailsActivity) { appIconImage ->
if (appIconImage != null) {
localIcon.isVisible = true
localIcon.load(appIconImage)
} else localIcon.isVisible = false
}
isLoading.observe(this@LocalAppDetailsActivity) { isLoading ->
if (isLoading) {
progressLine.show()
detailInfo.isVisible = false
detailInfo.setOnClickListener(null)
} else {
progressLine.hide()
if (isWeixin.value == false && isErr.value == false) {
detailInfo.isVisible = true
detailInfo.setOnClickListener {
val localInfoAllText = (if (targetSDK.value != 0) "Target SDK${
getString(R.string.colon)
}${targetSDK.value}" else "") + (if (minSDK.value != 0) "\nMin SDK${
getString(R.string.colon)
}${minSDK.value}" else "") + (if (compileSDK.value != 0) "\nCompile SDK${
getString(R.string.colon)
}${
compileSDK.value
}" else "") + "\nVersion Name${getString(R.string.colon)}${
versionName.value
}" + (if (rdmUUID.value != "") "\nRdm UUID${
getString(R.string.colon)
}${
rdmUUID.value
}" else "") + (if (versionCode.value != "") "\nVersion Code${
getString(R.string.colon)
}${
versionCode.value
}" else "") + (if (appSettingParams.value != "") "\nAppSetting_params${
getString(R.string.colon)
}${
appSettingParams.value
}" else "") + (if (appSettingParamsPad.value != "") "\nAppSetting_params_pad${
getString(R.string.colon)
}${
appSettingParamsPad.value
}" else "") + (if (qua.value != "") "\nQUA${
getString(R.string.colon)
}${qua.value}" else "")
val dialogLocalQqTimInfoBinding =
DialogLocalQqTimInfoBinding.inflate(
LayoutInflater.from(this@LocalAppDetailsActivity)
)
MaterialAlertDialogBuilder(this@LocalAppDetailsActivity).setView(
dialogLocalQqTimInfoBinding.root
).setTitle(R.string.localDetailsMsg)
.setIcon(R.drawable.phone_find_line).apply {
dialogLocalQqTimInfoBinding.apply {
val dialogLocalSdkDesc = localSDKText.value
dialogLocalSdk.apply {
setCellDescription(dialogLocalSdkDesc)
this.setOnClickListener {
context.copyText(
"Android SDK${
getString(R.string.colon)
}$dialogLocalSdkDesc"
)
}
}
dialogLocalVersionName.apply {
setCellDescription(versionName.value)
this.setOnClickListener {
context.copyText(
"Version Name${
getString(R.string.colon)
}${versionName.value}"
)
}
}
dialogLocalRdmUuid.apply {
if (rdmUUID.value != "") {
setCellDescription(rdmUUID.value)
this.setOnClickListener {
context.copyText(
"Rdm UUID${
getString(R.string.colon)
}${rdmUUID.value}"
)
}
} else dialogLocalRdmUuid.isVisible = false
}
dialogLocalVersionCode.apply {
if (versionCode.value != "") {
setCellDescription(versionCode.value)
this.setOnClickListener {
context.copyText(
"Version Code${
getString(R.string.colon)
}${versionCode.value}"
)
}
} else dialogLocalVersionCode.isVisible = false
}
dialogLocalAppsettingParams.apply {
if (appSettingParams.value != "") {
setCellDescription(appSettingParams.value)
this.setOnClickListener {
context.copyText(
"AppSetting_params${
getString(R.string.colon)
}${appSettingParams.value}"
)
}
} else dialogLocalAppsettingParams.isVisible = false
}
dialogLocalAppsettingParamsPad.apply {
if (appSettingParamsPad.value != "") {
setCellDescription(appSettingParamsPad.value)
this.setOnClickListener {
context.copyText(
"AppSetting_params_pad${
getString(R.string.colon)
}${appSettingParamsPad.value}"
)
}
} else dialogLocalAppsettingParamsPad.isVisible =
false
}
dialogLocalQua.apply {
if (qua.value != "") {
setCellDescription(qua.value)
this.setOnClickListener {
context.copyText(
"QUA${
getString(
R.string.colon
)
}${qua.value}"
)
}
} else dialogLocalQua.isVisible = false
}
dialogLocalCopyAll.setOnClickListener {
context.copyText(localInfoAllText)
}
}
}.show()
}
} else {
detailInfo.isVisible = false
detailInfo.setOnClickListener(null)
}
}
}
channelText.observe(this@LocalAppDetailsActivity) { text ->
if (text == "" || isWeixin.value == true) binding.localChannelCard.isVisible =
false else {
localChannelCard.isVisible = true
localChannelText.text = text
}
}
isErr.observe(this@LocalAppDetailsActivity) { isErr ->
stackInfo.isVisible = !isErr
}
observeString(appName, binding.localName)
observeString(localVersion, binding.localVersion)
observeString(localSDKText, binding.localSdk)
observeStringWithVisible(isTIM, timBasedVer, binding.localTimBase)
}
}
try {
val intent = intent
when {
intent.hasExtra("localAppType") -> {
val localAppType = intent.getStringExtra("localAppType")
if (localAppType == "QQ" || localAppType == "TIM" || localAppType == "Weixin") viewModel.getInfo(
this, localAppType
)
if (localAppType == "QQ") binding.topAppBar.title =
getString(R.string.localQQVersionDetails)
else if (localAppType == "TIM") binding.topAppBar.title =
getString(R.string.localTIMVersionDetails)
else if (localAppType == "Weixin") binding.topAppBar.title =
getString(R.string.localWeixinVersionDetails)
}
intent.action == Intent.ACTION_SEND || intent.action == Intent.ACTION_VIEW -> {
binding.topAppBar.title = getString(R.string.apkAnalyze)
val action = intent.action
val uri = when (action) {
Intent.ACTION_SEND -> IntentCompat.getParcelableExtra(
intent, Intent.EXTRA_STREAM, Uri::class.java
)
Intent.ACTION_VIEW -> intent.data
else -> null
}
if (uri != null) {
val path = uri.path
if (path != null) {
val normalizedPath =
if (SDK_INT >= Build.VERSION_CODES.O) FileSystems.getDefault()
.getPath(path).normalize()
.toString() else File(path).canonicalPath
if (!normalizedPath.startsWith("/data")) {
val cacheDir = File(cacheDir, "apkAnalysis")
if (cacheDir.exists()) cacheDir.deleteRecursively()
cacheDir.mkdirs()
val requiredSpace = 500 * 1024 * 1024L // 500MB
val freeSpace = cacheDir.freeSpace
val fileSize = getFileSizeFromUri(uri)
if (freeSpace >= requiredSpace + if (fileSize != -1L) fileSize else 0) {
val destinationFile = File(cacheDir, "temp_apk.apk")
contentResolver.openInputStream(uri)?.use { inputStream ->
FileUtils.copyInputStreamToFile(
inputStream, destinationFile
)
}
viewModel.getInfo(this, "inter", destinationFile.path)
} else viewModel.setAppName(getString(R.string.notEnoughSpaceOnTheDevice))
} else viewModel.setAppName(getString(R.string.unknownErr))
} else viewModel.setAppName(getString(R.string.unknownErr))
} else viewModel.setAppName(getString(R.string.unknownErr))
}
else -> {
viewModel.setAppName(getString(R.string.unknownErr))
binding.topAppBar.apply {
title = getString(R.string.app_name)
subtitle = null
}
}
}
} catch (e: Exception) {
e.printStackTrace()
dialogError(e)
}
}
override fun onDestroy() {
super.onDestroy()
viewModel.cleanCache(this)
}
private fun observeString(liveData: LiveData<String>, textView: TextView) {
liveData.observe(this@LocalAppDetailsActivity) { text ->
textView.text = text
}
}
private fun observeStringWithVisible(
liveDataBoolean: LiveData<Boolean>, liveDataString: LiveData<String>, textView: TextView
) {
liveDataBoolean.observe(this@LocalAppDetailsActivity) { boolean ->
liveDataString.observe(this@LocalAppDetailsActivity) { text ->
textView.isVisible = text != "" && boolean
textView.text = text
}
}
}
private fun showStackDescDialog(
titleRes: Int, messageRes: Int, url: String?, iconRes: Int? = null
) {
MaterialAlertDialogBuilder(this@LocalAppDetailsActivity).setTitle(titleRes)
.setMessage(messageRes).setPositiveButton(R.string.done) { _, _ -> }.apply {
if (iconRes != null) setIcon(iconRes)
if (url != null) setNeutralButton(R.string.details) { _, _ ->
openUrlWithChromeCustomTab(url)
}
}.show()
}
private fun getFileSizeFromUri(uri: Uri): Long {
return try {
contentResolver.query(uri, null, null, null, null)?.use { cursor ->
val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE)
cursor.moveToFirst()
if (sizeIndex != -1) cursor.getLong(sizeIndex) else -1L
} ?: -1L
} catch (_: Exception) {
-1L
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun LocalAppDetailsStackWindow(
result: MutableList<LocalAppStackResult>
) {
Column {
(if (result.isEmpty()) mutableListOf() else result).sortedWith(compareBy<LocalAppStackResult> {
if (RULES_ID_ORDER.indexOf(it.id) == -1) Int.MAX_VALUE
else RULES_ID_ORDER.indexOf(it.id)
}.thenComparing(compareBy<LocalAppStackResult> { it.id.lowercase() })
).forEach { item -> StackItem(item = item) }
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun StackItem(item: LocalAppStackResult) {
Card(modifier = Modifier
.fillMaxWidth()
.padding(5.dp), colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceContainerLow,
contentColor = MaterialTheme.colorScheme.onSurfaceVariant
), onClick = {
when (item.id) {
LocalAppDetailsActivityViewModel.RULE_ID_QQNT -> showStackDescDialog(
R.string.localDetailsQQNT,
R.string.localDetailsQQNTDesc,
DEX_PRE_RULES.find { it.id == item.id }?.url,
R.drawable.qqnt_logo_unofficial_fix
)
LocalAppDetailsActivityViewModel.RULE_ID_BUGLY -> showStackDescDialog(
R.string.localDetailsBugly,
R.string.localDetailsBuglyDesc,
DEX_PRE_RULES.find { it.id == item.id }?.url,
R.drawable.bugly_official
)
LocalAppDetailsActivityViewModel.RULE_ID_UE_LIBRARY -> showStackDescDialog(
R.string.localDetailsUELibrary,
R.string.localDetailsUELibraryDesc,
DEX_PRE_RULES.find { it.id == item.id }?.url,
R.drawable.ue_icon_2023_black
)
LocalAppDetailsActivityViewModel.RULE_ID_HIPPY -> showStackDescDialog(
R.string.localDetailsHippy,
R.string.localDetailsHippyDesc,
DEX_PRE_RULES.find { it.id == item.id }?.url,
R.drawable.hippy_official
)
LocalAppDetailsActivityViewModel.RULE_ID_KUIKLY -> showStackDescDialog(
R.string.localDetailsKuikly,
R.string.localDetailsKuiklyDesc,
DEX_PRE_RULES.find { it.id == item.id }?.url,
R.drawable.kuikly_official
)
LocalAppDetailsActivityViewModel.RULE_ID_SHIPLY -> showStackDescDialog(
R.string.localDetailsShiply,
R.string.localDetailsShiplyDesc,
DEX_PRE_RULES.find { it.id == item.id }?.url,
R.drawable.shiply_official
)
LocalAppDetailsActivityViewModel.RULE_ID_RIGHTLY -> showStackDescDialog(
R.string.localDetailsRightly,
R.string.localDetailsRightlyDesc,
DEX_PRE_RULES.find { it.id == item.id }?.url,
R.drawable.rightly_official
)
LocalAppDetailsActivityViewModel.RULE_ID_TENCENT_BEACON -> showStackDescDialog(
R.string.localDetailsTencentBeacon,
R.string.localDetailsTencentBeaconDesc,
DEX_PRE_RULES.find { it.id == item.id }?.url,
R.drawable.beacon_official
)
LocalAppDetailsActivityViewModel.RULE_ID_COMPOSE_MULTIPLATFORM -> showStackDescDialog(
R.string.localDetailsComposeMultiplatform,
R.string.localDetailsComposeMultiplatformDesc,
DEX_PRE_RULES.find { it.id == item.id }?.url,
R.drawable.compose
)
LocalAppDetailsActivityViewModel.RULE_ID_JETPACK_COMPOSE -> showStackDescDialog(
R.string.localDetailsJetpackCompose,
R.string.localDetailsJetpackComposeDesc,
DEX_PRE_RULES.find { it.id == item.id }?.url,
R.drawable.compose
)
LocalAppDetailsActivityViewModel.RULE_ID_FLUTTER -> showStackDescDialog(
R.string.localDetailsFlutter,
R.string.localDetailsFlutterDesc,
DEX_PRE_RULES.find { it.id == item.id }?.url,
R.drawable.flutter_line
)
LocalAppDetailsActivityViewModel.RULE_ID_MMKV -> showStackDescDialog(
R.string.localDetailsMMKV,
R.string.localDetailsMMKVDesc,
DEX_PRE_RULES.find { it.id == item.id }?.url,
R.drawable.oteam_official
)
LocalAppDetailsActivityViewModel.RULE_ID_WCDB -> showStackDescDialog(
R.string.localDetailsWCDB,
R.string.localDetailsWCDBDesc,
DEX_PRE_RULES.find { it.id == item.id }?.url,
R.drawable.oteam_official
)
LocalAppDetailsActivityViewModel.RULE_ID_MARS -> showStackDescDialog(
R.string.localDetailsMars,
R.string.localDetailsMarsDesc,
DEX_PRE_RULES.find { it.id == item.id }?.url,
R.drawable.oteam_official
)
LocalAppDetailsActivityViewModel.RULE_ID_MATRIX -> showStackDescDialog(
R.string.localDetailsMatrix,
R.string.localDetailsMatrixDesc,
DEX_PRE_RULES.find { it.id == item.id }?.url,
R.drawable.oteam_official
)
LocalAppDetailsActivityViewModel.RULE_ID_TINKER -> showStackDescDialog(
R.string.localDetailsTinker,
R.string.localDetailsTinkerDesc,
DEX_PRE_RULES.find { it.id == item.id }?.url,
R.drawable.oteam_official
)
LocalAppDetailsActivityViewModel.RULE_ID_REACT_NATIVE -> showStackDescDialog(
R.string.localDetailsReactNative,
R.string.localDetailsReactNativeDesc,
DEX_PRE_RULES.find { it.id == item.id }?.url,
R.drawable.reactjs_line
)
LocalAppDetailsActivityViewModel.RULE_ID_TENCENT_BROWSING_SERVICE -> showStackDescDialog(
R.string.localDetailsTBS,
R.string.localDetailsTBSDesc,
DEX_PRE_RULES.find { it.id == item.id }?.url,
R.drawable.tencent_logo
)
LocalAppDetailsActivityViewModel.RULE_ID_SKYLINE_RENDERING_ENGINE -> showStackDescDialog(
R.string.localDetailsSkylineRenderingEngine,
R.string.localDetailsSkylineRenderingEngineDesc,
DEX_PRE_RULES.find { it.id == item.id }?.url,
R.drawable.mini_program_line
)
else -> null
}
}) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Image(
painter = painterResource(
id = getIconResId(item.id)
),
contentDescription = null,
modifier = Modifier
.size(32.dp)
.padding(start = 4.dp, end = 4.dp),
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.primary),
)
Column(
modifier = Modifier
.weight(1f)
.padding(start = 6.dp, end = 6.dp)
) {
Text(
text = getItemTitle(item.id),
style = MaterialTheme.typography.titleSmall,
color = MaterialTheme.colorScheme.onSurface
)
Text(
text = stringResource(id = R.string.thisVerContains, item.dex),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(top = 4.dp)
)
}
Image(
painter = painterResource(id = R.drawable.arrow_right_s_line),
contentDescription = null,
modifier = Modifier.size(24.dp)
)
}
}
}
@Composable
private fun getIconResId(id: String): Int {
return when (id) {
LocalAppDetailsActivityViewModel.RULE_ID_QQNT -> R.drawable.qqnt_logo_unofficial_fix
LocalAppDetailsActivityViewModel.RULE_ID_BUGLY -> R.drawable.bugly_official
LocalAppDetailsActivityViewModel.RULE_ID_SHIPLY -> R.drawable.shiply_official
LocalAppDetailsActivityViewModel.RULE_ID_KUIKLY -> R.drawable.kuikly_official
LocalAppDetailsActivityViewModel.RULE_ID_HIPPY -> R.drawable.hippy_official
LocalAppDetailsActivityViewModel.RULE_ID_RIGHTLY -> R.drawable.rightly_official
LocalAppDetailsActivityViewModel.RULE_ID_UE_LIBRARY -> R.drawable.ue_icon_2023_black
LocalAppDetailsActivityViewModel.RULE_ID_TENCENT_BEACON -> R.drawable.beacon_official
LocalAppDetailsActivityViewModel.RULE_ID_JETPACK_COMPOSE -> R.drawable.compose
LocalAppDetailsActivityViewModel.RULE_ID_COMPOSE_MULTIPLATFORM -> R.drawable.compose
LocalAppDetailsActivityViewModel.RULE_ID_FLUTTER -> R.drawable.flutter_line
LocalAppDetailsActivityViewModel.RULE_ID_REACT_NATIVE -> R.drawable.reactjs_line
LocalAppDetailsActivityViewModel.RULE_ID_TENCENT_BROWSING_SERVICE -> R.drawable.tencent_logo
LocalAppDetailsActivityViewModel.RULE_ID_SKYLINE_RENDERING_ENGINE -> R.drawable.mini_program_line
else -> when (DEX_PRE_RULES.find { it.id == id }?.type) {
RULE_TYPE_PRIVATE_TENCENT -> R.drawable.tencent_logo
RULE_TYPE_OPEN_SOURCE_3RD_PARTY -> R.drawable.open_source_line
RULE_TYPE_OTEAM_TENCENT -> R.drawable.oteam_official
else -> R.drawable.tools_line
}
}
}
@Composable
private fun getItemTitle(id: String): String {
return when (id) {
LocalAppDetailsActivityViewModel.RULE_ID_QQNT -> stringResource(R.string.localDetailsQQNT)
LocalAppDetailsActivityViewModel.RULE_ID_BUGLY -> stringResource(R.string.localDetailsBugly)
LocalAppDetailsActivityViewModel.RULE_ID_SHIPLY -> stringResource(R.string.localDetailsShiply)
LocalAppDetailsActivityViewModel.RULE_ID_KUIKLY -> stringResource(R.string.localDetailsKuikly)
LocalAppDetailsActivityViewModel.RULE_ID_HIPPY -> stringResource(R.string.localDetailsHippy)
LocalAppDetailsActivityViewModel.RULE_ID_RIGHTLY -> stringResource(R.string.localDetailsRightly)
LocalAppDetailsActivityViewModel.RULE_ID_UE_LIBRARY -> stringResource(R.string.localDetailsUELibrary)
LocalAppDetailsActivityViewModel.RULE_ID_TENCENT_BEACON -> stringResource(R.string.localDetailsTencentBeacon)
LocalAppDetailsActivityViewModel.RULE_ID_JETPACK_COMPOSE -> stringResource(R.string.localDetailsJetpackCompose)
LocalAppDetailsActivityViewModel.RULE_ID_COMPOSE_MULTIPLATFORM -> stringResource(R.string.localDetailsComposeMultiplatform)
LocalAppDetailsActivityViewModel.RULE_ID_FLUTTER -> stringResource(R.string.localDetailsFlutter)
LocalAppDetailsActivityViewModel.RULE_ID_REACT_NATIVE -> stringResource(R.string.localDetailsReactNative)
LocalAppDetailsActivityViewModel.RULE_ID_TENCENT_BROWSING_SERVICE -> stringResource(R.string.localDetailsTBS)
LocalAppDetailsActivityViewModel.RULE_ID_SKYLINE_RENDERING_ENGINE -> stringResource(R.string.localDetailsSkylineRenderingEngine)
else -> id
}
}
}
================================================
FILE: app/src/main/java/com/xiaoniu/qqversionlist/ui/LocalAppDetailsActivityViewModel.kt
================================================
// SPDX-License-Identifier: AGPL-3.0-or-later
/*
Qverbow Util
Copyright (C) 2023 klxiaoniu
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.xiaoniu.qqversionlist.ui
import android.app.Activity
import android.content.Context
import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.pm.PermissionInfo
import android.content.pm.ProviderInfo
import android.content.pm.ServiceInfo
import android.graphics.drawable.Drawable
import android.os.Build
import android.os.Build.VERSION.SDK_INT
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.xiaoniu.qqversionlist.QverbowApplication.Companion.ANDROID_QQ_PACKAGE_NAME
import com.xiaoniu.qqversionlist.QverbowApplication.Companion.ANDROID_TIM_PACKAGE_NAME
import com.xiaoniu.qqversionlist.QverbowApplication.Companion.ANDROID_WECHAT_PACKAGE_NAME
import com.xiaoniu.qqversionlist.R
import com.xiaoniu.qqversionlist.data.LocalAppStackResult
import com.xiaoniu.qqversionlist.data.LocalAppStackRule
import com.xiaoniu.qqversionlist.util.DexResolver
import com.xiaoniu.qqversionlist.util.FileUtil.ZipFileCompat
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit
import kotlinx.coroutines.withContext
import org.apache.commons.io.IOUtils
import java.io.File
import java.io.FileInputStream
import java.nio.charset.Charset
import java.security.MessageDigest
import kotlin.use
class LocalAppDetailsActivityViewModel : ViewModel() {
companion object {
const val RULE_TYPE_PRIVATE_TENCENT = "Tencent Private" // 腾讯私有库
const val RULE_TYPE_PRIVATE_3RD_PARTY = "3rd Party Private" // 第三方私有库
const val RULE_TYPE_OTEAM_TENCENT = "Tencent Oteam" // 腾讯开源协同
const val RULE_TYPE_OPEN_SOURCE_3RD_PARTY = "3rd Party Open Source" // 第三方开源库
const val RULE_ID_QQNT = "QQNT"
const val RULE_ID_BUGLY = "Bugly"
const val RULE_ID_SHIPLY = "Shiply"
const val RULE_ID_KUIKLY = "Kuikly"
const val RULE_ID_HIPPY = "Hippy"
const val RULE_ID_RIGHTLY = "Rightly"
const val RULE_ID_UE_LIBRARY = "UELibrary"
const val RULE_ID_TENCENT_BEACON = "腾讯灯塔"
const val RULE_ID_JETPACK_COMPOSE = "Jetpack Compose"
const val RULE_ID_COMPOSE_MULTIPLATFORM = "Compose Multiplatform"
const val RULE_ID_FLUTTER = "Flutter"
const val RULE_ID_MMKV = "MMKV"
const val RULE_ID_WCDB = "WCDB"
const val RULE_ID_MARS = "Mars"
const val RULE_ID_MATRIX = "Matrix"
const val RULE_ID_TINKER = "Tinker"
const val RULE_ID_REACT_NATIVE = "React Native"
const val RULE_ID_TENCENT_BROWSING_SERVICE = "腾讯浏览服务"
const val RULE_ID_SKYLINE_RENDERING_ENGINE = "Skyline 渲染引擎"
val DEX_QQNT = arrayOf("com.tencent.qqnt")
val DEX_BUGLY = arrayOf("com.tencent.bugly")
val DEX_SHIPLY = arrayOf("com.tencent.rdelivery")
val DEX_KUIKLY = arrayOf("com.tencent.kuikly","com.tencent.kuikly-open", "kuikly.com.tencent")
val DEX_HIPPY = arrayOf("com.tencent.hippy")
val DEX_RIGHTLY = arrayOf("com.tdsrightly", "com.tencent.rightly", "com.tds.rightly")
val DEX_UE_LIBRARY = arrayOf("com.epicgames.ue4", "com.epicgames.ue5")
val DEX_TENCENT_BEACON = arrayOf("com.tencent.beacon")
val DEX_JETPACK_COMPOSE = arrayOf("androidx.compose")
val DEX_COMPOSE_MULTIPLATFORM = arrayOf("org.jetbrains.compose")
val DEX_FLUTTER = arrayOf("io.flutter")
val DEX_MMKV = arrayOf("com.tencent.mmkv")
val DEX_WCDB = arrayOf("com.tencent.wcdb")
val DEX_MARS = arrayOf("com.tencent.mars")
val DEX_MATRIX = arrayOf("com.tencent.matrix")
val DEX_TINKER = arrayOf("com.tencent.tinker")
val DEX_REACT_NATIVE = arrayOf("com.facebook.react")
val DEX_TENCENT_BROWSING_SERVICE = arrayOf("com.tencent.tbs")
val DEX_SKYLINE_RENDERING_ENGINE = arrayOf("com.tencent.skyline", "com.tencent.luggage.skyline")
const val URL_BUGLY = "https://bugly.tds.qq.com/v2/index/tds-main"
const val URL_UE_LIBRARY =
"https://dev.epicgames.com/documentation/unreal-engine/building-unreal-engine-as-a-library"
const val URL_KUIKLY = "https://kuikly.tds.qq.com/"
const val URL_HIPPY = "https://hippy.tds.qq.com/"
const val URL_SHIPLY = "https://shiply.tds.qq.com/"
const val URL_RIGHTLY = "https://rightly.tds.qq.com/"
const val URL_TENCENT_BEACON = "https://beacon.qq.com/"
const val URL_JETPACK_COMPOSE = "https://developer.android.com/compose"
const val URL_COMPOSE_MULTIPLATFORM = "https://www.jetbrains.com/compose-multiplatform/"
const val URL_FLUTTER = "https://flutter.dev/"
const val URL_MMKV = "https://github.com/Tencent/MMKV"
const val URL_WCDB = "https://github.com/Tencent/wcdb"
const val URL_MARS = "https://github.com/Tencent/Mars"
const val URL_MATRIX = "https://github.com/Tencent/matrix"
const val URL_TINKER = "https://github.com/Tencent/tinker"
const val URL_REACT_NATIVE = "https://reactnative.dev/"
const val URL_TENCENT_BROWSING_SERVICE = "https://x5.tencent.com/"
const val URL_SKYLINE_RENDERING_ENGINE = "https://developers.weixin.qq.com/miniprogram/dev/framework/runtime/skyline/introduction.html"
val DEX_PRE_RULES = listOf<LocalAppStackRule>(
LocalAppStackRule(RULE_ID_QQNT, DEX_QQNT, RULE_TYPE_PRIVATE_TENCENT),
LocalAppStackRule(RULE_ID_BUGLY, DEX_BUGLY, RULE_TYPE_PRIVATE_TENCENT, URL_BUGLY),
LocalAppStackRule(RULE_ID_SHIPLY, DEX_SHIPLY, RULE_TYPE_PRIVATE_TENCENT, URL_SHIPLY),
LocalAppStackRule(RULE_ID_KUIKLY, DEX_KUIKLY, RULE_TYPE_PRIVATE_TENCENT, URL_KUIKLY),
LocalAppStackRule(RULE_ID_HIPPY, DEX_HIPPY, RULE_TYPE_PRIVATE_TENCENT, URL_HIPPY),
LocalAppStackRule(RULE_ID_RIGHTLY, DEX_RIGHTLY, RULE_TYPE_PRIVATE_TENCENT, URL_RIGHTLY),
LocalAppStackRule(
RULE_ID_UE_LIBRARY, DEX_UE_LIBRARY, RULE_TYPE_PRIVATE_3RD_PARTY, URL_UE_LIBRARY
),
LocalAppStackRule(
RULE_ID_TENCENT_BEACON,
DEX_TENCENT_BEACON,
RULE_TYPE_PRIVATE_TENCENT,
URL_TENCENT_BEACON
),
LocalAppStackRule(
RULE_ID_JETPACK_COMPOSE,
DEX_JETPACK_COMPOSE,
RULE_TYPE_OPEN_SOURCE_3RD_PARTY,
URL_JETPACK_COMPOSE
),
LocalAppStackRule(
RULE_ID_COMPOSE_MULTIPLATFORM,
DEX_COMPOSE_MULTIPLATFORM,
RULE_TYPE_OPEN_SOURCE_3RD_PARTY,
URL_COMPOSE_MULTIPLATFORM
),
LocalAppStackRule(
RULE_ID_FLUTTER, DEX_FLUTTER, RULE_TYPE_OPEN_SOURCE_3RD_PARTY, URL_FLUTTER
),
LocalAppStackRule(RULE_ID_MMKV, DEX_MMKV, RULE_TYPE_OTEAM_TENCENT, URL_MMKV),
LocalAppStackRule(RULE_ID_WCDB, DEX_WCDB, RULE_TYPE_OTEAM_TENCENT, URL_WCDB),
LocalAppStackRule(RULE_ID_MARS, DEX_MARS, RULE_TYPE_OTEAM_TENCENT, URL_MARS),
LocalAppStackRule(RULE_ID_MATRIX, DEX_MATRIX, RULE_TYPE_OTEAM_TENCENT, URL_MATRIX),
LocalAppStackRule(RULE_ID_TINKER, DEX_TINKER, RULE_TYPE_OTEAM_TENCENT, URL_TINKER),
LocalAppStackRule(
RULE_ID_REACT_NATIVE,
DEX_REACT_NATIVE,
RULE_TYPE_OPEN_SOURCE_3RD_PARTY,
URL_REACT_NATIVE
),
LocalAppStackRule(
RULE_ID_TENCENT_BROWSING_SERVICE,
DEX_TENCENT_BROWSING_SERVICE,
RULE_TYPE_PRIVATE_TENCENT,
URL_TENCENT_BROWSING_SERVICE
),
LocalAppStackRule(
RULE_ID_SKYLINE_RENDERING_ENGINE,
DEX_SKYLINE_RENDERING_ENGINE,
RULE_TYPE_PRIVATE_TENCENT,
URL_SKYLINE_RENDERING_ENGINE
)
)
val RULES_ID_ORDER = listOf(
RULE_ID_QQNT,
RULE_ID_COMPOSE_MULTIPLATFORM,
RULE_ID_JETPACK_COMPOSE,
RULE_ID_FLUTTER,
RULE_ID_REACT_NATIVE,
RULE_ID_UE_LIBRARY,
RULE_ID_BUGLY,
RULE_ID_SHIPLY,
RULE_ID_KUIKLY,
RULE_ID_HIPPY,
RULE_ID_RIGHTLY
)
}
private val _isLoading = MutableLiveData<Boolean>().apply { value = false }
val isLoading: LiveData<Boolean> = _isLoading
private val _isErr = MutableLiveData<Boolean>().apply { value = false }
val isErr: LiveData<Boolean> = _isErr
private val _isAIShowing = MutableLiveData<Boolean>().apply { value = false }
val isAIShowing: LiveData<Boolean> = _isAIShowing
private val _localSM3 = MutableLiveData<String>().apply { value = "" }
val localSM3: LiveData<String> = _localSM3
private val _interSM3 = MutableLiveData<String>().apply { value = "" }
val interSM3: LiveData<String> = _interSM3
private val _localVersionNameWithInter = MutableLiveData<String>().apply { value = "" }
val localVersionNameWithInter: LiveData<String> = _localVersionNameWithInter
private val _localVersion = MutableLiveData<String>().apply { value = "" }
val localVersion: LiveData<String> = _localVersion
private val _channelText = MutableLiveData<String>().apply { value = "" }
val channelText: LiveData<String> = _channelText
private val _localSDKText = MutableLiveData<String>().apply { value = "" }
val localSDKText: LiveData<String> = _localSDKText
private val _isTIM = MutableLiveData<Boolean>().apply { value = false }
val isTIM: LiveData<Boolean> = _isTIM
private val _timBasedVer = MutableLiveData<String>().apply { value = "" }
val timBasedVer: LiveData<String> = _timBasedVer
private val _isWeixin = MutableLiveData<Boolean>().apply { value = false }
val isWeixin: LiveData<Boolean> = _isWeixin
// 基础信息
private val _appName = MutableLiveData<String>().apply { value = "" }
val appName: LiveData<String> = _appName
private val _appIconImage = MutableLiveData<Drawable>()
val appIconImage: LiveData<Drawable> = _appIconImage
private val _targetSDK = MutableLiveData<Int>().apply { value = 0 }
val targetSDK: LiveData<Int> = _targetSDK
private val _minSDK = MutableLiveData<Int>().apply { value = 0 }
val minSDK: LiveData<Int> = _minSDK
private val _compileSDK = MutableLiveData<Int>().apply { value = 0 }
val compileSDK: LiveData<Int> = _compileSDK
private val _versionName = MutableLiveData<String>().apply { value = "" }
val versionName: LiveData<String> = _versionName
private val _rdmUUID = MutableLiveData<String>().apply { value = "" }
val rdmUUID: LiveData<String> = _rdmUUID
private val _versionCode = MutableLiveData<String>().apply { value = "" }
val versionCode: LiveData<String> = _versionCode
private val _appSettingParams = MutableLiveData<String>().apply { value = "" }
val appSettingParams: LiveData<String> = _appSettingParams
private val _appSettingParamsPad = MutableLiveData<String>().apply { value = "" }
val appSettingParamsPad: LiveData<String> = _appSettingParamsPad
private val _qua = MutableLiveData<String>().apply { value = "" }
val qua: LiveData<String> = _qua
// 栈信息检查结果
private val _localAppStackResults =
MutableLiveData<MutableList<LocalAppStackResult>>().apply { value = mutableListOf() }
val localAppStackResults: LiveData<MutableList<LocalAppStackResult>> = _localAppStackResults
private val _activityDiff = MutableLiveData<String>().apply { value = "" }
val activityDiff: LiveData<String> = _activityDiff
private val _serviceDiff = MutableLiveData<String>().apply { value = "" }
val serviceDiff: LiveData<String> = _serviceDiff
private val _providerDiff = MutableLiveData<String>().apply { value = "" }
val providerDiff: LiveData<String> = _providerDiff
private val _receiverDiff = MutableLiveData<String>().apply { value = "" }
val receiverDiff: LiveData<String> = _receiverDiff
private val _permissionDiff = MutableLiveData<String>().apply { value = "" }
val permissionDiff: LiveData<String> = _permissionDiff
private val _isChangesBackLLMWorking = MutableLiveData<Boolean>().apply { value = false }
val isChangesBackLLMWorking: LiveData<Boolean> = _isChangesBackLLMWorking
private val _changesBackLLMGenText = MutableLiveData<String>().apply { value = "" }
val changesBackLLMGenText: LiveData<String> = _changesBackLLMGenText
fun setLoading(isLoading: Boolean) {
_isLoading.value = isLoading
}
fun setErr(isErr: Boolean) {
_isErr.value = isErr
}
fun setAIShowing(isAIShowing: Boolean) {
_isAIShowing.value = isAIShowing
}
fun setLocalSM3(sm3: String) {
_localSM3.value = sm3
}
fun setInterSM3(sm3: String) {
_interSM3.value = sm3
}
fun setLocalVersionNameWithInter(versionName: String) {
_localVersionNameWithInter.value = versionName
}
fun setLocalVersion(version: String) {
_localVersion.value = version
}
fun setChannelText(text: String) {
_channelText.value = text
}
fun setLocalSDKText(text: String) {
_localSDKText.value = text
}
fun setIsTIM(isTIM: Boolean) {
_isTIM.value = isTIM
}
fun setTIMBasedVer(context: Context, ver: String) {
_timBasedVer.value = context.getString(R.string.basedOnQQVer, ver)
}
fun setIsWeixin(isWeixin: Boolean) {
_isWeixin.value = isWeixin
}
fun setAppName(appName: String) {
_appName.value = appName
}
fun setAppIconImage(appIconImage: Drawable) {
_appIconImage.value = appIconImage
}
fun setTargetSDK(targetSDK: Int) {
_targetSDK.value = targetSDK
}
fun setMinSDK(minSDK: Int) {
_minSDK.value = minSDK
}
fun setCompileSDK(compileSDK: Int) {
_compileSDK.value = compileSDK
}
fun setVersionName(versionName: String) {
_versionName.value = versionName
}
fun setRdmUUID(rdmUUID: String) {
_rdmUUID.value = rdmUUID
}
fun setVersionCode(versionCode: String) {
_versionCode.value = versionCode
}
fun setAppSettingParams(appSettingParams: String) {
_appSettingParams.value = appSettingParams
}
fun setAppSettingParamsPad(appSettingParamsPad: String) {
_appSettingParamsPad.value = appSettingParamsPad
}
fun setQua(qua: String) {
_qua.value = qua
}
fun setLocalAppStackResults(result: MutableList<LocalAppStackResult>) {
_localAppStackResults.value = result
}
fun setActivityDiff(diff: String) {
_activityDiff.value = diff
}
fun setServiceDiff(diff: String) {
_serviceDiff.value = diff
}
fun setProviderDiff(diff: String) {
_providerDiff.value = diff
}
fun setReceiverDiff(diff: String) {
_receiverDiff.value = diff
}
fun setPermissionDiff(diff: String) {
_permissionDiff.value = diff
}
fun setChangesBackLLMWorking(isWorking: Boolean) {
_isChangesBackLLMWorking.value = isWorking
}
fun setChangesBackLLMGenText(text: String) {
_changesBackLLMGenText.value = text
}
fun getInfo(activity: Activity, type: String, appPath: String? = null) {
setLoading(true)
var packageInfo: PackageInfo? = null
var applicationInfo: ApplicationInfo? = null
if (type == "inter") {
val packageInfoPre = activity.packageManager.getPackageArchiveInfo(
appPath!!,
PackageManager.GET_META_DATA or PackageManager.GET_ACTIVITIES or PackageManager.GET_SERVICES or PackageManager.GET_RECEIVERS or PackageManager.GET_PROVIDERS or PackageManager.GET_PERMISSIONS
)
val applicationInfoPre = packageInfoPre?.applicationInfo
if (packageInfoPre != null && applicationInfoPre != null) {
packageInfo = packageInfoPre
applicationInfo = applicationInfoPre
}
} else {
setIsTIM(type == "TIM")
setIsWeixin(type == "Weixin")
packageInfo = activity.packageManager.getPackageInfo(
when (type) {
"TIM" -> ANDROID_TIM_PACKAGE_NAME
"Weixin" -> ANDROID_WECHAT_PACKAGE_NAME
else -> ANDROID_QQ_PACKAGE_NAME
}, 0
)
applicationInfo = activity.packageManager.getApplicationInfo(
when (type) {
"TIM" -> ANDROID_TIM_PACKAGE_NAME
"Weixin" -> ANDROID_WECHAT_PACKAGE_NAME
else -> ANDROID_QQ_PACKAGE_NAME
}, PackageManager.GET_META_DATA
)
}
if (packageInfo == null || applicationInfo == null) {
setErr(true)
setAppName(activity.getString(R.string.unknownErr))
cleanCache(activity)
setLoading(false)
return
}
val packageName = getAppPackageName(applicationInfo)
if (packageName == ANDROID_QQ_PACKAGE_NAME || packageName == ANDROID_TIM_PACKAGE_NAME || packageName == ANDROID_WECHAT_PACKAGE_NAME) {
when (packageName) {
ANDROID_TIM_PACKAGE_NAME -> setIsTIM(true)
ANDROID_WECHAT_PACKAGE_NAME -> setIsWeixin(true)
}
val allJobs = mutableListOf<Job>().apply {
add(viewModelScope.launch(Dispatchers.IO) {
val baseJobs = mutableListOf<Job>().apply {
add(viewModelScope.launch(Dispatchers.IO) {
val appName = getAppName(applicationInfo, activity)
withContext(Dispatchers.Main) {
setAppName(appName)
}
})
add(viewModelScope.launch(Dispatchers.IO) {
val appIconImage = getAppIconImage(applicationInfo, activity)
if (appIconImage != null) withContext(Dispatchers.Main) {
setAppIconImage(appIconImage)
}
})
add(viewModelScope.launch(Dispatchers.IO) {
val targetSDK = getTargetSDK(applicationInfo)
withContext(Dispatchers.Main) {
setTargetSDK(targetSDK)
}
})
add(viewModelScope.launch(Dispatchers.IO) {
val minSDK = getMinSDK(applicationInfo)
withContext(Dispatchers.Main) {
setMinSDK(minSDK)
}
})
add(viewModelScope.launch(Dispatchers.IO) {
val compileSDK = getCompileSDK(applicationInfo)
withContext(Dispatchers.Main) {
if (compileSDK != null) setCompileSDK(compileSDK) else setCompileSDK(
0
)
}
})
checkAndSetProperty(this, ::getVersionName, ::setVersionName, packageInfo)
checkAndSetProperty(this, ::getVersionCode, ::setVersionCode, packageInfo)
if (isWeixin.value == false) {
checkAndSetProperty(this, ::getRdmUUID, ::setRdmUUID, applicationInfo)
checkAndSetProperty(
this, ::getAppSettingParams, ::setAppSettingParams, applicationInfo
)
checkAndSetProperty(
this,
::getAppSettingParamsPad,
::setAppSettingParamsPad,
applicationInfo
)
add(viewModelScope.launch(Dispatchers.IO) {
val qua = getQua(packageInfo)
withContext(Dispatchers.Main) {
setQua(if (qua.isNullOrEmpty()) "" else qua.replace("\n", ""))
}
})
}
}
baseJobs.joinAll()
val checkIsSamePackageJobs = mutableListOf<Job>().apply {
add(viewModelScope.launch(Dispatchers.IO) {
if (type == "inter") {
val interSM3 = getSM3(applicationInfo)
withContext(Dispatchers.Main) {
setInterSM3(interSM3)
}
}
})
add(viewModelScope.launch(Dispatchers.IO) {
try {
val applicationInfo = activity.packageManager.getApplicationInfo(
packageName, PackageManager.GET_META_DATA
)
val packageInfo = activity.packageManager.getPackageInfo(
packageName, 0
)
val localSM3 = getSM3(applicationInfo)
val localVersionName = getVersionName(packageInfo)
withContext(Dispatchers.Main) {
setLocalSM3(localSM3)
if (localVersionName != null) setLocalVersionNameWithInter(
localVersionName
)
}
} catch (_: Exception) {
withContext(Dispatchers.Main) { setLocalSM3("") }
}
})
}
checkIsSamePackageJobs.joinAll()
val isSamePackage = localSM3.value == interSM3.value
val diffPackagesJob = mutableListOf<Job>().apply {
add(viewModelScope.launch(Dispatchers.IO) {
if (type == "inter" && !isSamePackage && localSM3.value != "" && SDK_INT >= Build.VERSION_CODES.P) {
val localPackageInfo = activity.packageManager.getPackageInfo(
packageName,
PackageManager.GET_ACTIVITIES or PackageManager.GET_SERVICES or PackageManager.GET_RECEIVERS or PackageManager.GET_PROVIDERS or PackageManager.GET_PERMISSIONS
)
val interPackageInfo = packageInfo
val localVersionCode = getVersionCode(localPackageInfo)
val interVersionCode = getVersionCode(interPackageInfo)
if (localVersionCode != null && interVersionCode != null) withContext(
Dispatchers.Main
) {
compareActivities(
localPackageInfo.activities, interPackageInfo.activities
)
compareServices(
localPackageInfo.services, interPackageInfo.services
)
compareReceivers(
localPackageInfo.receivers, interPackageInfo.receivers
)
compareProviders(
localPackageInfo.providers, interPackageInfo.providers
)
comparePermissions(
localPackageInfo.permissions, interPackageInfo.permissions
)
}
}
})
}
diffPackagesJob.joinAll()
withContext(Dispatchers.Main) {
compileSDK.value?.let { sdkVersion ->
if (sdkVersion != 0) setLocalSDKText("Target ${targetSDK.value} | Min ${minSDK.value} | Compile $sdkVersion") else setLocalSDKText(
"Target ${targetSDK.value} | Min ${minSDK.value}"
)
}
if (isWeixin.value == true) {
setLocalVersion("${versionName.value} (${versionCode.value})")
} else {
versionCode.value?.let { versionCode ->
rdmUUID.value?.let { rdmUUID ->
setLocalVersion("${versionName.value}.${rdmUUID.split("_")[0]} ($versionCode)")
} ?: setLocalVersion(
"${versionName.value}.${rdmUUID.value!!.split("_")[0]}"
)
}
?: setLocalVersion("${versionName.value}.${rdmUUID.value!!.split("_")[0]}")
appSettingParams.value?.let { appSettingParams ->
val parts = appSettingParams.split("#")
if (parts.size > 3) setChannelText(parts[3]) else setChannelText("")
} ?: setChannelText("")
if (isTIM.value == true) qua.value?.let { qua ->
setTIMBasedVer(
activity, if (qua.length > 3) qua.split("_")[3] else ""
)
}
}
if (type == "inter" && !isSamePackage && localSM3.value != "" && SDK_INT >= Build.VERSION_CODES.P) setAIShowing(
true
)
}
})
add(viewModelScope.launch(Dispatchers.IO) {
val semaphore = Semaphore(3)
val dexJobs = mutableListOf<Job>()
DEX_PRE_RULES.forEach { rule ->
dexJobs.add(viewModelScope.launch(Dispatchers.IO) {
semaphore.withPermit {
val findDex = checkLibrary(applicationInfo.sourceDir, rule.dex)
if (findDex != null) {
withContext(Dispatchers.Main) {
val oldList = localAppStackResults.value
val newList =
(if (oldList.isNullOrEmpty()) mutableListOf() else oldList).apply {
if (!this.any { it.id == rule.id }) {
this.add(LocalAppStackResult(rule.id, findDex))
}
}
setLocalAppStackResults(newList.toMutableList())
}
}
}
})
}
dexJobs.joinAll()
})
}
viewModelScope.launch {
allJobs.joinAll()
setLoading(false)
cleanCache(activity)
}
} else {
setErr(true)
setAppName(activity.getString(R.string.packageNameIsErr))
setLoading(false)
cleanCache(activity)
return
}
}
private fun getSM3(applicationInfo: ApplicationInfo): String {
val appSourceDir = applicationInfo.sourceDir
val messageDigest = MessageDigest.getInstance("SM3")
val fileInputStream = FileInputStream(appSourceDir)
val buffer = ByteArray(8192)
var bytesRead: Int
while (fileInputStream.read(buffer).also { bytesRead = it } != -1) messageDigest.update(
buffer, 0, bytesRead
)
return messageDigest.digest().joinToString("") { "%02X".format(it) }
}
private fun getAppPackageName(applicationInfo: ApplicationInfo): String {
return applicationInfo.packageName
}
private fun getAppName(applicationInfo: ApplicationInfo, activity: Activity): String {
return applicationInfo.loadLabel(activity.packageManager).toString()
}
private fun getAppIconImage(applicationInfo: ApplicationInfo, activity: Activity): Drawable? {
return applicationInfo.loadIcon(activity.packageManager)
}
private fun getTargetSDK(applicationInfo: ApplicationInfo): Int {
return applicationInfo.targetSdkVersion
}
private fun getMinSDK(applicationInfo: ApplicationInfo): Int {
return applicationInfo.minSdkVersion
}
private fun getCompileSDK(applicationInfo: ApplicationInfo): Int? {
return if (SDK_INT >= Build.VERSION_CODES.S) applicationInfo.compileSdkVersion else null
}
private fun getVersionName(packageInfo: PackageInfo): String? {
return packageInfo.versionName
}
private fun getRdmUUID(applicationInfo: ApplicationInfo): String? {
return applicationInfo.metaData?.getString("com.tencent.rdm.uuid")
}
private fun getVersionCode(packageInfo: PackageInfo): String? {
return if (SDK_INT >= Build.VERSION_CODES.P) packageInfo.longVersionCode.toString() else null
}
private fun getAppSettingParams(applicationInfo: ApplicationInfo): String? {
return applicationInfo.metaData?.getString("AppSetting_params")
}
private fun getAppSettingParamsPad(applicationInfo: ApplicationInfo): String? {
return applicationInfo.metaData?.getString("AppSetting_params_pad")
}
private fun getQua(packageInfo: PackageInfo): String? {
val sourceDir = packageInfo.applicationInfo?.sourceDir ?: return null
val file = File(sourceDir)
if (!file.exists()) return null
return runCatching {
ZipFileCompat(file).use { zipFile ->
val entry = zipFile.getEntry("assets/qua.ini") ?: return@runCatching null
zipFile.getInputStream(entry).use { inputStream ->
IOUtils.toString(inputStream, Charset.defaultCharset())
}
}
}.getOrElse { exception -> throw Exception(exception) }
}
private fun <T> checkAndSetProperty(
jobs: MutableList<Job>,
checkFunction: (T) -> String?,
setProperty: (String) -> Unit,
param: T
) {
jobs.add(viewModelScope.launch(Dispatchers.IO) {
val result = checkFunction(param)
withContext(Dispatchers.Main) { setProperty(if (result.isNullOrEmpty()) "" else result) }
})
}
private fun checkLibrary(appPath: String, dexList: Array<String>): String? {
dexList.forEach { dex ->
val findResult = DexResolver.findPackage(appPath, dex)
if (findResult.getOrDefault(false) == true) return dex
}
return null
}
private fun compareActivities(
components1: Array<ActivityInfo>?, components2: Array<ActivityInfo>?
) {
val set1 = components1?.map { it.name }?.toSet() ?: emptySet()
val set2 = components2?.map { it.name }?.toSet() ?: emptySet()
val added = set2 - set1
val removed = set1 - set2
val result = StringBuilder()
if (added.isNotEmpty()) result.append("Added activities: ").append(added.joinToString(", "))
.append("\n")
if (removed.isNotEmpty()) result.append("Removed activities: ")
.append(removed.joinToString(", ")).append("\n")
setActivityDiff(result.toString())
}
private fun compareServices(
components1: Array<ServiceInfo>?, components2: Array<ServiceInfo>?
) {
val set1 = components1?.map { it.name }?.toSet() ?: emptySet()
val set2 = components2?.map { it.name }?.toSet() ?: emptySet()
val added = set2 - set1
val removed = set1 - set2
val result = StringBuilder()
if (added.isNotEmpty()) result.append("Added services: ").append(added.joinToString(", "))
.append("\n")
if (removed.isNotEmpty()) result.append("Removed services: ")
.append(removed.joinToString(", ")).append("\n")
setServiceDiff(result.toString())
}
private fun compareReceivers(
components1: Array<ActivityInfo>?, components2: Array<ActivityInfo>?
) {
val set1 = components1?.map { it.name }?.toSet() ?: emptySet()
val set2 = components2?.map { it.name }?.toSet() ?: emptySet()
val added = set2 - set1
val removed = set1 - set2
val result = StringBuilder()
if (added.isNotEmpty()) result.append("Added receivers: ").append(added.joinToString(", "))
.append("\n")
if (removed.isNotEmpty()) result.append("Removed receivers: ")
.append(removed.joinToString(", ")).append("\n")
setReceiverDiff(result.toString())
}
private fun compareProviders(
components1: Array<ProviderInfo>?, components2: Array<ProviderInfo>?
) {
val set1 = components1?.map { it.name }?.toSet() ?: emptySet()
val set2 = components2?.map { it.name }?.toSet() ?: emptySet()
val added = set2 - set1
val removed = set1 - set2
val result = StringBuilder()
if (added.isNotEmpty()) result.append("Added providers: ").append(added.joinToString(", "))
.append("\n")
if (removed.isNotEmpty()) result.append("Removed providers: ")
.append(removed.joinToString(", ")).append("\n")
setProviderDiff(result.toString())
}
private fun comparePermissions(
permissions1: Array<PermissionInfo>?, permissions2: Array<PermissionInfo>?
) {
val set1 = permissions1?.map { it.name }?.toSet() ?: emptySet()
val set2 = permissions2?.map { it.name }?.toSet() ?: emptySet()
val added = set2 - set1
val removed = set1 - set2
val result = StringBuilder()
if (added.isNotEmpty()) result.append("Added permissions: ")
.append(added.joinToString(", ")).append("\n")
if (removed.isNotEmpty()) result.append("Removed permissions: ")
.append(removed.joinToString(", ")).append("\n")
setPermissionDiff(result.toString())
}
fun cleanCache(activity: Activity) {
val cacheDir = File(activity.cacheDir, "apkAnalysis")
if (cacheDir.exists()) cacheDir.deleteRecursively()
}
}
================================================
FILE: app/src/main/java/com/xiaoniu/qqversionlist/ui/LocalQQAdapter.kt
================================================
// SPDX-License-Identifier: AGPL-3.0-or-later
/*
Qverbow Util
Copyright (C) 2023 klxiaoniu
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.xiaoniu.qqversionlist.ui
import android.annotation.SuppressLint
import android.content.Intent
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.xiaoniu.qqversionlist.R
import com.xiaoniu.qqversionlist.databinding.DialogLocalQqTimInfoBinding
import com.xiaoniu.qqversionlist.databinding.LocalQqBinding
import com.xiaoniu.qqversionlist.util.ClipboardUtil.copyText
import com.xiaoniu.qqversionlist.util.DataStoreUtil
import com.xiaoniu.qqversionlist.util.InfoUtil.showToast
class LocalQQAdapter : RecyclerView.Adapter<LocalQQAdapter.LocalQQViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LocalQQViewHolder {
val binding = LocalQqBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return LocalQQViewHolder(binding)
}
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: LocalQQViewHolder, position: Int) {
val QQTargetInstallKV = DataStoreUtil.getStringKV("QQTargetInstall", "")
val QQMinInstallKV = DataStoreUtil.getStringKV("QQMinInstall", "")
val QQCompileInstallKV = DataStoreUtil.getStringKV("QQCompileInstall", "")
val QQVersionInstallKV = DataStoreUtil.getStringKV("QQVersionInstall", "")
val QQRdmUUIDInstallKV = DataStoreUtil.getStringKV("QQRdmUUIDInstall", "")
val QQVersionCodeInstallKV = DataStoreUtil.getStringKV("QQVersionCodeInstall", "")
val QQAppSettingParamsInstallKV = DataStoreUtil.getStringKV("QQAppSettingParamsInstall", "")
val QQAppSettingParamsPadInstallKV =
DataStoreUtil.getStringKV("QQAppSettingParamsPadInstall", "")
val QQQuaKV = DataStoreUtil.getStringKV("QQQua", "")
val QQRdmUUIDInstallProcessed =
if (QQRdmUUIDInstallKV != "") ".${QQRdmUUIDInstallKV.split("_")[0]}" else ""
val QQChannelInstallProcessed =
if (QQAppSettingParamsInstallKV != "") QQAppSettingParamsInstallKV.split("#")[3] else ""
holder.apply {
if (QQVersionInstallKV != "") {
itemQqInstallText.text =
itemView.context.getString(R.string.localQQVersion) + QQVersionInstallKV + QQRdmUUIDInstallProcessed + (if (QQVersionCodeInstallKV != "") " (${QQVersionCodeInstallKV})" else "") + (if (QQChannelInstallProcessed != "") " - $QQChannelInstallProcessed" else "")
itemQqInstallCard.isVisible = true
// 无障碍标记
/*if (DefaultArtifactVersion(QQVersionInstall2) >= DefaultArtifactVersion(
EARLIEST_ACCESSIBILITY_VERSION
)
) itemQqInstallText.setCompoundDrawablesWithIntrinsicBounds(
R.drawable.accessibility_new_24px, 0, 0, 0
) else itemQqInstallText.setCompoundDrawablesWithIntrinsicBounds(
R.drawable.phone_find_line, 0, 0, 0
)
val oldItemQqInstallCardDescribe =
itemQqInstallText.text.toString()
if (DefaultArtifactVersion(QQVersionInstall2) >= DefaultArtifactVersion(
EARLIEST_ACCESSIBILITY_VERSION
)
) itemQqInstallCard.contentDescription =
"$oldItemQqInstallCardDescribe。" + String(
Base64.decode(
getString(R.string.accessibilityTag),
Base64.NO_WRAP
), Charsets.UTF_8
)*/
itemQqInstallCard.setOnLongClickListener {
if (DataStoreUtil.getBooleanKV("longPressCard", true)) {
if (DataStoreUtil.getBooleanKV("useNewLocalPage", true)) {
itemView.context.startActivity(Intent(
itemView.context, LocalAppDetailsActivity::class.java
).apply { putExtra("localAppType", "QQ") })
} else {
val localInfoAllText = (if (QQTargetInstallKV != "") "Target SDK${
itemView.context.getString(R.string.colon)
}${QQTargetInstallKV}" else "") + (if (QQMinInstallKV != "") "\nMin SDK${
itemView.context.getString(R.string.colon)
}${QQMinInstallKV}" else "") + (if (QQCompileInstallKV != "") "\nCompile SDK${
itemView.context.getString(R.string.colon)
}${
QQCompileInstallKV
}" else "") + "\nVersion Name${itemView.context.getString(R.string.colon)}${
QQVersionInstallKV
}" + (if (QQRdmUUIDInstallKV != "") "\nRdm UUID${
itemView.context.getString(R.string.colon)
}${
QQRdmUUIDInstallKV
}" else "") + (if (QQVersionCodeInstallKV != "") "\nVersion Code${
itemView.context.getString(R.string.colon)
}${
QQVersionCodeInstallKV
}" else "") + (if (QQAppSettingParamsInstallKV != "") "\nAppSetting_params${
itemView.context.getString(R.string.colon)
}${
QQAppSettingParamsInstallKV
}" else "") + (if (QQAppSettingParamsPadInstallKV != "") "\nAppSetting_params_pad${
itemView.context.getString(R.string.colon)
}${
QQAppSettingParamsPadInstallKV
}" else "") + (if (QQQuaKV != "") "\nQUA${
itemView.context.getString(R.string.colon)
}${QQQuaKV}" else "")
val dialogLocalQqTimInfoBinding = DialogLocalQqTimInfoBinding.inflate(
LayoutInflater.from(itemView.context)
)
MaterialAlertDialogBuilder(itemView.context).setView(
dialogLocalQqTimInfoBinding.root
).setTitle(R.string.localQQVersionDetails)
.setIcon(R.drawable.phone_find_line).apply {
dialogLocalQqTimInfoBinding.apply {
val dialogLocalSdkDesc =
if (QQTargetInstallKV != "" && QQMinInstallKV != "" && QQChannelInstallProcessed != "") "Target $QQTargetInstallKV | Min $QQMinInstallKV | Compile $QQCompileInstallKV" else "Target $QQTargetInstallKV | Min $QQMinInstallKV"
dialogLocalSdk.apply {
setCellDescription(dialogLocalSdkDesc)
this.setOnClickListener {
context.copyText(
"Android SDK${
it
gitextract_9y815yus/ ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug-报告.md │ │ └── 功能请求.md │ ├── dependabot.yml │ ├── pull_request_template.md │ └── workflows/ │ ├── codeql.yml │ ├── crowdin.yml │ └── push_ci.yml ├── .gitignore ├── DataListShared.md ├── LICENSE ├── README.md ├── ReadmeAssets/ │ ├── Get-it-on-JiuQi-NotifCenter-WeChatMiniProgram.md │ └── Get-it-on-Obtainium.md ├── SECURITY.md ├── UserAgreement.md ├── app/ │ ├── .gitignore │ ├── build.gradle.kts │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── xiaoniu/ │ │ └── qqversionlist/ │ │ └── ExampleInstrumentedTest.kt │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── xiaoniu/ │ │ │ └── qqversionlist/ │ │ │ ├── QverbowApplication.kt │ │ │ ├── data/ │ │ │ │ ├── LocalAppStackResult.kt │ │ │ │ ├── LocalAppStackRule.kt │ │ │ │ ├── QQVersionBean.kt │ │ │ │ ├── QverbowRelease.kt │ │ │ │ ├── QverbowReleaseAssets.kt │ │ │ │ ├── TIMVersionBean.kt │ │ │ │ └── WeixinVersionBean.kt │ │ │ ├── service/ │ │ │ │ └── FirebaseMessagingService.kt │ │ │ ├── ui/ │ │ │ │ ├── ExpUrlListAdapter.kt │ │ │ │ ├── LocalAppDetailsActivity.kt │ │ │ │ ├── LocalAppDetailsActivityViewModel.kt │ │ │ │ ├── LocalQQAdapter.kt │ │ │ │ ├── LocalTIMAdapter.kt │ │ │ │ ├── LocalWeixinAdapter.kt │ │ │ │ ├── MainActivity.kt │ │ │ │ ├── MainActivityViewModel.kt │ │ │ │ ├── OSSLicensesActivity.kt │ │ │ │ ├── OSSLicensesMenuActivity.kt │ │ │ │ ├── QQVersionAdapter.kt │ │ │ │ ├── QQVersionListFragment.kt │ │ │ │ ├── ShiplyAdvancedConfigFragment.kt │ │ │ │ ├── TIMVersionAdapter.kt │ │ │ │ ├── TIMVersionListFragment.kt │ │ │ │ ├── VersionListPagerAdapter.kt │ │ │ │ ├── WeixinVersionAdapter.kt │ │ │ │ ├── WeixinVersionListFragment.kt │ │ │ │ ├── components/ │ │ │ │ │ └── cell/ │ │ │ │ │ ├── CellBottomClick.kt │ │ │ │ │ ├── CellBottomSwitch.kt │ │ │ │ │ ├── CellMiddleClick.kt │ │ │ │ │ ├── CellMiddleSwitch.kt │ │ │ │ │ ├── CellSingleClick.kt │ │ │ │ │ ├── CellSingleSwitch.kt │ │ │ │ │ ├── CellTopClick.kt │ │ │ │ │ └── CellTopSwitch.kt │ │ │ │ └── theme/ │ │ │ │ ├── Color.kt │ │ │ │ ├── Theme.kt │ │ │ │ └── Type.kt │ │ │ └── util/ │ │ │ ├── ClipboardUtil.kt │ │ │ ├── DataStoreUtil.kt │ │ │ ├── DexResolver.kt │ │ │ ├── EnumDexContainer.kt │ │ │ ├── Extensions.kt │ │ │ ├── FileUtil.kt │ │ │ ├── GitHubRestApiUtil.kt │ │ │ ├── InfoUtil.kt │ │ │ ├── KeyStoreUtil.kt │ │ │ ├── LogUtil.kt │ │ │ ├── OSSLicensesObject.kt │ │ │ ├── ShiplyUtil.kt │ │ │ ├── StringUtil.kt │ │ │ ├── VersionUtil.kt │ │ │ ├── ZhipuSDKUtil.kt │ │ │ └── boundo_LICENSE │ │ └── res/ │ │ ├── drawable/ │ │ │ ├── accessibility_new_20px.xml │ │ │ ├── accessibility_new_24px.xml │ │ │ ├── ai_generate_2.xml │ │ │ ├── alert_line.xml │ │ │ ├── apps_line.xml │ │ │ ├── arrow_down_s_line.xml │ │ │ ├── arrow_left_line.xml │ │ │ ├── arrow_right_s_line.xml │ │ │ ├── arrow_up_s_line.xml │ │ │ ├── beacon_official.xml │ │ │ ├── bottom_sheet_drag_handle.xml │ │ │ ├── braces_line.xml │ │ │ ├── bubble_chart_line.xml │ │ │ ├── bugly_official.xml │ │ │ ├── built_with_material_licensed_under_agpl_v3.xml │ │ │ ├── check_circle.xml │ │ │ ├── clipboard_line.xml │ │ │ ├── compose.xml │ │ │ ├── download_line.xml │ │ │ ├── error_warning_fill.xml │ │ │ ├── file_copy_line.xml │ │ │ ├── file_text_line.xml │ │ │ ├── flashlight_line.xml │ │ │ ├── flask_line.xml │ │ │ ├── flutter_line.xml │ │ │ ├── git_commit_line.xml │ │ │ ├── git_repository_line.xml │ │ │ ├── github_line.xml │ │ │ ├── gitlab_line.xml │ │ │ ├── hippy_official.xml │ │ │ ├── ic_launcher_background.xml │ │ │ ├── ic_launcher_foreground.xml │ │ │ ├── ic_launcher_foreground_splash.xml │ │ │ ├── ic_launcher_special_foreground.xml │ │ │ ├── info_card_line.xml │ │ │ ├── information_line.xml │ │ │ ├── key_line.xml │ │ │ ├── kuikly_official.xml │ │ │ ├── lightbulb_fill.xml │ │ │ ├── link.xml │ │ │ ├── mini_program_line.xml │ │ │ ├── open_source_line.xml │ │ │ ├── oteam_official.xml │ │ │ ├── palette_line.xml │ │ │ ├── phone_find_line.xml │ │ │ ├── qqnt_logo_unofficial.xml │ │ │ ├── qqnt_logo_unofficial_fix.xml │ │ │ ├── question_line.xml │ │ │ ├── qv_logo_notification.xml │ │ │ ├── reactjs_line.xml │ │ │ ├── refresh_line.xml │ │ │ ├── rightly_official.xml │ │ │ ├── save_line.xml │ │ │ ├── scan_line.xml │ │ │ ├── scan_shortcut_icon_foreground.xml │ │ │ ├── search_line.xml │ │ │ ├── settings_line.xml │ │ │ ├── shape_cell_bottom.xml │ │ │ ├── shape_cell_middle.xml │ │ │ ├── shape_cell_single.xml │ │ │ ├── shape_cell_top.xml │ │ │ ├── share_line.xml │ │ │ ├── shield_keyhole_line.xml │ │ │ ├── shiply_official.xml │ │ │ ├── skip_forward_line.xml │ │ │ ├── sparkling_line.xml │ │ │ ├── stack_line.xml │ │ │ ├── stop_fill.xml │ │ │ ├── stop_line.xml │ │ │ ├── tencent_logo.xml │ │ │ ├── tools_line.xml │ │ │ └── ue_icon_2023_black.xml │ │ ├── layout/ │ │ │ ├── activity_local_app_details.xml │ │ │ ├── activity_main.xml │ │ │ ├── applications_config_back_button.xml │ │ │ ├── bottomsheet_shiply_advanced_config.xml │ │ │ ├── cell_bottom_click.xml │ │ │ ├── cell_bottom_switch.xml │ │ │ ├── cell_middle_click.xml │ │ │ ├── cell_middle_switch.xml │ │ │ ├── cell_single_click.xml │ │ │ ├── cell_single_switch.xml │ │ │ ├── cell_top_click.xml │ │ │ ├── cell_top_switch.xml │ │ │ ├── dialog_about.xml │ │ │ ├── dialog_changes_llm_inference.xml │ │ │ ├── dialog_exp_back.xml │ │ │ ├── dialog_experimental_features.xml │ │ │ ├── dialog_firebase_first_info.xml │ │ │ ├── dialog_format_define.xml │ │ │ ├── dialog_guess.xml │ │ │ ├── dialog_hash.xml │ │ │ ├── dialog_loading.xml │ │ │ ├── dialog_local_qq_tim_info.xml │ │ │ ├── dialog_personalization.xml │ │ │ ├── dialog_private_token_setting.xml │ │ │ ├── dialog_setting.xml │ │ │ ├── dialog_shiply.xml │ │ │ ├── dialog_tencent_app_store.xml │ │ │ ├── exp_link_next_button.xml │ │ │ ├── item_exp_back_url_card.xml │ │ │ ├── item_qq_version.xml │ │ │ ├── item_qq_version_detail.xml │ │ │ ├── item_tim_version.xml │ │ │ ├── item_tim_version_detail.xml │ │ │ ├── item_weixin_version.xml │ │ │ ├── item_weixin_version_detail.xml │ │ │ ├── local_qq.xml │ │ │ ├── local_tim.xml │ │ │ ├── local_weixin.xml │ │ │ ├── recycle_qq_version.xml │ │ │ ├── recycle_tim_version.xml │ │ │ ├── success_button.xml │ │ │ ├── update_qvt_button.xml │ │ │ └── user_agreement.xml │ │ ├── layout-v26/ │ │ │ ├── dialog_changes_llm_inference.xml │ │ │ └── update_qvt_button.xml │ │ ├── mipmap-anydpi-v26/ │ │ │ ├── ic_launcher.xml │ │ │ ├── ic_launcher_round.xml │ │ │ ├── scan_shortcut_icon.xml │ │ │ └── scan_shortcut_icon_round.xml │ │ ├── values/ │ │ │ ├── arrays.xml │ │ │ ├── attrs.xml │ │ │ ├── colors.xml │ │ │ ├── ic_launcher_background.xml │ │ │ ├── scan_shortcut_icon_background.xml │ │ │ ├── strings.xml │ │ │ └── themes.xml │ │ ├── values-ar-rSA/ │ │ │ └── strings.xml │ │ ├── values-en-rUS/ │ │ │ └── strings.xml │ │ ├── values-es-rES/ │ │ │ └── strings.xml │ │ ├── values-fr-rFR/ │ │ │ └── strings.xml │ │ ├── values-ja-rJP/ │ │ │ └── strings.xml │ │ ├── values-ko-rKR/ │ │ │ └── strings.xml │ │ ├── values-night/ │ │ │ └── themes.xml │ │ ├── values-ru-rRU/ │ │ │ └── strings.xml │ │ ├── values-zh-rCN/ │ │ │ └── strings.xml │ │ ├── values-zh-rHK/ │ │ │ └── strings.xml │ │ ├── xml/ │ │ │ ├── backup_rules.xml │ │ │ └── data_extraction_rules.xml │ │ └── xml-v25/ │ │ └── shortcuts.xml │ └── test/ │ └── java/ │ └── com/ │ └── xiaoniu/ │ └── qqversionlist/ │ └── ExampleUnitTest.kt ├── build.gradle.kts ├── crowdin.yml ├── gradle/ │ ├── libs.versions.toml │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat └── settings.gradle.kts
Condensed preview — 225 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,485K chars).
[
{
"path": ".github/ISSUE_TEMPLATE/bug-报告.md",
"chars": 376,
"preview": "---\nname: Bug 报告\nabout: 创建报告以帮助我们改进\ntitle: ''\nlabels: bug\nassignees: ''\n\n---\n\n**描述错误**\n对错误的清晰而简洁的描述。\n\n**重现步骤**\n重现行为的步骤:\n"
},
{
"path": ".github/ISSUE_TEMPLATE/功能请求.md",
"chars": 241,
"preview": "---\nname: 功能请求\nabout: 为此项目提出建议\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**您的功能请求是否与问题有关?请描述。**\n对问题的清晰而简洁的描述。例如:我总是感到沮丧,当"
},
{
"path": ".github/dependabot.yml",
"chars": 882,
"preview": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where "
},
{
"path": ".github/pull_request_template.md",
"chars": 585,
"preview": "## 这个 PR 解决了什么问题?\n\n> 请补充以下信息\n\n### 需求背景\n\n……\n\n### 更新日志\n\n#### 修复\n\n- ……\n\n#### 新增\n\n- ……\n\n#### 优化\n\n- ……\n\n#### 其它更改\n\n- ……\n\n####"
},
{
"path": ".github/workflows/codeql.yml",
"chars": 4125,
"preview": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# Y"
},
{
"path": ".github/workflows/crowdin.yml",
"chars": 1441,
"preview": "name: Crowdin Action\n\non:\n push:\n branches: [ master ]\n schedule:\n - cron: '0 0 * * *'\n\njobs:\n synchronize-with"
},
{
"path": ".github/workflows/push_ci.yml",
"chars": 1955,
"preview": "name: Android CI\n\non:\n push:\n branches: [ \"master\" ]\n paths-ignore:\n - '**.md'\n - '**.txt'\n - '.gi"
},
{
"path": ".gitignore",
"chars": 158,
"preview": "*.iml\n.gradle\n/local.properties\n.DS_Store\n/build\n/captures\n.externalNativeBuild\n.cxx\nlocal.properties\n/app/release\n/.ide"
},
{
"path": "DataListShared.md",
"chars": 1188,
"preview": "# QQ 版本列表实用工具个人信息第三方共享清单\n\n1. 本清单仅反映您在全部功能模式下,我们对您的信息与第三方共享的情况。\n2. 为了完成相应的功能,我们会接入来自第三方的应用开发工具包(SDK)来获得技术支持,比如实现云端消息推送和帮助"
},
{
"path": "LICENSE",
"chars": 34522,
"preview": " GNU AFFERO GENERAL PUBLIC LICENSE\n Version 3, 19 November 2007\n\n Copyright (C)"
},
{
"path": "README.md",
"chars": 20021,
"preview": "# QQ 版本列表实用工具 for Android\n\n 方案\n\n您可以使用开源软件 [Obtainium](https://github.com/ImranR98/Obta"
},
{
"path": "SECURITY.md",
"chars": 315,
"preview": "## 上报漏洞 Reporting a Vulnerability\n\n如果您发现了关于此项目的任何漏洞,请不要公开,请在[此处](https://github.com/klxiaoniu/QQVersionList/security)向我们"
},
{
"path": "UserAgreement.md",
"chars": 4595,
"preview": "# QQ 版本列表实用工具用户协议\n\n> 此用户协议于 2024 年 10 月 28 日修订,并于 2024 年 10 月 29 日实施。\n\n## 导言\n\n欢迎您使用 QQ 版本列表实用工具。\n\n为使用 QQ 版本列表实用工具及服务,您应当"
},
{
"path": "app/.gitignore",
"chars": 6,
"preview": "/build"
},
{
"path": "app/build.gradle.kts",
"chars": 6036,
"preview": "import org.jetbrains.kotlin.gradle.dsl.JvmTarget\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n\r\n/*\r\n Qverbow Uti"
},
{
"path": "app/proguard-rules.pro",
"chars": 2621,
"preview": "# Qverbow Util\r\n# Copyright (C) 2023 klxiaoniu\r\n#\r\n# This program is free software: you can redistribute it and"
},
{
"path": "app/src/androidTest/java/com/xiaoniu/qqversionlist/ExampleInstrumentedTest.kt",
"chars": 696,
"preview": "package com.xiaoniu.qqversionlist\r\n\r\nimport androidx.test.ext.junit.runners.AndroidJUnit4\r\nimport androidx.test.platform"
},
{
"path": "app/src/main/AndroidManifest.xml",
"chars": 4226,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n xmlns:"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/QverbowApplication.kt",
"chars": 2733,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/data/LocalAppStackResult.kt",
"chars": 893,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/data/LocalAppStackRule.kt",
"chars": 1729,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/data/QQVersionBean.kt",
"chars": 1816,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/data/QverbowRelease.kt",
"chars": 1152,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/data/QverbowReleaseAssets.kt",
"chars": 943,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/data/TIMVersionBean.kt",
"chars": 1663,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/data/WeixinVersionBean.kt",
"chars": 1057,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/service/FirebaseMessagingService.kt",
"chars": 2058,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/ui/ExpUrlListAdapter.kt",
"chars": 6793,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/ui/LocalAppDetailsActivity.kt",
"chars": 43365,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/ui/LocalAppDetailsActivityViewModel.kt",
"chars": 36549,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/ui/LocalQQAdapter.kt",
"chars": 14601,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/ui/LocalTIMAdapter.kt",
"chars": 14778,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/ui/LocalWeixinAdapter.kt",
"chars": 3250,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/ui/MainActivity.kt",
"chars": 168929,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\r\n\r\n/*\r\n Qverbow Util\r\n Copyright (C) 2023 klxiaoniu\r\n\r\n This prog"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/ui/MainActivityViewModel.kt",
"chars": 3058,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/ui/OSSLicensesActivity.kt",
"chars": 10197,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/ui/OSSLicensesMenuActivity.kt",
"chars": 10608,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/ui/QQVersionAdapter.kt",
"chars": 17500,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/ui/QQVersionListFragment.kt",
"chars": 4724,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/ui/ShiplyAdvancedConfigFragment.kt",
"chars": 7082,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/ui/TIMVersionAdapter.kt",
"chars": 16932,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/ui/TIMVersionListFragment.kt",
"chars": 4748,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/ui/VersionListPagerAdapter.kt",
"chars": 1448,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/ui/WeixinVersionAdapter.kt",
"chars": 14472,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/ui/WeixinVersionListFragment.kt",
"chars": 4757,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/ui/components/cell/CellBottomClick.kt",
"chars": 3006,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/ui/components/cell/CellBottomSwitch.kt",
"chars": 3840,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/ui/components/cell/CellMiddleClick.kt",
"chars": 3006,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/ui/components/cell/CellMiddleSwitch.kt",
"chars": 3840,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/ui/components/cell/CellSingleClick.kt",
"chars": 3006,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/ui/components/cell/CellSingleSwitch.kt",
"chars": 3840,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/ui/components/cell/CellTopClick.kt",
"chars": 2994,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/ui/components/cell/CellTopSwitch.kt",
"chars": 3828,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/ui/theme/Color.kt",
"chars": 289,
"preview": "package com.xiaoniu.qqversionlist.ui.theme\n\nimport androidx.compose.ui.graphics.Color\n\nval Purple80 = Color(0xFFD0BCFF)\n"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/ui/theme/Theme.kt",
"chars": 1678,
"preview": "package com.xiaoniu.qqversionlist.ui.theme\n\nimport android.os.Build\nimport androidx.compose.foundation.isSystemInDarkThe"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/ui/theme/Type.kt",
"chars": 994,
"preview": "package com.xiaoniu.qqversionlist.ui.theme\n\nimport androidx.compose.material3.Typography\nimport androidx.compose.ui.text"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/util/ClipboardUtil.kt",
"chars": 1344,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\r\n\r\n/*\r\n Qverbow Util\r\n Copyright (C) 2023 klxiaoniu\r\n\r\n This prog"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/util/DataStoreUtil.kt",
"chars": 9514,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/util/DexResolver.kt",
"chars": 7138,
"preview": "// https://github.com/cliuff/boundo/blob/main/api_viewing/src/main/kotlin/com/madness/collision/unit/api_viewing/info/De"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/util/EnumDexContainer.kt",
"chars": 5177,
"preview": "// https://github.com/cliuff/boundo/blob/main/api_viewing/src/main/kotlin/com/madness/collision/unit/api_viewing/info/En"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/util/Extensions.kt",
"chars": 1073,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/util/FileUtil.kt",
"chars": 4105,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/util/GitHubRestApiUtil.kt",
"chars": 2913,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/util/InfoUtil.kt",
"chars": 8964,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\r\n\r\n/*\r\n Qverbow Util\r\n Copyright (C) 2023 klxiaoniu\r\n\r\n This prog"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/util/KeyStoreUtil.kt",
"chars": 8540,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/util/LogUtil.kt",
"chars": 1072,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\r\n\r\n/*\r\n Qverbow Util\r\n Copyright (C) 2023 klxiaoniu\r\n\r\n This prog"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/util/OSSLicensesObject.kt",
"chars": 1039,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/util/ShiplyUtil.kt",
"chars": 8665,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/util/StringUtil.kt",
"chars": 8025,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\r\n\r\n/*\r\n Qverbow Util\r\n Copyright (C) 2023 klxiaoniu\r\n\r\n This prog"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/util/VersionUtil.kt",
"chars": 18095,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/util/ZhipuSDKUtil.kt",
"chars": 2742,
"preview": "// SPDX-License-Identifier: AGPL-3.0-or-later\n\n/*\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is"
},
{
"path": "app/src/main/java/com/xiaoniu/qqversionlist/util/boundo_LICENSE",
"chars": 11358,
"preview": "\n Apache License\n Version 2.0, January 2004\n "
},
{
"path": "app/src/main/res/drawable/accessibility_new_20px.xml",
"chars": 722,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"20dp\"\n android:height=\"20dp\"\n "
},
{
"path": "app/src/main/res/drawable/accessibility_new_24px.xml",
"chars": 684,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"24dp\"\n android:height=\"24dp\"\n "
},
{
"path": "app/src/main/res/drawable/ai_generate_2.xml",
"chars": 1562,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/alert_line.xml",
"chars": 1411,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/apps_line.xml",
"chars": 1775,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/arrow_down_s_line.xml",
"chars": 1030,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/arrow_left_line.xml",
"chars": 1006,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/arrow_right_s_line.xml",
"chars": 997,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/arrow_up_s_line.xml",
"chars": 1022,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/beacon_official.xml",
"chars": 3655,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n ** 专有代码声明 **\n\n 此矢量图片文件来自腾讯灯塔官方标识矢量图,\n 腾讯灯塔标识商标(如有)和原创权利人为深圳市腾讯计算机系统"
},
{
"path": "app/src/main/res/drawable/bottom_sheet_drag_handle.xml",
"chars": 923,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n ~ Copyright (C) 2023 The Android Open Source Project\n ~\n ~ Licensed unde"
},
{
"path": "app/src/main/res/drawable/braces_line.xml",
"chars": 1434,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/bubble_chart_line.xml",
"chars": 1558,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/bugly_official.xml",
"chars": 4455,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n ** 专有代码声明 **\n\n 此矢量图片文件来自 Bugly 官方标识矢量图,\n Bugly 标识商标(如有)和原创权利人为深圳市腾讯"
},
{
"path": "app/src/main/res/drawable/built_with_material_licensed_under_agpl_v3.xml",
"chars": 31477,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"244.03dp\"\n android:height=\"39.9"
},
{
"path": "app/src/main/res/drawable/check_circle.xml",
"chars": 1212,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\r\n Copyright 2018 Remix-Design\r\n\r\n Licensed under the Apache License, Vers"
},
{
"path": "app/src/main/res/drawable/clipboard_line.xml",
"chars": 1095,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/compose.xml",
"chars": 2005,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n https://www.jetbrains.com/compose-multiplatform/\n-->\n<vector xmlns:android"
},
{
"path": "app/src/main/res/drawable/download_line.xml",
"chars": 1030,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/error_warning_fill.xml",
"chars": 1047,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/file_copy_line.xml",
"chars": 1184,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/file_text_line.xml",
"chars": 1111,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/flashlight_line.xml",
"chars": 988,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/flask_line.xml",
"chars": 1481,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/flutter_line.xml",
"chars": 1053,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/git_commit_line.xml",
"chars": 1167,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/git_repository_line.xml",
"chars": 1225,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/github_line.xml",
"chars": 3159,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/gitlab_line.xml",
"chars": 1943,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/hippy_official.xml",
"chars": 2791,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n ** 专有代码声明 **\n\n 此矢量图片文件来自 Hippy 官方标识矢量图,\n Hippy 标识商标(如有)和原创权利人为深圳市腾讯"
},
{
"path": "app/src/main/res/drawable/ic_launcher_background.xml",
"chars": 331,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:wi"
},
{
"path": "app/src/main/res/drawable/ic_launcher_foreground.xml",
"chars": 1663,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n ** 专有代码声明 **\n 此矢量图片文件由 QQ 版本列表实用工具原创绘制。\n 此矢量图片文件及其代码不受本项目 GNU Affer"
},
{
"path": "app/src/main/res/drawable/ic_launcher_foreground_splash.xml",
"chars": 1685,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n ** 专有代码声明 **\n 此矢量图片文件由 QQ 版本列表实用工具原创绘制。\n 此矢量图片文件及其代码不受本项目 GNU Affer"
},
{
"path": "app/src/main/res/drawable/ic_launcher_special_foreground.xml",
"chars": 3161,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"108dp\"\n android:height=\"108dp\"\n"
},
{
"path": "app/src/main/res/drawable/info_card_line.xml",
"chars": 1109,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/information_line.xml",
"chars": 1177,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\r\n Copyright 2018 Remix-Design\r\n\r\n Licensed under the Apache License, Vers"
},
{
"path": "app/src/main/res/drawable/key_line.xml",
"chars": 1162,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/kuikly_official.xml",
"chars": 2878,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n ** 专有代码声明 **\n\n 此矢量图片文件来自 Kuikly 官方标识矢量图,\n Kuikly 标识商标(如有)和原创权利人为深圳市"
},
{
"path": "app/src/main/res/drawable/lightbulb_fill.xml",
"chars": 1189,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/link.xml",
"chars": 1482,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/mini_program_line.xml",
"chars": 1881,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/open_source_line.xml",
"chars": 1589,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/oteam_official.xml",
"chars": 3442,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:tools=\"http://schemas.android.com/tools\"\n "
},
{
"path": "app/src/main/res/drawable/palette_line.xml",
"chars": 1826,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/phone_find_line.xml",
"chars": 1320,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/qqnt_logo_unofficial.xml",
"chars": 2670,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n ** 专有代码声明 **\n\n 此矢量图片文件由 QQ 版本列表实用工具(以下简称“本项目”)根据 QQNT 标识临摹绘制,\n 文件所包"
},
{
"path": "app/src/main/res/drawable/qqnt_logo_unofficial_fix.xml",
"chars": 2674,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n ** 专有代码声明 **\n\n 此矢量图片文件由 QQ 版本列表实用工具(以下简称“本项目”)根据 QQNT 标识临摹绘制,\n 文件所包"
},
{
"path": "app/src/main/res/drawable/question_line.xml",
"chars": 1419,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\r\n Copyright 2018 Remix-Design\r\n\r\n Licensed under the Apache License, Vers"
},
{
"path": "app/src/main/res/drawable/qv_logo_notification.xml",
"chars": 1611,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n ** 专有代码声明 **\n 此矢量图片文件由 QQ 版本列表实用工具原创绘制。\n 此矢量图片文件及其代码不受本项目 GNU Affer"
},
{
"path": "app/src/main/res/drawable/reactjs_line.xml",
"chars": 4563,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/refresh_line.xml",
"chars": 1286,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\r\n Copyright 2018 Remix-Design\r\n\r\n Licensed under the Apache License, Vers"
},
{
"path": "app/src/main/res/drawable/rightly_official.xml",
"chars": 5667,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n ** 专有代码声明 **\n\n 此矢量图片文件来自 Rightly 官方标识矢量图,\n Rightly 标识商标(如有)和原创权利人为深"
},
{
"path": "app/src/main/res/drawable/save_line.xml",
"chars": 1055,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/scan_line.xml",
"chars": 1228,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/scan_shortcut_icon_foreground.xml",
"chars": 2088,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/search_line.xml",
"chars": 1291,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\r\n Copyright 2018 Remix-Design\r\n\r\n Licensed under the Apache License, Vers"
},
{
"path": "app/src/main/res/drawable/settings_line.xml",
"chars": 1146,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/shape_cell_bottom.xml",
"chars": 1221,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/drawable/shape_cell_middle.xml",
"chars": 1056,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/drawable/shape_cell_single.xml",
"chars": 1057,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/drawable/shape_cell_top.xml",
"chars": 1221,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/drawable/share_line.xml",
"chars": 1726,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/shield_keyhole_line.xml",
"chars": 1364,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/shiply_official.xml",
"chars": 2442,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n ** 专有代码声明 **\n\n 此矢量图片文件来自 Shiply 官方标识矢量图,\n Shiply 标识商标(如有)和原创权利人为深圳市"
},
{
"path": "app/src/main/res/drawable/skip_forward_line.xml",
"chars": 1285,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/sparkling_line.xml",
"chars": 1524,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/stack_line.xml",
"chars": 1100,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"16dp\"\n android:height=\"16dp\"\n "
},
{
"path": "app/src/main/res/drawable/stop_fill.xml",
"chars": 1052,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\r\n Copyright 2018 Remix-Design\r\n\r\n Licensed under the Apache License, Vers"
},
{
"path": "app/src/main/res/drawable/stop_line.xml",
"chars": 1031,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/tencent_logo.xml",
"chars": 451,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n https://www.iconfont.cn/\n-->\n<vector xmlns:android=\"http://schemas.androi"
},
{
"path": "app/src/main/res/drawable/tools_line.xml",
"chars": 1465,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Copyright 2018 Remix-Design\n\n Licensed under the Apache License, Version"
},
{
"path": "app/src/main/res/drawable/ue_icon_2023_black.xml",
"chars": 1533,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n\n https://www.unrealengine.com/branding\n Unreal® 及其徽标是 Epic Games, Inc. 在美国"
},
{
"path": "app/src/main/res/layout/activity_local_app_details.xml",
"chars": 10848,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/activity_main.xml",
"chars": 6099,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\r\n Qverbow Util\r\n Copyright (C) 2023 klxiaoniu\r\n\r\n This program is fr"
},
{
"path": "app/src/main/res/layout/applications_config_back_button.xml",
"chars": 3274,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/bottomsheet_shiply_advanced_config.xml",
"chars": 7244,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/cell_bottom_click.xml",
"chars": 2655,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/cell_bottom_switch.xml",
"chars": 2775,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/cell_middle_click.xml",
"chars": 2693,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/cell_middle_switch.xml",
"chars": 2813,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/cell_single_click.xml",
"chars": 2522,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/cell_single_switch.xml",
"chars": 2740,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/cell_top_click.xml",
"chars": 2655,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/cell_top_switch.xml",
"chars": 2775,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/dialog_about.xml",
"chars": 4573,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/dialog_changes_llm_inference.xml",
"chars": 5824,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/dialog_exp_back.xml",
"chars": 3477,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/dialog_experimental_features.xml",
"chars": 4096,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/dialog_firebase_first_info.xml",
"chars": 6078,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/dialog_format_define.xml",
"chars": 18215,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/dialog_guess.xml",
"chars": 9685,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/dialog_hash.xml",
"chars": 2765,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/dialog_loading.xml",
"chars": 2346,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\r\n Qverbow Util\r\n Copyright (C) 2023 klxiaoniu\r\n\r\n This program is fr"
},
{
"path": "app/src/main/res/layout/dialog_local_qq_tim_info.xml",
"chars": 4441,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/dialog_personalization.xml",
"chars": 6441,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/dialog_private_token_setting.xml",
"chars": 7191,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/dialog_setting.xml",
"chars": 9287,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/dialog_shiply.xml",
"chars": 7787,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/dialog_tencent_app_store.xml",
"chars": 5127,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/exp_link_next_button.xml",
"chars": 2788,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/item_exp_back_url_card.xml",
"chars": 1926,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/item_qq_version.xml",
"chars": 12130,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/item_qq_version_detail.xml",
"chars": 13791,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/item_tim_version.xml",
"chars": 8310,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/item_tim_version_detail.xml",
"chars": 8751,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/item_weixin_version.xml",
"chars": 6094,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/item_weixin_version_detail.xml",
"chars": 6700,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/local_qq.xml",
"chars": 2216,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/local_tim.xml",
"chars": 4318,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/local_weixin.xml",
"chars": 2227,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/recycle_qq_version.xml",
"chars": 1343,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/recycle_tim_version.xml",
"chars": 1347,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/success_button.xml",
"chars": 3537,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/update_qvt_button.xml",
"chars": 4437,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout/user_agreement.xml",
"chars": 15525,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout-v26/dialog_changes_llm_inference.xml",
"chars": 5892,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/layout-v26/update_qvt_button.xml",
"chars": 4497,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
"chars": 1074,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
"chars": 1074,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/mipmap-anydpi-v26/scan_shortcut_icon.xml",
"chars": 361,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <b"
},
{
"path": "app/src/main/res/mipmap-anydpi-v26/scan_shortcut_icon_round.xml",
"chars": 358,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <b"
},
{
"path": "app/src/main/res/values/arrays.xml",
"chars": 1520,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\r\n Qverbow Util\r\n Copyright (C) 2023 klxiaoniu\r\n\r\n This program is fr"
},
{
"path": "app/src/main/res/values/attrs.xml",
"chars": 1015,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/values/colors.xml",
"chars": 999,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\r\n Qverbow Util\r\n Copyright (C) 2023 klxiaoniu\r\n\r\n This program is fr"
},
{
"path": "app/src/main/res/values/ic_launcher_background.xml",
"chars": 853,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
},
{
"path": "app/src/main/res/values/scan_shortcut_icon_background.xml",
"chars": 860,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n Qverbow Util\n Copyright (C) 2023 klxiaoniu\n\n This program is free s"
}
]
// ... and 25 more files (download for full content)
About this extraction
This page contains the full source code of the klxiaoniu/QQVersionList GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 225 files (1.2 MB), approximately 345.9k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.