├── settings.gradle
├── .github
├── ISSUE_TEMPLATE
│ ├── config.yml
│ ├── issue_zh_template_suggest.yml
│ ├── issue_zh_template_question.yml
│ ├── issue_en_template_suggest.yml
│ ├── issue_en_template_question.yml
│ ├── issue_zh_template_adaptation.yml
│ ├── issue_en_template_adaptation.yml
│ ├── issue_zh_template_bug.yml
│ └── issue_en_template_bug.yml
├── workflows
│ └── android.yml
└── FUNDING.yml
├── logo.png
├── library
├── src
│ └── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── com
│ │ └── hjq
│ │ └── device
│ │ └── compat
│ │ ├── SystemPropertyCompat.java
│ │ ├── DeviceBrand.java
│ │ ├── DeviceMarketName.java
│ │ └── DeviceOs.java
└── build.gradle
├── app
├── AppSignature.jks
├── gradle.properties
├── src
│ └── main
│ │ ├── res
│ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ │ └── ic_launcher.png
│ │ ├── values
│ │ │ ├── strings.xml
│ │ │ ├── colors.xml
│ │ │ └── styles.xml
│ │ ├── values-v23
│ │ │ └── styles.xml
│ │ └── layout
│ │ │ ├── system_property_activity.xml
│ │ │ └── main_activity.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── com
│ │ └── hjq
│ │ └── device
│ │ └── compat
│ │ └── demo
│ │ ├── SystemPropertyActivity.java
│ │ ├── MainActivity.java
│ │ └── ShellUtils.java
├── proguard-rules.pro
└── build.gradle
├── picture
├── demo_os_miui.jpg
├── demo_os_one_ui.jpg
├── demo_os_color_os.jpg
├── demo_os_magic_os.jpg
├── demo_os_harmony_os.jpg
├── android_brand_os_summary.png
├── download_demo_apk_qr_code.png
└── demo_os_harmony_os_next_android_compatible.jpg
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── gradle.properties
├── common.gradle
├── gradlew.bat
├── gradlew
├── LICENSE
└── README.md
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':library'
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
--------------------------------------------------------------------------------
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/getActivity/DeviceCompat/HEAD/logo.png
--------------------------------------------------------------------------------
/library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/AppSignature.jks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/getActivity/DeviceCompat/HEAD/app/AppSignature.jks
--------------------------------------------------------------------------------
/picture/demo_os_miui.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/getActivity/DeviceCompat/HEAD/picture/demo_os_miui.jpg
--------------------------------------------------------------------------------
/picture/demo_os_one_ui.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/getActivity/DeviceCompat/HEAD/picture/demo_os_one_ui.jpg
--------------------------------------------------------------------------------
/picture/demo_os_color_os.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/getActivity/DeviceCompat/HEAD/picture/demo_os_color_os.jpg
--------------------------------------------------------------------------------
/picture/demo_os_magic_os.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/getActivity/DeviceCompat/HEAD/picture/demo_os_magic_os.jpg
--------------------------------------------------------------------------------
/picture/demo_os_harmony_os.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/getActivity/DeviceCompat/HEAD/picture/demo_os_harmony_os.jpg
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/getActivity/DeviceCompat/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/picture/android_brand_os_summary.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/getActivity/DeviceCompat/HEAD/picture/android_brand_os_summary.png
--------------------------------------------------------------------------------
/picture/download_demo_apk_qr_code.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/getActivity/DeviceCompat/HEAD/picture/download_demo_apk_qr_code.png
--------------------------------------------------------------------------------
/app/gradle.properties:
--------------------------------------------------------------------------------
1 | StoreFile = AppSignature.jks
2 | StorePassword = AndroidProject
3 | KeyAlias = AndroidProject
4 | KeyPassword = AndroidProject
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | .idea
3 | .cxx
4 | .externalNativeBuild
5 | build
6 | captures
7 |
8 | ._*
9 | *.iml
10 | .DS_Store
11 | local.properties
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/getActivity/DeviceCompat/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/getActivity/DeviceCompat/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/getActivity/DeviceCompat/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | DeviceCompat
4 |
--------------------------------------------------------------------------------
/picture/demo_os_harmony_os_next_android_compatible.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/getActivity/DeviceCompat/HEAD/picture/demo_os_harmony_os_next_android_compatible.jpg
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #000000
4 | #000000
5 | #FF0033
6 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | zipStoreBase = GRADLE_USER_HOME
2 | zipStorePath = wrapper/dists
3 | distributionBase = GRADLE_USER_HOME
4 | distributionPath = wrapper/dists
5 | distributionUrl = https\://services.gradle.org/distributions/gradle-6.5-all.zip
--------------------------------------------------------------------------------
/.github/workflows/android.yml:
--------------------------------------------------------------------------------
1 | name: Android CI
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 |
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | - uses: actions/checkout@v2
12 | - name: set up JDK 1.8
13 | uses: actions/setup-java@v1
14 | with:
15 | java-version: 1.8
16 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v23/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
15 |
16 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: https://raw.githubusercontent.com/getActivity/Donate/master/picture/pay_ali.png
13 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in D:\SDK\Studio\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/common.gradle:
--------------------------------------------------------------------------------
1 | // 通用配置
2 | android {
3 |
4 | // 编译源码版本
5 | compileSdk 34
6 | defaultConfig {
7 | // 最低安装版本
8 | minSdk 17
9 | // Android 版本适配指南:https://github.com/getActivity/AndroidVersionAdapter
10 | targetSdk 34
11 | versionCode 25
12 | versionName "2.5"
13 | }
14 |
15 | // 支持 Java JDK 8
16 | compileOptions {
17 | targetCompatibility JavaVersion.VERSION_1_8
18 | sourceCompatibility JavaVersion.VERSION_1_8
19 | }
20 |
21 | // 读取 local.properties 文件配置
22 | def properties = new Properties()
23 | def localPropertiesFile = rootProject.file("local.properties")
24 | if (localPropertiesFile.exists()) {
25 | localPropertiesFile.withInputStream { inputStream ->
26 | properties.load(inputStream)
27 | }
28 | }
29 |
30 | String buildDirPath = properties.getProperty("build.dir")
31 | if (buildDirPath != null && buildDirPath != "") {
32 | // 将构建文件统一输出到指定的目录下
33 | setBuildDir(new File(buildDirPath, rootProject.name + "/build/${path.replaceAll(':', '/')}"))
34 | } else {
35 | // 将构建文件统一输出到项目根目录下的 build 文件夹
36 | setBuildDir(new File(rootDir, "build/${path.replaceAll(':', '/')}"))
37 | }
38 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/system_property_activity.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
18 |
19 |
26 |
27 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hjq/device/compat/demo/SystemPropertyActivity.java:
--------------------------------------------------------------------------------
1 | package com.hjq.device.compat.demo;
2 |
3 | import android.content.Intent;
4 | import android.net.Uri;
5 | import android.os.Bundle;
6 | import android.support.v7.app.AppCompatActivity;
7 | import android.util.Log;
8 | import android.widget.TextView;
9 | import com.hjq.bar.OnTitleBarListener;
10 | import com.hjq.bar.TitleBar;
11 | import com.hjq.device.compat.demo.ShellUtils.CommandResult;
12 |
13 | public final class SystemPropertyActivity extends AppCompatActivity {
14 |
15 | @Override
16 | protected void onCreate(Bundle savedInstanceState) {
17 | super.onCreate(savedInstanceState);
18 | setContentView(R.layout.system_property_activity);
19 |
20 | TextView messageView = findViewById(R.id.tv_system_property_messages);
21 |
22 | TitleBar titleBar = findViewById(R.id.tb_system_property_bar);
23 | titleBar.setOnTitleBarListener(new OnTitleBarListener() {
24 | @Override
25 | public void onTitleClick(TitleBar titleBar) {
26 | Intent intent = new Intent(Intent.ACTION_VIEW);
27 | intent.setData(Uri.parse(titleBar.getTitle().toString()));
28 | startActivity(intent);
29 | }
30 | });
31 |
32 | new Thread(() -> {
33 | CommandResult systemPropertyInfo = ShellUtils.execCmd("getprop", false, true);
34 | Log.i("DeviceCompat", systemPropertyInfo.successMsg);
35 | runOnUiThread(() -> messageView.setText(systemPropertyInfo.successMsg));
36 | }).start();
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/issue_zh_template_suggest.yml:
--------------------------------------------------------------------------------
1 | name: 提交建议
2 | description: 请告诉我框架的不足之处,让我做得更好!
3 | title: "[建议]:"
4 | labels: ["help wanted"]
5 |
6 | body:
7 | - type: markdown
8 | attributes:
9 | value: |
10 | ## [【警告:请务必按照 issue 模板填写,不要抱有侥幸心理,一旦发现 issue 没有按照模板认真填写,一律直接关闭】](https://github.com/getActivity/IssueTemplateGuide)
11 | - type: textarea
12 | id: input_id_1
13 | attributes:
14 | label: 你觉得框架有什么不足之处?【必答】
15 | description: 你可以描述框架有什么令你不满意的地方
16 | validations:
17 | required: true
18 | - type: dropdown
19 | id: input_id_2
20 | attributes:
21 | label: issue 是否有人曾提过类似的建议?【必答】
22 | description: 一旦出现重复提问我将不会再次解答
23 | multiple: false
24 | options:
25 | - "未选择"
26 | - "是"
27 | - "否"
28 | validations:
29 | required: true
30 | - type: dropdown
31 | id: input_id_3
32 | attributes:
33 | label: 框架文档是否提及了该问题【必答】
34 | description: 文档会提供最常见的问题解答,可以先看看是否有自己想要的
35 | multiple: false
36 | options:
37 | - "未选择"
38 | - "是"
39 | - "否"
40 | validations:
41 | required: true
42 | - type: dropdown
43 | id: input_id_4
44 | attributes:
45 | label: 是否已经查阅框架文档但还未能解决的【必答】
46 | description: 如果查阅了文档但还是没有解决的话,可以选择是
47 | multiple: false
48 | options:
49 | - "未选择"
50 | - "是"
51 | - "否"
52 | validations:
53 | required: true
54 | - type: textarea
55 | id: input_id_5
56 | attributes:
57 | label: 你觉得该怎么去完善会比较好?【非必答】
58 | description: 你可以提供一下自己的想法或者做法供作者参考
59 | validations:
60 | required: false
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply from : '../common.gradle'
3 |
4 | android {
5 |
6 | defaultConfig {
7 | applicationId "com.hjq.device.compat.demo"
8 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
9 | }
10 |
11 | // Apk 签名的那些事:https://www.jianshu.com/p/a1f8e5896aa2
12 | signingConfigs {
13 | config {
14 | storeFile file(StoreFile)
15 | storePassword StorePassword
16 | keyAlias KeyAlias
17 | keyPassword KeyPassword
18 | }
19 | }
20 |
21 | buildTypes {
22 | debug {
23 | minifyEnabled false
24 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
25 | signingConfig signingConfigs.config
26 | }
27 |
28 | release {
29 | minifyEnabled true
30 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
31 | signingConfig signingConfigs.config
32 | }
33 | }
34 |
35 | applicationVariants.configureEach { variant ->
36 | // apk 输出文件名配置
37 | variant.outputs.configureEach { output ->
38 | outputFileName = rootProject.getName() + '.apk'
39 | }
40 | }
41 | }
42 |
43 | dependencies {
44 | // 依赖 libs 目录下所有的 jar 和 aar 包
45 | implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs')
46 |
47 | implementation project(':library')
48 |
49 | implementation 'com.android.support:appcompat-v7:28.0.0'
50 | implementation 'com.android.support:design:28.0.0'
51 |
52 | // 标题栏框架:https://github.com/getActivity/TitleBar
53 | implementation 'com.github.getActivity:TitleBar:10.6'
54 | }
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/issue_zh_template_question.yml:
--------------------------------------------------------------------------------
1 | name: 提出疑问
2 | description: 提出你的困惑,我会给你解答
3 | title: "[疑惑]:"
4 | labels: ["question"]
5 |
6 | body:
7 | - type: markdown
8 | attributes:
9 | value: |
10 | ## [【警告:请务必按照 issue 模板填写,不要抱有侥幸心理,一旦发现 issue 没有按照模板认真填写,一律直接关闭】](https://github.com/getActivity/IssueTemplateGuide)
11 | - type: textarea
12 | id: input_id_1
13 | attributes:
14 | label: 问题描述【必填】
15 | description: 请描述一下你的问题(注意:如果确定是框架 bug 请不要在这里提,否则一概不受理)
16 | validations:
17 | required: true
18 | - type: dropdown
19 | id: input_id_2
20 | attributes:
21 | label: 框架文档是否提及了该问题【必答】
22 | description: 文档会提供最常见的问题解答,可以先看看是否有自己想要的
23 | multiple: false
24 | options:
25 | - "未选择"
26 | - "是"
27 | - "否"
28 | validations:
29 | required: true
30 | - type: dropdown
31 | id: input_id_3
32 | attributes:
33 | label: 是否已经查阅框架文档但还未能解决的【必答】
34 | description: 如果查阅了文档但还是没有解决的话,可以选择是
35 | multiple: false
36 | options:
37 | - "未选择"
38 | - "是"
39 | - "否"
40 | validations:
41 | required: true
42 | - type: dropdown
43 | id: input_id_4
44 | attributes:
45 | label: issue 列表中是否有人曾提过类似的问题【必答】
46 | description: 可以在 issue 列表在搜索问题关键字,参考一下别人的解决方案
47 | multiple: false
48 | options:
49 | - "未选择"
50 | - "是"
51 | - "否"
52 | validations:
53 | required: true
54 | - type: dropdown
55 | id: input_id_5
56 | attributes:
57 | label: 是否已经搜索过了 issue 列表但还未能解决的【必答】
58 | description: 如果搜索过了 issue 列表但是问题没有解决的话,可以选择是
59 | multiple: false
60 | options:
61 | - "未选择"
62 | - "是"
63 | - "否"
64 | validations:
65 | required: true
--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply from : '../common.gradle'
3 |
4 | android {
5 |
6 | resourcePrefix "demo_"
7 |
8 | // lintOptions {
9 | // // 忽略 lint 检查
10 | // ignoreWarnings = true
11 | // }
12 |
13 | lintOptions {
14 | abortOnError false // true by default
15 | checkAllWarnings false
16 | checkReleaseBuilds false
17 | ignoreWarnings true // false by default
18 | quiet true // false by default
19 | }
20 |
21 | android.libraryVariants.configureEach { variant ->
22 | // aar 输出文件名配置
23 | variant.outputs.all { output ->
24 | outputFileName = "${rootProject.name}-${android.defaultConfig.versionName}.aar"
25 | }
26 | }
27 | }
28 |
29 | dependencies {
30 | // noinspection GradleCompatible
31 | compileOnly 'com.android.support:support-annotations:24.2.0'
32 | }
33 |
34 | afterEvaluate {
35 | // 排除 BuildConfig.class 和 R.class
36 | generateReleaseBuildConfig.enabled = false
37 | generateDebugBuildConfig.enabled = false
38 | generateReleaseResValues.enabled = false
39 | generateDebugResValues.enabled = false
40 | }
41 |
42 | // 防止编码问题
43 | tasks.withType(Javadoc).configureEach {
44 | options.addStringOption('Xdoclint:none', '-quiet')
45 | options.addStringOption('encoding', 'UTF-8')
46 | options.addStringOption('charSet', 'UTF-8')
47 | }
48 |
49 | tasks.register('sourcesJar', Jar) {
50 | from android.sourceSets.main.java.srcDirs
51 | classifier = 'sources'
52 | }
53 |
54 | tasks.register('javadoc', Javadoc) {
55 | source = android.sourceSets.main.java.srcDirs
56 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
57 | }
58 |
59 | tasks.register('javadocJar', Jar) {
60 | dependsOn javadoc
61 | classifier = 'javadoc'
62 | from javadoc.destinationDir
63 | }
64 |
65 | artifacts {
66 | archives javadocJar
67 | archives sourcesJar
68 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/main_activity.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
18 |
19 |
25 |
26 |
31 |
32 |
38 |
39 |
45 |
46 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/issue_en_template_suggest.yml:
--------------------------------------------------------------------------------
1 | name: Submit Suggestion
2 | description: Please let me know the shortcomings of the framework, so that I can improve it!
3 | title: "[Suggestion]:"
4 | labels: ["help wanted"]
5 |
6 | body:
7 | - type: markdown
8 | attributes:
9 | value: |
10 | ## [Warning: Please make sure to fill in the issue template accurately. If an issue is found to be filled incorrectly, it will be closed without further notice.](https://github.com/getActivity/IssueTemplateGuide)
11 | - type: textarea
12 | id: input_id_1
13 | attributes:
14 | label: What are the shortcomings you have noticed in the framework? [Required]
15 | description: You can describe any aspects of the framework that you are not satisfied with.
16 | validations:
17 | required: true
18 | - type: dropdown
19 | id: input_id_2
20 | attributes:
21 | label: Has a similar suggestion been made in the issue list? [Required]
22 | description: If a similar suggestion has already been made, I will not address it again.
23 | multiple: false
24 | options:
25 | - "Not Selected"
26 | - "Yes"
27 | - "No"
28 | validations:
29 | required: true
30 | - type: dropdown
31 | id: input_id_3
32 | attributes:
33 | label: Is the suggestion mentioned in the framework documentation? [Required]
34 | description: The documentation provides answers to frequently asked questions. Please check if the information you are looking for is already provided.
35 | multiple: false
36 | options:
37 | - "Not Selected"
38 | - "Yes"
39 | - "No"
40 | validations:
41 | required: true
42 | - type: dropdown
43 | id: input_id_4
44 | attributes:
45 | label: Did you consult the framework documentation but couldn't find a solution? [Required]
46 | description: If you have consulted the documentation but still couldn't find a solution, you can select "Yes."
47 | multiple: false
48 | options:
49 | - "Not Selected"
50 | - "Yes"
51 | - "No"
52 | validations:
53 | required: true
54 | - type: textarea
55 | id: input_id_5
56 | attributes:
57 | label: How do you suggest improving it? [Optional]
58 | description: You can provide your ideas or approaches for the author's reference.
59 | validations:
60 | required: false
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/issue_en_template_question.yml:
--------------------------------------------------------------------------------
1 | name: Ask a Question
2 | description: Ask your questions, and I will provide you with answers.
3 | title: "[Question]:"
4 | labels: ["question"]
5 |
6 | body:
7 | - type: markdown
8 | attributes:
9 | value: |
10 | ## [Warning: Please make sure to fill in the issue template accurately. If an issue is found to be filled incorrectly, it will be closed without further notice.](https://github.com/getActivity/IssueTemplateGuide)
11 | - type: textarea
12 | id: input_id_1
13 | attributes:
14 | label: Question Description [Required]
15 | description: Please describe your question (Note, If it is a framework bug, please do not raise it here, as it will not be accepted).
16 | validations:
17 | required: true
18 | - type: dropdown
19 | id: input_id_2
20 | attributes:
21 | label: Is the issue mentioned in the framework documentation? [Required]
22 | description: The documentation provides answers to frequently asked questions. Please check if the information you are looking for is already provided.
23 | multiple: false
24 | options:
25 | - "Not Selected"
26 | - "Yes"
27 | - "No"
28 | validations:
29 | required: true
30 | - type: dropdown
31 | id: input_id_3
32 | attributes:
33 | label: Did you consult the framework documentation but couldn't find a solution? [Required]
34 | description: If you have consulted the documentation but still couldn't find a solution, you can select "Yes."
35 | multiple: false
36 | options:
37 | - "Not Selected"
38 | - "Yes"
39 | - "No"
40 | validations:
41 | required: true
42 | - type: dropdown
43 | id: input_id_4
44 | attributes:
45 | label: Has a similar issue been reported in the issue list? [Required]
46 | description: You can search the issue list for keywords related to your problem and refer to the solutions provided by others.
47 | multiple: false
48 | options:
49 | - "Not Selected"
50 | - "Yes"
51 | - "No"
52 | validations:
53 | required: true
54 | - type: dropdown
55 | id: input_id_5
56 | attributes:
57 | label: Have you searched the issue list but couldn't find a solution? [Required]
58 | description: If you have searched the issue list and couldn't find a solution, you can select "Yes."
59 | multiple: false
60 | options:
61 | - "Not Selected"
62 | - "Yes"
63 | - "No"
64 | validations:
65 | required: true
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hjq/device/compat/demo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.hjq.device.compat.demo;
2 |
3 | import android.content.Intent;
4 | import android.net.Uri;
5 | import android.os.Build;
6 | import android.os.Bundle;
7 | import android.provider.Settings;
8 | import android.support.v7.app.AppCompatActivity;
9 | import android.view.View;
10 | import android.view.View.OnClickListener;
11 | import android.widget.TextView;
12 | import com.hjq.bar.OnTitleBarListener;
13 | import com.hjq.bar.TitleBar;
14 | import com.hjq.device.compat.DeviceBrand;
15 | import com.hjq.device.compat.DeviceMarketName;
16 | import com.hjq.device.compat.DeviceOs;
17 |
18 | public final class MainActivity extends AppCompatActivity implements OnClickListener {
19 |
20 | @Override
21 | protected void onCreate(Bundle savedInstanceState) {
22 | super.onCreate(savedInstanceState);
23 | setContentView(R.layout.main_activity);
24 |
25 | TextView messageView = findViewById(R.id.tv_main_messages);
26 | StringBuilder stringBuilder = new StringBuilder()
27 | .append("BrandName: " + DeviceBrand.getBrandName())
28 | .append("\nMarketName: " + DeviceMarketName.getMarketName(this))
29 | .append("\nOsName: " + DeviceOs.getOsName())
30 | .append("\nOsVersionName: " + DeviceOs.getOsVersionName())
31 | .append("\nOsBigVersionCode: " + DeviceOs.getOsBigVersionCode())
32 | .append("\nAndroidVersion: Android " + Build.VERSION.RELEASE)
33 | .append("\nAndroidApiLevel: " + Build.VERSION.SDK_INT);
34 | messageView.setText(stringBuilder);
35 |
36 | findViewById(R.id.btn_main_about_device).setOnClickListener(this);
37 | findViewById(R.id.btn_main_get_system_property).setOnClickListener(this);
38 | findViewById(R.id.btn_main_android_settings).setOnClickListener(this);
39 |
40 | TitleBar titleBar = findViewById(R.id.tb_main_bar);
41 | titleBar.setOnTitleBarListener(new OnTitleBarListener() {
42 | @Override
43 | public void onTitleClick(TitleBar titleBar) {
44 | Intent intent = new Intent(Intent.ACTION_VIEW);
45 | intent.setData(Uri.parse(titleBar.getTitle().toString()));
46 | startActivity(intent);
47 | }
48 | });
49 | }
50 |
51 | @Override
52 | public void onClick(View view) {
53 | int viewId = view.getId();
54 | if (viewId == R.id.btn_main_about_device) {
55 | // 跳转到关于手机界面
56 | startActivity(new Intent(Settings.ACTION_DEVICE_INFO_SETTINGS));
57 | } else if (viewId == R.id.btn_main_get_system_property) {
58 | // 跳转到获取系统属性界面
59 | startActivity(new Intent(this, SystemPropertyActivity.class));
60 | } else if (viewId == R.id.btn_main_android_settings) {
61 | // 跳转到 Android 设置界面
62 | startActivity(new Intent(Settings.ACTION_SETTINGS));
63 | }
64 | }
65 | }
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/issue_zh_template_adaptation.yml:
--------------------------------------------------------------------------------
1 | name: 提交厂商适配问题
2 | description: 厂商系统的新增适配或者适配有误的问题
3 | title: "[Adaptation]:"
4 | labels: ["adaptation"]
5 |
6 | body:
7 | - type: markdown
8 | attributes:
9 | value: |
10 | ## [【警告:请务必按照 issue 模板填写,不要抱有侥幸心理,一旦发现 issue 没有按照模板认真填写,一律直接关闭】](https://github.com/getActivity/IssueTemplateGuide)
11 | - type: input
12 | id: input_id_1
13 | attributes:
14 | label: 框架版本【必填】
15 | description: 请输入你使用的框架版本
16 | validations:
17 | required: true
18 | - type: dropdown
19 | id: input_id_2
20 | attributes:
21 | label: 框架最新的版本是否存在这个问题【必答】
22 | description: 如果用的是旧版本的话,建议升级看问题是否还存在
23 | multiple: false
24 | options:
25 | - "未选择"
26 | - "是"
27 | - "否"
28 | validations:
29 | required: true
30 | - type: dropdown
31 | id: input_id_3
32 | attributes:
33 | label: 框架文档是否提及了该问题【必答】
34 | description: 文档会提供最常见的问题解答,可以先看看是否有自己想要的
35 | multiple: false
36 | options:
37 | - "未选择"
38 | - "是"
39 | - "否"
40 | validations:
41 | required: true
42 | - type: dropdown
43 | id: input_id_4
44 | attributes:
45 | label: issue 列表中是否有人曾提过类似的问题【必答】
46 | description: 可以在 issue 列表在搜索问题关键字,参考一下别人的解决方案
47 | multiple: false
48 | options:
49 | - "未选择"
50 | - "是"
51 | - "否"
52 | validations:
53 | required: true
54 | - type: dropdown
55 | id: input_id_5
56 | attributes:
57 | label: 是否已经搜索过了 issue 列表但还未能解决的【必答】
58 | description: 如果搜索过了 issue 列表但是问题没有解决的话,可以选择是
59 | multiple: false
60 | options:
61 | - "未选择"
62 | - "是"
63 | - "否"
64 | validations:
65 | required: true
66 | - type: input
67 | id: input_id_6
68 | attributes:
69 | label: 出现问题的设备信息【必填】
70 | description: 请填写出现问题设备的品牌和机型
71 | validations:
72 | required: true
73 | - type: input
74 | id: input_id_7
75 | attributes:
76 | label: 出现问题的 Android 版本【必填】
77 | description: 请填写出现问题的 Android 版本
78 | validations:
79 | required: true
80 | - type: input
81 | id: input_id_8
82 | attributes:
83 | label: 出现问题的厂商 Os 类型【必填】
84 | description: 请输入设备厂商的 Os 名称
85 | validations:
86 | required: true
87 | - type: input
88 | id: input_id_9
89 | attributes:
90 | label: 出现问题的厂商 Os 版本【必填】
91 | description: 请输入设备厂商的 Os 版本
92 | validations:
93 | required: true
94 | - type: textarea
95 | id: input_id_10
96 | attributes:
97 | label: 出现问题的设备系统属性【必填】
98 | description: 请用 adb shell getprop 命令获取所有的系统属性并且填写到此处
99 | validations:
100 | required: true
101 | - type: textarea
102 | id: input_id_11
103 | attributes:
104 | label: 出现问题的设备信息截图【必填】
105 | description: 如果是手机,请截图关于手机页面并且上传到此处,截图中应该有厂商 Os 名称和版本,其他设备也是同理
106 | validations:
107 | required: true
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/issue_en_template_adaptation.yml:
--------------------------------------------------------------------------------
1 | name: Submit Manufacturer Adaptation Issue
2 | description: Issues related to new adaptations or incorrect adaptations of manufacturer systems
3 | title: "[Adaptation]:"
4 | labels: ["adaptation"]
5 |
6 | body:
7 | - type: markdown
8 | attributes:
9 | value: |
10 | ## [【Warning: Please fill in according to the issue template. Do not take chances. Any issue found not seriously filled in according to the template will be closed directly】](https://github.com/getActivity/IssueTemplateGuide)
11 | - type: input
12 | id: input_id_1
13 | attributes:
14 | label: Framework Version【Required】
15 | description: Please enter the version of the framework you are using
16 | validations:
17 | required: true
18 | - type: dropdown
19 | id: input_id_2
20 | attributes:
21 | label: Does the latest version of the framework have this issue【Required】
22 | description: If you are using an older version, it is recommended to upgrade to see if the issue still exists
23 | multiple: false
24 | options:
25 | - "Not selected"
26 | - "Yes"
27 | - "No"
28 | validations:
29 | required: true
30 | - type: dropdown
31 | id: input_id_3
32 | attributes:
33 | label: Does the framework documentation mention this issue【Required】
34 | description: The documentation provides answers to the most common questions. You can check if it contains what you need first
35 | multiple: false
36 | options:
37 | - "Not selected"
38 | - "Yes"
39 | - "No"
40 | validations:
41 | required: true
42 | - type: dropdown
43 | id: input_id_4
44 | attributes:
45 | label: Has anyone raised a similar issue in the issue list【Required】
46 | description: You can search for keywords in the issue list to refer to others' solutions
47 | multiple: false
48 | options:
49 | - "Not selected"
50 | - "Yes"
51 | - "No"
52 | validations:
53 | required: true
54 | - type: dropdown
55 | id: input_id_5
56 | attributes:
57 | label: Have you searched the issue list but still couldn't solve it【Required】
58 | description: If you have searched the issue list but the problem remains unsolved, you can select "Yes"
59 | multiple: false
60 | options:
61 | - "Not selected"
62 | - "Yes"
63 | - "No"
64 | validations:
65 | required: true
66 | - type: input
67 | id: input_id_6
68 | attributes:
69 | label: Device Information with the Issue【Required】
70 | description: Please fill in the brand and model of the device with the issue
71 | validations:
72 | required: true
73 | - type: input
74 | id: input_id_7
75 | attributes:
76 | label: Android Version with the Issue【Required】
77 | description: Please fill in the Android version where the issue occurs
78 | validations:
79 | required: true
80 | - type: input
81 | id: input_id_8
82 | attributes:
83 | label: Manufacturer's OS Type with the Issue【Required】
84 | description: Please enter the manufacturer's OS name of the device
85 | validations:
86 | required: true
87 | - type: input
88 | id: input_id_9
89 | attributes:
90 | label: Manufacturer's OS Version with the Issue【Required】
91 | description: Please enter the manufacturer's OS version of the device
92 | validations:
93 | required: true
94 | - type: textarea
95 | id: input_id_10
96 | attributes:
97 | label: System Properties of the Device with the Issue【Required】
98 | description: Please use the adb shell getprop command to obtain all system properties and fill them in here
99 | validations:
100 | required: true
101 | - type: textarea
102 | id: input_id_11
103 | attributes:
104 | label: Screenshot of Device Information with the Issue【Required】
105 | description: If it's a mobile phone, please take a screenshot of the "About Phone" page and upload it here. The screenshot should include the manufacturer's OS name and version. The same applies to other devices
106 | validations:
107 | required: true
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/issue_zh_template_bug.yml:
--------------------------------------------------------------------------------
1 | name: 提交 Bug
2 | description: 请告诉我框架存在的问题,我会协助你解决此问题!
3 | title: "[Bug]:"
4 | labels: ["bug"]
5 |
6 | body:
7 | - type: markdown
8 | attributes:
9 | value: |
10 | ## [【警告:请务必按照 issue 模板填写,不要抱有侥幸心理,一旦发现 issue 没有按照模板认真填写,一律直接关闭】](https://github.com/getActivity/IssueTemplateGuide)
11 | - type: input
12 | id: input_id_1
13 | attributes:
14 | label: 框架版本【必填】
15 | description: 请输入你使用的框架版本
16 | validations:
17 | required: true
18 | - type: textarea
19 | id: input_id_2
20 | attributes:
21 | label: 问题描述【必填】
22 | description: 请输入你对这个问题的描述
23 | validations:
24 | required: true
25 | - type: textarea
26 | id: input_id_3
27 | attributes:
28 | label: 复现步骤【必填】
29 | description: 请输入问题的复现步骤
30 | validations:
31 | required: true
32 | - type: dropdown
33 | id: input_id_4
34 | attributes:
35 | label: 是否必现【必填】
36 | multiple: false
37 | options:
38 | - "未选择"
39 | - "是"
40 | - "否"
41 | validations:
42 | required: true
43 | - type: input
44 | id: input_id_5
45 | attributes:
46 | label: 项目 targetSdkVersion【必填】
47 | validations:
48 | required: true
49 | - type: input
50 | id: input_id_6
51 | attributes:
52 | label: 出现问题的手机信息【必填】
53 | description: 请填写出现问题的品牌和机型
54 | validations:
55 | required: true
56 | - type: input
57 | id: input_id_7
58 | attributes:
59 | label: 出现问题的安卓版本【必填】
60 | description: 请填写出现问题的 Android 版本
61 | validations:
62 | required: true
63 | - type: dropdown
64 | id: input_id_8
65 | attributes:
66 | label: 问题信息的来源渠道【必填】
67 | multiple: true
68 | options:
69 | - 自己遇到的
70 | - Bugly 看到的
71 | - 用户反馈
72 | - 其他渠道
73 | - type: input
74 | id: input_id_9
75 | attributes:
76 | label: 是部分机型还是所有机型都会出现【必答】
77 | description: 部分/全部(例如:某为,某 Android 版本会出现)
78 | validations:
79 | required: true
80 | - type: dropdown
81 | id: input_id_10
82 | attributes:
83 | label: 框架最新的版本是否存在这个问题【必答】
84 | description: 如果用的是旧版本的话,建议升级看问题是否还存在
85 | multiple: false
86 | options:
87 | - "未选择"
88 | - "是"
89 | - "否"
90 | validations:
91 | required: true
92 | - type: dropdown
93 | id: input_id_11
94 | attributes:
95 | label: 框架文档是否提及了该问题【必答】
96 | description: 文档会提供最常见的问题解答,可以先看看是否有自己想要的
97 | multiple: false
98 | options:
99 | - "未选择"
100 | - "是"
101 | - "否"
102 | validations:
103 | required: true
104 | - type: dropdown
105 | id: input_id_12
106 | attributes:
107 | label: 是否已经查阅框架文档但还未能解决的【必答】
108 | description: 如果查阅了文档但还是没有解决的话,可以选择是
109 | multiple: false
110 | options:
111 | - "未选择"
112 | - "是"
113 | - "否"
114 | validations:
115 | required: true
116 | - type: dropdown
117 | id: input_id_13
118 | attributes:
119 | label: issue 列表中是否有人曾提过类似的问题【必答】
120 | description: 可以在 issue 列表在搜索问题关键字,参考一下别人的解决方案
121 | multiple: false
122 | options:
123 | - "未选择"
124 | - "是"
125 | - "否"
126 | validations:
127 | required: true
128 | - type: dropdown
129 | id: input_id_14
130 | attributes:
131 | label: 是否已经搜索过了 issue 列表但还未能解决的【必答】
132 | description: 如果搜索过了 issue 列表但是问题没有解决的话,可以选择是
133 | multiple: false
134 | options:
135 | - "未选择"
136 | - "是"
137 | - "否"
138 | validations:
139 | required: true
140 | - type: dropdown
141 | id: input_id_15
142 | attributes:
143 | label: 是否可以通过 Demo 来复现该问题【必答】
144 | description: 排查一下是不是自己的项目代码写得有问题导致的
145 | multiple: false
146 | options:
147 | - "未选择"
148 | - "是"
149 | - "否"
150 | validations:
151 | required: true
152 | - type: textarea
153 | id: input_id_16
154 | attributes:
155 | label: 提供报错堆栈
156 | description: 如果有报错的话必填,注意不要拿被混淆过的代码堆栈上来
157 | render: text
158 | validations:
159 | required: false
160 | - type: textarea
161 | id: input_id_17
162 | attributes:
163 | label: 提供截图或视频
164 | description: 根据需要提供,此项不强制
165 | validations:
166 | required: false
167 | - type: textarea
168 | id: input_id_18
169 | attributes:
170 | label: 提供解决方案
171 | description: 如果已经解决了的话,此项不强制
172 | validations:
173 | required: false
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/library/src/main/java/com/hjq/device/compat/SystemPropertyCompat.java:
--------------------------------------------------------------------------------
1 | package com.hjq.device.compat;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.os.Environment;
5 | import android.support.annotation.NonNull;
6 | import android.support.annotation.Nullable;
7 | import android.text.TextUtils;
8 | import java.io.BufferedReader;
9 | import java.io.File;
10 | import java.io.FileInputStream;
11 | import java.io.IOException;
12 | import java.io.InputStreamReader;
13 | import java.lang.reflect.InvocationTargetException;
14 | import java.lang.reflect.Method;
15 | import java.util.Properties;
16 |
17 | /**
18 | * author : Android 轮子哥
19 | * github : https://github.com/getActivity/XXPermissions
20 | * time : 2025/08/13
21 | * desc : 系统属性兼容类
22 | */
23 | public final class SystemPropertyCompat {
24 |
25 | private SystemPropertyCompat() {
26 | // 私有化构造方法,禁止外部实例化
27 | }
28 |
29 | /**
30 | * 获取单个系统属性值
31 | */
32 | @NonNull
33 | public static String getSystemPropertyValue(@Nullable String key) {
34 | if (key == null || key.isEmpty()) {
35 | return "";
36 | }
37 |
38 | String propertyValue = null;
39 | try {
40 | propertyValue = getSystemPropertyByReflect(key);
41 | } catch (Exception ignored) {
42 | // default implementation ignored
43 | }
44 |
45 | if (propertyValue != null && !propertyValue.isEmpty()) {
46 | return propertyValue;
47 | }
48 |
49 | try {
50 | propertyValue = getSystemPropertyByShell(key);
51 | } catch (IOException ignored) {
52 | // default implementation ignored
53 | }
54 |
55 | if (propertyValue != null && !propertyValue.isEmpty()) {
56 | return propertyValue;
57 | }
58 |
59 | try {
60 | propertyValue = getSystemPropertyByStream(key);
61 | } catch (IOException ignored) {
62 | // default implementation ignored
63 | }
64 |
65 | if (propertyValue != null && !propertyValue.isEmpty()) {
66 | return propertyValue;
67 | }
68 |
69 | return "";
70 | }
71 |
72 | /**
73 | * 获取多个系统属性值
74 | */
75 | @NonNull
76 | public static String[] getSystemPropertyValues(@Nullable String[] keys) {
77 | if (keys == null) {
78 | return new String[0];
79 | }
80 |
81 | String[] propertyValues = new String[keys.length];
82 |
83 | for (int i = 0; i < keys.length; i++) {
84 | propertyValues[i] = getSystemPropertyValue(keys[i]);
85 | }
86 | return propertyValues;
87 | }
88 |
89 | /**
90 | * 获取多个系统属性中的任一一个值
91 | */
92 | @NonNull
93 | public static String getSystemPropertyAnyOneValue(@Nullable String[] keys) {
94 | if (keys == null) {
95 | return "";
96 | }
97 |
98 | for (String key : keys) {
99 | String propertyValue = getSystemPropertyValue(key);
100 | if (!propertyValue.isEmpty()) {
101 | return propertyValue;
102 | }
103 | }
104 | return "";
105 | }
106 |
107 | /**
108 | * 判断某个系统属性是否存在
109 | */
110 | public static boolean isSystemPropertyExist(@Nullable String key) {
111 | return !TextUtils.isEmpty(getSystemPropertyValue(key));
112 | }
113 |
114 | /**
115 | * 判断多个系统属性是否有任一一个存在
116 | */
117 | public static boolean isSystemPropertyAnyOneExist(@Nullable String[] keys) {
118 | if (keys == null) {
119 | return false;
120 | }
121 | for (String key : keys) {
122 | if (isSystemPropertyExist(key)) {
123 | return true;
124 | }
125 | }
126 | return false;
127 | }
128 |
129 | /**
130 | * 获取系统属性值(通过反射系统类)
131 | */
132 | @SuppressLint("PrivateApi")
133 | private static String getSystemPropertyByReflect(@NonNull String key) throws ClassNotFoundException, InvocationTargetException,
134 | NoSuchMethodException, IllegalAccessException {
135 | Class> clz = Class.forName("android.os.SystemProperties");
136 | Method getMethod = clz.getMethod("get", String.class, String.class);
137 | return (String) getMethod.invoke(clz, key, "");
138 | }
139 |
140 | /**
141 | * 获取系统属性值(通过 shell 命令)
142 | */
143 | private static String getSystemPropertyByShell(@NonNull String key) throws IOException {
144 | BufferedReader input = null;
145 | try {
146 | Process p = Runtime.getRuntime().exec("getprop " + key);
147 | input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024);
148 | String firstLine = input.readLine();
149 | if (firstLine != null) {
150 | return firstLine;
151 | }
152 | } finally {
153 | if (input != null) {
154 | try {
155 | input.close();
156 | } catch (IOException ignored) {
157 | // default implementation ignored
158 | }
159 | }
160 | }
161 | return null;
162 | }
163 |
164 | /**
165 | * 获取系统属性值(通过读取系统文件)
166 | */
167 | private static String getSystemPropertyByStream(@NonNull String key) throws IOException {
168 | FileInputStream inputStream = null;
169 | try {
170 | Properties prop = new Properties();
171 | File file = new File(Environment.getRootDirectory(), "build.prop");
172 | inputStream = new FileInputStream(file);
173 | prop.load(inputStream);
174 | return prop.getProperty(key, "");
175 | } finally {
176 | if (inputStream != null) {
177 | try {
178 | inputStream.close();
179 | } catch (IOException ignored) {
180 | // default implementation ignored
181 | }
182 | }
183 | }
184 | }
185 | }
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/issue_en_template_bug.yml:
--------------------------------------------------------------------------------
1 | name: Submit Bug
2 | description: Please let me know the issues with the framework, and I will assist you in resolving them!
3 | title: "[Bug]:"
4 | labels: ["bug"]
5 |
6 | body:
7 | - type: markdown
8 | attributes:
9 | value: |
10 | ## [Warning: Please make sure to fill in the issue template accurately. If an issue is found to be filled incorrectly, it will be closed without further notice.](https://github.com/getActivity/IssueTemplateGuide)
11 | - type: input
12 | id: input_id_1
13 | attributes:
14 | label: Framework Version [Required]
15 | description: Please enter the version of the framework you are using.
16 | validations:
17 | required: true
18 | - type: textarea
19 | id: input_id_2
20 | attributes:
21 | label: Issue Description [Required]
22 | description: Please describe the issue you are facing.
23 | validations:
24 | required: true
25 | - type: textarea
26 | id: input_id_3
27 | attributes:
28 | label: Steps to Reproduce [Required]
29 | description: Please provide steps to reproduce the issue.
30 | validations:
31 | required: true
32 | - type: dropdown
33 | id: input_id_4
34 | attributes:
35 | label: Is the Issue Reproducible? [Required]
36 | multiple: false
37 | options:
38 | - "Not Selected"
39 | - "Yes"
40 | - "No"
41 | validations:
42 | required: true
43 | - type: input
44 | id: input_id_5
45 | attributes:
46 | label: Project targetSdkVersion [Required]
47 | validations:
48 | required: true
49 | - type: input
50 | id: input_id_6
51 | attributes:
52 | label: Device Information [Required]
53 | description: Please provide the brand and model of the device where the issue occurred.
54 | validations:
55 | required: true
56 | - type: input
57 | id: input_id_7
58 | attributes:
59 | label: Android Version [Required]
60 | description: Please provide the Android version where the issue occurred.
61 | validations:
62 | required: true
63 | - type: dropdown
64 | id: input_id_8
65 | attributes:
66 | label: Issue Source Channel [Required]
67 | multiple: true
68 | options:
69 | - Encountered by myself
70 | - Identified in Bugly
71 | - User feedback
72 | - Other channels
73 | - type: input
74 | id: input_id_9
75 | attributes:
76 | label: Is it specific to certain device models? [Required]
77 | description: Specify whether the issue is specific to certain devices (e.g., specific brand or Android version).
78 | validations:
79 | required: true
80 | - type: dropdown
81 | id: input_id_10
82 | attributes:
83 | label: Does the latest version of the framework have this issue? [Required]
84 | description: If you are using an older version, it is recommended to upgrade and check if the issue still persists.
85 | multiple: false
86 | options:
87 | - "Not Selected"
88 | - "Yes"
89 | - "No"
90 | validations:
91 | required: true
92 | - type: dropdown
93 | id: input_id_11
94 | attributes:
95 | label: Is the issue mentioned in the framework documentation? [Required]
96 | description: The documentation provides answers to frequently asked questions. Please check if the information you are looking for is already provided.
97 | multiple: false
98 | options:
99 | - "Not Selected"
100 | - "Yes"
101 | - "No"
102 | validations:
103 | required: true
104 | - type: dropdown
105 | id: input_id_12
106 | attributes:
107 | label: Did you consult the framework documentation but couldn't find a solution? [Required]
108 | description: If you have consulted the documentation but still couldn't find a solution, you can select "Yes."
109 | multiple: false
110 | options:
111 | - "Not Selected"
112 | - "Yes"
113 | - "No"
114 | validations:
115 | required: true
116 | - type: dropdown
117 | id: input_id_13
118 | attributes:
119 | label: Has a similar issue been reported in the issue list? [Required]
120 | description: You can search the issue list for keywords related to your problem and refer to the solutions provided by others.
121 | multiple: false
122 | options:
123 | - "Not Selected"
124 | - "Yes"
125 | - "No"
126 | validations:
127 | required: true
128 | - type: dropdown
129 | id: input_id_14
130 | attributes:
131 | label: Have you searched the issue list but couldn't find a solution? [Required]
132 | description: If you have searched the issue list and couldn't find a solution, you can select "Yes."
133 | multiple: false
134 | options:
135 | - "Not Selected"
136 | - "Yes"
137 | - "No"
138 | validations:
139 | required: true
140 | - type: dropdown
141 | id: input_id_15
142 | attributes:
143 | label: Can the issue be reproduced with a demo project? [Required]
144 | description: Check if the issue can be reproduced in a minimal demo project to isolate potential issues in your own code.
145 | multiple: false
146 | options:
147 | - "Not Selected"
148 | - "Yes"
149 | - "No"
150 | validations:
151 | required: true
152 | - type: textarea
153 | id: input_id_16
154 | attributes:
155 | label: Provide Error Stack Trace
156 | description: If there is an error, please provide the stack trace. Note, Do not include obfuscated code in the stack trace.
157 | render: text
158 | validations:
159 | required: false
160 | - type: textarea
161 | id: input_id_17
162 | attributes:
163 | label: Provide Screenshots or Videos
164 | description: Provide screenshots or videos if necessary. This field is optional.
165 | validations:
166 | required: false
167 | - type: textarea
168 | id: input_id_18
169 | attributes:
170 | label: Provide a Solution
171 | description: If you have already found a solution, this field is optional.
172 | validations:
173 | required: false
--------------------------------------------------------------------------------
/app/src/main/java/com/hjq/device/compat/demo/ShellUtils.java:
--------------------------------------------------------------------------------
1 | package com.hjq.device.compat.demo;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.DataOutputStream;
5 | import java.io.IOException;
6 | import java.io.InputStreamReader;
7 | import java.util.List;
8 |
9 | public final class ShellUtils {
10 |
11 | private static final String LINE_SEP = System.getProperty("line.separator");
12 |
13 | private ShellUtils() {
14 | throw new UnsupportedOperationException("u can't instantiate me...");
15 | }
16 |
17 | /**
18 | * Execute the command.
19 | *
20 | * @param command The command.
21 | * @param isRooted True to use root, false otherwise.
22 | * @return the single {@link CommandResult} instance
23 | */
24 | public static CommandResult execCmd(final String command, final boolean isRooted) {
25 | return execCmd(new String[]{command}, isRooted, true);
26 | }
27 |
28 | /**
29 | * Execute the command.
30 | *
31 | * @param command The command.
32 | * @param envp The environment variable settings.
33 | * @param isRooted True to use root, false otherwise.
34 | * @return the single {@link CommandResult} instance
35 | */
36 | public static CommandResult execCmd(final String command, final List envp, final boolean isRooted) {
37 | return execCmd(new String[]{command},
38 | envp == null ? null : envp.toArray(new String[]{}),
39 | isRooted,
40 | true);
41 | }
42 |
43 | /**
44 | * Execute the command.
45 | *
46 | * @param commands The commands.
47 | * @param isRooted True to use root, false otherwise.
48 | * @return the single {@link CommandResult} instance
49 | */
50 | public static CommandResult execCmd(final List commands, final boolean isRooted) {
51 | return execCmd(commands == null ? null : commands.toArray(new String[]{}), isRooted, true);
52 | }
53 |
54 | /**
55 | * Execute the command.
56 | *
57 | * @param commands The commands.
58 | * @param envp The environment variable settings.
59 | * @param isRooted True to use root, false otherwise.
60 | * @return the single {@link CommandResult} instance
61 | */
62 | public static CommandResult execCmd(final List commands,
63 | final List envp,
64 | final boolean isRooted) {
65 | return execCmd(commands == null ? null : commands.toArray(new String[]{}),
66 | envp == null ? null : envp.toArray(new String[]{}),
67 | isRooted,
68 | true);
69 | }
70 |
71 | /**
72 | * Execute the command.
73 | *
74 | * @param commands The commands.
75 | * @param isRooted True to use root, false otherwise.
76 | * @return the single {@link CommandResult} instance
77 | */
78 | public static CommandResult execCmd(final String[] commands, final boolean isRooted) {
79 | return execCmd(commands, isRooted, true);
80 | }
81 |
82 | /**
83 | * Execute the command.
84 | *
85 | * @param command The command.
86 | * @param isRooted True to use root, false otherwise.
87 | * @param isNeedResultMsg True to return the message of result, false otherwise.
88 | * @return the single {@link CommandResult} instance
89 | */
90 | public static CommandResult execCmd(final String command,
91 | final boolean isRooted,
92 | final boolean isNeedResultMsg) {
93 | return execCmd(new String[]{command}, isRooted, isNeedResultMsg);
94 | }
95 |
96 | /**
97 | * Execute the command.
98 | *
99 | * @param command The command.
100 | * @param envp The environment variable settings.
101 | * @param isRooted True to use root, false otherwise.
102 | * @param isNeedResultMsg True to return the message of result, false otherwise.
103 | * @return the single {@link CommandResult} instance
104 | */
105 | public static CommandResult execCmd(final String command,
106 | final List envp,
107 | final boolean isRooted,
108 | final boolean isNeedResultMsg) {
109 | return execCmd(new String[]{command}, envp == null ? null : envp.toArray(new String[]{}),
110 | isRooted,
111 | isNeedResultMsg);
112 | }
113 |
114 | /**
115 | * Execute the command.
116 | *
117 | * @param command The command.
118 | * @param envp The environment variable settings array.
119 | * @param isRooted True to use root, false otherwise.
120 | * @param isNeedResultMsg True to return the message of result, false otherwise.
121 | * @return the single {@link CommandResult} instance
122 | */
123 | public static CommandResult execCmd(final String command,
124 | final String[] envp,
125 | final boolean isRooted,
126 | final boolean isNeedResultMsg) {
127 | return execCmd(new String[]{command}, envp, isRooted, isNeedResultMsg);
128 | }
129 |
130 | /**
131 | * Execute the command.
132 | *
133 | * @param commands The commands.
134 | * @param isRooted True to use root, false otherwise.
135 | * @param isNeedResultMsg True to return the message of result, false otherwise.
136 | * @return the single {@link CommandResult} instance
137 | */
138 | public static CommandResult execCmd(final List commands,
139 | final boolean isRooted,
140 | final boolean isNeedResultMsg) {
141 | return execCmd(commands == null ? null : commands.toArray(new String[]{}),
142 | isRooted,
143 | isNeedResultMsg);
144 | }
145 |
146 | /**
147 | * Execute the command.
148 | *
149 | * @param commands The commands.
150 | * @param isRooted True to use root, false otherwise.
151 | * @param isNeedResultMsg True to return the message of result, false otherwise.
152 | * @return the single {@link CommandResult} instance
153 | */
154 | public static CommandResult execCmd(final String[] commands,
155 | final boolean isRooted,
156 | final boolean isNeedResultMsg) {
157 | return execCmd(commands, null, isRooted, isNeedResultMsg);
158 | }
159 |
160 | /**
161 | * Execute the command.
162 | *
163 | * @param commands The commands.
164 | * @param envp Array of strings, each element of which
165 | * has environment variable settings in the format
166 | * name=value, or
167 | * null if the subprocess should inherit
168 | * the environment of the current process.
169 | * @param isRooted True to use root, false otherwise.
170 | * @param isNeedResultMsg True to return the message of result, false otherwise.
171 | * @return the single {@link CommandResult} instance
172 | */
173 | public static CommandResult execCmd(final String[] commands,
174 | final String[] envp,
175 | final boolean isRooted,
176 | final boolean isNeedResultMsg) {
177 | int result = -1;
178 | if (commands == null || commands.length == 0) {
179 | return new CommandResult(result, "", "");
180 | }
181 | Process process = null;
182 | BufferedReader successResult = null;
183 | BufferedReader errorResult = null;
184 | StringBuilder successMsg = null;
185 | StringBuilder errorMsg = null;
186 | DataOutputStream os = null;
187 | try {
188 | process = Runtime.getRuntime().exec(isRooted ? "su" : "sh", envp, null);
189 | os = new DataOutputStream(process.getOutputStream());
190 | for (String command : commands) {
191 | if (command == null) continue;
192 | os.write(command.getBytes());
193 | os.writeBytes(LINE_SEP);
194 | os.flush();
195 | }
196 | os.writeBytes("exit" + LINE_SEP);
197 | os.flush();
198 | result = process.waitFor();
199 | if (isNeedResultMsg) {
200 | successMsg = new StringBuilder();
201 | errorMsg = new StringBuilder();
202 | successResult = new BufferedReader(
203 | new InputStreamReader(process.getInputStream(), "UTF-8")
204 | );
205 | errorResult = new BufferedReader(
206 | new InputStreamReader(process.getErrorStream(), "UTF-8")
207 | );
208 | String line;
209 | if ((line = successResult.readLine()) != null) {
210 | successMsg.append(line);
211 | while ((line = successResult.readLine()) != null) {
212 | successMsg.append(LINE_SEP).append(line);
213 | }
214 | }
215 | if ((line = errorResult.readLine()) != null) {
216 | errorMsg.append(line);
217 | while ((line = errorResult.readLine()) != null) {
218 | errorMsg.append(LINE_SEP).append(line);
219 | }
220 | }
221 | }
222 | } catch (Exception e) {
223 | e.printStackTrace();
224 | } finally {
225 | try {
226 | if (os != null) {
227 | os.close();
228 | }
229 | } catch (IOException e) {
230 | e.printStackTrace();
231 | }
232 | try {
233 | if (successResult != null) {
234 | successResult.close();
235 | }
236 | } catch (IOException e) {
237 | e.printStackTrace();
238 | }
239 | try {
240 | if (errorResult != null) {
241 | errorResult.close();
242 | }
243 | } catch (IOException e) {
244 | e.printStackTrace();
245 | }
246 | if (process != null) {
247 | process.destroy();
248 | }
249 | }
250 | return new CommandResult(
251 | result,
252 | successMsg == null ? "" : successMsg.toString(),
253 | errorMsg == null ? "" : errorMsg.toString()
254 | );
255 | }
256 |
257 | /**
258 | * The result of command.
259 | */
260 | public static class CommandResult {
261 | public int result;
262 | public String successMsg;
263 | public String errorMsg;
264 |
265 | public CommandResult(final int result, final String successMsg, final String errorMsg) {
266 | this.result = result;
267 | this.successMsg = successMsg;
268 | this.errorMsg = errorMsg;
269 | }
270 |
271 | @Override
272 | public String toString() {
273 | return "result: " + result + "\n" +
274 | "successMsg: " + successMsg + "\n" +
275 | "errorMsg: " + errorMsg;
276 | }
277 | }
278 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, August 2025
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright 2025 Huang JinQun
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 设备兼容框架
2 |
3 | 
4 |
5 | * 项目地址:[Github](https://github.com/getActivity/DeviceCompat)
6 |
7 | * 博文地址:[尝试解决 Android 适配的最后一公里](https://juejin.cn/post/7540524749425180735)
8 |
9 | * 可以扫码下载 Demo 进行演示或者测试,如果扫码下载不了的,[点击此处可直接下载](https://github.com/getActivity/DeviceCompat/releases/download/2.5/DeviceCompat.apk)
10 |
11 | 
12 |
13 | #### 框架介绍
14 |
15 | * Android 碎片化至始至终是一个令人非常头疼的问题,特别为 [XXPermissions](https://github.com/getActivity/XXPermissions) 上面为不同的厂商做适配的时候就非常头疼,因为市面上能找到的开源库只能判断机型的品牌,而不能判断 Android 厂商定制的系统类型,用机型的品牌去做适配会导致出现误判的情况,例如在小米手机上面运行的厂商系统不一定是 MIUI 或者 HyperOS,也有可能是被人刷成了 Android 原生的系统或者其他,反过来也一样,我时常在想,要是有这么一个工具就好了,可以判断 Android 厂商系统的类型及获取厂商系统的版本号,这样就很方便我们做 Android 适配,于是 [DeviceCompat](https://github.com/getActivity/DeviceCompat) 就诞生了,可以轻松识别各种国内外手机厂商和系统版本,**帮助大家解决 Android 适配最后一公里的问题**。
16 |
17 | * 截至到目前,我是行业内第一个也是唯一一个开源这套方案的人,在这里先感谢网上的同行分享的各种方法和思路,让我在开发的过程中少走了很多弯路,另外我也很能理解为什么行业内一直没有人愿意站出来开源这种框架,因为过程非常麻烦,这不仅仅是一个技术问题,还是一个苦活,因为要针对成千上万的机型进行适配。
18 |
19 |   
20 |
21 |   
22 |
23 | * 用一张图带你梳理 Android 各大厂商 Os 系统
24 |
25 | 
26 |
27 | #### 框架亮点
28 |
29 | * 支持识别各种定制 Android 系统(HarmonyOS、MagicOS、MIUI、HyperOS、ColorOS、OriginOS 等)
30 |
31 | * 支持判断多种手机厂商品牌(华为、小米、OPPO、vivo、三星等)
32 |
33 | * 使用简单,一行代码即可判断设备品牌、厂商系统类型、厂商系统版本
34 |
35 | * 兼容性好,支持 Android 4.0 及以上系统
36 |
37 | * 体积小巧,仅 **21 KB**,不会增加应用体积负担
38 |
39 | #### 集成步骤
40 |
41 | * 如果你的项目 Gradle 配置是在 `7.0` 以下,需要在 `build.gradle` 文件中加入
42 |
43 | ```groovy
44 | allprojects {
45 | repositories {
46 | // JitPack 远程仓库:https://jitpack.io
47 | maven { url 'https://jitpack.io' }
48 | }
49 | }
50 | ```
51 |
52 | * 如果你的 Gradle 配置是 `7.0` 及以上,则需要在 `settings.gradle` 文件中加入
53 |
54 | ```groovy
55 | dependencyResolutionManagement {
56 | repositories {
57 | // JitPack 远程仓库:https://jitpack.io
58 | maven { url 'https://jitpack.io' }
59 | }
60 | }
61 | ```
62 |
63 | * 配置完远程仓库后,在项目 app 模块下的 `build.gradle` 文件中加入远程依赖
64 |
65 | ```groovy
66 | dependencies {
67 | // 设备兼容框架:https://github.com/getActivity/DeviceCompat
68 | implementation 'com.github.getActivity:DeviceCompat:2.5'
69 | }
70 | ```
71 |
72 | #### 框架 API 介绍
73 |
74 | * 判断系统类型
75 |
76 | ```java
77 | // 判断当前设备的厂商系统是否为 HyperOS(小米手机、红米手机的系统)
78 | DeviceOs.isHyperOs();
79 | // 判断当前设备的厂商系统是否为国内版本的 HyperOS
80 | DeviceOs.isHyperOsByChina();
81 | // 判断当前设备的厂商系统是否为国际版本的 HyperOS
82 | DeviceOs.isHyperOsByGlobal();
83 | // 判断当前设备的厂商系统开启了 HyperOS 的系统优化选项
84 | DeviceOs.isHyperOsOptimization();
85 |
86 | // 判断当前设备的厂商系统是否为 MIUI(小米手机、红米手机的老系统)
87 | DeviceOs.isMiui();
88 | // 判断当前设备的厂商系统是否为国内版本的 MIUI
89 | DeviceOs.isMiuiByChina();
90 | // 判断当前设备的厂商系统是否为国际版本的 MIUI
91 | DeviceOs.isMiuiByGlobal();
92 | // 判断当前设备的厂商系统是否开启了 MIUI 优化选项
93 | DeviceOs.isMiuiOptimization();
94 |
95 | // 判断当前设备的厂商系统是否为 realmeUI(真我手机的系统)
96 | DeviceOs.isRealmeUi();
97 |
98 | // 判断当前设备的厂商系统是否为 ColorOS(OPPO 手机、一加手机的系统)
99 | DeviceOs.isColorOs();
100 |
101 | // 判断当前设备的厂商系统是否为 OriginOS(vivo 手机的系统)
102 | DeviceOs.isOriginOs();
103 |
104 | // 判断当前设备的厂商系统是否为 FuntouchOS(vivo 手机的老系统)
105 | DeviceOs.isFuntouchOs();
106 |
107 | // 判断当前设备的厂商系统是否为 MagicOS 或者 MagicUI(荣耀手机的系统)
108 | DeviceOs.isMagicOs();
109 |
110 | // 判断当前设备的厂商系统是否为 HarmonyOS(华为手机、荣耀手机的系统)
111 | DeviceOs.isHarmonyOs();
112 |
113 | // 判断是否在 HarmonyOS NEXT(纯血鸿蒙)的卓易通或者出境易环境上运行
114 | DeviceOs.isHarmonyOsNextAndroidCompatible();
115 |
116 | // 判断当前设备的厂商系统是否为 EMUI 或者 EmotionUI(华为手机、荣耀手机的老系统)
117 | DeviceOs.isEmui();
118 |
119 | // 判断当前设备的厂商系统是否为 OneUI(三星手机的系统)
120 | DeviceOs.isOneUi();
121 |
122 | // 判断当前设备的厂商系统是否为 OxygenOS(一加手机的老系统,相当于 H2OS 的海外版)
123 | DeviceOs.isOxygenOs();
124 |
125 | // 判断当前设备的厂商系统是否为 H2OS(一加手机的老系统,相当于 OxygenOS 的国内版)
126 | DeviceOs.isH2Os();
127 |
128 | // 判断当前设备的厂商系统是否为 Flyme(魅族手机的系统)
129 | DeviceOs.isFlyme();
130 |
131 | // 判断当前设备的厂商系统是否为 RedMagicOS(努比亚红魔手机的系统,努比亚红魔是中兴旗下的子品牌)
132 | DeviceOs.isRedMagicOs();
133 |
134 | // 判断当前设备的厂商系统是否为 NebulaAIOS(努比亚手机的系统)
135 | DeviceOs.isNebulaAiOs();
136 |
137 | // 判断当前设备的厂商系统是否为 MyOS(中兴手机、努比亚手机的系统)
138 | DeviceOs.isMyOs();
139 |
140 | // 判断当前设备的厂商系统是否为 MifavorUI(中兴手机的老系统)
141 | DeviceOs.isMifavorUi();
142 |
143 | // 判断当前设备的厂商系统是否为 SmartisanOS(锤子手机的系统)
144 | DeviceOs.isSmartisanOs();
145 |
146 | // 判断当前设备的厂商系统是否为 EUI(乐视手机的系统)
147 | DeviceOs.isEui();
148 |
149 | // 判断当前设备的厂商系统是否为 ZUXOS(联想手机、摩托罗拉手机的系统)
150 | DeviceOs.isZuxOs();
151 |
152 | // 判断当前设备的厂商系统是否为 ZUI(联想手机、摩托罗拉手机的老系统)
153 | DeviceOs.isZui();
154 |
155 | // 判断当前设备的厂商系统是否为 nubiaUI(努比亚手机的老系统)
156 | DeviceOs.isNubiaUi();
157 |
158 | // 判断当前设备的厂商系统是否为 ObricUI(豆包手机的系统)
159 | DeviceOs.isObricUi();
160 |
161 | // 判断当前设备的厂商系统是否为 ROGUI(华硕手机的系统)
162 | DeviceOs.isRogUi();
163 |
164 | // 判断当前设备的厂商系统是否为 360UI(360 手机的系统)
165 | DeviceOs.is360Ui();
166 |
167 | // 获取当前设备的厂商系统名称
168 | DeviceOs.getOsName();
169 |
170 | // 获取经过美化的厂商系统版本名称
171 | DeviceOs.getOsVersionName();
172 |
173 | // 获取厂商系统版本的大版本号(如果获取不到则返回 -1)
174 | DeviceOs.getOsBigVersionCode();
175 | ```
176 |
177 | * 判断设备品牌
178 |
179 | ```java
180 | // 判断当前设备的品牌是否为红米
181 | DeviceBrand.isRedMi();
182 |
183 | // 判断当前设备的品牌是否为小米
184 | DeviceBrand.isXiaoMi();
185 |
186 | // 判断当前设备的品牌是否为真我
187 | DeviceBrand.isRealMe();
188 |
189 | // 判断当前设备的品牌是否为一加
190 | DeviceBrand.isOnePlus();
191 |
192 | // 判断当前设备的品牌是否为 OPPO
193 | DeviceBrand.isOppo();
194 |
195 | // 判断当前设备的品牌是否为 vivo
196 | DeviceBrand.isVivo();
197 |
198 | // 判断当前设备的品牌是否为荣耀
199 | DeviceBrand.isHonor();
200 |
201 | // 判断当前设备的品牌是否为华为
202 | DeviceBrand.isHuaWei();
203 |
204 | // 判断当前设备的品牌是否为魅族
205 | DeviceBrand.isMeiZu();
206 |
207 | // 判断当前设备的品牌是否为三星
208 | DeviceBrand.isSamsung();
209 |
210 | // 判断当前设备的品牌是否为努比亚
211 | DeviceBrand.isNubia();
212 |
213 | // 判断当前设备的品牌是否为中兴
214 | DeviceBrand.isZte();
215 |
216 | // 判断当前设备的品牌是否为摩托罗拉
217 | DeviceBrand.isMotorola();
218 |
219 | // 判断当前设备的品牌是否为联想
220 | DeviceBrand.isLenovo();
221 |
222 | // 判断当前设备的品牌是否为华硕
223 | DeviceBrand.isAsus();
224 |
225 | // 判断当前设备的品牌是否为索尼
226 | DeviceBrand.isSony();
227 |
228 | // 判断当前设备的品牌是否为锤子
229 | DeviceBrand.isSmartisan();
230 |
231 | // 判断当前设备的品牌是否为乐视
232 | DeviceBrand.isLeEco();
233 |
234 | // 判断当前设备的品牌是否为 360
235 | DeviceBrand.is360();
236 |
237 | // 判断当前设备的品牌是否为酷派
238 | DeviceBrand.isCoolPad();
239 |
240 | // 判断当前设备的品牌是否为 LG
241 | DeviceBrand.isLg();
242 |
243 | // 判断当前设备的品牌是否为 HTC
244 | DeviceBrand.isHtc();
245 |
246 | // 判断当前设备的品牌是否为金立
247 | DeviceBrand.isGionee();
248 |
249 | // 判断当前设备的品牌是否为传音
250 | DeviceBrand.isTranssion();
251 |
252 | // 判断当前设备的品牌是否为朵唯
253 | DeviceBrand.isDoov();
254 |
255 | // 判断当前设备的品牌是否为飞利浦
256 | DeviceBrand.isPhilips();
257 |
258 | // 判断当前设备的品牌是否为黑鲨
259 | DeviceBrand.isBlackShark();
260 |
261 | // 判断当前设备的品牌是否为海信
262 | DeviceBrand.isHisense();
263 |
264 | // 判断当前设备的品牌是否为天语
265 | DeviceBrand.isKTouch();
266 |
267 | // 判断当前设备的品牌是否为美图
268 | DeviceBrand.isMeiTu();
269 |
270 | // 判断当前设备的品牌是否为诺基亚
271 | DeviceBrand.isNokia();
272 |
273 | // 判断当前设备的品牌是否为 Google
274 | DeviceBrand.isGoogle();
275 |
276 | // 获取当前设备的品牌名称
277 | DeviceBrand.getBrandName();
278 | ```
279 |
280 | * 获取设备的市场名称
281 |
282 | ```java
283 | // 例如:Xiaomi Pad 5
284 | DeviceMarketName.getMarketName(@NonNull Context context);
285 | ```
286 |
287 | * 获取和判断系统属性相关的方法
288 |
289 | ```java
290 | // 获取单个系统属性值
291 | SystemPropertyCompat.getSystemPropertyValue((@Nullable String key);
292 |
293 | // 获取多个系统属性值
294 | SystemPropertyCompat.getSystemPropertyValues(@Nullable String[] keys);
295 |
296 | // 获取多个系统属性中的任一一个值
297 | SystemPropertyCompat.getSystemPropertyAnyOneValue(@Nullable String[] keys);
298 |
299 | // 判断某个系统属性是否存在
300 | SystemPropertyCompat.isSystemPropertyExist(@Nullable String key);
301 |
302 | // 判断多个系统属性是否有任一一个存在
303 | SystemPropertyCompat.isSystemPropertyAnyOneExist(@Nullable String[] keys);
304 | ```
305 |
306 | #### 需要大家协助
307 |
308 | * 这个框架一经发布深受大家的喜欢,但是很快我就遇到了瓶颈,这个瓶颈不是技术导致的,而是我手头没有那么多设备,为了 DeviceCompat 能够做得更好(判断更加精准),现在需要征集大家手机的系统属性,目前已经收集了上百个机型的系统属性,但是这远远不够,需要借助大家的力量一起完善,参与地址:[AndroidSystemPropertyCollect](https://github.com/getActivity/AndroidSystemPropertyCollect)
309 |
310 | #### 作者的其他开源项目
311 |
312 | * 安卓技术中台:[AndroidProject](https://github.com/getActivity/AndroidProject)  
313 |
314 | * 安卓技术中台 Kt 版:[AndroidProject-Kotlin](https://github.com/getActivity/AndroidProject-Kotlin)  
315 |
316 | * 权限框架:[XXPermissions](https://github.com/getActivity/XXPermissions)  
317 |
318 | * 吐司框架:[Toaster](https://github.com/getActivity/Toaster)  
319 |
320 | * 网络框架:[EasyHttp](https://github.com/getActivity/EasyHttp)  
321 |
322 | * 标题栏框架:[TitleBar](https://github.com/getActivity/TitleBar)  
323 |
324 | * 悬浮窗框架:[EasyWindow](https://github.com/getActivity/EasyWindow)  
325 |
326 | * ShapeView 框架:[ShapeView](https://github.com/getActivity/ShapeView)  
327 |
328 | * ShapeDrawable 框架:[ShapeDrawable](https://github.com/getActivity/ShapeDrawable)  
329 |
330 | * 语种切换框架:[MultiLanguages](https://github.com/getActivity/MultiLanguages)  
331 |
332 | * Gson 解析容错:[GsonFactory](https://github.com/getActivity/GsonFactory)  
333 |
334 | * 日志查看框架:[Logcat](https://github.com/getActivity/Logcat)  
335 |
336 | * 嵌套滚动布局框架:[NestedScrollLayout](https://github.com/getActivity/NestedScrollLayout)  
337 |
338 | * Android 版本适配:[AndroidVersionAdapter](https://github.com/getActivity/AndroidVersionAdapter)  
339 |
340 | * Android 代码规范:[AndroidCodeStandard](https://github.com/getActivity/AndroidCodeStandard)  
341 |
342 | * Android 资源大汇总:[AndroidIndex](https://github.com/getActivity/AndroidIndex)  
343 |
344 | * Android 开源排行榜:[AndroidGithubBoss](https://github.com/getActivity/AndroidGithubBoss)  
345 |
346 | * Studio 精品插件:[StudioPlugins](https://github.com/getActivity/StudioPlugins)  
347 |
348 | * 表情包大集合:[EmojiPackage](https://github.com/getActivity/EmojiPackage)  
349 |
350 | * AI 资源大汇总:[AiIndex](https://github.com/getActivity/AiIndex)  
351 |
352 | * 省市区 Json 数据:[ProvinceJson](https://github.com/getActivity/ProvinceJson)  
353 |
354 | * Markdown 语法文档:[MarkdownDoc](https://github.com/getActivity/MarkdownDoc)  
355 |
356 | #### 微信公众号:Android轮子哥
357 |
358 | 
359 |
360 | #### Android 技术 Q 群:10047167
361 |
362 | #### 如果您觉得我的开源库帮你节省了大量的开发时间,请扫描下方的二维码随意打赏,要是能打赏个 10.24 :monkey_face:就太:thumbsup:了。您的支持将鼓励我继续创作:octocat:([点击查看捐赠列表](https://github.com/getActivity/Donate))
363 |
364 |  
365 |
366 | ## License
367 |
368 | ```text
369 | Copyright 2025 Huang JinQun
370 |
371 | Licensed under the Apache License, Version 2.0 (the "License");
372 | you may not use this file except in compliance with the License.
373 | You may obtain a copy of the License at
374 |
375 | http://www.apache.org/licenses/LICENSE-2.0
376 |
377 | Unless required by applicable law or agreed to in writing, software
378 | distributed under the License is distributed on an "AS IS" BASIS,
379 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
380 | See the License for the specific language governing permissions and
381 | limitations under the License.
382 | ```
--------------------------------------------------------------------------------
/library/src/main/java/com/hjq/device/compat/DeviceBrand.java:
--------------------------------------------------------------------------------
1 | package com.hjq.device.compat;
2 |
3 | import android.os.Build;
4 | import android.support.annotation.NonNull;
5 | import android.text.TextUtils;
6 |
7 | /**
8 | * author : Android 轮子哥
9 | * github : https://github.com/getActivity/XXPermissions
10 | * time : 2025/08/12
11 | * desc : 厂商品牌判断
12 | */
13 | public final class DeviceBrand {
14 |
15 | private static final int BRAND_TYPE_REDMI = 78837197;
16 | static final String BRAND_NAME_REDMI = "Redmi";
17 | private static final String[] BRAND_ID_LOWER_CASE_REDMI = { "redmi" };
18 |
19 | private static final int BRAND_TYPE_XIAOMI = -1675632421;
20 | static final String BRAND_NAME_XIAOMI = "Xiaomi";
21 | private static final String[] BRAND_ID_LOWER_CASE_XIAOMI = { "xiaomi" };
22 |
23 | private static final int BRAND_TYPE_REALME = -934971466;
24 | static final String BRAND_NAME_REALME = "realme";
25 | private static final String[] BRAND_ID_LOWER_CASE_REALME = { "realme" };
26 |
27 | private static final int BRAND_TYPE_ONEPLUS = 343319808;
28 | static final String BRAND_NAME_ONEPLUS = "OnePlus";
29 | private static final String[] BRAND_ID_LOWER_CASE_ONEPLUS = { "oneplus" };
30 |
31 | private static final int BRAND_TYPE_OPPO = 2432928;
32 | static final String BRAND_NAME_OPPO = "OPPO";
33 | private static final String[] BRAND_ID_LOWER_CASE_OPPO = { "oppo" };
34 |
35 | private static final int BRAND_TYPE_VIVO = 3620012;
36 | static final String BRAND_NAME_VIVO = "vivo";
37 | private static final String[] BRAND_ID_LOWER_CASE_VIVO = { "vivo" };
38 |
39 | private static final int BRAND_TYPE_HONOR = 68924490;
40 | static final String BRAND_NAME_HONOR = "HONOR";
41 | private static final String[] BRAND_ID_LOWER_CASE_HONOR = { "honor" };
42 |
43 | private static final int BRAND_TYPE_HUAWEI = 2141820391;
44 | static final String BRAND_NAME_HUAWEI = "HUAWEI";
45 | private static final String[] BRAND_ID_LOWER_CASE_HUAWEI = { "huawei" };
46 |
47 | private static final int BRAND_TYPE_MEIZU = 73239724;
48 | static final String BRAND_NAME_MEIZU = "MEIZU";
49 | private static final String[] BRAND_ID_LOWER_CASE_MEIZU = { "meizu" };
50 |
51 | private static final int BRAND_TYPE_SAMSUNG = -765372454;
52 | static final String BRAND_NAME_SAMSUNG = "Samsung";
53 | private static final String[] BRAND_ID_LOWER_CASE_SAMSUNG = { "samsung" };
54 |
55 | private static final int BRAND_TYPE_NUBIA = 105170387;
56 | static final String BRAND_NAME_NUBIA = "nubia";
57 | private static final String[] BRAND_ID_LOWER_CASE_NUBIA = { "nubia" };
58 |
59 | private static final int BRAND_TYPE_ZTE = 89163;
60 | static final String BRAND_NAME_ZTE = "ZTE";
61 | private static final String[] BRAND_ID_LOWER_CASE_ZTE = { "zte" };
62 |
63 | private static final int BRAND_TYPE_MOTOROLA = -151542385;
64 | static final String BRAND_NAME_MOTOROLA = "motorola";
65 | private static final String[] BRAND_ID_LOWER_CASE_MOTOROLA = { "motorola" };
66 |
67 | private static final int BRAND_TYPE_LENOVO = -2022488749;
68 | static final String BRAND_NAME_LENOVO = "Lenovo";
69 | private static final String[] BRAND_ID_LOWER_CASE_LENOVO = { "lenovo", "zuk" };
70 |
71 | private static final int BRAND_TYPE_LEECO = 73265976;
72 | static final String BRAND_NAME_LEECO = "LeEco";
73 | private static final String[] BRAND_ID_LOWER_CASE_LEECO = { "leeco", "letv" };
74 |
75 | private static final int BRAND_TYPE_ASUS = 2018896;
76 | static final String BRAND_NAME_ASUS = "ASUS";
77 | private static final String[] BRAND_ID_LOWER_CASE_ASUS = { "asus" };
78 |
79 | private static final int BRAND_TYPE_SONY = 2551079;
80 | static final String BRAND_NAME_SONY = "SONY";
81 | private static final String[] BRAND_ID_LOWER_CASE_SONY = { "sony" };
82 |
83 | private static final int BRAND_TYPE_SMARTISAN = 560537600;
84 | static final String BRAND_NAME_SMARTISAN = "Smartisan";
85 | private static final String[] BRAND_ID_LOWER_CASE_SMARTISAN = { "smartisan", "deltainno" };
86 |
87 | private static final int BRAND_TYPE_360 = 50733;
88 | static final String BRAND_NAME_360 = "360";
89 | private static final String[] BRAND_ID_LOWER_CASE_360 = { "360", "qiku" };
90 |
91 | private static final int BRAND_TYPE_COOLPAD = -1678088054;
92 | static final String BRAND_NAME_COOLPAD = "Coolpad";
93 | private static final String[] BRAND_ID_LOWER_CASE_COOLPAD = { "coolpad", "yulong", "cp" };
94 |
95 | private static final int BRAND_TYPE_LG = 2427;
96 | static final String BRAND_NAME_LG = "LG";
97 | private static final String[] BRAND_ID_LOWER_CASE_LG = { "lg", "lge" };
98 |
99 | private static final int BRAND_TYPE_HTC = 71863;
100 | static final String BRAND_NAME_HTC = "HTC";
101 | private static final String[] BRAND_ID_LOWER_CASE_HTC = { "htc" };
102 |
103 | private static final int BRAND_TYPE_GIONEE = 2133055169;
104 | static final String BRAND_NAME_GIONEE = "Gionee";
105 | private static final String[] BRAND_ID_LOWER_CASE_GIONEE = { "gionee", "amigo" };
106 |
107 | private static final int BRAND_TYPE_TRANSSION = -1237951171;
108 | static final String BRAND_NAME_TRANSSION = "Transsion";
109 | private static final String[] BRAND_ID_LOWER_CASE_TRANSSION = { "infinix mobility limited", "itel", "tecno" };
110 |
111 | private static final int BRAND_TYPE_DOOV = 2104242;
112 | static final String BRAND_NAME_DOOV = "DOOV";
113 | private static final String[] BRAND_ID_LOWER_CASE_DOOV = { "DOOV" };
114 |
115 | private static final int BRAND_TYPE_PHILIPS = 116903185;
116 | static final String BRAND_NAME_PHILIPS = "PHILIPS";
117 | private static final String[] BRAND_ID_LOWER_CASE_PHILIPS = { "philips" };
118 |
119 | private static final int BRAND_TYPE_BLACKSHARK = 344052550;
120 | static final String BRAND_NAME_BLACKSHARK = "BlackShark";
121 | private static final String[] BRAND_ID_LOWER_CASE_BLACKSHARK = { "blackshark" };
122 |
123 | private static final int BRAND_TYPE_HISENSE = -1703827667;
124 | static final String BRAND_NAME_HISENSE = "Hisense";
125 | private static final String[] BRAND_ID_LOWER_CASE_HISENSE = { "hisense" };
126 |
127 | private static final int BRAND_TYPE_KTOUCH = -787390691;
128 | static final String BRAND_NAME_KTOUCH = "K-Touch";
129 | private static final String[] BRAND_ID_LOWER_CASE_KTOUCH = { "k-touch", "ktouch" };
130 |
131 | private static final int BRAND_TYPE_MEITU = 74224626;
132 | static final String BRAND_NAME_MEITU = "Meitu";
133 | private static final String[] BRAND_ID_LOWER_CASE_MEITU = { "meitu" };
134 |
135 | private static final int BRAND_TYPE_NOKIA = 74462530;
136 | static final String BRAND_NAME_NOKIA = "NOKIA";
137 | private static final String[] BRAND_ID_LOWER_CASE_NOKIA = { "nokia" };
138 |
139 | private static final int BRAND_TYPE_GOOGLE = 2138589785;
140 | static final String BRAND_NAME_GOOGLE = "Google";
141 | private static final String[] BRAND_ID_LOWER_CASE_GOOGLE = { "google" };
142 |
143 | @NonNull
144 | static final String CURRENT_BRAND_NAME;
145 | private static final int CURRENT_BRAND_TYPE;
146 |
147 | static {
148 | String brand = Build.BRAND.toLowerCase();
149 | String manufacturer = Build.MANUFACTURER.toLowerCase();
150 |
151 | if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_REDMI)) {
152 | CURRENT_BRAND_TYPE = BRAND_TYPE_REDMI;
153 | CURRENT_BRAND_NAME = BRAND_NAME_REDMI;
154 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_XIAOMI)) {
155 | CURRENT_BRAND_TYPE = BRAND_TYPE_XIAOMI;
156 | CURRENT_BRAND_NAME = BRAND_NAME_XIAOMI;
157 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_REALME)) {
158 | CURRENT_BRAND_NAME = BRAND_NAME_REALME;
159 | CURRENT_BRAND_TYPE = BRAND_TYPE_REALME;
160 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_ONEPLUS)) {
161 | CURRENT_BRAND_NAME = BRAND_NAME_ONEPLUS;
162 | CURRENT_BRAND_TYPE = BRAND_TYPE_ONEPLUS;
163 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_OPPO)) {
164 | CURRENT_BRAND_TYPE = BRAND_TYPE_OPPO;
165 | CURRENT_BRAND_NAME = BRAND_NAME_OPPO;
166 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_VIVO)) {
167 | CURRENT_BRAND_NAME = BRAND_NAME_VIVO;
168 | CURRENT_BRAND_TYPE = BRAND_TYPE_VIVO;
169 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_HONOR)) {
170 | CURRENT_BRAND_TYPE = BRAND_TYPE_HONOR;
171 | CURRENT_BRAND_NAME = BRAND_NAME_HONOR;
172 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_HUAWEI)) {
173 | CURRENT_BRAND_TYPE = BRAND_TYPE_HUAWEI;
174 | CURRENT_BRAND_NAME = BRAND_NAME_HUAWEI;
175 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_MEIZU)) {
176 | CURRENT_BRAND_TYPE = BRAND_TYPE_MEIZU;
177 | CURRENT_BRAND_NAME = BRAND_NAME_MEIZU;
178 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_SAMSUNG)) {
179 | CURRENT_BRAND_TYPE = BRAND_TYPE_SAMSUNG;
180 | CURRENT_BRAND_NAME = BRAND_NAME_SAMSUNG;
181 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_NUBIA)) {
182 | CURRENT_BRAND_TYPE = BRAND_TYPE_NUBIA;
183 | CURRENT_BRAND_NAME = BRAND_NAME_NUBIA;
184 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_ZTE)) {
185 | CURRENT_BRAND_TYPE = BRAND_TYPE_ZTE;
186 | CURRENT_BRAND_NAME = BRAND_NAME_ZTE;
187 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_MOTOROLA)) {
188 | CURRENT_BRAND_TYPE = BRAND_TYPE_MOTOROLA;
189 | CURRENT_BRAND_NAME = BRAND_NAME_MOTOROLA;
190 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_LENOVO)) {
191 | CURRENT_BRAND_TYPE = BRAND_TYPE_LENOVO;
192 | CURRENT_BRAND_NAME = BRAND_NAME_LENOVO;
193 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_ASUS)) {
194 | CURRENT_BRAND_TYPE = BRAND_TYPE_ASUS;
195 | CURRENT_BRAND_NAME = BRAND_NAME_ASUS;
196 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_SONY)) {
197 | CURRENT_BRAND_TYPE = BRAND_TYPE_SONY;
198 | CURRENT_BRAND_NAME = BRAND_NAME_SONY;
199 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_SMARTISAN)) {
200 | CURRENT_BRAND_TYPE = BRAND_TYPE_SMARTISAN;
201 | CURRENT_BRAND_NAME = BRAND_NAME_SMARTISAN;
202 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_LEECO)) {
203 | CURRENT_BRAND_TYPE = BRAND_TYPE_LEECO;
204 | CURRENT_BRAND_NAME = BRAND_NAME_LEECO;
205 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_360)) {
206 | CURRENT_BRAND_TYPE = BRAND_TYPE_360;
207 | CURRENT_BRAND_NAME = BRAND_NAME_360;
208 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_COOLPAD)) {
209 | CURRENT_BRAND_TYPE = BRAND_TYPE_COOLPAD;
210 | CURRENT_BRAND_NAME = BRAND_NAME_COOLPAD;
211 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_LG)) {
212 | CURRENT_BRAND_TYPE = BRAND_TYPE_LG;
213 | CURRENT_BRAND_NAME = BRAND_NAME_LG;
214 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_HTC)) {
215 | CURRENT_BRAND_TYPE = BRAND_TYPE_HTC;
216 | CURRENT_BRAND_NAME = BRAND_NAME_HTC;
217 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_GIONEE)) {
218 | CURRENT_BRAND_TYPE = BRAND_TYPE_GIONEE;
219 | CURRENT_BRAND_NAME = BRAND_NAME_GIONEE;
220 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_TRANSSION)) {
221 | CURRENT_BRAND_TYPE = BRAND_TYPE_TRANSSION;
222 | CURRENT_BRAND_NAME = BRAND_NAME_TRANSSION;
223 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_DOOV)) {
224 | CURRENT_BRAND_TYPE = BRAND_TYPE_DOOV;
225 | CURRENT_BRAND_NAME = BRAND_NAME_DOOV;
226 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_PHILIPS)) {
227 | CURRENT_BRAND_TYPE = BRAND_TYPE_PHILIPS;
228 | CURRENT_BRAND_NAME = BRAND_NAME_PHILIPS;
229 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_BLACKSHARK)) {
230 | CURRENT_BRAND_TYPE = BRAND_TYPE_BLACKSHARK;
231 | CURRENT_BRAND_NAME = BRAND_NAME_BLACKSHARK;
232 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_HISENSE)) {
233 | CURRENT_BRAND_TYPE = BRAND_TYPE_HISENSE;
234 | CURRENT_BRAND_NAME = BRAND_NAME_HISENSE;
235 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_KTOUCH)) {
236 | CURRENT_BRAND_TYPE = BRAND_TYPE_KTOUCH;
237 | CURRENT_BRAND_NAME = BRAND_NAME_KTOUCH;
238 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_MEITU)) {
239 | CURRENT_BRAND_TYPE = BRAND_TYPE_MEITU;
240 | CURRENT_BRAND_NAME = BRAND_NAME_MEITU;
241 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_NOKIA)) {
242 | CURRENT_BRAND_TYPE = BRAND_TYPE_NOKIA;
243 | CURRENT_BRAND_NAME = BRAND_NAME_NOKIA;
244 | } else if (compareBrand(brand, manufacturer, BRAND_ID_LOWER_CASE_GOOGLE)) {
245 | CURRENT_BRAND_TYPE = BRAND_TYPE_GOOGLE;
246 | CURRENT_BRAND_NAME = BRAND_NAME_GOOGLE;
247 | } else {
248 | CURRENT_BRAND_TYPE = 0;
249 | if (!TextUtils.isEmpty(brand)) {
250 | CURRENT_BRAND_NAME = brand;
251 | } else if (!TextUtils.isEmpty(manufacturer)) {
252 | CURRENT_BRAND_NAME = manufacturer;
253 | } else {
254 | CURRENT_BRAND_NAME = "";
255 | }
256 | }
257 | }
258 |
259 | /**
260 | * 判断当前设备的品牌是否为红米
261 | */
262 | public static boolean isRedMi() {
263 | return CURRENT_BRAND_TYPE == BRAND_TYPE_REDMI;
264 | }
265 |
266 | /**
267 | * 判断当前设备的品牌是否为小米
268 | */
269 | public static boolean isXiaoMi() {
270 | return CURRENT_BRAND_TYPE == BRAND_TYPE_XIAOMI;
271 | }
272 |
273 | /**
274 | * 判断当前设备的品牌是否为真我
275 | */
276 | public static boolean isRealMe() {
277 | return CURRENT_BRAND_TYPE == BRAND_TYPE_REALME;
278 | }
279 |
280 | /**
281 | * 判断当前设备的品牌是否为一加
282 | */
283 | public static boolean isOnePlus() {
284 | return CURRENT_BRAND_TYPE == BRAND_TYPE_ONEPLUS;
285 | }
286 |
287 | /**
288 | * 判断当前设备的品牌是否为 oppo
289 | */
290 | public static boolean isOppo() {
291 | return CURRENT_BRAND_TYPE == BRAND_TYPE_OPPO;
292 | }
293 |
294 | /**
295 | * 判断当前设备的品牌是否为 vivo
296 | */
297 | public static boolean isVivo() {
298 | return CURRENT_BRAND_TYPE == BRAND_TYPE_VIVO;
299 | }
300 |
301 | /**
302 | * 判断当前设备的品牌是否为荣耀
303 | */
304 | public static boolean isHonor() {
305 | return CURRENT_BRAND_TYPE == BRAND_TYPE_HONOR;
306 | }
307 |
308 | /**
309 | * 判断当前设备的品牌是否为华为
310 | */
311 | public static boolean isHuaWei() {
312 | return CURRENT_BRAND_TYPE == BRAND_TYPE_HUAWEI;
313 | }
314 |
315 | /**
316 | * 判断当前设备的品牌是否为魅族
317 | */
318 | public static boolean isMeiZu() {
319 | return CURRENT_BRAND_TYPE == BRAND_TYPE_MEIZU;
320 | }
321 |
322 | /**
323 | * 判断当前设备的品牌是否为三星
324 | */
325 | public static boolean isSamsung() {
326 | return CURRENT_BRAND_TYPE == BRAND_TYPE_SAMSUNG;
327 | }
328 |
329 | /**
330 | * 判断当前设备的品牌是否为努比亚
331 | */
332 | public static boolean isNubia() {
333 | return CURRENT_BRAND_TYPE == BRAND_TYPE_NUBIA;
334 | }
335 |
336 | /**
337 | * 判断当前设备的品牌是否为中兴
338 | */
339 | public static boolean isZte() {
340 | return CURRENT_BRAND_TYPE == BRAND_TYPE_ZTE;
341 | }
342 |
343 | /**
344 | * 判断当前设备的品牌是否为摩托罗拉
345 | */
346 | public static boolean isMotorola() {
347 | return CURRENT_BRAND_TYPE == BRAND_TYPE_MOTOROLA;
348 | }
349 |
350 | /**
351 | * 判断当前设备的品牌是否为联想
352 | */
353 | public static boolean isLenovo() {
354 | return CURRENT_BRAND_TYPE == BRAND_TYPE_LENOVO;
355 | }
356 |
357 | /**
358 | * 判断当前设备的品牌是否为华硕
359 | */
360 | public static boolean isAsus() {
361 | return CURRENT_BRAND_TYPE == BRAND_TYPE_ASUS;
362 | }
363 |
364 | /**
365 | * 判断当前设备的品牌是否为索尼
366 | */
367 | public static boolean isSony() {
368 | return CURRENT_BRAND_TYPE == BRAND_TYPE_SONY;
369 | }
370 |
371 | /**
372 | * 判断当前设备的品牌是否为锤子
373 | */
374 | public static boolean isSmartisan() {
375 | return CURRENT_BRAND_TYPE == BRAND_TYPE_SMARTISAN;
376 | }
377 |
378 | /**
379 | * 判断当前设备的品牌是否为乐视
380 | */
381 | public static boolean isLeEco() {
382 | return CURRENT_BRAND_TYPE == BRAND_TYPE_LEECO;
383 | }
384 |
385 | /**
386 | * 判断当前设备的品牌是否为 360
387 | */
388 | public static boolean is360() {
389 | return CURRENT_BRAND_TYPE == BRAND_TYPE_360;
390 | }
391 |
392 | /**
393 | * 判断当前设备的品牌是否为酷派
394 | */
395 | public static boolean isCoolPad() {
396 | return CURRENT_BRAND_TYPE == BRAND_TYPE_COOLPAD;
397 | }
398 |
399 | /**
400 | * 判断当前设备的品牌是否为 LG
401 | */
402 | public static boolean isLg() {
403 | return CURRENT_BRAND_TYPE == BRAND_TYPE_LG;
404 | }
405 |
406 | /**
407 | * 判断当前设备的品牌是否为 HTC
408 | */
409 | public static boolean isHtc() {
410 | return CURRENT_BRAND_TYPE == BRAND_TYPE_HTC;
411 | }
412 |
413 | /**
414 | * 判断当前设备的品牌是否为金立
415 | */
416 | public static boolean isGionee() {
417 | return CURRENT_BRAND_TYPE == BRAND_TYPE_GIONEE;
418 | }
419 |
420 | /**
421 | * 判断当前设备的品牌是否为传音
422 | */
423 | public static boolean isTranssion() {
424 | return CURRENT_BRAND_TYPE == BRAND_TYPE_TRANSSION;
425 | }
426 |
427 | /**
428 | * 判断当前设备的品牌是否为朵唯
429 | */
430 | public static boolean isDoov() {
431 | return CURRENT_BRAND_TYPE == BRAND_TYPE_DOOV;
432 | }
433 |
434 | /**
435 | * 判断当前设备的品牌是否为飞利浦
436 | */
437 | public static boolean isPhilips() {
438 | return CURRENT_BRAND_TYPE == BRAND_TYPE_PHILIPS;
439 | }
440 |
441 | /**
442 | * 判断当前设备的品牌是否为黑鲨
443 | */
444 | public static boolean isBlackShark() {
445 | return CURRENT_BRAND_TYPE == BRAND_TYPE_BLACKSHARK;
446 | }
447 |
448 | /**
449 | * 判断当前设备的品牌是否为海信
450 | */
451 | public static boolean isHisense() {
452 | return CURRENT_BRAND_TYPE == BRAND_TYPE_HISENSE;
453 | }
454 |
455 | /**
456 | * 判断当前设备的品牌是否为天语
457 | */
458 | public static boolean isKTouch() {
459 | return CURRENT_BRAND_TYPE == BRAND_TYPE_KTOUCH;
460 | }
461 |
462 | /**
463 | * 判断当前设备的品牌是否为美图
464 | */
465 | public static boolean isMeiTu() {
466 | return CURRENT_BRAND_TYPE == BRAND_TYPE_MEITU;
467 | }
468 |
469 | /**
470 | * 判断当前设备的品牌是否为诺基亚
471 | */
472 | public static boolean isNokia() {
473 | return CURRENT_BRAND_TYPE == BRAND_TYPE_NOKIA;
474 | }
475 |
476 | /**
477 | * 判断当前设备的品牌是否为 Google
478 | */
479 | public static boolean isGoogle() {
480 | return CURRENT_BRAND_TYPE == BRAND_TYPE_GOOGLE;
481 | }
482 |
483 | /**
484 | * 获取当前设备品牌的名称
485 | */
486 | public static String getBrandName() {
487 | return CURRENT_BRAND_NAME;
488 | }
489 |
490 | /**
491 | * 比较品牌或者制造商名称是否包含指定的名称
492 | */
493 | private static boolean compareBrand(String brand, String manufacturer, String... names) {
494 | for (String name : names) {
495 | if (brand.contains(name) || manufacturer.contains(name)) {
496 | return true;
497 | }
498 | }
499 | return false;
500 | }
501 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/hjq/device/compat/DeviceMarketName.java:
--------------------------------------------------------------------------------
1 | package com.hjq.device.compat;
2 |
3 | import android.content.Context;
4 | import android.os.Build;
5 | import android.provider.Settings.Global;
6 | import android.support.annotation.NonNull;
7 | import android.support.annotation.Nullable;
8 | import android.text.TextUtils;
9 | import java.util.HashMap;
10 | import java.util.Map;
11 | import java.util.Set;
12 |
13 | /**
14 | * author : Android 轮子哥
15 | * github : https://github.com/getActivity/XXPermissions
16 | * time : 2025/08/12
17 | * desc : 设备市场名称
18 | */
19 | public final class DeviceMarketName {
20 |
21 | /**
22 | * 设备市场名称
23 | */
24 | @NonNull
25 | private static String sMarketName = "";
26 |
27 | /**
28 | * 是否初始化过设备市场名称
29 | */
30 | private static boolean sInitMarketName = false;
31 |
32 | /* ------------------------------------------------------------------------------------------ */
33 |
34 | /**
35 | * 网络名称
36 | */
37 | private static final String MARKET_NAME_NET = "net.hostname";
38 |
39 | /**
40 | * 型号代码
41 | */
42 | private static final String MARKET_NAME_MODEL = "ro.product.model";
43 |
44 | /* ---------------------------------------- 小米、红米 ---------------------------------------- */
45 |
46 | /**
47 | * 小米、红米设备市场名称系统属性
48 | *
49 | * [ro.product.marketname]: [Xiaomi Pad 5]
50 | * [ro.product.odm.marketname]: [Xiaomi Pad 5]
51 | * [ro.product.vendor.marketname]: [Xiaomi Pad 5]
52 | */
53 | private static final String[] MARKET_NAME_XIAOMI_OR_REDMI = { "ro.product.marketname",
54 | "ro.product.odm.marketname",
55 | "ro.product.vendor.marketname" };
56 |
57 | /**
58 | * HyperOS 市场名称系统属性
59 | */
60 | private static final String[] MARKET_NAME_HYPER_OS = MARKET_NAME_XIAOMI_OR_REDMI;
61 |
62 | /**
63 | * MIUI 市场名称系统属性
64 | */
65 | private static final String[] MARKET_NAME_MIUI = MARKET_NAME_XIAOMI_OR_REDMI;
66 |
67 | /* ---------------------------------------- 真我、一加、OPPO ---------------------------------------- */
68 |
69 | /**
70 | * 真我、一加、OPPO 设备市场名称系统属性
71 | *
72 | * [ro.vendor.oplus.market.enname]: [OnePlus Ace 5 Pro]
73 | * [ro.vendor.oplus.market.name]: [一加 Ace 5 Pro]
74 | * [ro.oppo.market.name]: [OPPO K3]
75 | */
76 | private static final String[] MARKET_NAME_ONEPLUS_OR_REALME_OR_OPPO = { "ro.vendor.oplus.market.enname",
77 | "ro.vendor.oplus.market.name",
78 | "ro.oppo.market.name" };
79 | /**
80 | * realmeUI 市场名称系统属性
81 | */
82 | private static final String[] MARKET_NAME_REALME_UI = MARKET_NAME_ONEPLUS_OR_REALME_OR_OPPO;
83 |
84 | /**
85 | * ColorOS 设备市场名称系统属性
86 | */
87 | private static final String[] MARKET_NAME_COLOR_OS = MARKET_NAME_ONEPLUS_OR_REALME_OR_OPPO;
88 |
89 | /**
90 | * H2OS 设备市场名称系统属性
91 | *
92 | * [ro.common.soft]: [OnePlus6T]
93 | * [ro.display.series]: [OnePlus 6T]
94 | */
95 | private static final String[] MARKET_NAME_H2OS = { "ro.display.series",
96 | "ro.common.soft" };
97 |
98 | /**
99 | * OxygenOS 设备市场名称系统属性
100 | */
101 | private static final String[] MARKET_NAME_OXYGEN_OS = { MARKET_NAME_ONEPLUS_OR_REALME_OR_OPPO[0],
102 | MARKET_NAME_ONEPLUS_OR_REALME_OR_OPPO[1],
103 | MARKET_NAME_ONEPLUS_OR_REALME_OR_OPPO[2],
104 | MARKET_NAME_H2OS[0],
105 | MARKET_NAME_H2OS[1] };
106 |
107 | /* ---------------------------------------- vivo ---------------------------------------- */
108 |
109 | /**
110 | * vivo 设备市场名称系统属性
111 | *
112 | * [ro.vivo.internet.name]: [iQOO Neo 855]
113 | * [ro.vivo.market.name]: [iQOO Neo 855版]
114 | */
115 | private static final String[] MARKET_NAME_VIVO = { "ro.vivo.internet.name",
116 | "ro.vivo.market.name" };
117 |
118 | /**
119 | * OriginOS 设备市场名称系统属性
120 | */
121 | private static final String[] MARKET_NAME_ORIGIN_OS = MARKET_NAME_VIVO;
122 |
123 | /**
124 | * FuntouchOS 设备市场名称系统属性
125 | */
126 | private static final String[] MARKET_NAME_FUNTOUCH_OS = MARKET_NAME_VIVO;
127 |
128 | /* ---------------------------------------- 华为、荣耀 ---------------------------------------- */
129 |
130 | /**
131 | * 华为设备市场名称系统属性
132 | *
133 | * [ro.config.marketing_name]: [HUAWEI P20]
134 | */
135 | private static final String MARKET_NAME_HUAWEI_OR_HONOR = "ro.config.marketing_name";
136 |
137 | /**
138 | * HarmonyOS 设备市场名称系统属性
139 | */
140 | private static final String MARKET_NAME_HARMONY_OS = MARKET_NAME_HUAWEI_OR_HONOR;
141 |
142 | /**
143 | * 卓易通 HarmonyOS NEXT 设备市场名称系统属性
144 | */
145 | private static final String MARKET_NAME_ZYT_ON_HARMONY_OS_NEXT = MARKET_NAME_HUAWEI_OR_HONOR;
146 |
147 | /**
148 | * MagicOS 设备市场名称系统属性
149 | */
150 | private static final String MARKET_NAME_MAGIC_OS = MARKET_NAME_HUAWEI_OR_HONOR;
151 |
152 | /**
153 | * EMUI 设备市场名称系统属性
154 | */
155 | private static final String MARKET_NAME_EMUI = MARKET_NAME_HUAWEI_OR_HONOR;
156 |
157 | /* ---------------------------------------- 中兴、努比亚 ---------------------------------------- */
158 |
159 | /**
160 | * 中兴设备市场名称系统属性
161 | *
162 | * [ro.vendor.product.ztename]: [ZTE Axon 20 5G]
163 | * [ro.vendor.product.ztename]: [nubia Z50 Ultra]
164 | */
165 | private static final String MARKET_NAME_ZTE = "ro.vendor.product.ztename";
166 |
167 | /**
168 | * NebulaAIOS 设备市场名称系统属性
169 | */
170 | private static final String MARKET_NAME_NEBULA_AI_OS = MARKET_NAME_ZTE;
171 |
172 | /**
173 | * MyOS 设备市场名称系统属性
174 | */
175 | private static final String MARKET_NAME_MY_OS = MARKET_NAME_ZTE;
176 |
177 | /**
178 | * MifavorUI 设备市场名称系统属性
179 | *
180 | * [ro.product.model]: [ZTE A2017]
181 | * [ro.product.model]: [ZTE A2021]
182 | */
183 | private static final String MARKET_NAME_MIFAVOR_UI = MARKET_NAME_MODEL;
184 |
185 | /**
186 | * 努比亚设备市场名称系统属性
187 | *
188 | * [persist.sys.devicename]: [nubia X]
189 | * [persist.sys.exif.model]: [nubia X]
190 | */
191 | private static final String[] MARKET_NAME_NUBIA = { "persist.sys.devicename",
192 | "persist.sys.exif.model",
193 | MARKET_NAME_ZTE };
194 |
195 | /**
196 | * NubiaUI 设备市场名称系统属性
197 | */
198 | private static final String[] MARKET_NAME_NUBIA_UI = { MARKET_NAME_NUBIA[0],
199 | MARKET_NAME_NUBIA[1] };
200 |
201 | /**
202 | * RedMagicOS 设备市场名称系统属性
203 | */
204 | private static final String[] MARKET_NAME_RED_MAGIC_OS = MARKET_NAME_NUBIA;
205 |
206 | /* ---------------------------------------- 联想、摩托罗拉 ---------------------------------------- */
207 |
208 | /**
209 | * 联想设备市场名称系统属性
210 | *
211 | * [ro.zuk.product.market]: [拯救者电竞手机2 Pro]
212 | * [ro.product.display]: [拯救者电竞手机2 Pro]
213 | * [ro.product.en.display]: [Legion Phone2 Pro]
214 | */
215 | private static final String[] MARKET_NAME_LENOVO = { "ro.zuk.product.market",
216 | "ro.product.display",
217 | "ro.product.en.display" };
218 |
219 | /**
220 | * ZUXOS 设备市场名称系统属性
221 | */
222 | private static final String[] MARKET_NAME_ZUXOS = MARKET_NAME_LENOVO;
223 |
224 | /**
225 | * ZUI 设备市场名称系统属性
226 | */
227 | private static final String[] MARKET_NAME_ZUI = MARKET_NAME_LENOVO;
228 |
229 | /* ---------------------------------------- 华硕 ---------------------------------------- */
230 |
231 | /**
232 | * 华硕设备市场名称系统属性
233 | *
234 | * [ro.vendor.asus.product.mkt_name]: [ROG Phone 5]
235 | */
236 | private static final String MARKET_NAME_ASUS = "ro.vendor.asus.product.mkt_name";
237 |
238 | /**
239 | * NubiaUI 设备市场名称系统属性
240 | */
241 | private static final String MARKET_NAME_ROG_UI = MARKET_NAME_ASUS;
242 |
243 | /* ---------------------------------------- 锤子 ---------------------------------------- */
244 |
245 | /**
246 | * 锤子设备市场名称系统属性
247 | *
248 | * [net.devicename]: [坚果 3]
249 | * [ro.product.codename]: [Smartisan U3]
250 | */
251 | private static final String[] MARKET_NAME_SMARTISAN = { "net.devicename",
252 | "ro.product.codename" };
253 |
254 | /**
255 | * SmartisanOS 设备市场名称系统属性
256 | */
257 | private static final String[] MARKET_NAME_SMARTISAN_OS = MARKET_NAME_SMARTISAN;
258 |
259 | /* ---------------------------------------- 乐视 ---------------------------------------- */
260 |
261 | /**
262 | * 乐视设备市场名称系统属性
263 | *
264 | * [ro.product.letv_name]: [乐2]
265 | */
266 | private static final String MARKET_NAME_LEECO = "ro.product.letv_name";
267 |
268 | /**
269 | * EUI 设备市场名称系统属性
270 | */
271 | private static final String MARKET_NAME_EUI = MARKET_NAME_LEECO;
272 |
273 | /* ---------------------------------------- 360 ---------------------------------------- */
274 |
275 | /**
276 | * 360 设备市场名称系统属性
277 | *
278 | * [ro.qiku.product.devicename]: [N7]
279 | */
280 | private static final String MARKET_NAME_360 = "ro.qiku.product.devicename";
281 |
282 | /**
283 | * 360UI 设备市场名称系统属性
284 | */
285 | private static final String MARKET_NAME_360_UI = MARKET_NAME_360;
286 |
287 | /** 机型市场名称前缀替换集合 */
288 | private static final Map PREFIX_REPLACE_MAP = new HashMap<>();
289 |
290 | static {
291 | // [ro.product.marketname]: [Mi 10 Ultra]
292 | // [ro.product.model]: [MI PAD 4]
293 | String miName = DeviceBrand.isRedMi() ? DeviceBrand.BRAND_NAME_REDMI : DeviceBrand.BRAND_NAME_XIAOMI;
294 | PREFIX_REPLACE_MAP.put("Mi", miName);
295 | PREFIX_REPLACE_MAP.put("MI", miName);
296 | PREFIX_REPLACE_MAP.put("mi", miName);
297 | PREFIX_REPLACE_MAP.put("小米", miName);
298 |
299 | // 华为畅享 60
300 | PREFIX_REPLACE_MAP.put("华为", DeviceBrand.BRAND_NAME_HUAWEI);
301 |
302 | // [ro.config.marketing_name]: [荣耀平板X8 Pro]
303 | PREFIX_REPLACE_MAP.put("荣耀平板", DeviceBrand.BRAND_NAME_HONOR + " Pad");
304 | // [ro.config.marketing_name]: [荣耀X30]
305 | // [ro.config.marketing_name]: [荣耀畅玩30]
306 | PREFIX_REPLACE_MAP.put("荣耀", DeviceBrand.BRAND_NAME_HONOR);
307 |
308 | // [ro.vendor.oplus.market.name]: [一加 12]
309 | // [ro.vendor.oplus.market.name]: [一加 Ace 3 Pro]
310 | PREFIX_REPLACE_MAP.put("一加", DeviceBrand.BRAND_NAME_ONEPLUS);
311 |
312 | // [ro.vendor.oplus.market.name]: [真我GT5 Pro]
313 | // [ro.vendor.oplus.market.name]: [真我Q3 Pro 5G]
314 | // [ro.vendor.oplus.market.name]: [真我10 Pro+]
315 | PREFIX_REPLACE_MAP.put("真我", DeviceBrand.BRAND_NAME_REALME);
316 |
317 | // [ro.product.brand]: [meizu]
318 | // [ro.product.model]: [MEIZU 20 Pro]
319 | // [ro.product.name]: [meizu_20Pro_CN]
320 | PREFIX_REPLACE_MAP.put("meizu", DeviceBrand.BRAND_NAME_MEIZU);
321 | PREFIX_REPLACE_MAP.put("魅族", DeviceBrand.BRAND_NAME_MEIZU);
322 |
323 | // [ro.zuk.product.market]: [拯救者电竞手机2 Pro]
324 | PREFIX_REPLACE_MAP.put("拯救者电竞手机", DeviceBrand.BRAND_NAME_LENOVO + " Legion Phone");
325 | // [ro.product.display]: [拯救者平板 Y700]
326 | PREFIX_REPLACE_MAP.put("拯救者平板", DeviceBrand.BRAND_NAME_LENOVO + " Legion Tab");
327 | PREFIX_REPLACE_MAP.put("联想", DeviceBrand.BRAND_NAME_LENOVO);
328 |
329 | // [ro.vendor.product.ztename]: [红魔10 Air]
330 | // [ro.vendor.product.ztename]: [红魔9 Pro游戏手机]
331 | // [persist.sys.devicename]: [红魔7S Pro 氘锋透明版]
332 | PREFIX_REPLACE_MAP.put("努比亚红魔", DeviceBrand.BRAND_NAME_NUBIA + " RedMagic");
333 | PREFIX_REPLACE_MAP.put("红魔", DeviceBrand.BRAND_NAME_NUBIA + " RedMagic");
334 | PREFIX_REPLACE_MAP.put("努比亚", DeviceBrand.BRAND_NAME_NUBIA);
335 | // 中兴畅行50
336 | PREFIX_REPLACE_MAP.put("中兴", DeviceBrand.BRAND_NAME_ZTE);
337 |
338 | PREFIX_REPLACE_MAP.put("华硕", DeviceBrand.BRAND_NAME_ASUS);
339 | PREFIX_REPLACE_MAP.put("黑鲨", DeviceBrand.BRAND_NAME_BLACKSHARK);
340 | PREFIX_REPLACE_MAP.put("锤子", DeviceBrand.BRAND_NAME_SMARTISAN);
341 | PREFIX_REPLACE_MAP.put("乐视", DeviceBrand.BRAND_NAME_LEECO);
342 | }
343 |
344 | private DeviceMarketName() {
345 | // 私有化构造方法,禁止外部实例化
346 | }
347 |
348 | /**
349 | * 获取设备的市场名称
350 | */
351 | @NonNull
352 | public static String getMarketName(@NonNull Context context) {
353 | if (!sInitMarketName) {
354 | sInitMarketName = true;
355 | initMarketName(context);
356 | }
357 | return sMarketName;
358 | }
359 |
360 | private static void initMarketName(@NonNull Context context) {
361 | /* ---------------------------------------- 通过判断厂商系统来获取(上策) ---------------------------------------- */
362 |
363 | if (DeviceOs.isHyperOs()) {
364 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_HYPER_OS);
365 | } else if (DeviceOs.isMiui()) {
366 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_MIUI);
367 | }
368 |
369 | if (TextUtils.isEmpty(sMarketName)) {
370 | if (DeviceOs.isRealmeUi()) {
371 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_REALME_UI);
372 | } else if (DeviceOs.isColorOs()) {
373 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_COLOR_OS);
374 | } else if (DeviceOs.isH2Os()) {
375 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_H2OS);
376 | } else if (DeviceOs.isOxygenOs()) {
377 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_OXYGEN_OS);
378 | }
379 | }
380 |
381 | if (TextUtils.isEmpty(sMarketName)) {
382 | if (DeviceOs.isOriginOs()) {
383 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_ORIGIN_OS);
384 | } else if (DeviceOs.isFuntouchOs()) {
385 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_FUNTOUCH_OS);
386 | }
387 | }
388 |
389 | if (TextUtils.isEmpty(sMarketName)) {
390 | if (DeviceOs.isHarmonyOs()) {
391 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_HARMONY_OS);
392 | } else if (DeviceOs.isHarmonyOsNextAndroidCompatible()) {
393 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_ZYT_ON_HARMONY_OS_NEXT);
394 | } else if (DeviceOs.isMagicOs()) {
395 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_MAGIC_OS);
396 | } else if (DeviceOs.isEmui()) {
397 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_EMUI);
398 | }
399 | }
400 |
401 | if (TextUtils.isEmpty(sMarketName)) {
402 | if (DeviceOs.isNebulaAiOs()) {
403 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_NEBULA_AI_OS);
404 | } else if (DeviceOs.isRedMagicOs()) {
405 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_RED_MAGIC_OS);
406 | } else if (DeviceOs.isMyOs()) {
407 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_MY_OS);
408 | } else if (DeviceOs.isNubiaUi()) {
409 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_NUBIA_UI);
410 | } else if (DeviceOs.isMifavorUi()) {
411 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_MIFAVOR_UI);
412 | }
413 | }
414 |
415 | if (TextUtils.isEmpty(sMarketName)) {
416 | if (DeviceOs.isZuxOs()) {
417 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_ZUXOS);
418 | } else if (DeviceOs.isZui()) {
419 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_ZUI);
420 | }
421 | }
422 |
423 | if (TextUtils.isEmpty(sMarketName) && DeviceOs.isRogUi()) {
424 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_ROG_UI);
425 | }
426 |
427 | if (TextUtils.isEmpty(sMarketName) && DeviceOs.isSmartisanOs()) {
428 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_SMARTISAN_OS);
429 | }
430 |
431 | if (TextUtils.isEmpty(sMarketName) && DeviceOs.isEui()) {
432 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_EUI);
433 | }
434 |
435 | if (TextUtils.isEmpty(sMarketName) && DeviceOs.is360Ui()) {
436 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_360_UI);
437 | }
438 |
439 | if (TextUtils.isEmpty(sMarketName) && DeviceOs.isOneUi()) {
440 | try {
441 | // 获取到的值:Galaxy Z Flip7
442 | // 参考三星设置的源码:com.samsung.android.settings.deviceinfo.SecDeviceInfoUtils.getDefaultDeviceName();
443 | // 不能读取 device_name 值,因为这个值是设备的名称,是可以被用户修改的
444 | // 而是应该用 default_device_name 值,这样就算用户修改了设备的名称,也不会有影响
445 | String defaultDeviceName = Global.getString(context.getContentResolver(), "default_device_name");
446 | if (isDeviceMarketNameLegitimacy(defaultDeviceName)) {
447 | retrofitAndSetMarketName(defaultDeviceName);
448 | }
449 | } catch (Exception ignored) {
450 | // default implementation ignored
451 | }
452 | }
453 |
454 | /* ---------------------------------------- 通过判断手机品牌来获取(下策) ---------------------------------------- */
455 |
456 | if (DeviceBrand.isXiaoMi() || DeviceBrand.isRedMi()) {
457 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_XIAOMI_OR_REDMI);
458 | }
459 |
460 | if (TextUtils.isEmpty(sMarketName) && (DeviceBrand.isHuaWei() || DeviceBrand.isHonor())) {
461 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_HUAWEI_OR_HONOR);
462 | }
463 |
464 | if (TextUtils.isEmpty(sMarketName) && (DeviceBrand.isOnePlus() || DeviceBrand.isRealMe() || DeviceBrand.isOppo())) {
465 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_ONEPLUS_OR_REALME_OR_OPPO);
466 | }
467 |
468 | if (TextUtils.isEmpty(sMarketName) && DeviceBrand.isVivo()) {
469 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_VIVO);
470 | }
471 |
472 | if (TextUtils.isEmpty(sMarketName) && DeviceBrand.isZte()) {
473 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_ZTE);
474 | }
475 |
476 | if (TextUtils.isEmpty(sMarketName) && DeviceBrand.isNubia()) {
477 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_NUBIA);
478 | }
479 |
480 | if (TextUtils.isEmpty(sMarketName) && DeviceBrand.isLenovo()) {
481 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_LENOVO);
482 | }
483 |
484 | if (TextUtils.isEmpty(sMarketName) && DeviceBrand.isAsus()) {
485 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_ASUS);
486 | }
487 |
488 | if (TextUtils.isEmpty(sMarketName) && DeviceBrand.isSmartisan()) {
489 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_SMARTISAN);
490 | }
491 |
492 | if (TextUtils.isEmpty(sMarketName) && DeviceBrand.isLeEco()) {
493 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_LEECO);
494 | }
495 |
496 | if (TextUtils.isEmpty(sMarketName) && DeviceBrand.is360()) {
497 | traversalMarketNameSystemPropertyKeys(MARKET_NAME_360);
498 | }
499 |
500 | /* ---------------------------------------- 通过其他手段来获取(下下策) ---------------------------------------- */
501 |
502 | if (TextUtils.isEmpty(sMarketName)) {
503 | // 魅族就是通过下面手段获取的
504 | // [ro.product.brand]: [meizu]
505 | // [ro.product.model]: [MEIZU 20 Pro]
506 | // [ro.product.name]: [meizu_20Pro_CN]
507 | String productBrand = Build.BRAND;
508 | String productModel = Build.MODEL;
509 | String productName = Build.PRODUCT;
510 |
511 | String productBrandLowerCase = productBrand.trim().toLowerCase();
512 | String productModelLowerCase = productModel.trim().toLowerCase();
513 | String productNameLowerCase = productName.trim().toLowerCase();
514 |
515 | if (!TextUtils.isEmpty(productBrandLowerCase)) {
516 | if (productModelLowerCase.startsWith(productBrandLowerCase) &&
517 | isDeviceMarketNameLegitimacy(productModelLowerCase)) {
518 | retrofitAndSetMarketName(productModel);
519 | } else if (productNameLowerCase.startsWith(productBrandLowerCase) &&
520 | isDeviceMarketNameLegitimacy(productNameLowerCase)) {
521 | retrofitAndSetMarketName(productName);
522 | }
523 | }
524 |
525 | if (TextUtils.isEmpty(sMarketName)) {
526 | if (productModel.contains(" ") &&
527 | isDeviceMarketNameLegitimacy(productModel)) {
528 | retrofitAndSetMarketName(productModel);
529 | } else if (productName.contains(" ") &&
530 | isDeviceMarketNameLegitimacy(productName)) {
531 | retrofitAndSetMarketName(productName);
532 | }
533 | }
534 | }
535 |
536 | if (!TextUtils.isEmpty(sMarketName)) {
537 | return;
538 | }
539 |
540 | String value = SystemPropertyCompat.getSystemPropertyValue(MARKET_NAME_NET);
541 | // [net.hostname]: [android-42fffc50809f0094]
542 | if (value.matches("^(?i)android")) {
543 | return;
544 | }
545 |
546 | // [net.hostname]: [nova_6_(5G)-da9527a235b5b]
547 | // [net.hostname]: [MAIMANG_7-ef21d90352d53d6]
548 | // [net.hostname]: [nova_8-b271b8158e6c97ae]
549 | // [net.hostname]: [HUAWEI_P30-65a958fef52110]
550 | // [net.hostname]: [AQM-AL00-4b2ea8cb5ecb06d5]
551 | // [net.hostname]: [HONOR_20_PRO-a6e52d54d2]
552 | if (value.matches(".+-[a-zA-Z0-9]{10,}$")) {
553 | value = value.replaceFirst("-[a-zA-Z0-9]{10,}$", "");
554 | }
555 |
556 | if (!isDeviceMarketNameLegitimacy(value)) {
557 | return;
558 | }
559 |
560 | retrofitAndSetMarketName(value);
561 | }
562 |
563 | /**
564 | * 修剪并设置设备的市场名称
565 | */
566 | private static void retrofitAndSetMarketName(@Nullable String marketName) {
567 | if (marketName == null) {
568 | marketName = "";
569 | }
570 |
571 | // 字符串头尾删空
572 | marketName = marketName.trim();
573 |
574 | Set keys = PREFIX_REPLACE_MAP.keySet();
575 | for (String key : keys) {
576 | if (marketName.startsWith(key)) {
577 | marketName = PREFIX_REPLACE_MAP.get(key) + " " + marketName.replaceFirst(key, "").trim();
578 | break;
579 | }
580 | }
581 |
582 | if (DeviceBrand.isHuaWei() && marketName.startsWith("nova")) {
583 | // [ro.config.marketing_name]: [nova 4e]
584 | marketName = DeviceBrand.BRAND_NAME_HUAWEI + " " + marketName;
585 | }
586 |
587 | // [ro.vivo.internet.name]: [iQOO Neo 855]
588 | // [ro.vivo.market.name]: [iQOO Neo 855版]
589 | if (DeviceBrand.isVivo() && marketName.startsWith("iQOO")) {
590 | marketName = DeviceBrand.BRAND_NAME_VIVO + " " + marketName;
591 | }
592 |
593 | // Galaxy Z Flip7
594 | if (DeviceBrand.isSamsung() && marketName.startsWith("Galaxy")) {
595 | marketName = DeviceBrand.BRAND_NAME_SAMSUNG + " " + marketName;
596 | }
597 |
598 | // [ro.product.name]: [meizu_20Pro_CN]
599 | if (DeviceBrand.isMeiZu() && marketName.endsWith("_CN")) {
600 | marketName = marketName.replace("_CN", " ");
601 | }
602 |
603 | // [ro.product.en.display]: [Legion Phone2 Pro]
604 | // [ro.product.en.display]: [Legion Tab Y700]
605 | if (DeviceBrand.isLenovo() && marketName.startsWith("Legion")) {
606 | marketName = DeviceBrand.BRAND_NAME_LENOVO + " " + marketName;
607 | }
608 |
609 | // [net.devicename]: [坚果 3]
610 | // [net.devicename]: [坚果 3]
611 | // [net.devicename]: [坚果 Pro 3]
612 | if (DeviceBrand.isSmartisan() && marketName.startsWith("坚果")) {
613 | marketName = DeviceBrand.BRAND_NAME_SMARTISAN + " " + marketName;
614 | }
615 |
616 | // [ro.product.letv_name]: [乐2]
617 | if (DeviceBrand.isLeEco() && marketName.startsWith("乐")) {
618 | marketName = DeviceBrand.BRAND_NAME_LEECO + " Le " + marketName.replaceFirst("乐", "").trim();
619 | }
620 |
621 | // [ro.qiku.product.devicename]: [N7]
622 | if (DeviceBrand.is360() && !marketName.startsWith("360")) {
623 | marketName = DeviceBrand.BRAND_NAME_360 + " " + marketName;
624 | }
625 |
626 | // [ro.product.name]: [meizu_20Pro_CN]
627 | if (marketName.contains("_")) {
628 | marketName = marketName.replace("_", " ");
629 | }
630 |
631 | // [net.hostname]: [OPPO-R17]
632 | if (marketName.contains("-")) {
633 | marketName = marketName.replace("-", " ");
634 | }
635 |
636 | // [ro.vendor.oplus.market.enname]: [realme 10 Pro+ 5G]
637 | // [ro.vendor.oplus.market.name]: [真我10 Pro+]
638 | if (marketName.contains("Pro+")) {
639 | marketName = marketName.replace("Pro+", "Pro Plus");
640 | }
641 |
642 | // [ro.vivo.internet.name]: [vivo Z3i Basic]
643 | // [ro.vivo.market.name]: [vivo Z3i 标准版]
644 | // if (marketName.endsWith("标准版")) {
645 | // marketName = marketName.replace("标准版", "").trim() + " Basic";
646 | // }
647 |
648 | // [ro.vendor.product.ztename]: [红魔9 Pro游戏手机]
649 | // [persist.sys.devicename]: [红魔7S Pro 氘锋透明版]
650 | // if (marketName.matches("[\\u4e00-\\u9fa5]+$")) {
651 | // // 最好不要这么写,因为有的机型的名称就是纯中文的,这样写会把具体的机型名称给替换掉
652 | // marketName = marketName.replaceFirst("[\\u4e00-\\u9fa5]+$", "");
653 | // }
654 |
655 | sMarketName = marketName;
656 | }
657 |
658 | /**
659 | * 判断设备型号名称的合法性
660 | */
661 | private static boolean isDeviceMarketNameLegitimacy(@NonNull String marketName) {
662 | if (TextUtils.isEmpty(marketName)) {
663 | return false;
664 | }
665 |
666 | // 排除以下这种值
667 | // [ro.product.model]: [LIO-AL00]
668 | // [ro.product.model]: [ANG-AN00]
669 | // [ro.product.model]: [OXF-AN00]
670 | // [ro.product.model]: [OXF-AN10]
671 | // [ro.product.model]: [HMA-AL00]
672 | // [ro.product.model]: [SM-G9500]
673 | // [ro.product.model]: [SM-G9600]
674 | // [ro.product.model]: [SM-F7070]
675 | // [ro.product.model]: [SM-N9810]
676 | // [ro.product.model]: [SM-T970]
677 | // [ro.product.model]: [SM-S9210]
678 | // [ro.product.model]: [SM-A5560]
679 | // [ro.product.model]: [JKM-AL00b]
680 | if (marketName.matches("[A-Z]{2,3}-[A-Z|0-9]{4}[a-zA-Z|0-9]*")) {
681 | return false;
682 | }
683 |
684 | // 排除以下这种值
685 | // [ro.product.model]: [OE106]
686 | // [ro.product.model]: [V2220A]
687 | // [ro.product.model]: [V2429A]
688 | // [ro.product.model]: [V2359A]
689 | // [ro.product.model]: [PCAT00]
690 | // [ro.product.model]: [LE2120]
691 | // [ro.product.model]: [PFCM00]
692 | // [ro.product.model]: [NE2213]
693 | // [ro.product.model]: [NX616J]
694 | // [ro.product.model]: [TB320FC]
695 | // [ro.product.model]: [RMX3687]
696 | // [ro.product.model]: [DT1901A]
697 | // [ro.product.name]: [QK1807]
698 | // [ro.product.name]: [PD1813E]
699 | // [ro.product.name]: [PD1730]
700 | // [ro.product.name]: [NX569J]
701 | // [ro.product.name]: [RMX2117]
702 | // [ro.product.name]: [RMX3687]
703 | // [ro.product.model]: [M2004J7AC]
704 | // [ro.product.model]: [M2007J1SC]
705 | // [ro.product.model]: [M2101K7AG]
706 | // [ro.product.model]: [2201123C]
707 | // [ro.product.model]: [21121119SG]
708 | if (marketName.matches("[A-Z0-9]{5,}")) {
709 | return false;
710 | }
711 |
712 | // 排除以下这种值
713 | // [ro.product.model]: [1807-A01]
714 | // [ro.product.model]: [SCMR-W09]
715 | // [ro.product.name]: [SCMR-W09]
716 | if (marketName.matches("[A-Z0-9]{4}-[A-Z0-9]{3,}")) {
717 | return false;
718 | }
719 |
720 | // 排除以下这种值
721 | // [ro.product.model]: [XT2301-5]
722 | // [ro.product.model]: [XT1924-9]
723 | if (marketName.matches("[A-Z]{2}\\d{4,}-[A-Z0-9]+")) {
724 | return false;
725 | }
726 |
727 | return true;
728 | }
729 |
730 | /**
731 | * 遍历可能是设备型号名称的系统属性
732 | */
733 | private static void traversalMarketNameSystemPropertyKeys(@NonNull String... systemPropertyKeys) {
734 | for (String key : systemPropertyKeys) {
735 | String value = SystemPropertyCompat.getSystemPropertyValue(key);
736 | if (isDeviceMarketNameLegitimacy(value)) {
737 | retrofitAndSetMarketName(value);
738 | // 找到目标,跳出循环,避免不必要的消耗
739 | break;
740 | }
741 | }
742 | }
743 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/hjq/device/compat/DeviceOs.java:
--------------------------------------------------------------------------------
1 | package com.hjq.device.compat;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.os.Build;
5 | import android.support.annotation.NonNull;
6 | import android.support.annotation.Nullable;
7 | import android.text.TextUtils;
8 | import java.lang.reflect.Field;
9 | import java.lang.reflect.Method;
10 | import java.util.regex.Matcher;
11 | import java.util.regex.Pattern;
12 |
13 | /**
14 | * author : Android 轮子哥
15 | * github : https://github.com/getActivity/XXPermissions
16 | * time : 2025/08/12
17 | * desc : 厂商系统判断
18 | */
19 | @SuppressWarnings("JavaReflectionMemberAccess")
20 | public final class DeviceOs {
21 |
22 | /* ---------------------------------------- 我是一条华丽的分割线 ---------------------------------------- */
23 |
24 | static final String REGEX_VERSION_NAME = "\\d+(?:\\.\\d+)+";
25 |
26 | static final String REGEX_NUMBER = "\\d+";
27 |
28 | /* ---------------------------------------- 我是一条华丽的分割线 ---------------------------------------- */
29 |
30 | static final String SYSTEM_PROPERTY_BUILD_VERSION_INCREMENTAL = "ro.build.version.incremental";
31 | static final String SYSTEM_PROPERTY_BUILD_DISPLAY_ID = "ro.build.display.id";
32 |
33 | /* ---------------------------------------- 下面是小米或者红米的系统 ---------------------------------------- */
34 |
35 | /**
36 | * 国内版本:
37 | * [ro.miui.build.region]: [cn]
38 | * [ro.miui.region]: [CN]
39 | * [ro.vendor.miui.region]: [CN]
40 | *
41 | * 国际版本:
42 | * [ro.miui.build.region]: [global]
43 | * [ro.miui.region]: [HK]
44 | * [ro.vendor.miui.region]: [HK]
45 | */
46 | static final String[] OS_REGION_MI = { "ro.miui.build.region",
47 | "ro.miui.region",
48 | "ro.vendor.miui.region" };
49 |
50 | static final int OS_TYPE_HYPER_OS = -1248529104;
51 | static final String OS_NAME_HYPER_OS = "HyperOS";
52 | /**
53 | * [ro.mi.os.version.incremental]: [OS1.0.3.0.TKXCNXM]
54 | */
55 | static final String OS_VERSION_NAME_HYPER_OS = "ro.mi.os.version.incremental";
56 | /**
57 | * [ro.mi.os.version.incremental]: [OS1.0.3.0.TKXCNXM]
58 | * [ro.mi.os.version.name]: [OS1.0]
59 | * [ro.mi.os.version.code]: [1]
60 | */
61 | static final String[] OS_CONDITIONS_HYPER_OS = { "ro.mi.os.version.name",
62 | "ro.mi.os.version.code",
63 | OS_VERSION_NAME_HYPER_OS };
64 |
65 | static final String[] OS_REGION_HYPER_OS = OS_REGION_MI;
66 |
67 | static final int OS_TYPE_MIUI = 2366768;
68 | static final String OS_NAME_MIUI = "MIUI";
69 |
70 | /**
71 | * [ro.build.version.incremental]: [V9.6.1.0.MHOCNFD]
72 | * [ro.build.description]: [kenzo-user 6.0.1 MMB29M V9.6.1.0.MHOCNFD release-keys]
73 | * [ro.build.fingerprint]: [Xiaomi/kenzo/kenzo:6.0.1/MMB29M| V9.6.1.0.MH0cNFD:user/release-keys]
74 | * [ro.bootimage.build.fingerprint]: [Xiaomi/kenzo/kenzo:6.0.1/MMB29M/ V9.6.1.0.MHOCNFD:user/release-keys]
75 | */
76 | static final String OS_VERSION_NAME_MIUI = SYSTEM_PROPERTY_BUILD_VERSION_INCREMENTAL;
77 |
78 | /**
79 | * miui 9.6.1.0:[ro.miui.ui.version.name]: [V9]
80 | * miui 13.0.12:[ro.miui.ui.version.name]: [V130]
81 | *
82 | * miui 9.6.1:[ro.miui.ui.version.code]: [7]
83 | * miui 13.0.12:[ro.miui.ui.version.code]: [13]
84 | *
85 | * 如何识别小米设备/MIUI系统:https://dev.mi.com/console/doc/detail?pId=915
86 | */
87 | static final String[] OS_CONDITIONS_MIUI = { "ro.miui.ui.version.name",
88 | "ro.miui.ui.version.code" };
89 |
90 | /**
91 | * 国内版本:
92 | * [ro.miui.region]: [CN]
93 | * [ro.vendor.miui.region]: [CN]
94 | * [ro.miui.build.region]: [cn]
95 | *
96 | * 国际版本:
97 | * [ro.miui.region]: [HK]
98 | * [ro.vendor.miui.region]: [HK]
99 | * [ro.miui.build.region]: [global]
100 | */
101 | static final String[] OS_REGION_MIUI = OS_REGION_MI;
102 |
103 | /* ---------------------------------------- 下面是真我、OPPO 的系统 ---------------------------------------- */
104 |
105 | static final int OS_TYPE_REALME_UI = -859411254;
106 | static final String OS_NAME_REALME_UI = "realmeUI";
107 | /**
108 | * [ro.build.version.realmeui]: [V5.0]
109 | */
110 | static final String OS_VERSION_NAME_REALME_UI = "ro.build.version.realmeui";
111 |
112 | static final int OS_TYPE_COLOR_OS = -1680767897;
113 | static final String OS_NAME_COLOR_OS = "ColorOS";
114 |
115 | /**
116 | * ColorOS 高版本:
117 | * [ro.build.version.oplusrom]: [V12.1]
118 | */
119 | static final String OS_CONDITIONS_NAME_COLOR_OS_NEW = "ro.build.version.oplusrom";
120 |
121 | /**
122 | * ColorOS 高版本:
123 | * [ro.build.display.id]: [IN2010_13.1.0.190(CN01)]
124 | * [ro.build.display.id.show]: [PJD110_15.0.0.840(CN01)]
125 | * [persist.sys.oplus.ota_ver_display]: [IN2010_13.1.0.190(CN01)]
126 | * [ro.build.version.oplusrom]: [V13.1.0]
127 | * [ro.build.version.oplusrom.confidential]: [V13.1.0]
128 | */
129 | static final String[] OS_VERSION_NAME_COLOR_OS_NEW = { SYSTEM_PROPERTY_BUILD_DISPLAY_ID,
130 | "ro.build.display.id.show",
131 | "persist.sys.oplus.ota_ver_display",
132 | OS_CONDITIONS_NAME_COLOR_OS_NEW,
133 | "ro.build.version.oplusrom.confidential" };
134 |
135 | /**
136 | * ColorOS 低版本:
137 | * [ro.build.version.opporom]: [V11.2]
138 | */
139 | static final String OS_VERSION_NAME_COLOR_OS_OLD = "ro.build.version.opporom";
140 |
141 | /* ---------------------------------------- 下面是 VIVO 的系统 ---------------------------------------- */
142 |
143 | /**
144 | * [ro.vivo.os.build.display.id]: [OriginOS 4]
145 | * [ro.vivo.os.build.display.id]: [OriginOS 5]
146 | * [ro.vivo.os.build.display.id]: [OriginOS 3]
147 | * [ro.vivo.os.build.display.id]: [OriginOS 1.0]
148 | *
149 | * [ro.vivo.os.build.display.id]: [Funtouch OS_10]
150 | * [ro.vivo.os.build.display.id]: [Funtouch OS_4.0]
151 | * [ro.vivo.os.build.display.id]: [Funtouch 0S_2.5]
152 | */
153 | static final String OS_CONDITIONS_VIVO_OS = "ro.vivo.os.build.display.id";
154 |
155 | static final int OS_TYPE_ORIGIN_OS = 1443687338;
156 | static final String OS_NAME_ORIGIN_OS = "OriginOS";
157 |
158 | /**
159 | * [ro.vivo.product.version]: [PD2359C_A_15.1.19.20.W10.V000L1]
160 | * [ro.vivo.default.version]: [PD2309_A_15.1.19.20.W10.V000L1]
161 | * [ro.vivo.build.version.incremental]: [15.1.19.20.W10]
162 | *
163 | * 下面的属性在 OriginOS3 ~ OriginOS5 上面有返回,但是 OriginOS1 上面没有返回:
164 | * [ro.vivo.product.version.incremental]: [15.1.19.20.W10.V000L1]
165 | * [ro.vivo.build.version]: [PD2359C_A_15.1.19.20.W10]
166 | * [ro.build.software.version]: [PD2359C_A_15.1.19.20.W10]
167 | * [ro.vivo.system.product.version]: [PD2309_A_15.1.19.20.W10]
168 | */
169 | static final String[] OS_VERSION_NAME_ORIGIN_OS = { "ro.vivo.product.version",
170 | "ro.vivo.default.version",
171 | "ro.vivo.build.version.incremental",
172 | "ro.vivo.product.version.incremental",
173 | "ro.vivo.build.version",
174 | "ro.vivo.system.product.version",
175 | "ro.build.software.version" };
176 |
177 | static final int OS_TYPE_FUNTOUCH_OS = -294058204;
178 | static final String OS_NAME_FUNTOUCH_OS = "FuntouchOS";
179 |
180 | /**
181 | * FuntouchOS 4.0 版本属性:
182 | * [ro.vivo.os.build.display.id]: [Funtouch OS_4.0]
183 | * [ro.vivo.os.version]: [4.0]
184 | * [ro.vivo.rom]: [rom_4.0]
185 | * [ro.vivo.rom.version]: [rom_4.0]
186 | * [ro.vivo.product.version]: [PD1730_A_1.13.15](获取到的是错误)
187 | * [ro.build.netaccess.version]: [PD1730_A_1.13.15](获取到的是错误)
188 | * [ro.build.software.version]: [PD1730_A_1.13.15](获取到的是错误)
189 | * [ro.build.version.bbk]: [PD1730_A_1.13.15](获取到的是错误)
190 | * [ro.vivo.product.version]: [PD1730_A_1.13.15](获取到的是错误)
191 | *
192 | * FuntouchOS 10.0 版本属性:
193 | * [ro.vivo.os.build.display.id]: [Funtouch OS_10]
194 | * [ro.build.netaccess.version]: [PD1813E_A_7.10.42]
195 | * [ro.build.software.version]: [PD1813E_A_7.10.42]
196 | * [ro.build.version.bbk]: [PD1813E_A_7.10.42]
197 | * [ro.vivo.product.version]: [PD1813E_A_7.10.42]
198 | * [ro.vivo.os.version]: [11.0](获取到的是错误)
199 | * [ro.vivo.rom]: [rom_11.0](获取到的是错误)
200 | * [ro.vivo.rom.version]: [rom_11.0](获取到的是错误)
201 | *
202 | * 所以综合取舍下来最优解是:
203 | * [ro.vivo.os.build.display.id]: [Funtouch OS_10]
204 | */
205 | static final String OS_VERSION_NAME_FUNTOUCH_OS = OS_CONDITIONS_VIVO_OS;
206 |
207 | /* ---------------------------------------- 下面是华为或者荣耀的系统 ---------------------------------------- */
208 |
209 | static final int OS_TYPE_MAGIC_OS = -1801284559;
210 | static final String OS_NAME_MAGIC_OS = "MagicOS";
211 |
212 | /**
213 | * MagicOS 9.0 返回:[msc.config.magic.version]: [9.0]
214 | * MagicOS 7.1 返回:[msc.config.magic.version]: [7.1]
215 | * MagicOS 7.0 返回:[msc.config.magic.version]: [7.0]
216 | * MagicUI 6.1 返回:[msc.config.magic.version]: [6.1]
217 | * MagicUI 3.1.1 返回:空
218 | * MagicUI 3.0.1 返回:空
219 | *
220 | * MagicOS 9.0 返回:[ro.build.version.magic]: [MagicOS_9.0.0]
221 | * MagicOS 7.1 返回:[ro.build.version.magic]: [MagicOS_7.1.0]
222 | * MagicOS 7.0 返回:[ro.build.version.magic]: [MagicOS_7.1.0](获取到的是错误的)
223 | * MagicUI 6.1 返回:[ro.build.version.magic]: [MagicUI_6.1.0]
224 | * MagicUI 3.1.1 返回:[ro.build.version.magic]: [3.1.1]
225 | * MagicUI 3.0.1 返回:[ro.build.version.magic]: [3.0.1]
226 | */
227 | static final String[] OS_CONDITIONS_NAME_MAGIC_OS = { "msc.config.magic.version",
228 | "ro.build.version.magic" };
229 |
230 | static final int OS_TYPE_HARMONY_OS_NEXT_ANDROID_COMPATIBLE = -182666708;
231 | static final String OS_NAME_HARMONY_OS_NEXT_ANDROID_COMPATIBLE = "HarmonyOS NEXT AndroidCompatible";
232 |
233 | /**
234 | * HarmonyOS NEXT 6.0.0 版本属性:
235 | * [ro.sys.anco.product.software.version]: [PLA-AL10 6.0.0.100(SP6C00E47R4P3)]
236 | * [sys.anco.hwpatch.display.version]: []
237 | *
238 | * HarmonyOS NEXT 5.1.0 版本属性:
239 | * [ro.sys.anco.product.software.version]: [ADL-AL00U 5.1.0.150(SP10C00E128R2P1)]
240 | * [sys.anco.hwpatch.display.version]: [5.1.0.150(SP10C00E128R2P1patch03)]
241 | *
242 | * 所以综合取舍下来最优解是:
243 | * [ro.sys.anco.product.software.version]: [PLA-AL10 6.0.0.100(SP6C00E47R4P3)]
244 | */
245 | static final String OS_VERSION_HARMONY_OS_NEXT_ANDROID_COMPATIBLE = "ro.sys.anco.product.software.version";
246 |
247 | /**
248 | * [ro.product.anco.devicetype]: [phone]
249 | *
250 | * [ro.product.os.dist.anco.apiversion]: [60000]
251 | * [ro.product.os.dist.anco.apiversion]: [50005]
252 | *
253 | * [ro.product.os.dist.anco.releasetype]: [Release]
254 | * [ro.product.os.dist.anco.releasetype]: [Beta5]
255 | */
256 | static final String[] OS_CONDITIONS_HARMONY_OS_NEXT_ANDROID_COMPATIBLE = { "ro.product.anco.devicetype",
257 | OS_VERSION_HARMONY_OS_NEXT_ANDROID_COMPATIBLE,
258 | "ro.product.os.dist.anco.apiversion",
259 | "ro.product.os.dist.anco.releasetype" };
260 |
261 | /**
262 | * MagicOS 9.0 版本属性:
263 | * [mscw.hnouc.patch.display.version]: [9.0.0.175(C00E175R110P22H7)]
264 | * [mscw.hnouc.patch.version]: [9.0.0.175(C00E175R110P22patch07)]
265 | * [persist.sys.hiview.base_version]: [BVL-LGRP1-CHN 9.0.0.175]
266 | * [persist.sys.hiview.cust_version]: [BVL-AN16-CUST 9.0.0.175(C00)]
267 | * [ro.build.display.id]: [BVL-AN16 9.0.0.175(C00E175R110P22)]
268 | * [ro.build.ver.physical]: [BVL-AN16 9.0.0.175(C00E175R110P22)]
269 | * [ro.build.version.incremental]: [9.0.0.175C00E175R110P22]
270 | * [ro.comp.hl.product_base_version]: [BVL-LGRP1-CHN 9.0.0.175]
271 | * [ro.comp.hl.product_cust_version]: [BVL-AN16-CUST 9.0.0.175(C00)]
272 | * [ro.honor.build.display.id]: [BVL-AN16 9.0.0.175(C00E175R110P22)]
273 | * [ro.odm.build.version.incremental]: [9.0.0.175C00E175R110P22]
274 | *
275 | * MagicOS 7.1 版本属性:
276 | * [mscw.hnouc.patch.display.version]: [7.1.0.216(CNC00E171R3P1H1)]
277 | * [mscw.hnouc.patch.version]: [7.1.0.216(CNC00E171R3P1patch01)]
278 | * [persist.sys.hiview.base_version]: [ELNN-LGRP11-CHN 7.1.0.216]
279 | * [ro.build.display.id]: [ELN-W09 7.1.0.216(CNC00E171R3P1)]
280 | * [ro.build.ver.physical]: [ELN-W09 7.1.0.216(CNC00E171R3P1)]
281 | * [ro.build.version.incremental]: [7.1.0.216CNC00E171R3P1]
282 | * [ro.comp.hl.product_base_version]: [ELNN-LGRP11-CHN 7.1.0.216]
283 | * [ro.honor.build.display.id]: [ELN-W09 7.1.0.216(CNC00E171R3P1)]
284 | * [ro.odm.build.version.incremental]: [7.1.0.216CNC00E171R3P1]
285 | *
286 | * MagicOS 7.0 版本属性:
287 | * [persist.sys.hiview.base_version]: [ANY-LGRP1-CHN 7.0.0.225]
288 | * [ro.build.display.id]: [ANY-AN00 7.0.0.225(C00E225R1P4)]
289 | * [ro.comp.hl.product_base_version]: [ANY-LGRP1-CHN 7.0.0.225]
290 | * [ro.honor.build.display.id]: [ANY-AN00 7.0.0.225(C00E225R1P4)]
291 | *
292 | * MagicUI 6.1 版本属性:
293 | * [mscw.hnouc.patch.version]: [6.1.0.152(C00E145R1P4patch01)]
294 | * [persist.sys.hiview.base_version]: [VNE-LGRP1-CHN 6.1.0.152]
295 | * [ro.build.display.id]: [VNE-AN00 6.1.0.152(C00E145R1P4)]
296 | * [ro.build.ver.physical]: [VNE-AN00 6.1.0.152(C00E145R1P4)]
297 | * [ro.build.version.incremental]: [6.1.0.152C00]
298 | * [ro.comp.hl.product_base_version]: [VNE-LGRP1-CHN 6.1.0.152]
299 | * [ro.honor.build.display.id]: [VNE-AN00 6.1.0.152(C00E145R1P4)]
300 | *
301 | * MagicUI 3.1.1 版本属性:
302 | * [persist.mygote.build.id]: [TEL-AN00a 3.1.1.115(C00E110R3P1)]
303 | * [persist.sys.hiview.base_version]: [TEL-LGRP1-CHN 3.1.1.115]
304 | * [ro.build.display.id]: [TEL-AN00a 3.1.1.115(C00E110R3P1)]
305 | * [ro.build.version.incremental]: [3.1.1.115C00]
306 | * [ro.comp.hl.product_base_version]: [TEL-LGRP1-CHN 3.1.1.115]
307 | * [ro.huawei.build.display.id]: [TEL-AN00a 3.1.1.115(C00E110R3P1)]
308 | * [ro.huawei.build.version.incremental]: [3.1.1.115C00]
309 | *
310 | * MagicUI 3.0.1 版本属性:
311 | * [persist.mygote.build.id]: [OXF-AN00 3.0.1.178(C00E175R3P3)]
312 | * [persist.sys.hiview.base_version]: [OXF-LGRP1-CHN 3.0.1.178]
313 | * [ro.build.display.id]: [OXF-AN00 3.0.1.178(C00E175R3P3)]
314 | * [ro.build.version.incremental]: [3.0.1.178C00]
315 | * [ro.comp.hl.product_base_version]: [OXF-LGRP1-CHN 3.0.1.178]
316 | * [ro.huawei.build.display.id]: [OXF-AN00 3.0.1.178(C00E175R3P3)]
317 | * [ro.huawei.build.version.incremental]: [3.0.1.178C00]
318 | *
319 | * 所以综合取舍下来最优解是:
320 | * [ro.honor.build.display.id]: [ANY-AN00 7.0.0.225(C00E225R1P4)]
321 | * [persist.sys.hiview.base_version]: [ANY-LGRP1-CHN 7.0.0.225]
322 | * [ro.comp.hl.product_base_version]: [ANY-LGRP1-CHN 7.0.0.225]
323 | * [ro.build.display.id]: [ANY-AN00 7.0.0.225(C00E225R1P4)]
324 | */
325 | static final String[] OS_VERSION_NAME_MAGIC_OS = { "ro.honor.build.display.id",
326 | "persist.sys.hiview.base_version",
327 | "ro.comp.hl.product_base_version",
328 | SYSTEM_PROPERTY_BUILD_DISPLAY_ID };
329 |
330 | static final int OS_TYPE_HARMONY_OS = 1583864138;
331 | static final String OS_NAME_HARMONY_OS = "HarmonyOS";
332 |
333 | /**
334 | * HarmonyOS 4.3.0 版本属性:
335 | * [persist.ark.build.id]: [CLS-AL00 4.3.0.126(SP7C00E126R4P4)]
336 | * [persist.sys.hiview.base_version]: [CLS-LGRP1-CHN 4.3.0.126(SP7)]
337 | * [persist.sys.hiview.cust_version]: [CLS-AL00-CUST 4.3.0.126(C00)]
338 | * [ro.build.display.id]: [CLS-AL00 4.3.0.126(SP7C00E126R4P4)]
339 | * [ro.comp.hl.product_base_version]: [CLS-LGRP1-CHN 4.3.0.126(SP7)]
340 | * [ro.comp.hl.product_cust_version]: [CLS-AL00-CUST 4.3.0.126(C00)]
341 | * [ro.huawei.build.display.id]: [CLS-AL00 4.3.0.126(SP7C00E126R4P4)]
342 | *
343 | * HarmonyOS 4.2.0 版本属性:
344 | * [hwouc.hwpatch.version]: [4.2.0.120(SP1C00E100R5P4patch01)]
345 | * [persist.ark.build.id]: [LIO-AL00 4.2.0.120(SP1C00E100R5P4)]
346 | * [persist.sys.hiview.base_version]: [LIO-LGRP1-CHN 4.2.0.120(SP1)]
347 | * [ro.build.display.id]: [LIO-AL00 4.2.0.120(SP1C00E100R5P4)]
348 | * [ro.comp.hl.product_base_version]: [LIO-LGRP1-CHN 4.2.0.120(SP1)]
349 | * [ro.huawei.build.display.id]: [LIO-AL00 4.2.0.120(SP1C00E100R5P4)]
350 | * [hw_sc.build.platform.version]: [4.2.0]
351 | *
352 | * HarmonyOS 4.0.0 版本属性:
353 | * [hwouc.hwpatch.version]: [4.0.0.121(C00E120R8P4patch02)]
354 | * [persist.mygote.build.id]: [YAL-AL10 4.0.0.121(C00E120R8P4)]
355 | * [persist.sys.hiview.base_version]: [YAL-LGRP1-CHN 4.0.0.121]
356 | * [ro.build.ver.physical]: [YAL-AL10 104.0.0.121(C00E120R8P4)]
357 | * [ro.comp.hl.product_base_version]: [YAL-LGRP1-CHN 4.0.0.121]
358 | * [ro.comp.hl.product_base_version.real]: [YAL-LGRP1-CHN 104.0.0.121]
359 | * [ro.huawei.build.display.id]: [YAL-AL10 4.0.0.121(C00E120R8P4)]
360 | * [hw_sc.build.platform.version]: [4.0.0]
361 | *
362 | * HarmonyOS 3.0.0 版本属性:
363 | * [hwouc.hwpatch.version]: [3.0.0.165(C00E160R5P3patch03)]
364 | * [persist.mygote.build.id]: [YAL-AL50 3.0.0.165(C00E160R5P3)]
365 | * [persist.sys.hiview.base_version]: [YAL-LGRP3-CHN 3.0.0.165]
366 | * [ro.build.display.id]: [YAL-AL50 3.0.0.165(C00E160R5P3)]
367 | * [ro.comp.hl.product_base_version]: [YAL-LGRP3-CHN 3.0.0.165]
368 | * [ro.huawei.build.display.id]: [YAL-AL50 3.0.0.165(C00E160R5P3)]
369 | * [hw_sc.build.platform.version]: [3.0.0]
370 | *
371 | * HarmonyOS 2.0.0 版本属性:
372 | * [persist.sys.hiview.base_version]: [MAR-LGRP1-CHN 2.0.0.185]
373 | * [persist.sys.hiview.cust_version]: [MAR-AL00-CUST 2.0.0.185(C00)]
374 | * [ro.comp.hl.product_base_version]: [MAR-LGRP1-CHN 2.0.0.185]
375 | * [ro.comp.hl.product_cust_version]: [MAR-AL00-CUST 2.0.0.185(C00)]
376 | * [ro.huawei.build.display.id]: [MAR-AL00 2.0.0.185(C00E185R1P5)]
377 | * [hw_sc.build.platform.version]: [2.0.0]
378 | *
379 | * 所以综合取舍下来最优解是:
380 | * [ro.huawei.build.display.id]: [LIO-AL00 4.2.0.120(SP1C00E100R5P4)]
381 | * [ro.comp.hl.product_base_version]: [LIO-LGRP1-CHN 4.2.0.120(SP1)]
382 | * [persist.sys.hiview.base_version]: [LIO-LGRP1-CHN 4.2.0.120(SP1)]
383 | * [hw_sc.build.platform.version]: [4.2.0]
384 | */
385 | static final String[] OS_VERSION_NAME_HARMONY_OS = { "ro.huawei.build.display.id",
386 | "ro.comp.hl.product_base_version",
387 | "persist.sys.hiview.base_version",
388 | "hw_sc.build.platform.version" };
389 |
390 | /**
391 | * [ro.build.ohos.devicetype]: [phone]
392 | * [persist.sys.ohos.osd.cloud.switch]: [true]
393 | */
394 | static final String[] OS_CONDITIONS_HARMONY_OS = { "ro.build.ohos.devicetype",
395 | "persist.sys.ohos.osd.cloud.switch" };
396 |
397 | static final int OS_TYPE_EMUI = 2132284;
398 | static final String OS_NAME_EMUI = "EMUI";
399 | /**
400 | * [ro.build.version.emui]: [EmotionUI_8.0.0]
401 | * [ro.build.version.emui]: [EmotionUI_9.1.0]
402 | * [ro.build.version.emui]: [EmotionUI_9.1.1]
403 | * [ro.build.version.emui]: [EmotionUI_10.1.1]
404 | * [ro.build.version.emui]: [EmotionUI_11.1.0]
405 | * [ro.build.version.emui]: [EmotionUI_13.0.0]
406 | * [ro.build.version.emui]: [EmotionUI_14.0.0]
407 | * [ro.build.version.emui]: [EmotionUI_14.2.0]
408 | */
409 | static final String OS_VERSION_NAME_EMUI = "ro.build.version.emui";
410 |
411 | /* ---------------------------------------- 下面是三星的系统 ---------------------------------------- */
412 |
413 | static final int OS_TYPE_ONE_UI = 76334938;
414 | static final String OS_NAME_ONE_UI = "OneUI";
415 |
416 | /**
417 | * OneUI 高版本
418 | * OneUI 8.0:[ro.build.version.oneui]: [80000]
419 | * OneUI 7.0: [ro.build.version.oneui]: [70000]
420 | * OneUI 6.1:[ro.build.version.oneui]: [60101]
421 | * OneUI 5.1.1:[ro.build.version.oneui]: [50101]
422 | */
423 | static final String OS_VERSION_NAME_ONE_UI = "ro.build.version.oneui";
424 |
425 | /* ---------------------------------------- 下面是一加的系统 ---------------------------------------- */
426 |
427 | static final int OS_TYPE_OXYGEN_OS = -1363277916;
428 | static final String OS_NAME_OXYGEN_OS = "OxygenOS";
429 |
430 | /**
431 | * [ro.oxygen.version]: [9.0.4]
432 | */
433 | static final String OS_VERSION_NAME_OXYGEN_OS = "ro.oxygen.version";
434 |
435 | static final int OS_TYPE_H2_OS = 2195534;
436 | static final String OS_NAME_H2_OS = "H2OS";
437 | /**
438 | * Android 7.1.1:[ro.rom.version]: [H2OS V3.5]
439 | * Android 9.0:[ro.rom.version]: [9.0.11]
440 | * Android 11:[ro.rom.version]: [11.1.2.2]
441 | */
442 | static final String OS_VERSION_NAME_H2_OS = "ro.rom.version";
443 |
444 | /* ---------------------------------------- 下面是魅族的系统 ---------------------------------------- */
445 |
446 | static final int OS_TYPE_FLYME = 67983659;
447 | static final String OS_NAME_FLYME = "Flyme";
448 | /**
449 | * [ro.build.display.id]: [Flyme 6.2.0.2A]
450 | * [ro.build.display.id]: [Flyme 6.3.5.0A]
451 | * [ro.build.display.id]: [Flyme 7.1.5.2A]
452 | * [ro.build.display.id]: [Flyme 8.0.5.0A]
453 | * [ro.build.display.id]: [Flyme 8.1.8.0A]
454 | * [ro.build.display.id]: [Flyme 10.5.0.1A]
455 | * [ro.build.display.id]: [Flyme 11.2.1.0A]
456 | * [ro.build.display.id]: [Flyme 12.1.0.0A]
457 | */
458 | static final String OS_VERSION_NAME_FLYME = SYSTEM_PROPERTY_BUILD_DISPLAY_ID;
459 |
460 | /**
461 | * [ro.flyme.published]: [true]
462 | * [ro.flyme.version.id]: [Flyme 9.3.1.0A]
463 | */
464 | static final String[] OS_CONDITIONS_FLYME = { "ro.flyme.published",
465 | "ro.flyme.version.id" };
466 |
467 | /* ---------------------------------------- 下面是中兴或者努比亚的系统 ---------------------------------------- */
468 |
469 | /**
470 | * NebulaAIOS 返回:[ro.build.MiFavor_version]: [NebulaOS1.0]
471 | * RedMagicOS 返回:[ro.build.MiFavor_version]: [NebulaOS1.0]
472 | * MyOS 返回:[ro.build.MiFavor_version]: [12]
473 | * MiFavor 返回:[ro.build.MiFavor_version]: [10.1]
474 | */
475 | static final String OS_VERSION_ZTE_OS = "ro.build.MiFavor_version" ;
476 |
477 | /**
478 | * NebulaAIOS 返回:[ro.build.display.id]: [NebulaAIOS1.0.14_NX712J]
479 | * RedMagicOS 返回:[ro.build.display.id]: [RedMagicOS10.0.12]
480 | * MyOS 返回:[ro.build.display.id]: [MyOS12.0.14_A2121]
481 | * MifavorUI 返回:注意不能用 [ro.build.display.id]: [ZTE_A2021_PROV1.0.2B05](错误),应该用 ro.build.MiFavor_version
482 | */
483 | static final String OS_VERSION_NAME_ZTE_OS = SYSTEM_PROPERTY_BUILD_DISPLAY_ID;
484 |
485 | static final int OS_TYPE_RED_MAGIC_OS = -417455456;
486 | static final String OS_NAME_RED_MAGIC_OS = "RedMagicOS";
487 |
488 | static final int OS_TYPE_NEBULA_AIOS = -1668450325;
489 | static final String OS_NAME_NEBULA_AIOS = "NebulaAIOS";
490 |
491 | static final int OS_TYPE_MY_OS = 2412720;
492 | static final String OS_NAME_MY_OS = "MyOS";
493 |
494 | static final int OS_TYPE_MIFAVOR_UI = -203064298;
495 | static final String OS_NAME_MIFAVOR_UI = "MifavorUI";
496 | /**
497 | * [ro.build.MiFavor_version]: [10.1]
498 | * [ro.build.MiFavor_version]: [4.0]
499 | */
500 | static final String OS_VERSION_NAME_MIFAVOR_UI = OS_VERSION_ZTE_OS;
501 |
502 | /* ---------------------------------------- 下面是锤子的系统 ---------------------------------------- */
503 |
504 | static final int OS_TYPE_SMARTISAN_OS = 1805724132;
505 | static final String OS_NAME_SMARTISAN_OS = "SmartisanOS";
506 | static final String OS_VERSION_NAME_SMARTISAN_OS = "ro.smartisan.version";
507 | static final String[] OS_CONDITIONS_SMARTISAN_OS = { "ro.smartisan.sa",
508 | OS_VERSION_NAME_SMARTISAN_OS };
509 |
510 | /* ---------------------------------------- 下面是乐视的系统 ---------------------------------------- */
511 |
512 | static final int OS_TYPE_EUI_OS = 69017;
513 | static final String OS_NAME_EUI_OS = "EUI";
514 | /**
515 | * [ro.letv.release.version]: [6.0.030S]
516 | */
517 | static final String OS_VERSION_NAME_EUI_OS = "ro.letv.release.version";
518 | /**
519 | * [ro.letv.release.version_date]: [5.8.001D_09093]
520 | * [ro.product.letv_model]: [Le X620]
521 | * [ro.product.letv_name]: [乐2]
522 | * [sys.letv.fmodelaid]: [10120]
523 | * [persist.sys.leui.bootreason]: [0]
524 | * [ro.config.leui_ringtone_slot2]: [Default.ogg]
525 | * [ro.leui_oem_unlock_enable]: [1]
526 | */
527 | static final String[] OS_CONDITIONS_EUI_OS = { OS_VERSION_NAME_EUI_OS,
528 | "ro.letv.release.version_date",
529 | "ro.product.letv_model",
530 | "ro.product.letv_name",
531 | "sys.letv.fmodelaid",
532 | "persist.sys.leui.bootreason",
533 | "ro.config.leui_ringtone_slot2",
534 | "ro.leui_oem_unlock_enable" };
535 |
536 | /* ---------------------------------------- 下面是联想、摩托罗拉的系统 ---------------------------------------- */
537 |
538 | static final int OS_TYPE_ZUX_OS = 85736225;
539 | static final String OS_NAME_ZUX_OS = "ZUXOS";
540 | /**
541 | * [ro.config.lgsi.fp.incremental]: [ZUXOS_1.1.350_250418_PRC]
542 | * [ro.config.lgsi.os.version]: [1.1]
543 | */
544 | static final String[] OS_VERSION_NAME_ZUX_OS = { "ro.config.lgsi.fp.incremental",
545 | "ro.config.lgsi.os.version" };
546 | /**
547 | * [ro.config.lgsi.os.name]: [ZUXOS]
548 | */
549 | static final String OS_CONDITIONS_ZUX_OS = "ro.config.lgsi.os.name";
550 |
551 | static final int OS_TYPE_ZUI = 89198;
552 | static final String OS_NAME_ZUI = "ZUI";
553 | /**
554 | * [ro.com.zui.version]: [3.5]
555 | */
556 | static final String OS_VERSION_NAME_ZUI = "ro.com.zui.version";
557 | /**
558 | * [ro.zui.version.status]: [ST]
559 | * [ro.zui.hardware.displayid]: [H201]
560 | * [persist.radio.zui.feature]: [true]
561 | * [ro.config.zuisdk.enabled]: [true]
562 | */
563 | static final String[] OS_CONDITIONS_ZUI = { OS_VERSION_NAME_ZUI,
564 | "ro.zui.version.status",
565 | "ro.zui.hardware.displayid",
566 | "persist.radio.zui.feature",
567 | "ro.config.zuisdk.enabled" };
568 |
569 | /* ---------------------------------------- 下面是努比亚的老系统 ---------------------------------------- */
570 |
571 | static final int OS_TYPE_NUBIA_UI = -2010470489;
572 | static final String OS_NAME_NUBIA_UI = "nubiaUI";
573 | /**
574 | * [ro.build.nubia.rom.code]: [V1.0]
575 | * [ro.build.nubia.rom.code]: [V1.6]
576 | * [ro.build.nubia.rom.code]: [V2.0]
577 | * [ro.build.nubia.rom.code]: [V3.0]
578 | * [ro.build.nubia.rom.code]: [V3.7]
579 | * [ro.build.nubia.rom.code]: [V4.0]
580 | * [ro.build.nubia.rom.code]: [V6.0]
581 | */
582 | static final String OS_VERSION_NAME_NUBIA_UI = "ro.build.nubia.rom.code";
583 |
584 | /**
585 | * [ro.build.nubia.rom.name]: [nubiaUI]
586 | */
587 | static final String OS_CONDITIONS_NUBIA_UI = "ro.build.nubia.rom.name";
588 |
589 | /* ---------------------------------------- 下面是豆包的系统 ---------------------------------------- */
590 |
591 | static final int OS_TYPE_OBRIC_UI = 12510861;
592 | static final String OS_NAME_OBRIC_UI = "ObricUI";
593 | /**
594 | * [ro.build.id]: [1.1.0.0]
595 | * [ro.product.build.id]: [1.1.0.0]
596 | * [ro.system.build.id]: [1.1.0.0]
597 | * [ro.system_ext.build.id]: [1.1.0.0]
598 | * [ro.build.display.id]: [1.1.0.0 release-keys]
599 | */
600 | static final String[] OS_VERSION_NAME_OBRIC_UI = { "ro.build.id",
601 | "ro.product.build.id",
602 | "ro.system.build.id",
603 | "ro.system_ext.build.id",
604 | SYSTEM_PROPERTY_BUILD_DISPLAY_ID };
605 | /**
606 | * [init.svc.bytecellular]: [running]
607 | */
608 | static final String OS_CONDITIONS_OBRIC_UI = "init.svc.bytecellular";
609 |
610 | /* ---------------------------------------- 下面是华硕的系统 ---------------------------------------- */
611 |
612 | static final int OS_TYPE_ROG_UI = 78153150;
613 | static final String OS_NAME_ROG_UI = "ROGUI";
614 | /**
615 | * [ro.build.version.incremental]: [33.0210.0210.235-0]
616 | */
617 | static final String OS_VERSION_NAME_ROG_UI = SYSTEM_PROPERTY_BUILD_VERSION_INCREMENTAL;
618 | /**
619 | * [ro.asus.rog]: [1]
620 | */
621 | static final String OS_CONDITIONS_ROG_UI = "ro.asus.rog";
622 |
623 | /* ---------------------------------------- 下面是 360 的系统 ---------------------------------------- */
624 |
625 | static final int OS_TYPE_360_UI = 48757121;
626 | static final String OS_NAME_360_UI = "360UI";
627 | /**
628 | * Android 8.0:[ro.build.uiversion]: [360UI:V3.0]
629 | */
630 | static final String OS_VERSION_NAME_360_UI = "ro.build.uiversion";
631 |
632 | private static int sCurrentOsType;
633 | @Nullable
634 | private static String sCurrentOsName;
635 | @Nullable
636 | private static String sCurrentOsVersionName;
637 |
638 | private DeviceOs() {
639 | // 私有化构造方法,禁止外部实例化
640 | }
641 |
642 | static {
643 | // 需要注意的是:该逻辑需要在判断 MIUI 系统之前判断,因为在 HyperOS 系统上面判断当前设备的厂商系统是否为 MIUI 系统也会返回 true
644 | // 这是因为 HyperOS 系统本身就是从 MIUI 系统演变而来,有这个问题也很正常,主要是厂商为了系统兼容性而保留的
645 | if (SystemPropertyCompat.isSystemPropertyAnyOneExist(OS_CONDITIONS_HYPER_OS)) {
646 | sCurrentOsType = OS_TYPE_HYPER_OS;
647 | sCurrentOsName = OS_NAME_HYPER_OS;
648 | sCurrentOsVersionName = getBestVersionNameBySystemProperties(OS_VERSION_NAME_HYPER_OS);
649 | } else if (SystemPropertyCompat.isSystemPropertyAnyOneExist(OS_CONDITIONS_MIUI)) {
650 | sCurrentOsType = OS_TYPE_MIUI;
651 | sCurrentOsName = OS_NAME_MIUI;
652 | sCurrentOsVersionName = getBestVersionNameBySystemProperties(OS_VERSION_NAME_MIUI);
653 | }
654 |
655 | if (sCurrentOsName == null) {
656 | String realmeUiVersion = SystemPropertyCompat.getSystemPropertyValue(OS_VERSION_NAME_REALME_UI);
657 | // realmeUI 一定要放在 ColorOS 之前判断,因为 realmeUI 是 ColorOS 的另外一个分支
658 | if (!TextUtils.isEmpty(realmeUiVersion)) {
659 | sCurrentOsType = OS_TYPE_REALME_UI;
660 | sCurrentOsName = OS_NAME_REALME_UI;
661 | sCurrentOsVersionName = getBestVersionNameByText(realmeUiVersion);
662 | } else if (SystemPropertyCompat.isSystemPropertyExist(OS_CONDITIONS_NAME_COLOR_OS_NEW)) {
663 | sCurrentOsType = OS_TYPE_COLOR_OS;
664 | sCurrentOsName = OS_NAME_COLOR_OS;
665 | // Github issue 地址:https://github.com/getActivity/DeviceCompat/issues/10
666 | sCurrentOsVersionName = getBestVersionNameBySystemProperties(OS_VERSION_NAME_COLOR_OS_NEW);
667 | } else {
668 | String colorOsVersion = SystemPropertyCompat.getSystemPropertyValue(OS_VERSION_NAME_COLOR_OS_OLD);
669 | if (!TextUtils.isEmpty(colorOsVersion)) {
670 | sCurrentOsType = OS_TYPE_COLOR_OS;
671 | sCurrentOsName = OS_NAME_COLOR_OS;
672 | sCurrentOsVersionName = getBestVersionNameByText(colorOsVersion);
673 | }
674 | }
675 | }
676 |
677 | if (sCurrentOsName == null) {
678 | String vivoOsName = SystemPropertyCompat.getSystemPropertyValue(OS_CONDITIONS_VIVO_OS);
679 | if (!TextUtils.isEmpty(vivoOsName)) {
680 | if (vivoOsName.toLowerCase().contains("origin")) {
681 | sCurrentOsType = OS_TYPE_ORIGIN_OS;
682 | sCurrentOsName = OS_NAME_ORIGIN_OS;
683 | // OriginOS 5 获取到的版本包含 15.x.x,例如:[ro.vivo.product.version]: [PD2429_A_15.0.18.12.W10.V000L1]
684 | // OriginOS 4 获取到的版本包含 14.x.x,例如:[ro.vivo.product.version]: [PD2220D_A_14.2.6.5.W10.V000L1]
685 | sCurrentOsVersionName = getBestVersionNameBySystemProperties(OS_VERSION_NAME_ORIGIN_OS);
686 | } else if (vivoOsName.toLowerCase().contains("funtouch")) {
687 | // 不要用 ro.vivo.os.name 属性判断是否为 FuntouchOS 系统,因为在 FuntouchOS 和 OriginOs 系统上面获取到的值是 Funtouch
688 | sCurrentOsType = OS_TYPE_FUNTOUCH_OS;
689 | sCurrentOsName = OS_NAME_FUNTOUCH_OS;
690 | sCurrentOsVersionName = getBestVersionNameBySystemProperties(OS_VERSION_NAME_FUNTOUCH_OS);
691 | }
692 | }
693 | }
694 |
695 | if (sCurrentOsName == null && SystemPropertyCompat.isSystemPropertyAnyOneExist(OS_CONDITIONS_NAME_MAGIC_OS)) {
696 | sCurrentOsType = OS_TYPE_MAGIC_OS;
697 | sCurrentOsName = OS_NAME_MAGIC_OS;
698 | sCurrentOsVersionName = getBestVersionNameBySystemProperties(OS_VERSION_NAME_MAGIC_OS);
699 | }
700 |
701 | // 判断是否为纯血鸿蒙应该要放在残血鸿蒙之前,因为纯血鸿蒙有 persist.sys.ohos.osd.cloud.switch 这个系统属性
702 | // 经过验证得出:如果这段代码放在残血鸿蒙之后再进行判断,会出现误判的情况,所以这里需要注意代码判断的顺序
703 | if (sCurrentOsName == null && SystemPropertyCompat.isSystemPropertyAnyOneExist(OS_CONDITIONS_HARMONY_OS_NEXT_ANDROID_COMPATIBLE)) {
704 | sCurrentOsType = OS_TYPE_HARMONY_OS_NEXT_ANDROID_COMPATIBLE;
705 | sCurrentOsName = OS_NAME_HARMONY_OS_NEXT_ANDROID_COMPATIBLE;
706 | sCurrentOsVersionName = getBestVersionNameBySystemProperties(OS_VERSION_HARMONY_OS_NEXT_ANDROID_COMPATIBLE);
707 | }
708 |
709 | if (sCurrentOsName == null && SystemPropertyCompat.isSystemPropertyAnyOneExist(OS_CONDITIONS_HARMONY_OS)) {
710 | sCurrentOsType = OS_TYPE_HARMONY_OS;
711 | sCurrentOsName = OS_NAME_HARMONY_OS;
712 | sCurrentOsVersionName = getBestVersionNameBySystemProperties(OS_VERSION_NAME_HARMONY_OS);
713 | }
714 |
715 | if (sCurrentOsName == null) {
716 | String emuiVersion = SystemPropertyCompat.getSystemPropertyValue(OS_VERSION_NAME_EMUI);
717 | // 在 MagicUI 6.1.0 上会返回 [ro.build.version.emui]: [MagicUI_6.1.0],这里要注意过滤掉
718 | if (!TextUtils.isEmpty(emuiVersion) && emuiVersion.toLowerCase().contains("emotionui")) {
719 | sCurrentOsType = OS_TYPE_EMUI;
720 | sCurrentOsName = OS_NAME_EMUI;
721 | sCurrentOsVersionName = getBestVersionNameByText(emuiVersion);
722 | }
723 | }
724 |
725 | if (sCurrentOsName == null) {
726 | // 参考三星设置的源码:com.samsung.android.settings.deviceinfo.softwareinfo.OneUIVersionPreferenceController.getDisplayVersion()
727 | String oneUiVersion = SystemPropertyCompat.getSystemPropertyValue(OS_VERSION_NAME_ONE_UI);
728 | if (!TextUtils.isEmpty(oneUiVersion)) {
729 | sCurrentOsType = OS_TYPE_ONE_UI;
730 | sCurrentOsName = OS_NAME_ONE_UI;
731 | if (oneUiVersion.matches(REGEX_NUMBER)) {
732 | try {
733 | // OneUI 5.1.1 获取到的值是 50101 再经过一通计算得出 5.1.1
734 | int oneUiVersionCode;
735 | oneUiVersionCode = Integer.parseInt(oneUiVersion);
736 | sCurrentOsVersionName = getOneUiVersionNameByVersionCode(oneUiVersionCode);
737 | } catch (Exception e) {
738 | // default implementation ignored
739 | }
740 | } else if (oneUiVersion.matches(REGEX_VERSION_NAME)) {
741 | sCurrentOsVersionName = oneUiVersion;
742 | }
743 | }
744 |
745 | if (sCurrentOsName == null || TextUtils.isEmpty(sCurrentOsVersionName)) {
746 | try {
747 | Field semPlatformIntField = Build.VERSION.class.getDeclaredField("SEM_PLATFORM_INT");
748 | semPlatformIntField.setAccessible(true);
749 | int semPlatformVersion = semPlatformIntField.getInt(null);
750 | sCurrentOsType = OS_TYPE_ONE_UI;
751 | sCurrentOsName = OS_NAME_ONE_UI;
752 | int superfluousValue = 90000;
753 | if (semPlatformVersion >= superfluousValue) {
754 | // https://stackoverflow.com/questions/60122037/how-can-i-detect-samsung-one-ui
755 | // OneUI 7.0 获取到的值是 160000,160000 - 90000 = 70000,70000 再经过一通计算得出 7.0 的版本号
756 | // OneUI 5.1.1 获取到的值是 140500,无法通过计算得出 5.1.1 的版本号,所以这种方法不是最佳的答案
757 | // OneUI 2.5 获取到的值是 110500,110500 - 90000 = 25000,20500 再经过一通计算得出 2.5 的版本号
758 | int oneUiVersionCode = semPlatformVersion - superfluousValue;
759 | sCurrentOsVersionName = getOneUiVersionNameByVersionCode(oneUiVersionCode);
760 | }
761 | } catch (Exception ignore) {
762 | // default implementation ignored
763 | }
764 | }
765 | }
766 |
767 | if (sCurrentOsName == null) {
768 | String oxygenOsVersion = SystemPropertyCompat.getSystemPropertyValue(OS_VERSION_NAME_OXYGEN_OS);
769 | if (!TextUtils.isEmpty(oxygenOsVersion)) {
770 | sCurrentOsType = OS_TYPE_OXYGEN_OS;
771 | sCurrentOsName = OS_NAME_OXYGEN_OS;
772 | sCurrentOsVersionName = getBestVersionNameByText(oxygenOsVersion);
773 | }
774 | }
775 | if (sCurrentOsName == null) {
776 | String h2OsVersion = SystemPropertyCompat.getSystemPropertyValue(OS_VERSION_NAME_H2_OS);
777 | if (!TextUtils.isEmpty(h2OsVersion)) {
778 | sCurrentOsType = OS_TYPE_H2_OS;
779 | sCurrentOsName = OS_NAME_H2_OS;
780 | sCurrentOsVersionName = getBestVersionNameByText(h2OsVersion);
781 | }
782 | }
783 |
784 | if (sCurrentOsName == null && SystemPropertyCompat.isSystemPropertyAnyOneExist(OS_CONDITIONS_FLYME)) {
785 | sCurrentOsType = OS_TYPE_FLYME;
786 | sCurrentOsName = OS_NAME_FLYME;
787 | sCurrentOsVersionName = getBestVersionNameBySystemProperties(OS_VERSION_NAME_FLYME);
788 | }
789 |
790 | if (sCurrentOsName == null && SystemPropertyCompat.isSystemPropertyExist(OS_VERSION_ZTE_OS)) {
791 | String osVersion = SystemPropertyCompat.getSystemPropertyValue(OS_VERSION_NAME_ZTE_OS);
792 | if (!TextUtils.isEmpty(osVersion)) {
793 | String lowerCaseOsVersion = osVersion.toLowerCase();
794 | if (lowerCaseOsVersion.contains("nebulaaios")) {
795 | sCurrentOsType = OS_TYPE_NEBULA_AIOS;
796 | sCurrentOsName = OS_NAME_NEBULA_AIOS;
797 | sCurrentOsVersionName = getBestVersionNameByText(osVersion);
798 | } else if (lowerCaseOsVersion.contains("redmagicos")) {
799 | sCurrentOsType = OS_TYPE_RED_MAGIC_OS;
800 | sCurrentOsName = OS_NAME_RED_MAGIC_OS;
801 | sCurrentOsVersionName = getBestVersionNameByText(osVersion);
802 | } else if (lowerCaseOsVersion.contains("myos")) {
803 | sCurrentOsType = OS_TYPE_MY_OS;
804 | sCurrentOsName = OS_NAME_MY_OS;
805 | sCurrentOsVersionName = getBestVersionNameByText(osVersion);
806 | } else if (lowerCaseOsVersion.contains("zte")) {
807 | sCurrentOsType = OS_TYPE_MIFAVOR_UI;
808 | sCurrentOsName = OS_NAME_MIFAVOR_UI;
809 | sCurrentOsVersionName = getBestVersionNameBySystemProperties(OS_VERSION_NAME_MIFAVOR_UI);
810 | }
811 | }
812 | }
813 |
814 | if (sCurrentOsName == null) {
815 | String osName = SystemPropertyCompat.getSystemPropertyValue(OS_CONDITIONS_NUBIA_UI);
816 | if (!TextUtils.isEmpty(osName) && osName.toLowerCase().contains("nubiaui")) {
817 | sCurrentOsType = OS_TYPE_NUBIA_UI;
818 | sCurrentOsName = OS_NAME_NUBIA_UI;
819 | sCurrentOsVersionName = getBestVersionNameBySystemProperties(OS_VERSION_NAME_NUBIA_UI);
820 | // 最近发现在 RedMagicOS 6.0 Android 13 获取到系统属性的是:
821 | // [ro.build.nubia.rom.name]: [nubiaUI]
822 | // [ro.build.nubia.rom.code]: [V6.0]
823 | // 会导致将 RedMagicOS 误判成 nubiaUI 系统
824 | // Buy Nubia Z30 Pro - Giztop:https://www.giztop.com/nubia-z30-pro.html
825 | // 努比亚Z30 Pro的系统UI体验怎么样,好用吗_九锋网:https://www.jiuphone.com/ask/2021/05217409.html
826 | // ZTE nubia Play - Full phone specifications:https://www.gsmarena.com/zte_nubia_play-10193.php
827 | // 努比亚科技 - 维基百科 --- Nubia Technology - Wikipedia:https://en.wikipedia.org/wiki/Nubia_Technology
828 | // 从上面三个链接可以得出来一个信息:nubiaUI 8.0 基于 Android 10、nubiaUI 9.0 基于 Android 11,nubiaUI 9.0 是最后一个版本
829 | // 另外通过分析 RedMagicOS 系统的历史,可以得出来一个结论:
830 | // 1. https://en.wikipedia.org/wiki/Nubia_Technology,
831 | // 2. https://beebom.com/nubia-red-magic-review/
832 | // 3. https://product.pconline.com.cn/mobile/nubia/1091411_detail.html
833 | // 4. https://g.pconline.com.cn/product/mobile/nubia/1091429_detail.html
834 | // 5. https://blog.csdn.net/romleyuan/article/details/138323889
835 | // 6. https://bbs.redmagic.com/detail/2815906
836 | // RedMagicOS 1.0 是基于 Android 8.1,RedMagicOS 2.0 是基于 Android 9.0,RedMagicOS 3.0 是基于 Android 10
837 | // RedMagicOS 4.0、4.5 是基于 Android 11,RedMagicOS 5.0 是基于 Android 12,RedMagicOS 6.0 是基于 Android 13
838 | // RedMagicOS 8.0 是基于 Android 13、RedMagicOS 9.0 是基于 Android 14、RedMagicOS 10.0 是基于 Android 15
839 | // 实测 RedMagicOS 10.0 及之后的版本可以通过 ro.build.display.id 属性识别到是 RedMagicOS,所以永远不会走到这里来
840 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 &&
841 | (extractBigVersionCodeByText(Build.VERSION.RELEASE) - getOsBigVersionCode()) >= 5) {
842 | sCurrentOsType = OS_TYPE_RED_MAGIC_OS;
843 | sCurrentOsName = OS_NAME_RED_MAGIC_OS;
844 | }
845 | }
846 | }
847 |
848 | if (sCurrentOsName == null) {
849 | String zuxOsName = SystemPropertyCompat.getSystemPropertyValue(OS_CONDITIONS_ZUX_OS);
850 | // ZUXOS 一定要放在 ZUI 之前判断,因为 ZUXOS 是 ZUI 的另外一个分支
851 | if (!TextUtils.isEmpty(zuxOsName) && zuxOsName.toLowerCase().contains("zuxos")) {
852 | sCurrentOsType = OS_TYPE_ZUX_OS;
853 | sCurrentOsName = OS_NAME_ZUX_OS;
854 | sCurrentOsVersionName = getBestVersionNameBySystemProperties(OS_VERSION_NAME_ZUX_OS);
855 | } else if (SystemPropertyCompat.isSystemPropertyAnyOneExist(OS_CONDITIONS_ZUI)) {
856 | sCurrentOsType = OS_TYPE_ZUI;
857 | sCurrentOsName = OS_NAME_ZUI;
858 | sCurrentOsVersionName = getBestVersionNameBySystemProperties(OS_VERSION_NAME_ZUI);
859 | }
860 | }
861 |
862 | if (sCurrentOsName == null && SystemPropertyCompat.isSystemPropertyExist(OS_CONDITIONS_OBRIC_UI)) {
863 | sCurrentOsType = OS_TYPE_OBRIC_UI;
864 | sCurrentOsName = OS_NAME_OBRIC_UI;
865 | sCurrentOsVersionName = getBestVersionNameBySystemProperties(OS_VERSION_NAME_OBRIC_UI);
866 | }
867 |
868 | if (sCurrentOsName == null && SystemPropertyCompat.isSystemPropertyExist(OS_CONDITIONS_ROG_UI)) {
869 | sCurrentOsType = OS_TYPE_ROG_UI;
870 | sCurrentOsName = OS_NAME_ROG_UI;
871 | sCurrentOsVersionName = getBestVersionNameBySystemProperties(OS_VERSION_NAME_ROG_UI);
872 | }
873 |
874 | if (sCurrentOsName == null && SystemPropertyCompat.isSystemPropertyAnyOneExist(OS_CONDITIONS_SMARTISAN_OS)) {
875 | sCurrentOsType = OS_TYPE_SMARTISAN_OS;
876 | sCurrentOsName = OS_NAME_SMARTISAN_OS;
877 | sCurrentOsVersionName = getBestVersionNameBySystemProperties(OS_VERSION_NAME_SMARTISAN_OS);
878 | }
879 |
880 | if (sCurrentOsName == null && SystemPropertyCompat.isSystemPropertyAnyOneExist(OS_CONDITIONS_EUI_OS)) {
881 | sCurrentOsType = OS_TYPE_EUI_OS;
882 | sCurrentOsName = OS_NAME_EUI_OS;
883 | sCurrentOsVersionName = getBestVersionNameBySystemProperties(OS_VERSION_NAME_EUI_OS);
884 | }
885 |
886 | if (sCurrentOsName == null) {
887 | String osVersion = SystemPropertyCompat.getSystemPropertyValue(OS_VERSION_NAME_360_UI);
888 | if (!TextUtils.isEmpty(osVersion) && osVersion.toLowerCase().contains("360ui")) {
889 | sCurrentOsType = OS_TYPE_360_UI;
890 | sCurrentOsName = OS_NAME_360_UI;
891 | sCurrentOsVersionName = getBestVersionNameByText(osVersion);
892 | }
893 | }
894 |
895 | if (sCurrentOsName == null) {
896 | try {
897 | // 如果走到这个判断中来,则证明用系统属性的方式已经判断不了 HarmonyOS,只能用代码反射特定类的方式去判断
898 | // 这里其实也可以用 ohos.system.version.SystemVersion 这个类来判断,但是考虑这种方式比较小众,所以就没有采用
899 | Class> buildExClass = Class.forName("com.huawei.system.BuildEx");
900 | Method getOsBrandMethod = buildExClass.getMethod("getOsBrand");
901 | getOsBrandMethod.setAccessible(true);
902 | Object osBrand = getOsBrandMethod.invoke(buildExClass);
903 | // 在 HarmonyOS 2.0、3.0 上测试,osBrand 字段的值等于 harmony,但是这里为了逻辑严谨,还是用 contains 去判断
904 | if (osBrand != null && String.valueOf(osBrand).toLowerCase().contains("harmony")) {
905 | sCurrentOsType = OS_TYPE_HARMONY_OS;
906 | sCurrentOsName = OS_NAME_HARMONY_OS;
907 | // 如果真的走到这里来,用系统属性大概率也是获取不到 HarmonyOS 的版本,
908 | // 因为前面无法用系统属性判断是否为 HarmonyOS 系统,这样写是死马当作活马医
909 | sCurrentOsVersionName = getBestVersionNameBySystemProperties(OS_VERSION_NAME_HARMONY_OS);
910 | }
911 | } catch (Exception ignore) {
912 | // default implementation ignored
913 | }
914 | }
915 |
916 | if (sCurrentOsName == null) {
917 | sCurrentOsName = "";
918 | }
919 |
920 | if (sCurrentOsVersionName == null) {
921 | sCurrentOsVersionName = "";
922 | }
923 | }
924 |
925 | /**
926 | * 判断当前设备的厂商系统是否为 HyperOS(小米手机、红米手机的系统)
927 | */
928 | public static boolean isHyperOs() {
929 | return sCurrentOsType == OS_TYPE_HYPER_OS;
930 | }
931 |
932 | /**
933 | * 判断当前设备的厂商系统是否为国内版本的 HyperOS
934 | */
935 | public static boolean isHyperOsByChina() {
936 | if (!isHyperOs()) {
937 | return false;
938 | }
939 | String[] propertyValues = SystemPropertyCompat.getSystemPropertyValues(OS_REGION_HYPER_OS);
940 | for (String propertyValue : propertyValues) {
941 | if (propertyValue.equalsIgnoreCase("cn")) {
942 | return true;
943 | }
944 | }
945 | return false;
946 | }
947 |
948 | /**
949 | * 判断当前设备的厂商系统是否为国际版本的 HyperOS
950 | */
951 | public static boolean isHyperOsByGlobal() {
952 | if (!isHyperOs()) {
953 | return false;
954 | }
955 | String[] propertyValues = SystemPropertyCompat.getSystemPropertyValues(OS_REGION_HYPER_OS);
956 | for (String propertyValue : propertyValues) {
957 | if (propertyValue.equalsIgnoreCase("global")) {
958 | return true;
959 | }
960 | }
961 | return false;
962 | }
963 |
964 | /**
965 | * 判断当前设备的厂商系统开启了 HyperOS 的系统优化选项
966 | */
967 | public static boolean isHyperOsOptimization() {
968 | return isXiaoMiSystemOptimization();
969 | }
970 |
971 | /**
972 | * 判断当前设备的厂商系统是否为 MIUI(小米手机、红米手机的老系统)
973 | */
974 | public static boolean isMiui() {
975 | return sCurrentOsType == OS_TYPE_MIUI;
976 | }
977 |
978 | /**
979 | * 判断当前设备的厂商系统是否为国内版本的 MIUI
980 | */
981 | public static boolean isMiuiByChina() {
982 | if (!isMiui()) {
983 | return false;
984 | }
985 | String[] propertyValues = SystemPropertyCompat.getSystemPropertyValues(OS_REGION_MIUI);
986 | for (String propertyValue : propertyValues) {
987 | if (propertyValue.equalsIgnoreCase("cn")) {
988 | return true;
989 | }
990 | }
991 | return false;
992 | }
993 |
994 | /**
995 | * 判断当前设备的厂商系统是否为国际版本的 MIUI
996 | */
997 | public static boolean isMiuiByGlobal() {
998 | // https://github.com/getActivity/XXPermissions/issues/398#issuecomment-3181978796
999 | // https://xiaomi.eu/community/threads/how-to-enable-the-region-option-in-settings-for-eu-roms.56303/
1000 | // https://github.com/search?q=+ro.miui.region+&type=code
1001 | // https://c.mi.com/global/post/600955
1002 | if (!isMiui()) {
1003 | return false;
1004 | }
1005 | String[] propertyValues = SystemPropertyCompat.getSystemPropertyValues(OS_REGION_MIUI);
1006 | for (String propertyValue : propertyValues) {
1007 | if (propertyValue.equalsIgnoreCase("global")) {
1008 | return true;
1009 | }
1010 | }
1011 | return false;
1012 | }
1013 |
1014 | /**
1015 | * 判断当前设备的厂商系统是否开启了 MIUI 优化选项
1016 | */
1017 | public static boolean isMiuiOptimization() {
1018 | return isXiaoMiSystemOptimization();
1019 | }
1020 |
1021 | /**
1022 | * 判断小米手机是否开启了系统优化(默认开启)
1023 | *
1024 | * MIUI 关闭步骤为:开发者选项-> 启动 MIUI 优化 -> 点击关闭
1025 | * HyperOS 的关闭步骤为:开发者选项-> 启用系统优化 -> 点击关闭
1026 | *
1027 | * 需要注意的是,关闭优化后,可以跳转到小米定制的权限请求页面,但是开启权限仍然是没有效果的
1028 | * 另外关于 MIUI 国际版开发者选项中是没有优化选项的,但是代码判断是有开启优化选项,也就是默认开启,这样是正确的
1029 | * 相关 Github issue 地址:https://github.com/getActivity/XXPermissions/issues/38
1030 | */
1031 | @SuppressLint("PrivateApi")
1032 | private static boolean isXiaoMiSystemOptimization() {
1033 | try {
1034 | Class> clazz = Class.forName("android.os.SystemProperties");
1035 | Method getMethod = clazz.getMethod("get", String.class, String.class);
1036 | String ctsValue = String.valueOf(getMethod.invoke(clazz, "ro.miui.cts", ""));
1037 | Method getBooleanMethod = clazz.getMethod("getBoolean", String.class, boolean.class);
1038 | return Boolean.parseBoolean(
1039 | String.valueOf(getBooleanMethod.invoke(clazz, "persist.sys.miui_optimization", !"1".equals(ctsValue))));
1040 | } catch (Exception ignored) {
1041 | // default implementation ignored
1042 | }
1043 | return true;
1044 | }
1045 |
1046 | /**
1047 | * 判断当前是否为 realmeUI(真我手机的系统)
1048 | */
1049 | public static boolean isRealmeUi() {
1050 | return sCurrentOsType == OS_TYPE_REALME_UI;
1051 | }
1052 |
1053 | /**
1054 | * 判断当前设备的厂商系统是否为 ColorOS( OPPO、一加手机的系统)
1055 | */
1056 | public static boolean isColorOs() {
1057 | return sCurrentOsType == OS_TYPE_COLOR_OS;
1058 | }
1059 |
1060 | /**
1061 | * 判断当前设备的厂商系统是否为 OriginOS( vivo 手机的系统)
1062 | */
1063 | public static boolean isOriginOs() {
1064 | return sCurrentOsType == OS_TYPE_ORIGIN_OS;
1065 | }
1066 |
1067 | /**
1068 | * 判断当前设备的厂商系统是否为 FuntouchOS(vivo 手机的老系统)
1069 | */
1070 | public static boolean isFuntouchOs() {
1071 | return sCurrentOsType == OS_TYPE_FUNTOUCH_OS;
1072 | }
1073 |
1074 | /**
1075 | * 判断当前是否为 MagicOs 或者 MagicUI(荣耀手机的系统)
1076 | */
1077 | public static boolean isMagicOs() {
1078 | return sCurrentOsType == OS_TYPE_MAGIC_OS;
1079 | }
1080 |
1081 | /**
1082 | * 判断当前设备的厂商系统是否为 HarmonyOS(华为手机、荣耀手机的系统)
1083 | */
1084 | public static boolean isHarmonyOs() {
1085 | return sCurrentOsType == OS_TYPE_HARMONY_OS;
1086 | }
1087 |
1088 | /**
1089 | * @deprecated 该 API 已经过时,随时会被删除,请尽早迁移到 {@link #isHarmonyOsNextAndroidCompatible()}
1090 | */
1091 | public static boolean isZytOnHarmonyOsNext() {
1092 | return isHarmonyOsNextAndroidCompatible();
1093 | }
1094 |
1095 | /**
1096 | * 判断是否在 HarmonyOS NEXT(纯血鸿蒙)的卓易通或者出境易环境上运行
1097 | */
1098 | public static boolean isHarmonyOsNextAndroidCompatible() {
1099 | return sCurrentOsType == OS_TYPE_HARMONY_OS_NEXT_ANDROID_COMPATIBLE;
1100 | }
1101 |
1102 | /**
1103 | * 判断当前设备的厂商系统是否为 EMUI 或者 EmotionUI(华为手机、荣耀手机的老系统)
1104 | */
1105 | public static boolean isEmui() {
1106 | return sCurrentOsType == OS_TYPE_EMUI;
1107 | }
1108 |
1109 | /**
1110 | * 判断当前设备的厂商系统是否为 OneUI(三星手机的系统)
1111 | */
1112 | public static boolean isOneUi() {
1113 | return sCurrentOsType == OS_TYPE_ONE_UI;
1114 | }
1115 |
1116 | /**
1117 | * 判断当前设备的厂商系统是否为 OxygenOS(一加手机的老系统,相当于 H2OS 的海外版)
1118 | */
1119 | public static boolean isOxygenOs() {
1120 | return sCurrentOsType == OS_TYPE_OXYGEN_OS;
1121 | }
1122 |
1123 | /**
1124 | * 判断当前设备的厂商系统是否为 H2OS(一加手机的老系统,相当于 OxygenOS 的国内版)
1125 | */
1126 | public static boolean isH2Os() {
1127 | return sCurrentOsType == OS_TYPE_H2_OS;
1128 | }
1129 |
1130 | /**
1131 | * 判断当前设备的厂商系统是否为 Flyme(魅族手机的系统)
1132 | */
1133 | public static boolean isFlyme() {
1134 | return sCurrentOsType == OS_TYPE_FLYME;
1135 | }
1136 |
1137 | /**
1138 | * 判断当前设备的厂商系统是否为 RedMagicOS(努比亚红魔手机的系统,努比亚红魔是中兴旗下的子品牌)
1139 | */
1140 | public static boolean isRedMagicOs() {
1141 | return sCurrentOsType == OS_TYPE_RED_MAGIC_OS;
1142 | }
1143 |
1144 | /**
1145 | * 判断当前设备的厂商系统是否为 NebulaAIOS(努比亚手机的系统)
1146 | */
1147 | public static boolean isNebulaAiOs() {
1148 | return sCurrentOsType == OS_TYPE_NEBULA_AIOS;
1149 | }
1150 |
1151 | /**
1152 | * 判断当前设备的厂商系统是否为 MyOS(中兴手机、努比亚手机的系统)
1153 | */
1154 | public static boolean isMyOs() {
1155 | return sCurrentOsType == OS_TYPE_MY_OS;
1156 | }
1157 |
1158 | /**
1159 | * 判断当前设备的厂商系统是否为 MifavorUI(中兴手机的老系统)
1160 | */
1161 | public static boolean isMifavorUi() {
1162 | return sCurrentOsType == OS_TYPE_MIFAVOR_UI;
1163 | }
1164 |
1165 | /**
1166 | * 判断当前设备的厂商系统是否为 SmartisanOS(锤子手机的系统)
1167 | */
1168 | public static boolean isSmartisanOs() {
1169 | return sCurrentOsType == OS_TYPE_SMARTISAN_OS;
1170 | }
1171 |
1172 | /**
1173 | * 判断当前设备的厂商系统是否为 EUI(乐视手机的系统)
1174 | */
1175 | public static boolean isEui() {
1176 | return sCurrentOsType == OS_TYPE_EUI_OS;
1177 | }
1178 |
1179 | /**
1180 | * 判断当前设备的厂商系统是否为 ZUXOS(联想手机、摩托罗拉手机的系统)
1181 | */
1182 | public static boolean isZuxOs() {
1183 | return sCurrentOsType == OS_TYPE_ZUX_OS;
1184 | }
1185 |
1186 | /**
1187 | * 判断当前设备的厂商系统是否为 ZUI(联想手机、摩托罗拉手机的老系统)
1188 | */
1189 | public static boolean isZui() {
1190 | return sCurrentOsType == OS_TYPE_ZUI;
1191 | }
1192 |
1193 | /**
1194 | * 判断当前设备的厂商系统是否为 nubiaUI(努比亚手机的老系统)
1195 | */
1196 | public static boolean isNubiaUi() {
1197 | return sCurrentOsType == OS_TYPE_NUBIA_UI;
1198 | }
1199 |
1200 | /**
1201 | * 判断当前设备的厂商系统是否为 ObricUI(豆包手机的系统)
1202 | */
1203 | public static boolean isObricUi() {
1204 | return sCurrentOsType == OS_TYPE_OBRIC_UI;
1205 | }
1206 |
1207 | /**
1208 | * 判断当前设备的厂商系统是否为 ROGUI(华硕手机的系统)
1209 | */
1210 | public static boolean isRogUi() {
1211 | return sCurrentOsType == OS_TYPE_ROG_UI;
1212 | }
1213 |
1214 | /**
1215 | * 判断当前设备的厂商系统是否为 360UI(360 手机的系统)
1216 | */
1217 | public static boolean is360Ui() {
1218 | return sCurrentOsType == OS_TYPE_360_UI;
1219 | }
1220 |
1221 | /**
1222 | * 获取当前设备的厂商系统名称
1223 | *
1224 | * @return 如果获取不到则返回空字符串
1225 | */
1226 | @NonNull
1227 | public static String getOsName() {
1228 | return sCurrentOsName != null ? sCurrentOsName : "";
1229 | }
1230 |
1231 | /**
1232 | * 获取经过美化的厂商系统版本名称
1233 | */
1234 | @NonNull
1235 | public static String getOsVersionName() {
1236 | return sCurrentOsVersionName != null ? sCurrentOsVersionName : "";
1237 | }
1238 |
1239 | /**
1240 | * 获取厂商系统版本的大版本号
1241 | *
1242 | * @return 如果获取不到则返回 -1
1243 | */
1244 | public static int getOsBigVersionCode() {
1245 | return extractBigVersionCodeByText(getOsVersionName());
1246 | }
1247 |
1248 | /**
1249 | * 通过系统属性获得最佳版本号
1250 | */
1251 | private static String getBestVersionNameBySystemProperties(@NonNull String... systemPropertyKeys) {
1252 | for (String propertyKey : systemPropertyKeys) {
1253 | String systemPropertyValue = SystemPropertyCompat.getSystemPropertyValue(propertyKey);
1254 | String versionName = extractVersionNameByText(systemPropertyValue);
1255 | if (!TextUtils.isEmpty(versionName)) {
1256 | return versionName;
1257 | }
1258 | }
1259 | for (String systemPropertyKey : systemPropertyKeys) {
1260 | String systemPropertyValue = SystemPropertyCompat.getSystemPropertyValue(systemPropertyKey);
1261 | int number = extractNumberByText(systemPropertyValue);
1262 | if (number > 0) {
1263 | return number + ".0";
1264 | }
1265 | }
1266 | return "";
1267 | }
1268 |
1269 | /**
1270 | * 从文本中获得最佳的版本号
1271 | */
1272 | @NonNull
1273 | private static String getBestVersionNameByText(@Nullable String text) {
1274 | String versionName = extractVersionNameByText(text);
1275 | if (!TextUtils.isEmpty(versionName)) {
1276 | return versionName;
1277 | }
1278 | int number = extractNumberByText(text);
1279 | if (number > 0) {
1280 | return number + ".0";
1281 | }
1282 | return "";
1283 | }
1284 |
1285 | /**
1286 | * 根据 OneUI 的版本号计算出来 OneUI 的版本号
1287 | */
1288 | @NonNull
1289 | private static String getOneUiVersionNameByVersionCode(int oneUiVersionCode) {
1290 | // OneUI 8.0:[ro.build.version.oneui]: [80000]
1291 | // OneUI 7.0:[ro.build.version.oneui]: [70000]
1292 | // OneUI 6.1:[ro.build.version.oneui]: [60101]
1293 | // OneUI 5.1.1:[ro.build.version.oneui]: [50101]
1294 | int oneVersion = oneUiVersionCode / 10000;
1295 | int twoVersion = oneUiVersionCode % 10000;
1296 | int threeVersion = oneUiVersionCode % 100;
1297 | if (threeVersion > 0) {
1298 | // OneUI 5.1.1 的版本号是 50101,计算出来的结果是 5.1.1
1299 | // OneUI 6.1 的版本号是 60101,计算出来的结果是 6.1.1
1300 | return oneVersion + "." + (twoVersion / 100) + "." + threeVersion;
1301 | } else {
1302 | // OneUI 8.0 的版本号是 80000,计算出来的结果是 8.0
1303 | return oneVersion + "." + (twoVersion / 100);
1304 | }
1305 | }
1306 |
1307 | /**
1308 | * 从文本提取版本号(只保留数字和点号)
1309 | */
1310 | @NonNull
1311 | private static String extractVersionNameByText(@Nullable String text) {
1312 | if (TextUtils.isEmpty(text)) {
1313 | return "";
1314 | }
1315 |
1316 | // 使用正则表达式匹配数字和点号组成的版本号
1317 | // 这里需要注意:因为是获取正则表达式的分组,所以需要在 Pattern.compile 时加上括号
1318 | // Github 地址:https://github.com/getActivity/DeviceCompat/pull/3
1319 | Pattern pattern = Pattern.compile("(" + REGEX_VERSION_NAME + ")");
1320 | Matcher matcher = pattern.matcher(text);
1321 |
1322 | if (matcher.find() && matcher.groupCount() > 0) {
1323 | String result = matcher.group(1);
1324 | if (result != null) {
1325 | return result;
1326 | }
1327 | }
1328 | return "";
1329 | }
1330 |
1331 | /**
1332 | * 从文本提取数字
1333 | *
1334 | * @return 如果获取不到则返回 0
1335 | */
1336 | private static int extractNumberByText(@Nullable String text) {
1337 | // 需要注意的是 华为畅享 5S Android 5.1 获取到的厂商版本号是 EmotionUI_3,而不是 3.1 或者 3.0 这种
1338 | // 这里需要注意:因为是获取正则表达式的分组,所以需要在 Pattern.compile 时加上括号
1339 | // Github 地址:https://github.com/getActivity/DeviceCompat/pull/3
1340 | Pattern pattern = Pattern.compile("(" + REGEX_NUMBER + ")");
1341 | Matcher matcher = pattern.matcher(text);
1342 |
1343 | if (matcher.find() && matcher.groupCount() > 0) {
1344 | String result = matcher.group(1);
1345 | if (result != null) {
1346 | try {
1347 | return Integer.parseInt(result);
1348 | } catch (Exception ignore) {
1349 | // default implementation ignored
1350 | }
1351 | }
1352 | }
1353 | return 0;
1354 | }
1355 |
1356 | /**
1357 | * 从文本提取大版本号
1358 | *
1359 | * @return 如果获取不到则返回 -1
1360 | */
1361 | private static int extractBigVersionCodeByText(@Nullable String text) {
1362 | if (text == null || text.isEmpty()) {
1363 | return -1;
1364 | }
1365 | String[] array = text.split("\\.");
1366 | if (array.length == 0) {
1367 | return -1;
1368 | }
1369 | try {
1370 | return Integer.parseInt(array[0]);
1371 | } catch (Exception e) {
1372 | // java.lang.NumberFormatException: Invalid int: "0 "
1373 | return -1;
1374 | }
1375 | }
1376 | }
--------------------------------------------------------------------------------